Tracking call frame usage
On the following pages, learn more about:
What do you want to do?
Read more about:
Call frame information overview
Call frame information (CFI) is information about the call frames. Typically, a call frame contains a return address, function arguments, saved register values, compiler temporaries, and local variables. Call frame information holds enough information about call frames to support two important features:
C-SPY can use call frame information to reconstruct the entire call chain from the current
PC(program counter) and show the values of local variables in each function in the call chain. This information is used, for example, in the Call Stack window.Call frame information can be used, together with information about possible calls for calculating the total stack usage in the application. Note that this feature might not be supported by the product you are using.
The compiler automatically generates call frame information for all C and C++ source code. Call frame information is also typically provided for each assembler routine in the system library. However, if you have other assembler routines and want to enable C-SPY to show the call stack when executing these routines, you must add the required call frame information annotations to your assembler source code. Stack usage can also be handled this way (by adding the required annotations for each function call), but you can also specify stack usage information for any routines in a stack usage control file (see The stack usage control file), which is typically easier.
Call frame information in more detail
You can add call frame information to assembler files by using cfi directives. You can use these to specify:
The start address of the call frame, which is referred to as the canonical frame address (CFA). There are two different types of call frames:
On a stack—stack frames. For stack frames the CFA is typically the value of the stack pointer after the return from the routine.
In static memory, as used in a static overlay system—static overlay frames. This type of call frame is not required by the RH850 microcontroller and is therefore not supported.
How to find the return address.
How to restore various resources, like registers, when returning from the routine.
When adding the call frame information for each assembler module, you must:
Provide a names block where you describe the resources to be tracked.
Provide a common block where you define the resources to be tracked and specify their default values. This information must correspond to the calling convention used by the compiler.
Annotate the resources used in your source code, which in practice means that you describe the changes performed on the call frame. Typically, this includes information about when the stack pointer is changed, and when permanent registers are stored or restored on the stack.
To do this you must define a data block that encloses a continuous piece of source code where you specify rules for each resource to be tracked. When the descriptive power of the rules is not enough, you can instead use CFI expressions.
A full description of the calling convention might require extensive call frame information. In many cases, a more limited approach will suffice. The recommended way to create an assembler language routine that handles call frame information correctly is to start with a C skeleton function that you compile to generate assembler output. For an example, see Creating skeleton codethe IAR C/C++ Development documentation.
Defining a names block
A names block is used for declaring the resources available for a processor. Inside the names block, all resources that can be tracked are defined.
Start and end a names block with the directives:
CFI NAMESnameCFI ENDNAMESname
where name is the name of the block.
Only one names block can be open at a time.
Inside a names block, four different kinds of declarations can appear—a resource declaration, a stack frame declaration, a static overlay frame declaration, and a base address declaration:
To declare a resource, use one of the directives:
CFI RESOURCEresource:bitsCFI VIRTUALRESOURCEresource:bitsThe parameters are the name of the resource and the size of the resource in bits. The name must be one of the register names defined in the Renesas ABI specification. A virtual resource is a logical concept, in contrast to a physical resource such as a processor register. Virtual resources are usually used for the return address.
To declare more than one resource, separate them with commas.
A resource can also be a composite resource, made up of at least two parts. To declare the composition of a composite resource, use the directive:
CFI RESOURCEPARTS
resourcepart, part, …The parts are separated with commas. The resource and its parts must have been previously declared as resources, as described above.
To declare a stack frame CFA, use the directive:
CFI STACKFRAME
cfa resource typeThe parameters are the name of the stack frame CFA, the name of the associated resource (the stack pointer), and the memory type (to get the address space). To declare more than one stack frame CFA, separate them with commas.
When going back in the call stack, the value of the stack frame CFA is copied into the associated stack pointer resource to get a correct value for the previous function frame.
Defining a common block
The common block is used for declaring the initial contents of all tracked resources. Normally, there is one common block for each calling convention used.
Start a common block with the directive:
CFI COMMONnameUSINGnamesblock
where name is the name of the new block and namesblock is the name of a previously defined names block.
Declare the return address column with the directive:
CFI RETURNADDRESSresourcetype
where resource is a resource defined in namesblock and type is the memory in which the calling function resides. You must declare the return address column for the common block.
Inside a common block, you can declare the initial value of a CFA or a resource by using the directives available for common blocks, see Call frame information directives for common blocks. For more information about how to use these directives, see Specifying rules for tracking resources and the stack depth and Using CFI expressions for tracking complex cases.
End a common block with the directive:
CFI ENDCOMMON namewhere name is the name used to start the common block.
Annotating your source code within a data block
The data block contains the actual tracking information for one continuous piece of code.
Start a data block with the directive:
CFI BLOCKnameUSINGcommonblock
where name is the name of the new block and commonblock is the name of a previously defined common block.
If the piece of code for the current data block is part of a defined function, specify the name of the function with the directive:
CFI FUNCTION labelwhere label is the code label starting the function.
If the piece of code for the current data block is not part of a function, specify this with the directive:
CFI NOFUNCTION
End a data block with the directive:
CFI ENDBLOCK namewhere name is the name used to start the data block.
Inside a data block, you can manipulate the values of the resources by using the directives available for data blocks, see Call frame information directives for data blocks. For more information on how to use these directives, see Specifying rules for tracking resources and the stack depth, and Using CFI expressions for tracking complex cases.
Specifying rules for tracking resources and the stack depth
To describe the tracking information for individual resources, two sets of simple rules with specialized syntax can be used:
Rules for tracking resources
CFIresource{ UNDEFINED | SAMEVALUE | CONCAT }CFIresource{resource| FRAME(cfa,offset) }Rules for tracking the stack depth (CFAs)
CFIcfa{ NOTUSED | USED }CFIcfa{resource|resource+constant|resource-constant}
You can use these rules both in common blocks to describe the initial information for resources and CFAs, and inside data blocks to describe changes to the information for resources or CFAs.
In those rare cases where the descriptive power of the simple rules are not enough, you can use a full CFI expression with dedicated operators to describe the information, see Using CFI expressions for tracking complex cases. However, whenever possible, you should always use a rule instead of a CFI expression.
Rules for tracking resources
The rules for resources conceptually describe where to find a resource when going back one call frame. For this reason, the item following the resource name in a CFI directive is referred to as the location of the resource.
To declare that a tracked resource is restored, in other words, already correctly located, use SAMEVALUE as the location. Conceptually, this declares that the resource does not have to be restored because it already contains the correct value. For example, to declare that a register R11 is restored to the same value, use the directive:
CFI R11 SAMEVALUE
To declare that a resource is not tracked, use UNDEFINED as location. Conceptually, this declares that the resource does not have to be restored (when going back one call frame) because it is not tracked. Usually it is only meaningful to use it to declare the initial location of a resource. For example, to declare that R11 is a scratch register and does not have to be restored, use the directive:
CFI R11 UNDEFINED
To declare that a resource is temporarily stored in another resource, use the resource name as its location. For example, to declare that a register R11 is temporarily located in a register R12 (and should be restored from that register), use the directive:
CFI R11 R12
To declare that a resource is currently located somewhere on the stack, use FRAME(cfa, offset) as location for the resource, where cfa is the CFA identifier to use as “frame pointer” and offset is an offset relative the CFA. For example, to declare that a register R11 is located at offset –4 counting from the frame pointer CFA_SP, use the directive:
CFI R11 FRAME(CFA_SP,-4)
For a composite resource there is one additional location, CONCAT, which declares that the location of the resource can be found by concatenating the resource parts for the composite resource. For example, consider a composite resource RET with resource parts RETLO and RETHI. To declare that the value of RET can be found by investigating and concatenating the resource parts, use the directive:
CFI RET CONCAT
This requires that at least one of the resource parts has a definition, using the rules described above.
Rules for tracking the stack depth (CFAs)
In contrast to the rules for resources, the rules for CFAs describe the address of the beginning of the call frame. The call frame often includes the return address pushed by the assembler call instruction. The CFA rules describe how to compute the address of the beginning of the current stack frame.
Each stack frame CFA is associated with a stack pointer. When going back one call frame, the associated stack pointer is restored to the current CFA. For stack frame CFAs, there are two possible rules—an offset from a resource (not necessarily the resource associated with the stack frame CFA) or NOTUSED.
To declare that a CFA is not used, and that the associated stack pointer should be tracked as a normal resource, use NOTUSED as the address of the CFA. For example, to declare that the CFA with the name CFA_SP is not used in this code block, use the directive:
CFI CFA_SP NOTUSED
To declare that a CFA has an address that is offset relative the value of a resource, specify the stack pointer and the offset. For example, to declare that the CFA with the name CFA_SP can be obtained by adding 4 to the value of the SP resource, use the directive:
CFI CFA_SP SP + 4
Using CFI expressions for tracking complex cases
You can use call frame information expressions (CFI expressions) when the descriptive power of the rules for resources and CFAs is not enough. However, you should always use a simple rule if there is one.
CFI expressions consist of operands and operators. Three sets of operators are allowed in a CFI expression:
Unary operators
Binary operators
Ternary operators
In most cases, they have an equivalent operator in the regular assembler expressions.
In this example, R12 is restored to its original value. However, instead of saving it, the effect of the two post increments is undone by the subtract instruction.
AddTwo:
cfi block addTwoBlock using myCommon
cfi function addTwo
cfi nocalls
cfi r12 samevalue
add @r12+, r13
cfi r12 sub(r12, 2)
add @r12+, r13
cfi r12 sub(r12, 4)
sub #4, r12
cfi r12 samevalue
ret
cfi endblock addTwoBlockFor more information about the syntax for using the operators in CFI expressions, see Call frame information directives for tracking resources and CFAs.
Stack usage analysis directives
The stack usage analysis directives (CFI FUNCALL, CFI TAILCALL, CFI INDIRECTCALL, and CFI NOCALLS) are used for building a call graph which is needed for stack usage analysis. These directives can be used only in data blocks. When the data block is a function block (in other words, when the CFI FUNCTION directive has been used in the data block), you should not specify a caller parameter. When a stack usage analysis directive is used in code that is shared between functions, you must use the caller parameter to specify which of the possible functions the information applies to.
The CFI FUNCALL, CFI TAILCALL, and CFI INDIRECTCALL directives must be placed immediately before the instruction that performs the call. The CFI NOCALLS directive can be placed anywhere in the data block.