]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
MIPS: Add bitwise instructions for mips16e2
authorJie Mei <jie.mei@oss.cipunited.com>
Mon, 19 Jun 2023 08:29:53 +0000 (16:29 +0800)
committerYunQiang Su <yunqiang.su@cipunited.com>
Mon, 3 Jul 2023 03:34:46 +0000 (11:34 +0800)
There are shortened bitwise instructions in the mips16e2 ASE,
for instance, ANDI, ORI/XORI, EXT, INS etc. .

This patch adds these instrutions with corresponding tests.

gcc/ChangeLog:

* config/mips/constraints.md(Yz): New constraints for mips16e2.
* config/mips/mips-protos.h(mips_bit_clear_p): Declared new function.
(mips_bit_clear_info): Same as above.
* config/mips/mips.cc(mips_bit_clear_info): New function for
generating instructions.
(mips_bit_clear_p): Same as above.
* config/mips/mips.h(ISA_HAS_EXT_INS): Add clause for ISA_HAS_MIPS16E2.
* config/mips/mips.md(extended_mips16): Generates EXT and INS instructions.
(*and<mode>3): Generates INS instruction.
(*and<mode>3_mips16): Generates EXT, INS and ANDI instructions.
(ior<mode>3): Add logics for ORI instruction.
(*ior<mode>3_mips16_asmacro): Generates ORI instrucion.
(*ior<mode>3_mips16): Add logics for XORI instruction.
(*xor<mode>3_mips16): Generates XORI instrucion.
(*extzv<mode>): Add logics for EXT instruction.
(*insv<mode>): Add logics for INS instruction.
* config/mips/predicates.md(bit_clear_operand): New predicate for
generating bitwise instructions.
(and_reg_operand): Add logics for generating bitwise instructions.

gcc/testsuite/ChangeLog:

* gcc.target/mips/mips16e2.c: New tests for mips16e2.

gcc/config/mips/constraints.md
gcc/config/mips/mips-protos.h
gcc/config/mips/mips.cc
gcc/config/mips/mips.h
gcc/config/mips/mips.md
gcc/config/mips/predicates.md
gcc/testsuite/gcc.target/mips/mips16e2.c [new file with mode: 0644]

index 49d1a43c613b19f5dbccb0cf40f78c8ddf4bb35e..22d4d84f074cc7ff1b1eb68adeb06dc6fad71757 100644 (file)
   (and (match_code "const_vector")
        (match_test "op == CONST0_RTX (mode)")))
 
+(define_constraint "Yz"
+  "@internal"
+  (match_operand 0 "bit_clear_operand"))
+
 (define_constraint "YA"
   "@internal
    An unsigned 6-bit constant."
index da7902c235be2a6fa4be0eb524f955b93f1b2f0f..a5a6f6fcbd306187e696a2e18a6a27c9979e7d92 100644 (file)
@@ -390,4 +390,8 @@ extern void mips_expand_vec_cmp_expr (rtx *);
 
 extern void mips_emit_speculation_barrier_function (void);
 
+extern bool mips_bit_clear_p (enum machine_mode, unsigned HOST_WIDE_INT);
+extern void mips_bit_clear_info (enum machine_mode, unsigned HOST_WIDE_INT,
+                                 int *, int *);
+
 #endif /* ! GCC_MIPS_PROTOS_H */
index 4e50a87d71cce48935a1f17d4ed014b9f08d2101..941562ecece2652f1c00b056892600bd558fbb54 100644 (file)
@@ -3976,6 +3976,10 @@ mips16_constant_cost (int code, HOST_WIDE_INT x)
        return 0;
       return -1;
 
+    case ZERO_EXTRACT:
+      /* The bit position and size are immediate operands.  */
+      return ISA_HAS_EXT_INS ? COSTS_N_INSNS (1) : -1;
+
     default:
       return -1;
     }
@@ -22877,7 +22881,68 @@ mips_asm_file_end (void)
   if (NEED_INDICATE_EXEC_STACK)
     file_end_indicate_exec_stack ();
 }
