Machine-specific features


Pragma directives

Pragmas are recognised by the compiler in two forms:

#pragma -LetterOptional digit
#pragma [no]feature-name

A short-form pragma given without a digit resets that pragma to its default state; otherwise to the state specified.

For example:

#pragma -s1
#pragma nocheck_stack

#pragma -p2
#pragma profile_statements

The list of recognised pragmas is:

---------------------------------------------------------
Pragma Name               |Short Form |`no' Form         
---------------------------------------------------------
warn_implicit_fn_decls    |a1 *       |a0                
---------------------------------------------------------
check_memory_accesses     |c1         |c0 *              
---------------------------------------------------------
warn_deprecated           |d1 *       |d0                
---------------------------------------------------------
continue_after_hash_error |e1         |e0 *              
---------------------------------------------------------
FP register variable      |f1-f4      |f0 *              
---------------------------------------------------------
include_only_once         |i1         |i0 *              
---------------------------------------------------------
optimise_crossjump        |j1 *       |j0                
---------------------------------------------------------
optimise_multiple_loads   |m1 *       |m0                
---------------------------------------------------------
profile                   |p1         |p0 *              
---------------------------------------------------------
profile_statements        |p2         |p0 *              
---------------------------------------------------------
integer register variable |r1-r7      |r0 *              
---------------------------------------------------------
check_stack               |s0 *       |s1                
---------------------------------------------------------
force_top_level           |t1         |t0 *              
---------------------------------------------------------
check_printf_formats      |v1         |v0 *              
---------------------------------------------------------
check_scanf_formats       |v2         |v0 *              
---------------------------------------------------------
side_effects              |y0 *       |y1                
---------------------------------------------------------
optimise_cse              |z1 *       |z0                
---------------------------------------------------------

In each case, the default setting is starred.

Specifying pragmas from the command line

Any pragma can be specified from the compiler's command line using:

-zpLetterDigit

Certain of the pragmas give more local control over what can be controlled per compilation unit, from the command line. For example:

--------------------------------------------------------
Pragma Name                |Command Line Form           
--------------------------------------------------------
nowarn_implicit_fn_decls   |-Wf                         
--------------------------------------------------------
nowarn_deprecated          |-Wd                         
--------------------------------------------------------
profile                    |-p                          
--------------------------------------------------------
profile_statements         |-px                         
--------------------------------------------------------

Pragmas controlling the preprocessor

The pragma continue_after_hash_error in effect implements a #warning "..." preprocessor directive. Pragma include_only_once asserts that the containing #include file is to be included only once, and that if its name recurs in a subsequent #include directive then the directive is to be ignored.

Pragma force_top_level asserts that the containing #include file should only be included at the top level of a file. A syntax error will result if the file is included, say, within the body of a function.

Pragmas controlling printf/scanf argument checking

Pragmas check_printf_formats and check_scanf_formats control whether the actual arguments to printf and scanf, respectively, are type-checked against the format designators in a literal format string. Of course, calls using non-literal format strings cannot be checked. By default, all calls involving literal format strings are checked.

Pragmas controlling optimisation

Pragmas optimise_crossjump, optimise_multiple_loads and optimise_cse give fine control over where these optimisations are applied. For example, it is sometimes advantageous to disable cross-jumping (the 'common tail' optimisation) in the critical loop of an interpreter; and it may be helpful in a timing loop to disable common subexpression elimination and the opportunistic optimisation of multiple load instructions to load multiples. Note that correct use of the volatile qualifier should remove most of the more obvious needs for this degree of control (and volatile is also available in the ARM C compiler's -pcc mode unless -strict is specified).

By default, functions are assumed to be impure, so function invocations are not candidates for common subexpression elimination. Pragma noside_effects asserts that the following function declarations (until the next #pragma side_effects) describe pure functions, invocations of which can be CSEs. See also __pure.

Pragmas Controlling Code Generation

If the compiler is configured to compile code for the explicit stack limit variant of the ARM Procedure Call Standard (documented in ARM Procedure Call Standard), then #pragma nocheck_stack disables the generation of code at function entry which checks for stack limit violation. In reality there is little advantage to turning off this check: it typically costs only two instructions and two machine cycles per function call. The one circumstance in which nocheck_stack must be used is in writing a signal handler for the SIGSTAK event. When this occurs, stack overflow has already been detected, so checking for it again in the handler would result in a fatal circular recursion.

The pragma check_memory_accesses instructs the compiler to precede each access to memory by a call to the appropriate one of:

__rt_rd?chk   (?=1,2,4 for byte, short, long reads, respectively) 
__rt_wr?chk   (?=1,2,4 for byte, short, long writes, respectively)

It is up to your library implementation to check that the address given is reasonable.

The pragmas f0-f4 and r0-r7 have no long form counterparts. Each introduces or terminates a list of extern, file-scope variable declarations. Each such declaration declares a name for the same register variable. For example:

#pragma r1                    /* 1st global register */
extern int *sp;
#pragma r2                    /* 2nd global register */
extern int *fp, *ap;                    /* synonyms */
#pragma r0                    /* end of global declaration */
#pragma f1
extern double pi;                    /* 1st global FP register */
#pragma f0

Any type that can be allocated to a register (see Registers), can be allocated to a global register. Similarly, any floating point type can be allocated to a floating point register variable.

Global register r1 is the same as register v1 in the ARM Procedure Call Standard (APCS); similarly r2 equates to v2, etc. Depending on the APCS variant, between 5 and 7 integer registers (v1-v7, machine registers R4-R10) and 4 floating point registers (F4-F7) are available as register variables. In practice it is probably unwise to use more than 3 global integer register variables and 2 global floating-point register variables.

Provided the same declarations are made in each separate compilation unit, a global register variable may exist program-wide.

Otherwise, because a global register variable maps to a callee-saved register, its value will be saved and restored across a call to a function in a compilation unit which does not use it as a global register variable, such as a library function.

A corollary of the safety of direct calls out of a global-register-using compilation unit, is that calls back into it are dangerous. In particular, a global-register-using function called from a compilation unit which uses that register as a compiler-allocated register, will probably read the wrong values from its supposed global register variables.

Currently, there is no link-time check that direct calls are sensible. And even if there were, indirect calls via function arguments pose a hazard which is harder to detect. This facility must be used with care. Preferably, the declaration of the global register variable should be made in each compilation unit of the program. See also __global_reg(n).

Special function declaration keywords

Several special function declaration options are available to tell armcc to treat that function in a special way. None of these are portable to other C compilers.

__value_in_regs

This allows the compiler to return a structure in registers rather than returning a pointer to the structure. eg.

typedef struct int64_structt {
  unsigned int lo;
  unsigned int hi;
} int64;

__value_in_regs extern int64 mul64(unsigned a, unsigned b);

See ARM Procedure Call Standard for details of the default way in which structures are passed and returned.

__swi and __swi_indirect

A SWI taking up to four arguments (in registers 0 to argcount-1) and returning up to four results (in registers 0 to resultcount-1) can be described by a C function declaration, which causes uses of the function to be compiled inline as a SWI SWI. For a SWI returning 0 results use:

void __swi(swi_number) swi_name(int arg1, ..., int argn);

for example

void __swi(42) terminate_process(int arg1, ..., int argn);

For a swi returning 1 result, use:

int __swi(swi_number) swi_name(int arg1, ..., int argn);

For a swi returning more than 1 result

struct { int res1, ... resn }
  __value_in_regs
    __swi(swi_number) swi_name(int arg1, ... int argn);

Note that __value_in_regs is needed to specify that a (short) structure value is returned in registers, rather than by the usual indirection mechanism specified in the ARM Procedure Call Standard.

If there is an indirect SWI (taking the number of a SWI to call as an argument in r12), calls through this SWI can similarly be described by a C function declaration such as:

int __swi_indirect(swi_indirect_number)
    swi_name(int real_swi_number, int arg1, ... argn);

For example,

int __swi_indirect(0) ioctl(int swino, int fn, void *argp);

This might be called as:

ioctl(IOCTL+4, RESET, NULL);

__irq

This allows a C function to be used as an interrupt routine. All registers (excluding floating point registers) are preserved (not just those normally presefved under the APCS). Also the function is exited by setting the pc to lr-4 and the psr is set to its original value.

__pure

By default, functions are assumed to be impure (ie they have side effects), so function invocations are not candidates for common subexpression elimination. __pure has the same effect as pragma noside_effects, and asserts that the function declared is a pure function, invocations of which can be CSEs.

Special variable declaration keywords

__global_reg(n)

Allocates the declared variable to a global integer register variable, in the same way as #pragma rn. The variable must have an integral or pointer type. See also Pragmas Controlling Code Generation.

__global_freg(n)

Allocates the declared variable to a global floating point regioster variable, in the same way as #pragma fn. The variable must have type float or double. See also Pragmas Controlling Code Generation.

Note that the global register, whether specified by keyword or pragmas, musr be specified in all declarations of the same variable. Thus

int x;
__global_reg(1) x;

is an error.