@@ -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."
@@ -388,4 +388,8 @@ extern void mips_register_frame_header_opt (void);
extern void mips_expand_vec_cond_expr (machine_mode, machine_mode, rtx *);
extern void mips_expand_vec_cmp_expr (rtx *);
+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 */
@@ -3895,6 +3895,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;
}
@@ -22753,7 +22757,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"
@@ -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 \
@@ -461,7 +461,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")
@@ -3312,12 +3312,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)
{
@@ -3342,20 +3343,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:
@@ -3371,12 +3380,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")
@@ -3384,7 +3413,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]);
})
@@ -3401,11 +3430,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>")])
@@ -3430,19 +3471,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")
@@ -4342,6 +4395,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"
@@ -4392,6 +4446,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
@@ -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")
new file mode 100644
@@ -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);
+}
+