]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[RISC-V][PR target/116283] Fix split code for recent Zbs improvements with masked...
authorJeff Law <jlaw@ventanamicro.com>
Fri, 9 Aug 2024 23:46:01 +0000 (17:46 -0600)
committerJeff Law <jlaw@ventanamicro.com>
Fri, 9 Aug 2024 23:46:01 +0000 (17:46 -0600)
So Patrick's fuzzer found an interesting little buglet in the Zbs improvements
I added a couple months back.

Specifically when we have masked bit position for a Zbs instruction.  If the
mask has extraneous bits set we'll generate an unrecognizable insn due to an
invalid constant.

More concretely, let's take this pattern:

> (define_insn_and_split ""
>   [(set (match_operand:DI 0 "register_operand" "=r")
>         (any_extend:DI
>          (ashift:SI (const_int 1)
>                     (subreg:QI                        (and:DI (match_operand:DI 1 "register_operand" "r")
>                               (match_operand 2 "const_int_operand")) 0))))]
What we need to know to transform this into bset for rv64.

After masking the shift count we want to know the low 5 bits aren't 0x1f.  If
they were 0x1f, then the constant generated would be 0x80000000 which would
then need sign extension out to 64bits, which the bset instruction will not do
for us.

We can ignore anything outside the low 5 bits.  The mode of the shift is SI, so
shifting by 32+ bits is undefined behavior.

It's also worth explicitly mentioning that the hardware is going to mask the
count against 0x3f.

The net is if (operands[2] & 0x1f) != 0x1f, then this transformation is safe.
So onto the generated split code...

>   [(set (match_dup 0) (and:DI (match_dup 1) (match_dup 2)))
>    (set (match_dup 0) (zero_extend:DI (ashift:SI
>                                      (const_int 1)
>                                      (subreg:QI (match_dup 0) 0))))]

Which would seemingly do exactly what we want.   The problem is the first split
insn.  If the constant does not fit into a simm12, that insn won't be
recognized resulting in the ICE.

The fix is simple, we just need to mask the constant before generating RTL.  We
can just mask it against 0x1f since we only care about the low 5 bits.

This affects multiple patterns.  I've added the appropriate fix to all of them.

Tested in my tester.  Waiting for the pre-commit bits to run before pushing.

PR target/116283
gcc/
* config/riscv/bitmanip.md (Zbs combiner patterns/splitters): Mask the
bit position in the split code appropriately.

gcc/testsuite/

* gcc.target/riscv/pr116283.c: New test

gcc/config/riscv/bitmanip.md
gcc/testsuite/gcc.target/riscv/pr116283.c [new file with mode: 0644]

index b19295cd942480c27fa78dbc0f682eceaba4d9db..6872ee56022c285fd6e3ae90034013e27f9d5351 100644 (file)
     (set (match_dup 3) (and:DI (not:DI (match_dup 1)) (match_dup 3)))
     (set (match_dup 0) (zero_extend:DI
                         (ashift:SI (const_int 1) (match_dup 4))))]
-  { operands[4] = gen_lowpart (QImode, operands[3]); }
+{
+  operands[4] = gen_lowpart (QImode, operands[3]);
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+}
   [(set_attr "type" "bitmanip")])
 
 (define_insn_and_split ""
    (set (match_dup 0) (zero_extend:DI (ashift:SI
                                     (const_int 1)
                                     (subreg:QI (match_dup 0) 0))))]
-  { }
+  { operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); }
   [(set_attr "type" "bitmanip")])
 
 ;; Similarly two patterns for IOR/XOR generating bset/binv to
    [(set (match_dup 4) (match_dup 2))
     (set (match_dup 4) (and:DI (not:DI (match_dup 1)) (match_dup 4)))
     (set (match_dup 0) (any_or:DI (ashift:DI (const_int 1) (match_dup 5)) (match_dup 3)))]
-  { operands[5] = gen_lowpart (QImode, operands[4]); }
+{
+  operands[5] = gen_lowpart (QImode, operands[4]);
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+}
   [(set_attr "type" "bitmanip")])
 
 (define_insn_and_split ""
   "&& reload_completed"
    [(set (match_dup 4) (and:DI (match_dup 1) (match_dup 2)))
     (set (match_dup 0) (any_or:DI (ashift:DI (const_int 1) (subreg:QI (match_dup 4) 0)) (match_dup 3)))]
-  { }
+  { operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); }
   [(set_attr "type" "bitmanip")])
 
 ;; Similarly two patterns for AND generating bclr to
    [(set (match_dup 4) (match_dup 2))
     (set (match_dup 4) (and:DI (not:DI (match_dup 1)) (match_dup 4)))
     (set (match_dup 0) (and:DI (rotate:DI (const_int -2) (match_dup 5)) (match_dup 3)))]
-  { operands[5] = gen_lowpart (QImode, operands[4]); }
+{
+  operands[5] = gen_lowpart (QImode, operands[4]);
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+}
   [(set_attr "type" "bitmanip")])
 
 
   "&& reload_completed"
    [(set (match_dup 4) (and:DI (match_dup 1) (match_dup 2)))
     (set (match_dup 0) (and:DI (rotate:DI (const_int -2) (match_dup 5)) (match_dup 3)))]
-  { operands[5] = gen_lowpart (QImode, operands[4]); }
+{
+  operands[5] = gen_lowpart (QImode, operands[4]);
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+}
   [(set_attr "type" "bitmanip")])
 
 (define_insn "*bset<mode>_1_mask"
diff --git a/gcc/testsuite/gcc.target/riscv/pr116283.c b/gcc/testsuite/gcc.target/riscv/pr116283.c
new file mode 100644 (file)
index 0000000..c9ff336
--- /dev/null
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=gnu99 -w -march=rv64id_zbs -mabi=lp64d" } */
+
+char c;
+#define d(a, b)                                                                \
+  {                                                                            \
+    __typeof__(a) e = a;                                                       \
+    e;                                                                         \
+  }
+long f;
+void g(signed h[][9][9][9][9]) {
+  for (unsigned i = f; i; i += 3)
+    c = (d(1 << (3629 & h[i][i][1][5][i]), ));
+}
+