I'm trying to read in the address from my c-pointer to a register using inline thumb assembly.
here's a reproducible:
static uint32_t volatile * volatile CurrentTaskStackPtr;
CurrentTaskStackPtr = (uint32_t *) 0x20000001;
__asm volatile("LDR R8, =%0" : : "i"(CurrentTaskStackPtr) );
Also, I'm trying to avoid changing the state of any 'context' registers, and that's why I use an immediate read.
I keep getting this error:
warning: 'asm' operand 0 probably does not match constraints
187 | __asm volatile("LDR R8, =%0" : : "i"(CurrentTaskStackPtr) );
I have tried using different constraint characters, but I'm not familiar enough with assembly to make any progress, any help would be appreciated.
Thanks.
"i"(CurrentTaskStackPtr)
is asking for the value of a C variable as an immediate. That can only work if the C variable is a compile-time constant, like uint32_t *const CurrentTaskStackPtr = ...
. But your variable is volatile
(separately from pointing to volatile
), so even with optimization enabled, you've forbidden the compiler from doing constant-propagation through the assignment to make "i"(0x20000001)
.
If you want the address, take the address, "i"(&CurrentTaskStackPtr)
.
Or better, ask the compiler to have the address in R8 for you, instead of putting an ldr
pseudo-instruction into your asm template, unless you need to control how it materializes the address or something.
register void *tmp_r8 asm("r8"); // forces "r" constraints to pick R8.
tmp_r8 = &CurrentTaskStackPtr;
asm("..."
: // outputs
: "r"(tmp_r8)
: "memory" // presumably you're going to deref that pointer
);
See also https://stackoverflow.com/tags/inline-assembly/info and ARM inline asm: exit system call with value read from memory for an example of how this compiles.
The GCC manual (https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html) is explicit that the only guaranteed behaviour of an register asm local variable is picking the specified register for inline asm
statements; it's not guaranteed to use that register at any other time.
See also How can I indicate that the memory *pointed* to by an inline ASM argument may be used? for more about why the "memory"
clobber is necessary. Probably best to use that instead of dummy input/output operands; you don't want the compiler trying to optimize around a context-switch anyway.
Trying to do context switching
You might have a better time writing a context-switch function as __attribute__((naked))
, so you don't have to worry about when the compiler accesses local vars relative to the stack pointer. Changing the stack pointer inside an asm
statement is not officially supported. (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#index-asm-clobbers - the clobber list shouldn't contain the stack pointer.)
Except in a naked
function, where you're writing the entire function's asm in a Basic Asm statement, and it can't inline into any callers since it's like __attribute__((noinline,noipa))
, or like writing it in a separate .s
file and just defining a prototype.