ARM Technical Support Knowledge Articles

FUNCTIONS THAT ARE REENTRANT

Applies to: C51 C Compiler

Answer


Information in this article applies to:


QUESTION

How can I make a function reentrant without using the reentrant keyword?

ANSWER

Functions (including many library routines) written using the C51 compiler are typically NOT reentrant.

In C, a function may be passed arguments and may return a value to the caller. Many C compilers pass arguments on the stack and return values in registers.

Due to the limited size of the 8051 stack, the C51 compiler must pass arguments in fixed memory locations. The linker builds a call tree and overlays parts of memory that are mutually exclusive.

A function that is reentrant may be passed arguments and may return a value to the caller--just like a function that is non-reentrant. The difference is that a function that is reentrant does not use variables that can be overwritten by another instance (or simultaneous invocation) of that function. An example will help better illustrate this.

Suppose there is a function called nr_func. It takes 4 long (4-byte) arguments and returns a long.

long nr_func (long a, long b, long c, long d)
{
return (a+b+c+d);
}

The code generated by the compiler for this function is:

             ; FUNCTION nr_func (BEGIN)
0000 E500        R     MOV     A,a+03H
0002 2500        R     ADD     A,b+03H
0004 FF                MOV     R7,A
0005 E500        R     MOV     A,a+02H
0007 3500        R     ADDC    A,b+02H
0009 FE                MOV     R6,A
000A E500        R     MOV     A,a+01H
000C 3500        R     ADDC    A,b+01H
000E FD                MOV     R5,A
000F E500        R     MOV     A,a
0011 3500        R     ADDC    A,b
0013 FC                MOV     R4,A
0014 EF                MOV     A,R7
0015 2500        R     ADD     A,c+03H
0017 FF                MOV     R7,A
0018 EE                MOV     A,R6
0019 3500        R     ADDC    A,c+02H
001B FE                MOV     R6,A
001C ED                MOV     A,R5
001D 3500        R     ADDC    A,c+01H
001F FD                MOV     R5,A
0020 EC                MOV     A,R4
0021 3500        R     ADDC    A,c
0023 FC                MOV     R4,A
0024 EF                MOV     A,R7
0025 2500        R     ADD     A,d+03H
0027 FF                MOV     R7,A
0028 EE                MOV     A,R6
0029 3500        R     ADDC    A,d+02H
002B FE                MOV     R6,A
002C ED                MOV     A,R5
002D 3500        R     ADDC    A,d+01H
002F FD                MOV     R5,A
0030 EC                MOV     A,R4
0031 3500        R     ADDC    A,d
0033 FC                MOV     R4,A
0034         ?C0010:
0034 22                RET
             ; FUNCTION nr_func (END)

This generated code accesses arguments a, b, c, and d using fixed memory addresses. That makes this function non-reentrant. When the function is entered, the values for a, b, c, and d must have already been stored by the caller. In C51, the memory space for these arguments is fixed (these arguments are NOT passed on the stack).

If an interrupt occurs (while this function is executing) and the interrupt calls this function, the arguments a, b, c, and d will be overwritten. When the interrupt exits and returns control, the first instance of nr_func, the a, b, c, and d arguments will have been overwritten by the interrupt's call to this routine and the result will be corrupted.

If we create a function that only uses registers (and no fixed memory locations for variables), then we can rely on the ISR to save and restore the registers used. And, the function will be reentrant. For example:

int r_func (int a, int b)
{
return (a+b);
}

This function generates the following code:

             ; FUNCTION _r_func (BEGIN)
;---- Variable 'a' assigned to Register 'R6/R7' ----
;---- Variable 'b' assigned to Register 'R4/R5' ----
0000 EF                MOV     A,R7
0001 2D                ADD     A,R5
0002 FF                MOV     R7,A
0003 EE                MOV     A,R6
0004 3C                ADDC    A,R4
0005 FE                MOV     R6,A
0006 22                RET
             ; FUNCTION _r_func (END)

which uses only registers. As long as an interrupt saves these registers, this function may be reentered.

Note: Do not confuse a function that is reentrant with reentrant functions you declare using the reentrant keyword.

SEE ALSO

Article last edited on: 2004-06-25 11:36:12

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential