Bug 114130 - [11 Regression] RISC-V: `__atomic_compare_exchange` does not use sign-extended value for RV64
Summary: [11 Regression] RISC-V: `__atomic_compare_exchange` does not use sign-extende...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 13.2.0
: P3 normal
Target Milestone: 11.5
Assignee: Kito Cheng
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2024-02-27 13:42 UTC by MaxXing
Modified: 2024-04-12 08:06 UTC (History)
2 users (show)

See Also:
Host:
Target: riscv64
Build:
Known to work:
Known to fail:
Last reconfirmed: 2024-02-28 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description MaxXing 2024-02-27 13:42:30 UTC
GCC 13.2 does not generate sign-extension for value to be compared with the result of `lr.w` instruction in `__atomic_compare_exchange`: https://godbolt.org/z/nafKhPa1Y

Code:

```
void foo(uint32_t *p) {
    uintptr_t x = *(uintptr_t *)p;
    uint32_t e = !p ? 0 : (uintptr_t)p >> 1;
    uint32_t d = (uintptr_t)x;
    __atomic_compare_exchange(p, &e, &d, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
```

Assembly generated by `gcc -O3`:

```
foo:
        ld      a4,0(a0)
        srli    a5,a0,1
         1: lr.w a3,0(a0); bne a3,a5,1f; sc.w a2,a4,0(a0); bnez a2,1b; 1:
        ret
```

Which `a5` should be sign-extended, since the RISC-V ISA manual says `lr.w` returns a sign-extended value in RV64.

But `gcc -O3 -fno-delete-null-pointer-checks` generates correct code:

```
foo:
        ld      a4,0(a0)
        li      a5,0
        beq     a0,zero,.L2
        srli    a5,a0,1
        sext.w  a5,a5
.L2:
         1: lr.w a3,0(a0); bne a3,a5,1f; sc.w a2,a4,0(a0); bnez a2,1b; 1:
        ret
```

`gcc -O3 -fno-tree-ter`'s output is slight different, but also sign-extended.

`clang -O3` always generates correct code:

```
foo:                                    # @foo
        lw      a1, 0(a0)
        srli    a2, a0, 1
        sext.w  a2, a2
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        lr.w    a3, (a0)
        bne     a3, a2, .LBB0_3
        sc.w    a4, a1, (a0)
        bnez    a4, .LBB0_1
.LBB0_3:
        ret
```
Comment 1 Andrew Pinski 2024-02-28 05:14:22 UTC
foo(unsigned int*):
        srli    a5,a0,1
        ld      a4,0(a0)
        1:
        lr.w    a3,0(a0)
        bne     a3,a5,1f
        sc.w    a2,a4,0(a0)
        bnez    a2,1b
        1:
        ret

foo:
        ld      a4,0(a0)
        srli    a5,a0,1
         1: lr.w a3,0(a0); bne a3,a5,1f; sc.w a2,a4,0(a0); bnez a2,1b; 1:
        ret
Comment 2 GCC Commits 2024-02-29 03:05:42 UTC
The master branch has been updated by Kito Cheng <kito@gcc.gnu.org>:

https://gcc.gnu.org/g:fd07a29e39f5347d6cef3e7042a32306f97a1719

commit r14-9231-gfd07a29e39f5347d6cef3e7042a32306f97a1719
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Wed Feb 28 16:01:52 2024 +0800

    RISC-V: Fix __atomic_compare_exchange with 32 bit value on RV64
    
    atomic_compare_and_swapsi will use lr.w to do obtain the original value,
    which sign extends to DI.  RV64 only has DI comparisons, so we also need
    to sign extend the expected value to DI as otherwise the comparison will
    fail when the expected value has the 32nd bit set.
    
    gcc/ChangeLog:
    
            PR target/114130
            * config/riscv/sync.md (atomic_compare_and_swap<mode>): Sign
            extend the expected value if needed.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/pr114130.c: New.
    
    Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
Comment 3 GCC Commits 2024-04-11 01:21:22 UTC
The releases/gcc-13 branch has been updated by Kito Cheng <kito@gcc.gnu.org>:

https://gcc.gnu.org/g:fb6ec6df54317ed3f6e6f878b6ea29dbef6bfe2d

commit r13-8598-gfb6ec6df54317ed3f6e6f878b6ea29dbef6bfe2d
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Wed Feb 28 16:01:52 2024 +0800

    RISC-V: Fix __atomic_compare_exchange with 32 bit value on RV64
    
    atomic_compare_and_swapsi will use lr.w to do obtain the original value,
    which sign extends to DI.  RV64 only has DI comparisons, so we also need
    to sign extend the expected value to DI as otherwise the comparison will
    fail when the expected value has the 32nd bit set.
    
    gcc/ChangeLog:
    
            PR target/114130
            * config/riscv/sync.md (atomic_compare_and_swap<mode>): Sign
            extend the expected value if needed.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/pr114130.c: New.
    
    Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
    (cherry picked from commit fd07a29e39f5347d6cef3e7042a32306f97a1719)
Comment 4 GCC Commits 2024-04-11 06:52:48 UTC
The releases/gcc-12 branch has been updated by Kito Cheng <kito@gcc.gnu.org>:

https://gcc.gnu.org/g:d37be5c0413783c5459c5664b6ffb9f47acfca4e

commit r12-10319-gd37be5c0413783c5459c5664b6ffb9f47acfca4e
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Wed Feb 28 16:01:52 2024 +0800

    RISC-V: Fix __atomic_compare_exchange with 32 bit value on RV64
    
    atomic_compare_and_swapsi will use lr.w to do obtain the original value,
    which sign extends to DI.  RV64 only has DI comparisons, so we also need
    to sign extend the expected value to DI as otherwise the comparison will
    fail when the expected value has the 32nd bit set.
    
    gcc/ChangeLog:
    
            PR target/114130
            * config/riscv/sync.md (atomic_compare_and_swap<mode>): Sign
            extend the expected value if needed.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/pr114130.c: New.
    
    Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
    (cherry picked from commit fd07a29e39f5347d6cef3e7042a32306f97a1719)
Comment 5 GCC Commits 2024-04-12 08:06:02 UTC
The releases/gcc-11 branch has been updated by Kito Cheng <kito@gcc.gnu.org>:

https://gcc.gnu.org/g:cb68221c59e8f98e107bb5842d319bee3a66b8dc

commit r11-11317-gcb68221c59e8f98e107bb5842d319bee3a66b8dc
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Wed Feb 28 16:01:52 2024 +0800

    RISC-V: Fix __atomic_compare_exchange with 32 bit value on RV64
    
    atomic_compare_and_swapsi will use lr.w to do obtain the original value,
    which sign extends to DI.  RV64 only has DI comparisons, so we also need
    to sign extend the expected value to DI as otherwise the comparison will
    fail when the expected value has the 32nd bit set.
    
    gcc/ChangeLog:
    
            PR target/114130
            * config/riscv/sync.md (atomic_compare_and_swap<mode>): Sign
            extend the expected value if needed.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/pr114130.c: New.
    
    Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
    (cherry picked from commit fd07a29e39f5347d6cef3e7042a32306f97a1719)
Comment 6 Kito Cheng 2024-04-12 08:06:34 UTC
Fixed on trunk also backport to 11~13 branch.