The 56800E compilers build frames for hierarchies of function calls using the stack pointer register (SP) to locate the next available free X memory location in which to locate a function call’s frame information. There is usually no explicit frame pointer register. Normally, the size of a frame is fixed at compile time. The total amount of stack space required for incoming arguments, local variables, function return information, register save locations (including those in pragma interrupt functions) is calculated and the stack frame is allocated at the beginning of a function call.
Sometimes, you may need to modify the SP at runtime to allocate temporary local storage using inline assembly calls. This invalidates all the stack frame offsets from the SP used to access local variables, arguments on the stack, etc. With the User Stack Allocation feature, you can use inline assembly instructions (with some restrictions) to modify the SP while maintaining accurate local variable, compiler temps, and argument offsets, that is these variables can still be accessed since the compiler knows you have modified the stack pointer.
The User Stack Allocation feature is enabled with the #pragma check_inline_sp_effects [on|off|reset] pragma setting. The pragma may be set on individual functions. By default the pragma is off at the beginning of compilation of each file in a project.
Point 1 above is required when you think about an if-then-else type statement. If one branch of a decision point modifies the SP one way and the other branch modifies SP another way, then the value of the SP is run-time dependent, and the compiler is unable to determine where stack-based variables are located at run-time. To prevent this from happening, the User Stack Allocation feature traverses the control flow graph, recording the inline assembly SP modifications through all program paths. It then checks all control flow merge points to make sure that the SP has been modified consistently in each branch converging on the merge point. If not, a warning is emitted citing the inconsistency.
A single new pragma is defined. #pragma check_inline_sp_effects [on|off|reset] will generate a warning if the user specifies an inline assembly instruction which modifies the SP by a run-time dependent amount. If the pragma is not specified, then stack offsets used to access stack-based variables will be incorrect. It is the user’s responsibility to enable #pragma check_inline_sp_effects, if they desire to modify the SP with inline assembly and access local stack-based variables. This pragma has no effect in function level assembly functions or separate assembly only source files (.asm files).
In general, inline assembly may be used to create arbitrary flow graphs and not all can be detected by the compiler.
For example:
REP #3 ADDA #2,SP
This example would modify the SP by three, but the compiler would only see a modification of one. Other cases such as these might be created by the user using inline jumps or branches. These are dangerous constructs and are not detected by the compiler.
In cases where the SP is modified by a run-time dependent amount, a warning is issued.
#define EnterCritical() { asm(adda #2,SP);\
asm(move.l SR,X:(SP)+); \
asm(bfset #0x0300,SR); \
asm(nop); \
asm(nop);}
#define ExitCritical() { asm(deca.l SP);\
asm(move.l x:(SP)-,SR); \
asm(nop);\
asm(nop);}
#pragma check_inline_sp_effects on
int func()
{
int a=1, b=1, c;
EnterCritical();
c = a+b;
ExitCritical();
}
This case will work because there are no control flow merge points. SP is modified consistently along all paths from the beginning to the end of the function and is properly aligned.
#define EnterCritical() { asm(adda #2,SP);\
asm(move.l SR,X:(SP)+); \
asm(bfset #0x0300,SR); \
asm(nop); \
asm(nop);}
#define ExitCritical() { asm(deca.l SP);\
asm(move.l x:(SP)-,SR); \
asm(nop);\
asm(nop);}
#pragma check_inline_sp_effects on
int func()
{
int a=1, b=1, c;
if (a)
{
EnterCritical();
c = a+b;
}
else {
c = b++;
}
ExitCritical();
return (b+c);
}
This example will generate the following warning because the SP entering the "ExitCritical" macro is different depending on which branch is taken in the if. Therefore, accesses to variables a, b, or c may not be correct.
Warning : Inconsistent inline assembly modification of SP in this function. M56800E_main.c line 29 ExitCritical();
#define EnterCritical() { asm(adda R0,SP);\
asm(move,l SR,X:(SP)+); \
asm(bfset #0x0300,SR); \
asm(nop); \
asm(nop);}
#define ExitCritical() { asm(deca.l SP);\
asm(move.l X:(SP)-,SR); \
asm(nop);\
asm(nop);}
#pragma check_inline_sp_effects on
int func()
{
int a=1, b=1, c;
if (a)
{
EnterCritical();
c = a+b;
}
else {
EnterCritical();
c = b++;
}
return (b+c);
}
This example will generate the following warning:
Warning : Cannot determine SP modification value at compile time M56800E_main.c line 20 EnterCritical();
This example is not legal since the SP is modified by run-time dependent amount.
If all inline assembly modifications to the SP along all branches are equal approaching the exit of a function, it is not necessary to explicitly deallocate the increased stack space. The compiler "cleans up" the extra inline assembly stack allocation automatically at the end of the function.
#pragma check_inline_sp_effects on
int func()
{
int a=1, b=1, c;
if (a)
{
EnterCritical();
c = a+b;
}
else {
EnterCritical();
c = b++;
}
return (b+c);
}
This example does not need to call the "ExitCritical" macro because the compiler will automatically clean up the extra inline assembly stack allocation.