1

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...

Lance E.T. Compte
  • 932
  • 1
  • 11
  • 33
  • 1
    Does the error persist if you replace the `BASE+foo` with the actual value? – Jester May 24 '23 at 21:05
  • 2
    That said, since the address will be in a register anyway, try using `r`. You also want it to be in a register, otherwise your asm of `0(%[location])` would not work. – Jester May 24 '23 at 21:11
  • 1
    If you generate the assembly file created by the compiler, and send it to the assembler, you might get a more informative error message. – Lindydancer May 24 '23 at 21:34
  • @Jester, no. It remains the same. I'll try "r" and report back (likely tomorrow). Thank you for the advice! – Lance E.T. Compte May 24 '23 at 21:56
  • @Lindydancer, That's a good thought. I expected that `gcc` was telling me everything that `as`, but you might be right. – Lance E.T. Compte May 24 '23 at 21:57
  • 2
    Except this looks like a compiler error so you won't get any assembly file. – Jester May 24 '23 at 22:01
  • 1
    Sounds like you have mental model for constraints that doesn't match how GCC "thinks about" them. The type of the C variable (or in general, the expression) determines the type. This can even be a whole array, for values of array type (as in [How can I indicate that the memory \*pointed\* to by an inline ASM argument may be used?](https://stackoverflow.com/q/56432259)). The constraint tells the compiler what kind of asm location you want to let it pick from, e.g. immediate, register, memory, certain kinds of registers (some machines like m68k have some address registers and some data regs.) – Peter Cordes May 26 '23 at 02:33
  • 1
    See https://gcc.gnu.org/onlinedocs/gcc/Constraints.html - https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html documents `"p"` as *‘p’ in the `constraint` must be accompanied by `address_operand` as the predicate in the `match_operand`*. I think that's talking about GCC internals, which use the same constraint syntax in their .md machine-description files that teach GCC how to compile for a given ISA. I don't understand that, so I wouldn't use a `p` constraint in inline asm. On x86, I'd use an `"m"` constraint as an operand for `lea` (load effective address, asm equivalent of a C `&`) – Peter Cordes May 26 '23 at 02:38
  • 1
    It's not just that .md files and asm constraints use the same syntax, they literally use the same file to generate the docs, with IFDEFs to filter out things that "don't belong." Check out the [internals doc](https://gcc.gnu.org/onlinedocs/gccint/Simple-Constraints.html). Look familiar? But notice the part at the end about `instruction patterns` which is omitted from the inline asm docs. I cleaned up as much as I could back when I was working on this, but there's not a lot of info on what the constraints mean and where they're meant to be used. I may have missed one meant for 'internal.' Oops. – David Wohlferd May 26 '23 at 03:50

0 Answers0