aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJie Mei <jie.mei@oss.cipunited.com>2023-06-19 16:29:53 +0800
committerYunQiang Su <yunqiang.su@cipunited.com>2023-07-03 11:34:46 +0800
commit42d6b905c454e8f1b59d9248465d62a489b64972 (patch)
tree17f1a76b9927d06e0eb1bf62595c2132563846a5 /gcc
parent26aa2a2ccecf125af8eb9977b7638c7ca2d053e8 (diff)
downloadgcc-42d6b905c454e8f1b59d9248465d62a489b64972.zip
gcc-42d6b905c454e8f1b59d9248465d62a489b64972.tar.gz
gcc-42d6b905c454e8f1b59d9248465d62a489b64972.tar.bz2
MIPS: Add bitwise instructions for mips16e2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/mips/constraints.md4
-rw-r--r--gcc/config/mips/mips-protos.h4
-rw-r--r--gcc/config/mips/mips.cc67
-rw-r--r--gcc/config/mips/mips.h3
-rw-r--r--gcc/config/mips/mips.md91
-rw-r--r--gcc/config/mips/predicates.md13
-rw-r--r--gcc/testsuite/gcc.target/mips/mips16e2.c102
7 files changed, 263 insertions, 21 deletions
diff --git a/gcc/config/mips/constraints.md b/gcc/config/mips/constraints.md
index 49d1a43..22d4d84 100644
--- a/gcc/config/mips/constraints.md
+++ b/gcc/config/mips/constraints.md
@@ -264,6 +264,10 @@
(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."
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index da7902c..a5a6f6f 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -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 */
diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index 4e50a87..941562ec 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -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 ();
}
-
+
+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"
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 3ec33fb..eefe2aa 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -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 \
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 973d265..b9eb541 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -463,7 +463,7 @@
(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")
@@ -3314,12 +3314,13 @@
;; 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)
{
@@ -3344,20 +3345,28 @@
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:
@@ -3373,12 +3382,32 @@
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")
@@ -3386,7 +3415,7 @@
(match_operand:GPR 2 "uns_arith_operand")))]
""
{
- if (TARGET_MIPS16)
+ if (TARGET_MIPS16 && !ISA_HAS_MIPS16E2)
operands[2] = force_reg (<MODE>mode, operands[2]);
})
@@ -3403,11 +3432,23 @@
(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>")])
@@ -3432,19 +3473,31 @@
(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")
@@ -4344,6 +4397,7 @@
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"
@@ -4394,6 +4448,7 @@
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
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
index 4f9458e..e8a85ac 100644
--- a/gcc/config/mips/predicates.md
+++ b/gcc/config/mips/predicates.md
@@ -170,6 +170,10 @@
(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")
@@ -184,8 +188,15 @@
(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
index 0000000..ce8b4f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/mips16e2.c
@@ -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);
+}
+