https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120433

            Bug ID: 120433
           Summary: RISC-V:Unexpected Sign-Extension Behavior for uint32_t
                    Return Values in RV64
           Product: gcc
           Version: 14.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bigmagicreadsun at gmail dot com
  Target Milestone: ---

I encountered an inconsistency with sign-extension of uint32_t return values in
RV64 when comparing values in C code versus inline assembly. Below is a minimal
reproducer:

-march=rv64gc -mabi=lp64d  -O2

https://godbolt.org/z/qPbj1dWfz

#include <stdint.h>
#include <stdio.h>
#define TEST_NUM  0xFFFF0231
__attribute__((noinline)) uint32_t a0_in_asm() {
    asm volatile (
        "lui a0, 0xFFFF\n"
        "slli a0, a0, 0x4\n"
        "addi a0, a0, 0x231\n"
        "ecall\n"
    );
}
int main(void) {
    uint32_t res = a0_in_asm();
    uint32_t val = (res != TEST_NUM) ? 20 : 30;  // Fails unexpectedly
    printf("val is %d\n", val);  // Outputs 20 (unexpected)
    return 0;

Assembly Output:

a0_in_asm:
        lui a0, 0xFFFF
slli a0, a0, 0x4
addi a0, a0, 0x231
ecall

        ret
.LC0:
        .string "val is %d\n"
main:
        addi    sp,sp,-16
        sd      ra,8(sp)
        call    a0_in_asm
        li      a5,-65536
        addi    a5,a5,561
        li      a1,20
        bne     a0,a5,.L4
        li      a1,30
.L4:
        lui     a0,%hi(.LC0)
        addi    a0,a0,%lo(.LC0)
        call    printf
        ld      ra,8(sp)
        li      a0,0
        addi    sp,sp,16
        jr      ra

The function a0_in_asm loads 0xFFFF0231 into a0 (as expected):


However, the comparison res != TEST_NUM evaluates to true because:

res (from a0_in_asm) is not sign-extended: 0x00000000FFFF0231 (RV64 a0
post-call).
TEST_NUM is sign-extended to 0xFFFFFFFFFFFF0231 due to RISC-V calling
conventions (treating uint32_t as sign-extended).

The comparison in main shows:

li a5, -65536     # a5 = 0xFFFFFFFFFFFF0000
addi a5, a5, 561  # a5 = 0xFFFFFFFFFFFF0231 (sign-extended TEST_NUM)
bne a0, a5, .L4   # Mismatch due to lack of sign-extension in `a0`

Why does the toolchain not automatically sign-extend the uint32_t return value
from a0_in_asm, even though the calling convention mandates it? Is this a
compiler bug or intended behavior?

Reply via email to