Please consider the example from Figure 8.1 in Section 8.2 of the RISC-V Unprivileged Specification...
cas:
lr.w t0, (a0) # Load original value.
bne t0, a1, fail # Doesn’t match, so fail.
sc.w t0, a2, (a0) # Try to update.
bnez t0, cas # Retry if store-conditional failed.
li a0, 0 # Set return to success.
jr ra # Return.
fail:
li a0, 1 # Set return to failure.
jr ra # Return.
Here's a slight variation (in inline assembly, using hardcoded values) of this approach...
#define BASE 0x8000000000ULL
void foo(void) {
asm volatile(
"1: \n\t"
" lr.w t0, 0(%[location]) \n\t"
" bne t0, %[expected], 2f \n\t" // mismatch
" sc.w t0, %[desired], 0(%[location])\n\t"
" bnez t0, 1b \n\t" // retry
" li t0, 0 \n\t" // success
" j 3f \n\t"
"2: \n\t"
" li t0, 1 \n\t" // fail
"3: \n\t"
" sw t0, 0(%[result]) \n\t"
:
: [result] "p" ((uint32_t*)(BASE+0x013823dc)),
[location] "p" ((uint32_t*)(BASE+0x29a5501c)),
[expected] "r" (0x581595cf),
[desired] "r" (0xace52647)
: "t0", "memory"
);
}
(Here that code is on Godbolt where you can use different gcc/clang versions to compile it).
My gcc is: riscv64-unknown-elf-gcc (SiFive GCC 8.3.0-2019.08.0) 8.3.0
.
My command line is this:
/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-centos6/bin/riscv64-unknown-elf-gcc \
-ggdb3 \
-std=gnu99 \
-Wall \
-Wextra \
-Werror \
-Wno-implicit-fallthrough \
-fdata-sections \
-ffunction-sections \
-O0 \
-nostartfiles \
--specs=nano.specs \
-Wl,--gc-sections \
-march=rv64gc \
-mabi=lp64d \
-mcmodel=medany \
-malign-data=natural \
-mriscv-attribute \
foo.c \
start.riscv64.o \
-o foo.elf \
-T./link.riscv64.ld \
;
That works! Thousands of these instructions on many cores, intermixed with all sorts of other interesting things all work great!
HOWEVER, when I change -O0
to any of -O1
, -Os
, -Og
, -O2
, etc. then I see this error similar to this:
foo.c:99:7: error: invalid 'asm': invalid expression as operand
asm volatile(
^~~
Like I said, there are thousands of these instructions in the actual C file, but only one or two of them sees this error.
Godbolt never complains. I cannot reproduce this at Godbolt.
What could be the cause of this? Is it a gcc issue?
Should I use input operand constraint "p"
or "A"
or something else?
EDIT:
Based on a suggestion from @Jester, I changed the operand constraint from "p" to "r" and everything started working as I expect (Different optimization levels yield the same result.)
I regret I have only one upvote for them.
I really hate these constraints. I tried "A", before settling on "p" initially. To me, each constraint should match to a specific datatype. For example, maybe "p" wants "a pointer", but maybe "r" wants a "uint64_t" (on RV64). It doesn't work like that. There's always so much trial and error...