2020-06-11 18:37:25

by Nathan Huckleberry

[permalink] [raw]
Subject: [PATCH] riscv/atomic: Fix sign extension for RV64I

The argument passed to cmpxchg is not guaranteed to be sign
extended, but lr.w sign extends on RV64I. This makes cmpxchg
fail on clang built kernels when __old is negative.

To fix this, we just cast __old to long which sign extends on
RV64I. With this fix, clang built RISC-V kernels now boot.

Link: https://github.com/ClangBuiltLinux/linux/issues/867
Cc: [email protected]
Signed-off-by: Nathan Huckleberry <[email protected]>
---
arch/riscv/include/asm/cmpxchg.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h
index d969bab4a26b..262e5bbb2776 100644
--- a/arch/riscv/include/asm/cmpxchg.h
+++ b/arch/riscv/include/asm/cmpxchg.h
@@ -179,7 +179,7 @@
" bnez %1, 0b\n" \
"1:\n" \
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
- : "rJ" (__old), "rJ" (__new) \
+ : "rJ" ((long)__old), "rJ" (__new) \
: "memory"); \
break; \
case 8: \
@@ -224,7 +224,7 @@
RISCV_ACQUIRE_BARRIER \
"1:\n" \
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
- : "rJ" (__old), "rJ" (__new) \
+ : "rJ" ((long)__old), "rJ" (__new) \
: "memory"); \
break; \
case 8: \
@@ -270,7 +270,7 @@
" bnez %1, 0b\n" \
"1:\n" \
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
- : "rJ" (__old), "rJ" (__new) \
+ : "rJ" ((long)__old), "rJ" (__new) \
: "memory"); \
break; \
case 8: \
@@ -316,7 +316,7 @@
" fence rw, rw\n" \
"1:\n" \
: "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
- : "rJ" (__old), "rJ" (__new) \
+ : "rJ" ((long)__old), "rJ" (__new) \
: "memory"); \
break; \
case 8: \
--
2.27.0.278.ge193c7cf3a9-goog


2020-06-11 19:24:37

by Nick Desaulniers

[permalink] [raw]
Subject: Re: [PATCH] riscv/atomic: Fix sign extension for RV64I

On Thu, Jun 11, 2020 at 11:34 AM 'Nathan Huckleberry' via Clang Built
Linux <[email protected]> wrote:
>
> The argument passed to cmpxchg is not guaranteed to be sign
> extended, but lr.w sign extends on RV64I.

I had a hard time finding documentation on this sign extension. Is
lr.w just the atomic version of lw?

https://content.riscv.org/wp-content/uploads/2019/06/riscv-spec.pdf
pdf page 54, printed page 38 says:
The LW instruction loads a 32-bit value from memory and sign-extends
this to 64 bits before storing it in register rd for RV64I.

> This makes cmpxchg
> fail on clang built kernels when __old is negative.
>
> To fix this, we just cast __old to long which sign extends on
> RV64I. With this fix, clang built RISC-V kernels now boot.

Oh, indeed, nice! Thanks for digging into this issue, and sending the patch.
Tested-by: Nick Desaulniers <[email protected]> # QEMU boot, clang build

>
> Link: https://github.com/ClangBuiltLinux/linux/issues/867
> Cc: [email protected]
> Signed-off-by: Nathan Huckleberry <[email protected]>
> ---
> arch/riscv/include/asm/cmpxchg.h | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h
> index d969bab4a26b..262e5bbb2776 100644
> --- a/arch/riscv/include/asm/cmpxchg.h
> +++ b/arch/riscv/include/asm/cmpxchg.h
> @@ -179,7 +179,7 @@
> " bnez %1, 0b\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -224,7 +224,7 @@
> RISCV_ACQUIRE_BARRIER \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -270,7 +270,7 @@
> " bnez %1, 0b\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -316,7 +316,7 @@
> " fence rw, rw\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> --
--
Thanks,
~Nick Desaulniers

2020-06-18 22:42:19

by Palmer Dabbelt

[permalink] [raw]
Subject: Re: [PATCH] riscv/atomic: Fix sign extension for RV64I

On Thu, 11 Jun 2020 11:32:35 PDT (-0700), [email protected] wrote:
> The argument passed to cmpxchg is not guaranteed to be sign
> extended, but lr.w sign extends on RV64I. This makes cmpxchg
> fail on clang built kernels when __old is negative.
>
> To fix this, we just cast __old to long which sign extends on
> RV64I. With this fix, clang built RISC-V kernels now boot.
>
> Link: https://github.com/ClangBuiltLinux/linux/issues/867
> Cc: [email protected]
> Signed-off-by: Nathan Huckleberry <[email protected]>
> ---
> arch/riscv/include/asm/cmpxchg.h | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h
> index d969bab4a26b..262e5bbb2776 100644
> --- a/arch/riscv/include/asm/cmpxchg.h
> +++ b/arch/riscv/include/asm/cmpxchg.h
> @@ -179,7 +179,7 @@
> " bnez %1, 0b\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -224,7 +224,7 @@
> RISCV_ACQUIRE_BARRIER \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -270,7 +270,7 @@
> " bnez %1, 0b\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \
> @@ -316,7 +316,7 @@
> " fence rw, rw\n" \
> "1:\n" \
> : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
> - : "rJ" (__old), "rJ" (__new) \
> + : "rJ" ((long)__old), "rJ" (__new) \
> : "memory"); \
> break; \
> case 8: \

So we talked about this earlier, but just so everyone's one the same page: I
think this should be a compiler bug, but the spec doesn't define any of this
stuff well enough that it actually is. I'm sort of inclined to make it a
compiler bug, but I'm not sure if that's still possible and it requires a lot
more work. I'm writing up a bigger email, but it's been floating around for a
few days and I don't want to delay this on sorting out what our inline assembly
actually does.

I've put this on fixes.

Thanks!