-\f
+
+void
+mips_bit_clear_info (enum machine_mode mode, unsigned HOST_WIDE_INT m,
+                     int *start_pos, int *size)
+{
+  unsigned int shift = 0;
+  unsigned int change_count = 0;
+  unsigned int prev_val = 1;
+  unsigned int curr_val = 0;
+  unsigned int end_pos = GET_MODE_SIZE (mode) * BITS_PER_UNIT;
+
+  for (shift = 0 ; shift < (GET_MODE_SIZE (mode) * BITS_PER_UNIT) ; shift++)
+    {
+      curr_val = (unsigned int)((m & (unsigned int)(1 << shift)) >> shift);
+      if (curr_val != prev_val)
+       {
+         change_count++;
+         switch (change_count)
+           {
+             case 1:
+               *start_pos = shift;
+               break;
+             case 2:
+               end_pos = shift;
+               break;
+             default:
+               gcc_unreachable ();
+           }
+       }
+      prev_val = curr_val;
+   }
+  *size = (end_pos - *start_pos);
+}
+
+bool
+mips_bit_clear_p (enum machine_mode mode, unsigned HOST_WIDE_INT m)
+{
+  unsigned int shift = 0;
+  unsigned int change_count = 0;
+  unsigned int prev_val = 1;
+  unsigned int curr_val = 0;
+
+  if (mode != SImode && mode != VOIDmode)
+    return false;
+
+  if (!ISA_HAS_EXT_INS)
+    return false;
+
+  for (shift = 0 ; shift < (UNITS_PER_WORD * BITS_PER_UNIT) ; shift++)
+    {
+      curr_val = (unsigned int)((m & (unsigned int)(1 << shift)) >> shift);
+      if (curr_val != prev_val)
+       change_count++;
+      prev_val = curr_val;
+    }
+
+  if (change_count == 2)
+    return true;
+
+  return false;
+}
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
index 3ec33fbba7161839ab1a7c2297646530baee19b1..eefe2aa09100ff77aa313c566ff76af4df273b47 100644 (file)
@@ -1266,7 +1266,8 @@ struct mips_cpu_info {
 #define ISA_HAS_SEB_SEH                (mips_isa_rev >= 2 && !TARGET_MIPS16)
 
 /* ISA includes the MIPS32/64 rev 2 ext and ins instructions.  */
-#define ISA_HAS_EXT_INS                (mips_isa_rev >= 2 && !TARGET_MIPS16)
+#define ISA_HAS_EXT_INS                ((mips_isa_rev >= 2 && !TARGET_MIPS16)  \
+                                || ISA_HAS_MIPS16E2)
 
 /* ISA has instructions for accessing top part of 64-bit fp regs.  */
 #define ISA_HAS_MXHC1          (!TARGET_FLOAT32        \
