aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/liblzma/simple/riscv.c30
1 files changed, 22 insertions, 8 deletions
diff --git a/src/liblzma/simple/riscv.c b/src/liblzma/simple/riscv.c
index 7b30da83..aabbb052 100644
--- a/src/liblzma/simple/riscv.c
+++ b/src/liblzma/simple/riscv.c
@@ -511,15 +511,29 @@ riscv_encode(void *simple lzma_attribute((__unused__)),
// be the same.
// Arithmetic right shift makes sign extension
- // trivial but C doesn't guarantee it for
- // signed integers so a fallback is provided
- // for portability.
+ // trivial but (1) it's implementation-defined
+ // behavior (C99/C11/C23 6.5.7-p5) and so is
+ // (2) casting unsigned to signed (6.3.1.3-p3).
+ //
+ // One can check for (1) with
+ //
+ // if ((-1 >> 1) == -1) ...
+ //
+ // but (2) has to be checked from the
+ // compiler docs. GCC promises that (1)
+ // and (2) behave in the common expected
+ // way and thus
+ //
+ // addr += (uint32_t)(
+ // (int32_t)inst2 >> 20);
+ //
+ // does the same as the code below. But since
+ // the 100 % portable way is only a few bytes
+ // bigger code and there is no real speed
+ // difference, let's just use that, especially
+ // since the decoder doesn't need this at all.
uint32_t addr = inst & 0xFFFFF000;
- if ((-1 >> 1) == -1)
- addr += (uint32_t)(
- (int32_t)inst2 >> 20);
- else
- addr += (inst2 >> 20)
+ addr += (inst2 >> 20)
- ((inst2 >> 19) & 0x1000);
addr += now_pos + (uint32_t)i;