6.2 Compiler Stack Allocation
When building your program, the linker processes all the FN-type directives
in your program, generates the program's call graph, creates the symbols used by the
stack objects (those that have FNSIZE
directives), and allocates memory
for these objects, overlapping them where possible. You can see the result of this
process in the map file.
Call graph: (fully expanded)
*main size 0,4 offset 0
* add size 4,0 offset 4
incr size 2,0 offset 4
Indentation is used to indicate call depth. In the above, you can see that
main
calls add
and main
also
calls incr
. The size of the memory allocated for the routine's
parameter and auto-style objects is printed, followed by the auto-parameter block (APB)
offset into the compiled stack. Note that the offset is not an address; just a relative
position in the stack.
Notice in the call graph that the offset of the APB for add
and
incr
are identical. This implies that the memory they use is
shared, which is possible because add
and incr
do not
call each other in the code and so are never active at the same time.
A star, *
, before a routine name indicates that the APB memory used by
that routine is at a unique location and contributes to the total size of the program's
RAM usage. These routines are critical path nodes in the call graph. Reducing the size
of the APB used by these routines will reduce the total amount of data memory used by
the program. The memory blocks used by unstarred routines totally overlap with blocks
from other routines and do not contribute to the program's total data memory usage.
The -mcallgraph
option can be used to customize what call graph
information is displayed in the map file. Using -mcallgraph=crit
, for
example, will display only the nodes on critical paths, that is, all of the routines in
the graph that are starred.
The advantage of using a compiled stack is obvious in this example: Although the program needed a total of 10 bytes of local storage, only 8 bytes needed to be allocated memory, with 2 bytes being reused. And this memory reduction was done without the programmer having to employ the dangerous practice of sharing objects between routines.
It is important to note that incorrect overlapping of APBs can occur if the information
in the call graph is not accurate because of missing or erroneous FN-type directives in
your program. Consider placing the FNCALL
directive associated with a
call immediately before the call instruction itself, so you can clearly see if one is
missing. The FN-type directives can, however, be placed anywhere in the file, as they do
not contribute to the hex file, and there is no harm in the same directive being
repeated.
Symbol Table
?au_main udata_acs 000000 ?pa_add udata_acs 000004
?pa_incr udata_acs 000004 __Hcode code 000008
...
incval udata_acs 000002 result udata_acs 000000
Unlike the values printed for the offset in the call graph, the values in the symbol
table are always absolute addresses. In this example, the udata_acs
psect was linked to address 0, so the memory used by main
for
auto-style objects (?au_main
) begins at address 0 and is shown to be in
the udata_acs
psect (as we specified). The memory used by
add
for its parameters (?pa_add
) begins at address
4, as does the block used by incr
(?pa_incr
). The two
equated symbols (result
and incval
) are also shown in
this table.