index 973d265e27d5f005de25c9303e97d22bdd909186..b9eb541cf4a415748d806e2e1bcb464f82dc6b1e 100644 (file)
   (if_then_else (ior ;; In general, constant-pool loads are extended
                     ;; instructions.  We don't yet optimize for 16-bit
                     ;; PC-relative references.
-                    (eq_attr "move_type" "sll0,loadpool")
+                    (eq_attr "move_type" "sll0,loadpool,ext_ins")
                     (eq_attr "jal" "direct")
                     (eq_attr "got" "load"))
                (const_string "yes")
 ;;  register =op1                      x
 
 (define_insn "*and<mode>3"
-  [(set (match_operand:GPR 0 "register_operand" "=d,d,d,!u,d,d,d,!u,d")
-       (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "o,o,W,!u,d,d,d,0,d")
-                (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,Uean,K,Yx,Yw,!u,d")))]
+  [(set (match_operand:GPR 0 "register_operand" "=d,d,d,!u,d,d,d,!u,d,d")
+       (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "o,o,W,!u,d,d,d,0,d,0")
+                (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,Uean,K,Yx,Yw,!u,d,Yz")))]
   "!TARGET_MIPS16 && and_operands_ok (<MODE>mode, operands[1], operands[2])"
 {
   int len;
+  int pos;
 
   switch (which_alternative)
     {
     case 7:
     case 8:
       return "and\t%0,%1,%2";
+    case 9:
+      mips_bit_clear_info (<MODE>mode, INTVAL (operands[2]), &pos, &len);
+      operands[1] = GEN_INT (pos);
+      operands[2] = GEN_INT (len);
+      return "<d>ins\t%0,$0,%1,%2";
     default:
       gcc_unreachable ();
     }
 }
-  [(set_attr "move_type" "load,load,load,andi,andi,ext_ins,shift_shift,logical,logical")
-   (set_attr "compression" "*,*,*,micromips,*,*,*,micromips,*")
+  [(set_attr "move_type" "load,load,load,andi,andi,ext_ins,shift_shift,logical,logical,ext_ins")
+   (set_attr "compression" "*,*,*,micromips,*,*,*,micromips,*,*")
    (set_attr "mode" "<MODE>")])
 
 (define_insn "*and<mode>3_mips16"
-  [(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d")
-       (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "%W,W,W,d,0")
-                (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,Yw,d")))]
+  [(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d,d,d,d")
+       (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "%W,W,W,d,0,d,0,0?")
+                (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,Yw,d,Yx,Yz,K")))]
   "TARGET_MIPS16 && and_operands_ok (<MODE>mode, operands[1], operands[2])"
 {
+  int len;
+  int pos;
+
   switch (which_alternative)
     {
     case 0:
       return "#";
     case 4:
       return "and\t%0,%2";
+    case 5:
+      len = low_bitmask_len (<MODE>mode, INTVAL (operands[2]));
+      operands[2] = GEN_INT (len);
+      return "ext\t%0,%1,0,%2";
+    case 6:
+      mips_bit_clear_info (<MODE>mode, INTVAL (operands[2]), &pos, &len);
+      operands[1] = GEN_INT (pos);
+      operands[2] = GEN_INT (len);
+      return "ins\t%0,$0,%1,%2";
+    case 7:
+      return "andi\t%0,%x2";
     default:
       gcc_unreachable ();
     }
 }
-  [(set_attr "move_type" "load,load,load,shift_shift,logical")
-   (set_attr "mode" "<MODE>")])
+  [(set_attr "move_type" "load,load,load,shift_shift,logical,ext_ins,ext_ins,andi")
+   (set_attr "mode" "<MODE>")
+   (set_attr "extended_mips16" "no,no,no,no,no,yes,yes,yes")
+   (set (attr "enabled")
+   (cond [(and (eq_attr "alternative" "7")
+                          (not (match_test "ISA_HAS_MIPS16E2")))
+                 (const_string "no")
+                 (and (eq_attr "alternative" "0,1")
+                          (match_test "!GENERATE_MIPS16E"))
+                 (const_string "no")]
+                (const_string "yes")))])
 
 (define_expand "ior<mode>3"
   [(set (match_operand:GPR 0 "register_operand")
                 (match_operand:GPR 2 "uns_arith_operand")))]
   ""
 {
-  if (TARGET_MIPS16)
+  if (TARGET_MIPS16 && !ISA_HAS_MIPS16E2)
     operands[2] = force_reg (<MODE>mode, operands[2]);
 })
 
    (set_attr "compression" "micromips,*,*")
    (set_attr "mode" "<MODE>")])
 
+(define_insn "*ior<mode>3_mips16_asmacro"
+  [(set (match_operand:GPR 0 "register_operand" "=d,d")
+       (ior:GPR (match_operand:GPR 1 "register_operand" "%0,0")
+                (match_operand:GPR 2 "uns_arith_operand" "d,K")))]
+  "ISA_HAS_MIPS16E2"
+  "@
+   or\t%0,%2
+   ori\t%0,%x2"
+   [(set_attr "alu_type" "or")
+    (set_attr "mode" "<MODE>")
+    (set_attr "extended_mips16" "*,yes")])
+
 (define_insn "*ior<mode>3_mips16"
   [(set (match_operand:GPR 0 "register_operand" "=d")
        (ior:GPR (match_operand:GPR 1 "register_operand" "%0")
                 (match_operand:GPR 2 "register_operand" "d")))]
-  "TARGET_MIPS16"
+  "TARGET_MIPS16 && !ISA_HAS_MIPS16E2"
   "or\t%0,%2"
   [(set_attr "alu_type" "or")
    (set_attr "mode" "<MODE>")])
    (set_attr "compression" "micromips,*,*")
    (set_attr "mode" "<MODE>")])
 
+;; We increase statically the cost of the output register for XORI
+;; to counterweight LRA cost calculation as XORI tends to be chosen
+;; frequently hurting the code size.  The reason of not choosing CMPI is
+;; that LRA tends to add up the cost of the T register as it is in a small
+;; class and a possible reload.  In reality, the use of T register comes for
+;; free in a number of cases as we don't need any MIPS16 registers.
 (define_insn "*xor<mode>3_mips16"
-  [(set (match_operand:GPR 0 "register_operand" "=d,t,t,t")
-       (xor:GPR (match_operand:GPR 1 "register_operand" "%0,d,d,d")
-                (match_operand:GPR 2 "uns_arith_operand" "d,Uub8,K,d")))]
+  [(set (match_operand:GPR 0 "register_operand" "=d,t,t,t,d?")
+       (xor:GPR (match_operand:GPR 1 "register_operand" "%0,d,d,d,0")
+                (match_operand:GPR 2 "uns_arith_operand" "d,Uub8,K,d,K")))]
   "TARGET_MIPS16"
   "@
    xor\t%0,%2
    cmpi\t%1,%2
    cmpi\t%1,%2
-   cmp\t%1,%2"
+   cmp\t%1,%2
+   xori\t%0,%x2"
   [(set_attr "alu_type" "xor")
    (set_attr "mode" "<MODE>")
-   (set_attr "extended_mips16" "no,no,yes,no")])
+   (set_attr "extended_mips16" "no,no,yes,no,yes")
+   (set (attr "enabled")
+               (cond [(and (eq_attr "alternative" "4")
+                                       (not (match_test "ISA_HAS_MIPS16E2")))
+                          (const_string "no")]
+                         (const_string "yes")))])
 
 (define_insn "*nor<mode>3"
   [(set (match_operand:GPR 0 "register_operand" "=d")
                       INTVAL (operands[3]))"
   "<d>ext\t%0,%1,%3,%2"
   [(set_attr "type"    "arith")
+   (set_attr "extended_mips16"  "yes")
    (set_attr "mode"    "<MODE>")])
 
 (define_insn "*extzv_truncsi_exts"
                       INTVAL (operands[2]))"
   "<d>ins\t%0,%z3,%2,%1"
   [(set_attr "type"    "arith")
+   (set_attr "extended_mips16"  "yes")
    (set_attr "mode"    "<MODE>")])
 
 ;; Combiner pattern for cins (clear and insert bit field).  We can
index 4f9458ed72ffae128cb0b1e1333b21e406331f44..e8a85ac32fb7ed057637c892683d1a95818257e4 100644 (file)
   (and (match_code "const_int")
        (match_test "UINTVAL (op) == 0xffffffff")))
 
+(define_predicate "bit_clear_operand"
+  (and (match_code "const_int")
+       (match_test "mips_bit_clear_p (mode, INTVAL (op))")))
+
 (define_predicate "and_load_operand"
   (ior (match_operand 0 "qi_mask_operand")
        (match_operand 0 "hi_mask_operand")
   (ior (match_operand 0 "register_operand")
        (and (not (match_test "TARGET_MIPS16"))
            (match_operand 0 "const_uns_arith_operand"))
+       (and (match_test "ISA_HAS_MIPS16E2")
+           (match_operand 0 "const_uns_arith_operand")
+           (not (match_operand 0 "hi_mask_operand"))
+           (not (match_operand 0 "qi_mask_operand")))
+       (and (match_test "ISA_HAS_MIPS16E2")
+           (match_operand 0 "const_uns_arith_operand"))
        (match_operand 0 "low_bitmask_operand")
-       (match_operand 0 "si_mask_operand")))
+       (match_operand 0 "si_mask_operand")
+       (match_operand 0 "bit_clear_operand")))
 
 (define_predicate "and_operand"
   (ior (match_operand 0 "and_load_operand")
diff --git a/gcc/testsuite/gcc.target/mips/mips16e2.c b/gcc/testsuite/gcc.target/mips/mips16e2.c
new file mode 100644 (file)
index 0000000..ce8b4f1
--- /dev/null
@@ -0,0 +1,102 @@
+/* { dg-options "-mno-abicalls -mgpopt -G8 -mabi=32 -mips16 -mmips16e2" } */
+/* { dg-skip-if "per-function expected output" { *-*-* } { "-flto" } { "" } } */
+/* ANDI is a two operand instruction.  Hence, it won't be generated if src and
+ *    dest are in different registers.  */
+   
+/* { dg-final { scan-assembler "test01:.*\tandi\t.*test01\n" } } */
+unsigned int
+test01 (unsigned int a)
+{
+  return ((a + 0x2) & 0x3ff);
+}
+
+/* Test EXT */
+
+/* { dg-final { scan-assembler "test02:.*\text\t.*test02\n" } } */
+struct
+{
+  unsigned int a:9;
+  unsigned int d:31;
+  unsigned int e:9;
+  unsigned int f:10;
+} t02;
+
+unsigned int
+test02 (void)
+{
+  return t02.f;
+}
+
+/* Use EXT when ANDing with low-order bitmasks.  */
+
+/* { dg-final { scan-assembler "test03:.*\text\t.*test03\n" } } */
+/* { dg-final { scan-assembler-not "test03.*\tandi?\t.*test03\n" } } */
+unsigned int
+test03 (unsigned int x)
+{
+  return (x & 0x1fffffff);
+}
+
+/* Test INS */
+
+/* { dg-final { scan-assembler "test04:.*\tins\t.*test04\n" } } */
+struct
+{
+  unsigned int i : 9;
+  unsigned int j : 15;
+  unsigned int k : 4;
+} s04;
+
+void
+test04 (void)
+{
+  s04.j = 1;
+}
+
+/* Use INS with hardcoded $0.  */
+
+/* { dg-final { scan-assembler "test05:.*\tins\t\\\$.*,\\\$0.*test05\n" } } */
+struct
+{
+  unsigned int i : 8;
+  unsigned int j : 9;
+  unsigned int k : 10;
+} __attribute__ ((packed)) s05 __attribute__((aligned(1)));
+
+void
+test05 (void)
+{
+  s05.k = 0;
+}
+
+/* Use INS when ANDing to clear only one consecutive chunk of bits.  */
+
+/* { dg-final { scan-assembler "test06:.*\tins\t\\\$.*,\\\$0,11,5.*test06\n" } } */
+/* { dg-final { scan-assembler-not "test06:.*\tandi?\t.*test06\n" } } */
+unsigned int
+test06 (unsigned int x)
+{
+  return (x & 0xffff07ff);
+}
+
+/* ORI is a two operand instruction.  Hence, it won't be generated if src and
+   dest are in different registers.  */
+
+/* { dg-final { scan-assembler "test07:.*\tori\t.*test07\n" } } */
+unsigned int
+test07 (unsigned int a)
+{
+  return (a + 0x2) | 0x7f0;
+}
+
+/* XORI is a two operand instruction.  Hence, it won't be generated if src and
+   dest are in different registers.  */
+
+/* { dg-final { scan-assembler "test08:.*\txori\t.*test08\n" } } */
+unsigned int
+test08 (unsigned int a)
+{
+  return ((a + 0x2) ^ 0x3f0);
+}
+