@@ -80,6 +80,12 @@
(and (match_code "const_int")
(match_test "LUI_OPERAND (ival)")))
+(define_constraint "MiG"
+ "const can be represented as sum of any S12 values."
+ (and (match_code "const_int")
+ (ior (match_test "IN_RANGE (ival, 2048, 4094)")
+ (match_test "IN_RANGE (ival, -4096, -2049)"))))
+
(define_constraint "Ds3"
"@internal
1, 2 or 3 immediate"
@@ -428,6 +428,12 @@
return true;
})
+(define_predicate "const_two_s12"
+ (match_code "const_int")
+{
+ return SUM_OF_TWO_S12 (INTVAL (op));
+})
+
;; CORE-V Predicates:
(define_predicate "immediate_register_operand"
(ior (match_operand 0 "register_operand")
@@ -165,6 +165,7 @@ extern bool riscv_shamt_matches_mask_p (int, HOST_WIDE_INT);
extern void riscv_subword_address (rtx, rtx *, rtx *, rtx *, rtx *);
extern void riscv_lshift_subword (machine_mode, rtx, rtx, rtx *);
extern enum memmodel riscv_union_memmodels (enum memmodel, enum memmodel);
+extern bool riscv_reg_frame_related (rtx);
/* Routines implemented in riscv-c.cc. */
void riscv_cpu_cpp_builtins (cpp_reader *);
@@ -7145,6 +7145,17 @@ riscv_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM);
}
+/* Helper to determine if reg X pertains to stack. */
+bool
+riscv_reg_frame_related (rtx x)
+{
+ return REG_P (x)
+ && (REGNO (x) == FRAME_POINTER_REGNUM
+ || REGNO (x) == HARD_FRAME_POINTER_REGNUM
+ || REGNO (x) == ARG_POINTER_REGNUM
+ || REGNO (x) == VIRTUAL_STACK_VARS_REGNUM);
+}
+
/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer
or argument pointer. TO is either the stack pointer or hard frame
pointer. */
@@ -626,6 +626,21 @@ enum reg_class
(((VALUE) | ((1UL<<31) - IMM_REACH)) == ((1UL<<31) - IMM_REACH) \
|| ((VALUE) | ((1UL<<31) - IMM_REACH)) + IMM_REACH == 0)
+/* True if a VALUE (constant) can be expressed as sum of two S12 constants
+ (in range -2048 to 2047).
+ Range check logic:
+ from: min S12 + 1 (or -1 depending on what side of zero)
+ to: two times the min S12 value (to max out S12 bits). */
+
+#define SUM_OF_TWO_S12_N(VALUE) \
+ (((VALUE) >= (-2048 * 2)) && ((VALUE) <= (-2048 - 1)))
+
+#define SUM_OF_TWO_S12_P(VALUE) \
+ (((VALUE) >= (2047 + 1)) && ((VALUE) <= (2047 * 2)))
+
+#define SUM_OF_TWO_S12(VALUE) \
+ (SUM_OF_TWO_S12_N (VALUE) || SUM_OF_TWO_S12_P (VALUE))
+
/* If this is a single bit mask, then we can load it with bseti. Special
handling of SImode 0x80000000 on RV64 is done in riscv_build_integer_1. */
#define SINGLE_BIT_MASK_OPERAND(VALUE) \
@@ -703,6 +703,46 @@
[(set_attr "type" "arith")
(set_attr "mode" "DI")])
+;; Special case of adding a reg and constant if latter is sum of two S12
+;; values (in range -2048 to 2047). Avoid materialized the const and fuse
+;; into the add (with an additional add for 2nd value). Makes a 3 insn
+;; sequence into 2 insn.
+
+(define_insn_and_split "*add<mode>3_const_sum_of_two_s12"
+ [(set (match_operand:P 0 "register_operand" "=r,r")
+ (plus:P (match_operand:P 1 "register_operand" " r,r")
+ (match_operand:P 2 "const_two_s12" " MiG,r")))]
+ "!riscv_reg_frame_related (operands[0])"
+{
+ /* operand matching MiG constraint is always meant to be split. */
+ if (which_alternative == 0)
+ return "#";
+ else
+ return "add %0,%1,%2";
+}
+ ""
+ [(set (match_dup 0)
+ (plus:P (match_dup 1) (match_dup 3)))
+ (set (match_dup 0)
+ (plus:P (match_dup 0) (match_dup 4)))]
+{
+ int val = INTVAL (operands[2]);
+ if (SUM_OF_TWO_S12_P (val))
+ {
+ operands[3] = GEN_INT (2047);
+ operands[4] = GEN_INT (val - 2047);
+ }
+ else if (SUM_OF_TWO_S12_N (val))
+ {
+ operands[3] = GEN_INT (-2048);
+ operands[4] = GEN_INT (val + 2048);
+ }
+ else
+ gcc_unreachable ();
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<P:MODE>")])
+
(define_expand "addv<mode>4"
[(set (match_operand:GPR 0 "register_operand" "=r,r")
(plus:GPR (match_operand:GPR 1 "register_operand" " r,r")
new file mode 100644
@@ -0,0 +1,45 @@
+// TBD: This doesn't quite work for rv32 yet
+/* { dg-do compile } */
+/* { dg-options { -march=rv64gcv -mabi=lp64d } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" "-Os" "-Oz" } } */
+
+/* Ensure that gcc doesn't generate standlone li reg, 4096. */
+long
+plus1(unsigned long i)
+{
+ return i + 2048;
+}
+
+long
+plus2(unsigned long i)
+{
+ return i + 4094;
+}
+
+long
+plus3(unsigned long i)
+{
+ return i + 2064;
+}
+
+/* Ensure that gcc doesn't generate standlone li reg, -4096. */
+long
+minus1(unsigned long i)
+{
+ return i - 4096;
+}
+
+long
+minus2(unsigned long i)
+{
+ return i - 2049;
+}
+
+long
+minus3(unsigned long i)
+{
+ return i - 2064;
+}
+
+/* { dg-final { scan-assembler-not {li\t[a-x0-9]+,-4096} } } */
+/* { dg-final { scan-assembler-not {li\t[a-x0-9]+,4096} } } */
new file mode 100644
@@ -0,0 +1,15 @@
+/* Reduced from glibc/stdio-common/tempnam.c.
+ Can't have invalid insn in final output:
+ add s0, sp, 2048 */
+
+/* { dg-do compile } */
+/* { dg-options { -march=rv64gcv -mabi=lp64d -O2 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "O1" "-Og" "-Os" "-Oz" } } */
+
+int a() {
+ char b[4096];
+ if (a(b))
+ a(b);
+}
+
+/* { dg-final { scan-assembler-not {add\t[a-x0-9]+,sp,2048} } } */
new file mode 100644
@@ -0,0 +1,22 @@
+/* Reduced version of c-c++-common/torture/builtin-convertvector-1.c. */
+/* This should NOT ICE */
+
+/* { dg-do compile } */
+
+typedef long b __attribute__((vector_size(256 * sizeof(long))));
+typedef double c __attribute__((vector_size(256 * sizeof(double))));
+int d;
+void e(b *f, c *g) { *g = __builtin_convertvector(*f, c); }
+void h() {
+ struct {
+ b i;
+ } j;
+ union {
+ c i;
+ double a[6];
+ } k;
+ e(&j.i, &k.i);
+ if (k.a[d])
+ for (;;)
+ ;
+}