10.7 Symbol references and branches into and out of inline assembly

Symbols that are defined in an inline assembly statement can only be referred to from the same inline assembly statement.

The compiler can optimize functions containing inline assembly, which can result in the removal or duplication of the inline assembly statements. To define symbols in assembly and use them elsewhere, use file-scope inline assembly, or a separate assembly language source file.

With the exception of function calls, it is not permitted to branch out of an inline assembly block, including branching to other inline assembly blocks. The optimization passes of the compiler assume that inline assembly statements only exit by reaching the end of the assembly block, and optimize the surrounding function accordingly.

It is valid to call a function from inside inline assembly, as that function will return control-flow back to the inline assembly code.

Arm does not recommend directly referencing global data or functions from inside an assembly block by using their names in the assembly string. Often such references appear to work, but the compiler does not know about the reference.

If the global data or functions are only referenced inside inline assembly statements, then the compiler might remove these global data or functions.

To prevent the compiler from removing global data or functions which are referenced from inline assembly statements, you can:

  • use __attribute__((used)) with the global data or functions.
  • pass the reference to global data or functions as operands to inline assembly statements.

Arm recommends passing the reference to global data or functions as operands to inline assembly statements so that if the final image does not require the inline assembly statements and the referenced global data or function, then they can be removed.

Example

static void foo(void) { /* ... */ }
				
// This function attempts to call the function foo from inside inline assembly.
// In some situations this may appear to work, but if foo is not referenced
// anywhere else (including if all calls to it from C got inlined), the
// compiler could remove the definition of foo, so this would fail to link.
void bar() {
	__asm volatile(
			"bl foo"
		: /* no outputs */
		: /* no inputs */
		: "r0", "r1", "r2", "r3", "r12", "lr");
}

// This function is the same as above, except it passes a reference to foo into
// the inline assembly as an operand. This lets the compiler know about the
// reference, so the definition of foo will not be removed (unless, the
// definition of bar_fixed can also be removed). In C++, this has the
// additional advantage that the operand uses the source name of the function,
// not the mangled name (_ZL3foov) which would have to be used if writing the
// symbol name directly in the assembly string.
void bar_fixed() {
	__asm volatile(
			"bl %[foo]"
		: /* no outputs */
		: [foo] "i" (foo)
		: "r0", "r1", "r2", "r3", "r12", "lr");
}
Non-ConfidentialPDF file icon PDF versionDUI0774J
Copyright © 2014–2017, 2019 Arm Limited or its affiliates. All rights reserved.