Why does 64 bit RISC-V GCC sign-extends unsigned value after load?

Viewed 95

GCC and Clang generate different code for this snippet:

extern volatile unsigned  WATCHDOG;

void reset_watchdog() {
    unsigned t = WATCHDOG;
    WATCHDOG  = t;
}

Consider WATCHDOG as a memory-mapped I/O register.

When compiling with -O3 Clang 14 generates pretty straight forward code:

reset_watchdog():
        lui     a0, %hi(WATCHDOG)
        lw      a1, %lo(WATCHDOG)(a0)
        sw      a1, %lo(WATCHDOG)(a0)
        ret

However, GCC 10.0.2 inserts a superfluous sign-extend instruction after the load:

reset_watchdog():
        lui     a4,%hi(WATCHDOG)
        lw      a5,%lo(WATCHDOG)(a4)
        sext.w  a5,a5
        sw      a5,%lo(WATCHDOG)(a4)
        ret

See also: https://godbolt.org/z/zK1ss3Wsz

The sext.w is superfluous in two ways:

  1. lw already sign-extends the destination register under RV64I (lwu would zero-extend the destination, instead)
  2. for the sw instruction the content of the 4 most-significant bytes of the source register is irrelevant

So why does GCC generate such (suboptimal?) code?


GCC gets even more interesting when the function returns the original watchdog value:

extern volatile unsigned  WATCHDOG;

unsigned long reset_watchdog() {
    unsigned long t = WATCHDOG;

    WATCHDOG  = t;
    return t;
}

As expected, Clang basically just replaces the lw instruction with lwu to avoid the sign extension:

reset_watchdog():
        lui     a1, %hi(WATCHDOG)
        lwu     a0, %lo(WATCHDOG)(a1)
        sw      a0, %lo(WATCHDOG)(a1)
        ret

While GCC generates more convoluted code:

reset_watchdog():
        lui     a5,%hi(WATCHDOG)
        lw      a0,%lo(WATCHDOG)(a5)
        sext.w  a0,a0
        sw      a0,%lo(WATCHDOG)(a5)
        slli    a0,a0,32
        srli    a0,a0,32
        ret

Basically GCC really sticks with sign-extending the loaded 4 byte value and thus has to emit two additional instruction to undo a possible extension and clear the most significant 4 bytes. It's as if GCC doesn't know that RV64 has the lwu instruction and that lw already sign-extends the destination.

cf. https://godbolt.org/z/zW9qTzdWh

0 Answers
Related