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.

The call graph is printed towards the top of the map file, after the linker options and build parameters. For this example, it might look something like the following.
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.

You will also see in the symbol table, printed at the end of the map file, the addresses assigned to the special linker-generated symbols used by the stack. For this program, it might look something like the following.
                                  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.