https://gcc.gnu.org/g:7d5d2b879ae7636ca118fb4f3a08b22705cdeacb

commit r15-171-g7d5d2b879ae7636ca118fb4f3a08b22705cdeacb
Author: YunQiang Su <s...@gcc.gnu.org>
Date:   Mon Apr 29 00:33:44 2024 +0800

    expmed: TRUNCATE value1 if needed in store_bit_field_using_insv
    
    PR target/113179.
    
    In `store_bit_field_using_insv`, we just use SUBREG if value_mode
    >= op_mode, while in some ports, a sign_extend will be needed,
    such as MIPS64:
      If either GPR rs or GPR rt does not contain sign-extended 32-bit
      values (bits 63..31 equal), then the result of the operation is
      UNPREDICTABLE.
    
    The problem happens for the code like:
      struct xx {
            int a:4;
            int b:24;
            int c:3;
            int d:1;
      };
    
      void xx (struct xx *a, long long b) {
            a->d = b;
      }
    
    In the above code, the hard register contains `b`, may be note well
    sign-extended.
    
    gcc/
            PR target/113179
            * expmed.cc(store_bit_field_using_insv): TRUNCATE value1 if
            needed.
    
    gcc/testsuite
            PR target/113179
            * gcc.target/mips/pr113179.c: New tests.

Diff:
---
 gcc/expmed.cc                            | 12 +++++++++---
 gcc/testsuite/gcc.target/mips/pr113179.c | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index 4ec035e4843..20f3a36f38c 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -704,9 +704,15 @@ store_bit_field_using_insv (const extraction_insn *insv, 
rtx op0,
            }
          else
            {
-             tmp = gen_lowpart_if_possible (op_mode, value1);
-             if (! tmp)
-               tmp = gen_lowpart (op_mode, force_reg (value_mode, value1));
+             if (targetm.mode_rep_extended (op_mode, value_mode) != UNKNOWN)
+               tmp = simplify_gen_unary (TRUNCATE, op_mode,
+                                         value1, value_mode);
+             else
+               {
+                 tmp = gen_lowpart_if_possible (op_mode, value1);
+                 if (! tmp)
+                   tmp = gen_lowpart (op_mode, force_reg (value_mode, value1));
+               }
            }
          value1 = tmp;
        }
diff --git a/gcc/testsuite/gcc.target/mips/pr113179.c 
b/gcc/testsuite/gcc.target/mips/pr113179.c
new file mode 100644
index 00000000000..f32c5a16765
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr113179.c
@@ -0,0 +1,18 @@
+/* Check if the operand of INS is sign-extended on MIPS64.  */
+/* { dg-options "-mips64r2 -mabi=64" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+
+struct xx {
+        int a:1;
+        int b:24;
+        int c:6;
+        int d:1;
+};
+
+long long xx (struct xx *a, long long b) {
+        a->d = b;
+        return b+1;
+}
+
+/* { dg-final { scan-assembler "\tsll\t\\\$3,\\\$5,0" } } */
+/* { dg-final { scan-assembler "\tdaddiu\t\\\$2,\\\$5,1" } } */

Reply via email to