#include "output.h"
#include "tm-constrs.h"
#include "expr.h"
+#include "emit-rtl.h"
+#include "explow.h"
/* ------------------------------------------------------------------------ */
}
}
-/* A helper function to return memory format. */
+static int
+nds32_inverse_cond_code (int code)
+{
+ switch (code)
+ {
+ case NE:
+ return EQ;
+ case EQ:
+ return NE;
+ case GT:
+ return LE;
+ case LE:
+ return GT;
+ case GE:
+ return LT;
+ case LT:
+ return GE;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+static const char *
+nds32_cond_code_str (int code)
+{
+ switch (code)
+ {
+ case NE:
+ return "ne";
+ case EQ:
+ return "eq";
+ case GT:
+ return "gt";
+ case LE:
+ return "le";
+ case GE:
+ return "ge";
+ case LT:
+ return "lt";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+static void
+output_cond_branch (int code, const char *suffix, bool r5_p,
+ bool long_jump_p, rtx *operands)
+{
+ char pattern[256];
+ const char *cond_code;
+
+ if (r5_p && REGNO (operands[2]) == 5 && TARGET_16_BIT)
+ {
+ /* This is special case for beqs38 and bnes38,
+ second operand 2 can't be $r5 and it's almost meanless,
+ however it may occur after copy propgation. */
+ if (code == EQ)
+ {
+ /* $r5 == $r5 always taken! */
+ if (long_jump_p)
+ snprintf (pattern, sizeof (pattern),
+ "j\t%%3");
+ else
+ snprintf (pattern, sizeof (pattern),
+ "j8\t%%3");
+ }
+ else
+ /* Don't output anything since $r5 != $r5 never taken! */
+ pattern[0] = '\0';
+ }
+ else if (long_jump_p)
+ {
+ int inverse_code = nds32_inverse_cond_code (code);
+ cond_code = nds32_cond_code_str (inverse_code);
+
+ /* b<cond><suffix> $r0, $r1, .L0
+ =>
+ b<inverse_cond><suffix> $r0, $r1, .LCB0
+ j .L0
+ .LCB0:
+
+ or
+
+ b<cond><suffix> $r0, $r1, .L0
+ =>
+ b<inverse_cond><suffix> $r0, $r1, .LCB0
+ j .L0
+ .LCB0:
+ */
+ if (r5_p && TARGET_16_BIT)
+ {
+ snprintf (pattern, sizeof (pattern),
+ "b%ss38\t %%2, .LCB%%=\n\tj\t%%3\n.LCB%%=:",
+ cond_code);
+ }
+ else
+ {
+ snprintf (pattern, sizeof (pattern),
+ "b%s%s\t%%1, %%2, .LCB%%=\n\tj\t%%3\n.LCB%%=:",
+ cond_code, suffix);
+ }
+ }
+ else
+ {
+ cond_code = nds32_cond_code_str (code);
+ if (r5_p && TARGET_16_BIT)
+ {
+ /* b<cond>s38 $r1, .L0 */
+ snprintf (pattern, sizeof (pattern),
+ "b%ss38\t %%2, %%3", cond_code);
+ }
+ else
+ {
+ /* b<cond><suffix> $r0, $r1, .L0 */
+ snprintf (pattern, sizeof (pattern),
+ "b%s%s\t%%1, %%2, %%3", cond_code, suffix);
+ }
+ }
+
+ output_asm_insn (pattern, operands);
+}
+
+static void
+output_cond_branch_compare_zero (int code, const char *suffix,
+ bool long_jump_p, rtx *operands,
+ bool ta_implied_p)
+{
+ char pattern[256];
+ const char *cond_code;
+ if (long_jump_p)
+ {
+ int inverse_code = nds32_inverse_cond_code (code);
+ cond_code = nds32_cond_code_str (inverse_code);
+
+ if (ta_implied_p && TARGET_16_BIT)
+ {
+ /* b<cond>z<suffix> .L0
+ =>
+ b<inverse_cond>z<suffix> .LCB0
+ j .L0
+ .LCB0:
+ */
+ snprintf (pattern, sizeof (pattern),
+ "b%sz%s\t.LCB%%=\n\tj\t%%2\n.LCB%%=:",
+ cond_code, suffix);
+ }
+ else
+ {
+ /* b<cond>z<suffix> $r0, .L0
+ =>
+ b<inverse_cond>z<suffix> $r0, .LCB0
+ j .L0
+ .LCB0:
+ */
+ snprintf (pattern, sizeof (pattern),
+ "b%sz%s\t%%1, .LCB%%=\n\tj\t%%2\n.LCB%%=:",
+ cond_code, suffix);
+ }
+ }
+ else
+ {
+ cond_code = nds32_cond_code_str (code);
+ if (ta_implied_p && TARGET_16_BIT)
+ {
+ /* b<cond>z<suffix> .L0 */
+ snprintf (pattern, sizeof (pattern),
+ "b%sz%s\t%%2", cond_code, suffix);
+ }
+ else
+ {
+ /* b<cond>z<suffix> $r0, .L0 */
+ snprintf (pattern, sizeof (pattern),
+ "b%sz%s\t%%1, %%2", cond_code, suffix);
+ }
+ }
+
+ output_asm_insn (pattern, operands);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* Auxiliary function for expand RTL pattern. */
+
+enum nds32_expand_result_type
+nds32_expand_cbranch (rtx *operands)
+{
+ rtx tmp_reg;
+ enum rtx_code code;
+
+ code = GET_CODE (operands[0]);
+
+ /* If operands[2] is (const_int 0),
+ we can use beqz,bnez,bgtz,bgez,bltz,or blez instructions.
+ So we have gcc generate original template rtx. */
+ if (GET_CODE (operands[2]) == CONST_INT)
+ if (INTVAL (operands[2]) == 0)
+ if ((code != GTU)
+ && (code != GEU)
+ && (code != LTU)
+ && (code != LEU))
+ return EXPAND_CREATE_TEMPLATE;
+
+ /* For other comparison, NDS32 ISA only has slt (Set-on-Less-Than)
+ behavior for the comparison, we might need to generate other
+ rtx patterns to achieve same semantic. */
+ switch (code)
+ {
+ case GT:
+ case GTU:
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ /* GT reg_A, const_int => !(LT reg_A, const_int + 1) */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ /* We want to plus 1 into the integer value
+ of operands[2] to create 'slt' instruction.
+ This caculation is performed on the host machine,
+ which may be 64-bit integer.
+ So the meaning of caculation result may be
+ different from the 32-bit nds32 target.
+
+ For example:
+ 0x7fffffff + 0x1 -> 0x80000000,
+ this value is POSITIVE on 64-bit machine,
+ but the expected value on 32-bit nds32 target
+ should be NEGATIVE value.
+
+ Hence, instead of using GEN_INT(), we use gen_int_mode() to
+ explicitly create SImode constant rtx. */
+ enum rtx_code cmp_code;
+
+ rtx plus1 = gen_int_mode (INTVAL (operands[2]) + 1, SImode);
+ if (satisfies_constraint_Is15 (plus1))
+ {
+ operands[2] = plus1;
+ cmp_code = EQ;
+ if (code == GT)
+ {
+ /* GT, use slts instruction */
+ emit_insn (
+ gen_slts_compare (tmp_reg, operands[1], operands[2]));
+ }
+ else
+ {
+ /* GTU, use slt instruction */
+ emit_insn (
+ gen_slt_compare (tmp_reg, operands[1], operands[2]));
+ }
+ }
+ else
+ {
+ cmp_code = NE;
+ if (code == GT)
+ {
+ /* GT, use slts instruction */
+ emit_insn (
+ gen_slts_compare (tmp_reg, operands[2], operands[1]));
+ }
+ else
+ {
+ /* GTU, use slt instruction */
+ emit_insn (
+ gen_slt_compare (tmp_reg, operands[2], operands[1]));
+ }
+ }
+
+ PUT_CODE (operands[0], cmp_code);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+ }
+ else
+ {
+ /* GT reg_A, reg_B => LT reg_B, reg_A */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ if (code == GT)
+ {
+ /* GT, use slts instruction */
+ emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1]));
+ }
+ else
+ {
+ /* GTU, use slt instruction */
+ emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1]));
+ }
+
+ PUT_CODE (operands[0], NE);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+ }
+
+ case GE:
+ case GEU:
+ /* GE reg_A, reg_B => !(LT reg_A, reg_B) */
+ /* GE reg_A, const_int => !(LT reg_A, const_int) */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ if (code == GE)
+ {
+ /* GE, use slts instruction */
+ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
+ }
+ else
+ {
+ /* GEU, use slt instruction */
+ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
+ }
+
+ PUT_CODE (operands[0], EQ);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+
+ case LT:
+ case LTU:
+ /* LT reg_A, reg_B => LT reg_A, reg_B */
+ /* LT reg_A, const_int => LT reg_A, const_int */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ if (code == LT)
+ {
+ /* LT, use slts instruction */
+ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
+ }
+ else
+ {
+ /* LTU, use slt instruction */
+ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
+ }
+
+ PUT_CODE (operands[0], NE);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+
+ case LE:
+ case LEU:
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ /* LE reg_A, const_int => LT reg_A, const_int + 1 */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ enum rtx_code cmp_code;
+ /* Note that (le:SI X INT_MAX) is not the same as (lt:SI X INT_MIN).
+ We better have an assert here in case GCC does not properly
+ optimize it away. The INT_MAX here is 0x7fffffff for target. */
+ rtx plus1 = gen_int_mode (INTVAL (operands[2]) + 1, SImode);
+ if (satisfies_constraint_Is15 (plus1))
+ {
+ operands[2] = plus1;
+ cmp_code = NE;
+ if (code == LE)
+ {
+ /* LE, use slts instruction */
+ emit_insn (
+ gen_slts_compare (tmp_reg, operands[1], operands[2]));
+ }
+ else
+ {
+ /* LEU, use slt instruction */
+ emit_insn (
+ gen_slt_compare (tmp_reg, operands[1], operands[2]));
+ }
+ }
+ else
+ {
+ cmp_code = EQ;
+ if (code == LE)
+ {
+ /* LE, use slts instruction */
+ emit_insn (
+ gen_slts_compare (tmp_reg, operands[2], operands[1]));
+ }
+ else
+ {
+ /* LEU, use slt instruction */
+ emit_insn (
+ gen_slt_compare (tmp_reg, operands[2], operands[1]));
+ }
+ }
+
+ PUT_CODE (operands[0], cmp_code);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+ }
+ else
+ {
+ /* LE reg_A, reg_B => !(LT reg_B, reg_A) */
+ if (optimize_size || optimize == 0)
+ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
+ else
+ tmp_reg = gen_reg_rtx (SImode);
+
+ if (code == LE)
+ {
+ /* LE, use slts instruction */
+ emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1]));
+ }
+ else
+ {
+ /* LEU, use slt instruction */
+ emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1]));
+ }
+
+ PUT_CODE (operands[0], EQ);
+ operands[1] = tmp_reg;
+ operands[2] = const0_rtx;
+ emit_insn (gen_cbranchsi4 (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ return EXPAND_DONE;
+ }
+
+ case EQ:
+ case NE:
+ /* NDS32 ISA has various form for eq/ne behavior no matter
+ what kind of the operand is.
+ So just generate original template rtx. */
+
+ /* Put operands[2] into register if operands[2] is a large
+ const_int or ISAv2. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (!satisfies_constraint_Is11 (operands[2])
+ || TARGET_ISA_V2))
+ operands[2] = force_reg (SImode, operands[2]);
+
+ return EXPAND_CREATE_TEMPLATE;
+
+ default:
+ return EXPAND_FAIL;
+ }
+}
+
+enum nds32_expand_result_type
+nds32_expand_cstore (rtx *operands)
+{
+ rtx tmp_reg;
+ enum rtx_code code;
+
+ code = GET_CODE (operands[1]);
+
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ if (GET_CODE (operands[3]) == CONST_INT)
+ {
+ /* reg_R = (reg_A == const_int_B)
+ --> xori reg_C, reg_A, const_int_B
+ slti reg_R, reg_C, const_int_1
+ reg_R = (reg_A != const_int_B)
+ --> xori reg_C, reg_A, const_int_B
+ slti reg_R, const_int0, reg_C */
+ tmp_reg = gen_reg_rtx (SImode);
+
+ /* If the integer value is not in the range of imm15s,
+ we need to force register first because our addsi3 pattern
+ only accept nds32_rimm15s_operand predicate. */
+ rtx new_imm = gen_int_mode (-INTVAL (operands[3]), SImode);
+ if (satisfies_constraint_Is15 (new_imm))
+ emit_insn (gen_addsi3 (tmp_reg, operands[2], new_imm));
+ else
+ {
+ if (!(satisfies_constraint_Iu15 (operands[3])
+ || (TARGET_EXT_PERF
+ && satisfies_constraint_It15 (operands[3]))))
+ operands[3] = force_reg (SImode, operands[3]);
+ emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3]));
+ }
+
+ if (code == EQ)
+ emit_insn (gen_slt_eq0 (operands[0], tmp_reg));
+ else
+ emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg));
+
+ return EXPAND_DONE;
+ }
+ else
+ {
+ /* reg_R = (reg_A == reg_B)
+ --> xor reg_C, reg_A, reg_B
+ slti reg_R, reg_C, const_int_1
+ reg_R = (reg_A != reg_B)
+ --> xor reg_C, reg_A, reg_B
+ slti reg_R, const_int0, reg_C */
+ tmp_reg = gen_reg_rtx (SImode);
+ emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3]));
+ if (code == EQ)
+ emit_insn (gen_slt_eq0 (operands[0], tmp_reg));
+ else
+ emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg));
+
+ return EXPAND_DONE;
+ }
+ case GT:
+ case GTU:
+ /* reg_R = (reg_A > reg_B) --> slt reg_R, reg_B, reg_A */
+ /* reg_R = (reg_A > const_int_B) --> slt reg_R, const_int_B, reg_A */
+ if (code == GT)
+ {
+ /* GT, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0], operands[3], operands[2]));
+ }
+ else
+ {
+ /* GTU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0], operands[3], operands[2]));
+ }
+
+ return EXPAND_DONE;
+
+ case GE:
+ case GEU:
+ if (GET_CODE (operands[3]) == CONST_INT)
+ {
+ /* reg_R = (reg_A >= const_int_B)
+ --> movi reg_C, const_int_B - 1
+ slt reg_R, reg_C, reg_A */
+ tmp_reg = gen_reg_rtx (SImode);
+
+ emit_insn (gen_movsi (tmp_reg,
+ gen_int_mode (INTVAL (operands[3]) - 1,
+ SImode)));
+ if (code == GE)
+ {
+ /* GE, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0], tmp_reg, operands[2]));
+ }
+ else
+ {
+ /* GEU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0], tmp_reg, operands[2]));
+ }
+
+ return EXPAND_DONE;
+ }
+ else
+ {
+ /* reg_R = (reg_A >= reg_B)
+ --> slt reg_R, reg_A, reg_B
+ xori reg_R, reg_R, const_int_1 */
+ if (code == GE)
+ {
+ /* GE, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0],
+ operands[2], operands[3]));
+ }
+ else
+ {
+ /* GEU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0],
+ operands[2], operands[3]));
+ }
+
+ /* perform 'not' behavior */
+ emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
+
+ return EXPAND_DONE;
+ }
+
+ case LT:
+ case LTU:
+ /* reg_R = (reg_A < reg_B) --> slt reg_R, reg_A, reg_B */
+ /* reg_R = (reg_A < const_int_B) --> slt reg_R, reg_A, const_int_B */
+ if (code == LT)
+ {
+ /* LT, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0], operands[2], operands[3]));
+ }
+ else
+ {
+ /* LTU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0], operands[2], operands[3]));
+ }
+
+ return EXPAND_DONE;
+
+ case LE:
+ case LEU:
+ if (GET_CODE (operands[3]) == CONST_INT)
+ {
+ /* reg_R = (reg_A <= const_int_B)
+ --> movi reg_C, const_int_B + 1
+ slt reg_R, reg_A, reg_C */
+ tmp_reg = gen_reg_rtx (SImode);
+
+ emit_insn (gen_movsi (tmp_reg,
+ gen_int_mode (INTVAL (operands[3]) + 1,
+ SImode)));
+ if (code == LE)
+ {
+ /* LE, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0], operands[2], tmp_reg));
+ }
+ else
+ {
+ /* LEU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0], operands[2], tmp_reg));
+ }
+
+ return EXPAND_DONE;
+ }
+ else
+ {
+ /* reg_R = (reg_A <= reg_B) --> slt reg_R, reg_B, reg_A
+ xori reg_R, reg_R, const_int_1 */
+ if (code == LE)
+ {
+ /* LE, use slts instruction */
+ emit_insn (gen_slts_compare (operands[0],
+ operands[3], operands[2]));
+ }
+ else
+ {
+ /* LEU, use slt instruction */
+ emit_insn (gen_slt_compare (operands[0],
+ operands[3], operands[2]));
+ }
+
+ /* perform 'not' behavior */
+ emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
+
+ return EXPAND_DONE;
+ }
+
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+enum nds32_expand_result_type
+nds32_expand_movcc (rtx *operands)
+{
+ enum rtx_code code = GET_CODE (operands[1]);
+ enum rtx_code new_code = code;
+ machine_mode cmp0_mode = GET_MODE (XEXP (operands[1], 0));
+ rtx cmp_op0 = XEXP (operands[1], 0);
+ rtx cmp_op1 = XEXP (operands[1], 1);
+ rtx tmp;
+
+ if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
+ && XEXP (operands[1], 1) == const0_rtx)
+ {
+ /* If the operands[1] rtx is already (eq X 0) or (ne X 0),
+ we have gcc generate original template rtx. */
+ return EXPAND_CREATE_TEMPLATE;
+ }
+ else
+ {
+ /* Since there is only 'slt'(Set when Less Than) instruction for
+ comparison in Andes ISA, the major strategy we use here is to
+ convert conditional move into 'LT + EQ' or 'LT + NE' rtx combination.
+ We design constraints properly so that the reload phase will assist
+ to make one source operand to use same register as result operand.
+ Then we can use cmovz/cmovn to catch the other source operand
+ which has different register. */
+ int reverse = 0;
+
+ /* Main Goal: Use 'LT + EQ' or 'LT + NE' to target "then" part
+ Strategy : Reverse condition and swap comparison operands
+
+ For example:
+
+ a <= b ? P : Q (LE or LEU)
+ --> a > b ? Q : P (reverse condition)
+ --> b < a ? Q : P (swap comparison operands to achieve 'LT/LTU')
+
+ a >= b ? P : Q (GE or GEU)
+ --> a < b ? Q : P (reverse condition to achieve 'LT/LTU')
+
+ a < b ? P : Q (LT or LTU)
+ --> (NO NEED TO CHANGE, it is already 'LT/LTU')
+
+ a > b ? P : Q (GT or GTU)
+ --> b < a ? P : Q (swap comparison operands to achieve 'LT/LTU') */
+ switch (code)
+ {
+ case GE: case GEU: case LE: case LEU:
+ new_code = reverse_condition (code);
+ reverse = 1;
+ break;
+ case EQ:
+ case NE:
+ /* no need to reverse condition */
+ break;
+ default:
+ return EXPAND_FAIL;
+ }
+
+ /* For '>' comparison operator, we swap operands
+ so that we can have 'LT/LTU' operator. */
+ if (new_code == GT || new_code == GTU)
+ {
+ tmp = cmp_op0;
+ cmp_op0 = cmp_op1;
+ cmp_op1 = tmp;
+
+ new_code = swap_condition (new_code);
+ }
+
+ /* Use a temporary register to store slt/slts result. */
+ tmp = gen_reg_rtx (SImode);
+
+ if (new_code == EQ || new_code == NE)
+ {
+ emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1));
+ /* tmp == 0 if cmp_op0 == cmp_op1. */
+ operands[1] = gen_rtx_fmt_ee (new_code, VOIDmode, tmp, const0_rtx);
+ }
+ else
+ {
+ /* This emit_insn will create corresponding 'slt/slts'
+ insturction. */
+ if (new_code == LT)
+ emit_insn (gen_slts_compare (tmp, cmp_op0, cmp_op1));
+ else if (new_code == LTU)
+ emit_insn (gen_slt_compare (tmp, cmp_op0, cmp_op1));
+ else
+ gcc_unreachable ();
+
+ /* Change comparison semantic into (eq X 0) or (ne X 0) behavior
+ so that cmovz or cmovn will be matched later.
+
+ For reverse condition cases, we want to create a semantic that:
+ (eq X 0) --> pick up "else" part
+ For normal cases, we want to create a semantic that:
+ (ne X 0) --> pick up "then" part
+
+ Later we will have cmovz/cmovn instruction pattern to
+ match corresponding behavior and output instruction. */
+ operands[1] = gen_rtx_fmt_ee (reverse ? EQ : NE,
+ VOIDmode, tmp, const0_rtx);
+ }
+ }
+ return EXPAND_CREATE_TEMPLATE;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* Function to return memory format. */
enum nds32_16bit_address_type
nds32_mem_format (rtx op)
{
}
}
+const char *
+nds32_output_cbranchsi4_equality_zero (rtx_insn *insn, rtx *operands)
+{
+ enum rtx_code code;
+ bool long_jump_p = false;
+
+ code = GET_CODE (operands[0]);
+
+ /* This zero-comparison conditional branch has two forms:
+ 32-bit instruction => beqz/bnez imm16s << 1
+ 16-bit instruction => beqzs8/bnezs8/beqz38/bnez38 imm8s << 1
+
+ For 32-bit case,
+ we assume it is always reachable. (but check range -65500 ~ 65500)
+
+ For 16-bit case,
+ it must satisfy { 255 >= (label - pc) >= -256 } condition.
+ However, since the $pc for nds32 is at the beginning of the instruction,
+ we should leave some length space for current insn.
+ So we use range -250 ~ 250. */
+
+ switch (get_attr_length (insn))
+ {
+ case 8:
+ long_jump_p = true;
+ /* fall through */
+ case 2:
+ if (which_alternative == 0)
+ {
+ /* constraint: t */
+ /* b<cond>zs8 .L0
+ or
+ b<inverse_cond>zs8 .LCB0
+ j .L0
+ .LCB0:
+ */
+ output_cond_branch_compare_zero (code, "s8", long_jump_p,
+ operands, true);
+ return "";
+ }
+ else if (which_alternative == 1)
+ {
+ /* constraint: l */
+ /* b<cond>z38 $r0, .L0
+ or
+ b<inverse_cond>z38 $r0, .LCB0
+ j .L0
+ .LCB0:
+ */
+ output_cond_branch_compare_zero (code, "38", long_jump_p,
+ operands, false);
+ return "";
+ }
+ else
+ {
+ /* constraint: r */
+ /* For which_alternative==2, it should not be here. */
+ gcc_unreachable ();
+ }
+ case 10:
+ /* including constraints: t, l, and r */
+ long_jump_p = true;
+ /* fall through */
+ case 4:
+ /* including constraints: t, l, and r */
+ output_cond_branch_compare_zero (code, "", long_jump_p, operands, false);
+ return "";
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+const char *
+nds32_output_cbranchsi4_equality_reg (rtx_insn *insn, rtx *operands)
+{
+ enum rtx_code code;
+ bool long_jump_p, r5_p;
+ int insn_length;
+
+ insn_length = get_attr_length (insn);
+
+ long_jump_p = (insn_length == 10 || insn_length == 8) ? true : false;
+ r5_p = (insn_length == 2 || insn_length == 8) ? true : false;
+
+ code = GET_CODE (operands[0]);
+
+ /* This register-comparison conditional branch has one form:
+ 32-bit instruction => beq/bne imm14s << 1
+
+ For 32-bit case,
+ we assume it is always reachable. (but check range -16350 ~ 16350). */
+
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ output_cond_branch (code, "", r5_p, long_jump_p, operands);
+ return "";
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+const char *
+nds32_output_cbranchsi4_equality_reg_or_const_int (rtx_insn *insn,
+ rtx *operands)
+{
+ enum rtx_code code;
+ bool long_jump_p, r5_p;
+ int insn_length;
+
+ insn_length = get_attr_length (insn);
+
+ long_jump_p = (insn_length == 10 || insn_length == 8) ? true : false;
+ r5_p = (insn_length == 2 || insn_length == 8) ? true : false;
+
+ code = GET_CODE (operands[0]);
+
+ /* This register-comparison conditional branch has one form:
+ 32-bit instruction => beq/bne imm14s << 1
+ 32-bit instruction => beqc/bnec imm8s << 1
+
+ For 32-bit case, we assume it is always reachable.
+ (but check range -16350 ~ 16350 and -250 ~ 250). */
+
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ if (which_alternative == 2)
+ {
+ /* r, Is11 */
+ /* b<cond>c */
+ output_cond_branch (code, "c", r5_p, long_jump_p, operands);
+ }
+ else
+ {
+ /* r, r */
+ /* v, r */
+ output_cond_branch (code, "", r5_p, long_jump_p, operands);
+ }
+ return "";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+const char *
+nds32_output_cbranchsi4_greater_less_zero (rtx_insn *insn, rtx *operands)
+{
+ enum rtx_code code;
+ bool long_jump_p;
+ int insn_length;
+
+ insn_length = get_attr_length (insn);
+
+ gcc_assert (insn_length == 4 || insn_length == 10);
+
+ long_jump_p = (insn_length == 10) ? true : false;
+
+ code = GET_CODE (operands[0]);
+
+ /* This zero-greater-less-comparison conditional branch has one form:
+ 32-bit instruction => bgtz/bgez/bltz/blez imm16s << 1
+
+ For 32-bit case, we assume it is always reachable.
+ (but check range -65500 ~ 65500). */
+
+ switch (code)
+ {
+ case GT:
+ case GE:
+ case LT:
+ case LE:
+ output_cond_branch_compare_zero (code, "", long_jump_p, operands, false);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return "";
+}
+
/* Return true X is need use long call. */
bool
nds32_long_call_p (rtx symbol)
;; Conditional Move patterns
;; ----------------------------------------------------------------------------
-(define_expand "movsicc"
- [(set (match_operand:SI 0 "register_operand" "")
- (if_then_else:SI (match_operand 1 "comparison_operator" "")
- (match_operand:SI 2 "register_operand" "")
- (match_operand:SI 3 "register_operand" "")))]
- "TARGET_CMOV"
+(define_expand "mov<mode>cc"
+ [(set (match_operand:QIHISI 0 "register_operand" "")
+ (if_then_else:QIHISI (match_operand 1 "nds32_movecc_comparison_operator" "")
+ (match_operand:QIHISI 2 "register_operand" "")
+ (match_operand:QIHISI 3 "register_operand" "")))]
+ "TARGET_CMOV && !optimize_size"
{
- if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
- && GET_MODE (XEXP (operands[1], 0)) == SImode
- && XEXP (operands[1], 1) == const0_rtx)
- {
- /* If the operands[1] rtx is already (eq X 0) or (ne X 0),
- we have gcc generate original template rtx. */
- goto create_template;
- }
- else
+ enum nds32_expand_result_type result = nds32_expand_movcc (operands);
+ switch (result)
{
- /* Since there is only 'slt'(Set when Less Than) instruction for
- comparison in Andes ISA, the major strategy we use here is to
- convert conditional move into 'LT + EQ' or 'LT + NE' rtx combination.
- We design constraints properly so that the reload phase will assist
- to make one source operand to use same register as result operand.
- Then we can use cmovz/cmovn to catch the other source operand
- which has different register. */
- enum rtx_code code = GET_CODE (operands[1]);
- enum rtx_code new_code = code;
- rtx cmp_op0 = XEXP (operands[1], 0);
- rtx cmp_op1 = XEXP (operands[1], 1);
- rtx tmp;
- int reverse = 0;
-
- /* Main Goal: Use 'LT + EQ' or 'LT + NE' to target "then" part
- Strategy : Reverse condition and swap comparison operands
-
- For example:
-
- a <= b ? P : Q (LE or LEU)
- --> a > b ? Q : P (reverse condition)
- --> b < a ? Q : P (swap comparison operands to achieve 'LT/LTU')
-
- a >= b ? P : Q (GE or GEU)
- --> a < b ? Q : P (reverse condition to achieve 'LT/LTU')
-
- a < b ? P : Q (LT or LTU)
- --> (NO NEED TO CHANGE, it is already 'LT/LTU')
-
- a > b ? P : Q (GT or GTU)
- --> b < a ? P : Q (swap comparison operands to achieve 'LT/LTU') */
- switch (code)
- {
- case NE:
- /* (a != b ? P : Q)
- can be expressed as
- (a == b ? Q : P)
- so, fall through to reverse condition */
- case GE: case GEU: case LE: case LEU:
- new_code = reverse_condition (code);
- reverse = 1;
- break;
- case EQ: case GT: case GTU: case LT: case LTU:
- /* no need to reverse condition */
- break;
- default:
- FAIL;
- }
-
- /* For '>' comparison operator, we swap operands
- so that we can have 'LT/LTU' operator. */
- if (new_code == GT || new_code == GTU)
- {
- tmp = cmp_op0;
- cmp_op0 = cmp_op1;
- cmp_op1 = tmp;
-
- new_code = swap_condition (new_code);
- }
-
- /* Use a temporary register to store slt/slts result. */
- tmp = gen_reg_rtx (SImode);
-
- /* Split EQ and NE because we don't have direct comparison of EQ and NE.
- If we don't split it, the conditional move transformation will fail
- when producing (SET A (EQ B C)) or (SET A (NE B C)). */
- if (new_code == EQ)
- {
- emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1));
- emit_insn (gen_slt_compare (tmp, tmp, GEN_INT (1)));
- }
- else if (new_code == NE)
- {
- emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1));
- emit_insn (gen_slt_compare (tmp, GEN_INT (0), tmp));
- }
- else
- /* This emit_insn will create corresponding 'slt/slts' insturction. */
- emit_insn (gen_rtx_SET (tmp, gen_rtx_fmt_ee (new_code, SImode,
- cmp_op0, cmp_op1)));
-
- /* Change comparison semantic into (eq X 0) or (ne X 0) behavior
- so that cmovz or cmovn will be matched later.
-
- For reverse condition cases, we want to create a semantic that:
- (eq X 0) --> pick up "else" part
- For normal cases, we want to create a semantic that:
- (ne X 0) --> pick up "then" part
-
- Later we will have cmovz/cmovn instruction pattern to
- match corresponding behavior and output instruction. */
- operands[1] = gen_rtx_fmt_ee (reverse ? EQ : NE,
- VOIDmode, tmp, const0_rtx);
+ case EXPAND_DONE:
+ DONE;
+ break;
+ case EXPAND_FAIL:
+ FAIL;
+ break;
+ case EXPAND_CREATE_TEMPLATE:
+ break;
+ default:
+ gcc_unreachable ();
}
-
-create_template:
- do {} while(0); /* dummy line */
})
-(define_insn "cmovz"
- [(set (match_operand:SI 0 "register_operand" "=r, r")
- (if_then_else:SI (eq (match_operand:SI 1 "register_operand" " r, r")
+(define_insn "cmovz<mode>"
+ [(set (match_operand:QIHISI 0 "register_operand" "=r, r")
+ (if_then_else:QIHISI (eq (match_operand:SI 1 "register_operand" " r, r")
(const_int 0))
- (match_operand:SI 2 "register_operand" " r, 0")
- (match_operand:SI 3 "register_operand" " 0, r")))]
+ (match_operand:QIHISI 2 "register_operand" " r, 0")
+ (match_operand:QIHISI 3 "register_operand" " 0, r")))]
"TARGET_CMOV"
"@
cmovz\t%0, %2, %1
[(set_attr "type" "alu")
(set_attr "length" "4")])
-(define_insn "cmovn"
- [(set (match_operand:SI 0 "register_operand" "=r, r")
- (if_then_else:SI (ne (match_operand:SI 1 "register_operand" " r, r")
+(define_insn "cmovn<mode>"
+ [(set (match_operand:QIHISI 0 "register_operand" "=r, r")
+ (if_then_else:QIHISI (ne (match_operand:SI 1 "register_operand" " r, r")
(const_int 0))
- (match_operand:SI 2 "register_operand" " r, 0")
- (match_operand:SI 3 "register_operand" " 0, r")))]
+ (match_operand:QIHISI 2 "register_operand" " r, 0")
+ (match_operand:QIHISI 3 "register_operand" " 0, r")))]
"TARGET_CMOV"
"@
cmovn\t%0, %2, %1
[(set_attr "type" "alu")
(set_attr "length" "4")])
+;; A hotfix to help RTL combiner to merge a cmovn insn and a zero_extend insn.
+;; It should be removed once after we change the expansion form of the cmovn.
+(define_insn "*cmovn_simplified_<mode>"
+ [(set (match_operand:QIHISI 0 "register_operand" "=r")
+ (if_then_else:QIHISI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:QIHISI 2 "register_operand" "r")
+ (match_operand:QIHISI 3 "register_operand" "0")))]
+ ""
+ "cmovn\t%0, %2, %1"
+ [(set_attr "type" "alu")])
;; ----------------------------------------------------------------------------
;; Conditional Branch patterns
(pc)))]
""
{
- rtx tmp_reg;
- enum rtx_code code;
-
- code = GET_CODE (operands[0]);
-
- /* If operands[2] is (const_int 0),
- we can use beqz,bnez,bgtz,bgez,bltz,or blez instructions.
- So we have gcc generate original template rtx. */
- if (GET_CODE (operands[2]) == CONST_INT)
- if (INTVAL (operands[2]) == 0)
- if ((code != GTU)
- && (code != GEU)
- && (code != LTU)
- && (code != LEU))
- goto create_template;
-
- /* For other comparison, NDS32 ISA only has slt (Set-on-Less-Than)
- behavior for the comparison, we might need to generate other
- rtx patterns to achieve same semantic. */
- switch (code)
+ enum nds32_expand_result_type result = nds32_expand_cbranch (operands);
+ switch (result)
{
- case GT:
- case GTU:
- if (GET_CODE (operands[2]) == CONST_INT)
- {
- /* GT reg_A, const_int => !(LT reg_A, const_int + 1) */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- /* We want to plus 1 into the integer value
- of operands[2] to create 'slt' instruction.
- This caculation is performed on the host machine,
- which may be 64-bit integer.
- So the meaning of caculation result may be
- different from the 32-bit nds32 target.
-
- For example:
- 0x7fffffff + 0x1 -> 0x80000000,
- this value is POSITIVE on 64-bit machine,
- but the expected value on 32-bit nds32 target
- should be NEGATIVE value.
-
- Hence, instead of using GEN_INT(), we use gen_int_mode() to
- explicitly create SImode constant rtx. */
- operands[2] = gen_int_mode (INTVAL (operands[2]) + 1, SImode);
-
- if (code == GT)
- {
- /* GT, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
- }
- else
- {
- /* GTU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
- }
-
- PUT_CODE (operands[0], EQ);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
- DONE;
- }
- else
- {
- /* GT reg_A, reg_B => LT reg_B, reg_A */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- if (code == GT)
- {
- /* GT, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1]));
- }
- else
- {
- /* GTU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1]));
- }
-
- PUT_CODE (operands[0], NE);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
- DONE;
- }
-
- case GE:
- case GEU:
- /* GE reg_A, reg_B => !(LT reg_A, reg_B) */
- /* GE reg_A, const_int => !(LT reg_A, const_int) */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- if (code == GE)
- {
- /* GE, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
- }
- else
- {
- /* GEU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
- }
-
- PUT_CODE (operands[0], EQ);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
+ case EXPAND_DONE:
DONE;
-
- case LT:
- case LTU:
- /* LT reg_A, reg_B => LT reg_A, reg_B */
- /* LT reg_A, const_int => LT reg_A, const_int */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- if (code == LT)
- {
- /* LT, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
- }
- else
- {
- /* LTU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
- }
-
- PUT_CODE (operands[0], NE);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
- DONE;
-
- case LE:
- case LEU:
- if (GET_CODE (operands[2]) == CONST_INT)
- {
- /* LE reg_A, const_int => LT reg_A, const_int + 1 */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- /* Note that (le:SI X INT_MAX) is not the same as (lt:SI X INT_MIN).
- We better have an assert here in case GCC does not properly
- optimize it away. The INT_MAX here is 0x7fffffff for target. */
- gcc_assert (code != LE || INTVAL (operands[2]) != 0x7fffffff);
- operands[2] = gen_int_mode (INTVAL (operands[2]) + 1, SImode);
-
- if (code == LE)
- {
- /* LE, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2]));
- }
- else
- {
- /* LEU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2]));
- }
-
- PUT_CODE (operands[0], NE);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
- DONE;
- }
- else
- {
- /* LE reg_A, reg_B => !(LT reg_B, reg_A) */
- tmp_reg = gen_rtx_REG (SImode, TA_REGNUM);
-
- if (code == LE)
- {
- /* LE, use slts instruction */
- emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1]));
- }
- else
- {
- /* LEU, use slt instruction */
- emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1]));
- }
-
- PUT_CODE (operands[0], EQ);
- operands[1] = tmp_reg;
- operands[2] = const0_rtx;
- emit_insn (gen_cbranchsi4 (operands[0], operands[1],
- operands[2], operands[3]));
-
- DONE;
- }
-
- case EQ:
- case NE:
- /* NDS32 ISA has various form for eq/ne behavior no matter
- what kind of the operand is.
- So just generate original template rtx. */
- goto create_template;
-
- default:
+ break;
+ case EXPAND_FAIL:
FAIL;
+ break;
+ case EXPAND_CREATE_TEMPLATE:
+ break;
+ default:
+ gcc_unreachable ();
}
-
-create_template:
- do {} while(0); /* dummy line */
})
-(define_insn "*cbranchsi4_equality_zero"
+(define_insn "cbranchsi4_equality_zero"
[(set (pc)
(if_then_else (match_operator 0 "nds32_equality_comparison_operator"
- [(match_operand:SI 1 "register_operand" "t, l, r")
+ [(match_operand:SI 1 "register_operand" "t,l, r")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
""
{
- enum rtx_code code;
-
- code = GET_CODE (operands[0]);
-
- /* This zero-comparison conditional branch has two forms:
- 32-bit instruction => beqz/bnez imm16s << 1
- 16-bit instruction => beqzs8/bnezs8/beqz38/bnez38 imm8s << 1
-
- For 32-bit case,
- we assume it is always reachable. (but check range -65500 ~ 65500)
-
- For 16-bit case,
- it must satisfy { 255 >= (label - pc) >= -256 } condition.
- However, since the $pc for nds32 is at the beginning of the instruction,
- we should leave some length space for current insn.
- So we use range -250 ~ 250. */
-
- switch (get_attr_length (insn))
- {
- case 2:
- if (which_alternative == 0)
- {
- /* constraint: t */
- return (code == EQ) ? "beqzs8\t%2" : "bnezs8\t%2";
- }
- else if (which_alternative == 1)
- {
- /* constraint: l */
- return (code == EQ) ? "beqz38\t%1, %2" : "bnez38\t%1, %2";
- }
- else
- {
- /* constraint: r */
- /* For which_alternative==2, it should not be here. */
- gcc_unreachable ();
- }
- case 4:
- /* including constraints: t, l, and r */
- return (code == EQ) ? "beqz\t%1, %2" : "bnez\t%1, %2";
- case 6:
- if (which_alternative == 0)
- {
- /* constraint: t */
- if (code == EQ)
- {
- /* beqzs8 .L0
- =>
- bnezs8 .LCB0
- j .L0
- .LCB0:
- */
- return "bnezs8\t.LCB%=\;j\t%2\n.LCB%=:";
- }
- else
- {
- /* bnezs8 .L0
- =>
- beqzs8 .LCB0
- j .L0
- .LCB0:
- */
- return "beqzs8\t.LCB%=\;j\t%2\n.LCB%=:";
- }
- }
- else if (which_alternative == 1)
- {
- /* constraint: l */
- if (code == EQ)
- {
- /* beqz38 $r0, .L0
- =>
- bnez38 $r0, .LCB0
- j .L0
- .LCB0:
- */
- return "bnez38\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- }
- else
- {
- /* bnez38 $r0, .L0
- =>
- beqz38 $r0, .LCB0
- j .L0
- .LCB0:
- */
- return "beqz38\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- }
- }
- else
- {
- /* constraint: r */
- /* For which_alternative==2, it should not be here. */
- gcc_unreachable ();
- }
- case 8:
- /* constraint: t, l, r. */
- if (code == EQ)
- {
- /* beqz $r8, .L0
- =>
- bnez $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "bnez\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- }
- else
- {
- /* bnez $r8, .L0
- =>
- beqz $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "beqz\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- }
- default:
- gcc_unreachable ();
- }
+ return nds32_output_cbranchsi4_equality_zero (insn, operands);
}
[(set_attr "type" "branch")
- (set_attr "enabled" "yes")
+ (set_attr_alternative "enabled"
+ [
+ ;; Alternative 0
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_string "yes")
+ (const_string "no"))
+ ;; Alternative 1
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_string "yes")
+ (const_string "no"))
+ ;; Alternative 2
+ (const_string "yes")
+ ])
(set_attr_alternative "length"
[
;; Alternative 0
- (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250))
- (le (minus (match_dup 2) (pc)) (const_int 250)))
- (if_then_else (match_test "TARGET_16_BIT")
- (const_int 2)
- (const_int 4))
- (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
- (le (minus (match_dup 2) (pc)) (const_int 65500)))
- (const_int 4)
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250))
+ (le (minus (match_dup 2) (pc)) (const_int 250)))
(if_then_else (match_test "TARGET_16_BIT")
- (const_int 6)
- (const_int 8))))
+ (const_int 2)
+ (const_int 4))
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
+ (le (minus (match_dup 2) (pc)) (const_int 65500)))
+ (const_int 4)
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_int 8)
+ (const_int 10))))
+ (const_int 10))
;; Alternative 1
- (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250))
- (le (minus (match_dup 2) (pc)) (const_int 250)))
- (if_then_else (match_test "TARGET_16_BIT")
- (const_int 2)
- (const_int 4))
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250))
+ (le (minus (match_dup 2) (pc)) (const_int 250)))
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_int 2)
+ (const_int 4))
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
+ (le (minus (match_dup 2) (pc)) (const_int 65500)))
+ (const_int 4)
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_int 8)
+ (const_int 10))))
+ (const_int 10))
+ ;; Alternative 2
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
(if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
(le (minus (match_dup 2) (pc)) (const_int 65500)))
(const_int 4)
- (if_then_else (match_test "TARGET_16_BIT")
- (const_int 6)
- (const_int 8))))
- ;; Alternative 2
- (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
- (le (minus (match_dup 2) (pc)) (const_int 65500)))
- (const_int 4)
- (const_int 8))
+ (const_int 10))
+ (const_int 10))
])])
;; This pattern is dedicated to V2 ISA,
;; because V2 DOES NOT HAVE beqc/bnec instruction.
-(define_insn "*cbranchsi4_equality_reg"
+(define_insn "cbranchsi4_equality_reg"
[(set (pc)
(if_then_else (match_operator 0 "nds32_equality_comparison_operator"
- [(match_operand:SI 1 "register_operand" "r")
- (match_operand:SI 2 "nds32_reg_constant_operand" "r")])
+ [(match_operand:SI 1 "register_operand" "v, r")
+ (match_operand:SI 2 "register_operand" "l, r")])
(label_ref (match_operand 3 "" ""))
(pc)))]
"TARGET_ISA_V2"
{
- enum rtx_code code;
-
- code = GET_CODE (operands[0]);
-
- /* This register-comparison conditional branch has one form:
- 32-bit instruction => beq/bne imm14s << 1
-
- For 32-bit case,
- we assume it is always reachable. (but check range -16350 ~ 16350). */
-
- switch (code)
- {
- case EQ:
- /* r, r */
- switch (get_attr_length (insn))
- {
- case 4:
- return "beq\t%1, %2, %3";
- case 8:
- /* beq $r0, $r1, .L0
- =>
- bne $r0, $r1, .LCB0
- j .L0
- .LCB0:
- */
- return "bne\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
-
- case NE:
- /* r, r */
- switch (get_attr_length (insn))
- {
- case 4:
- return "bne\t%1, %2, %3";
- case 8:
- /* bne $r0, $r1, .L0
- =>
- beq $r0, $r1, .LCB0
- j .L0
- .LCB0:
- */
- return "beq\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
-
- default:
- gcc_unreachable ();
- }
+ return nds32_output_cbranchsi4_equality_reg (insn, operands);
}
[(set_attr "type" "branch")
- (set (attr "length")
- (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350))
- (le (minus (match_dup 3) (pc)) (const_int 16350)))
- (const_int 4)
- (const_int 8)))])
+ (set_attr_alternative "enabled"
+ [
+ ;; Alternative 0
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_string "yes")
+ (const_string "no"))
+ ;; Alternative 1
+ (const_string "yes")
+ ])
+ (set_attr_alternative "length"
+ [
+ ;; Alternative 0
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -250))
+ (le (minus (match_dup 3) (pc)) (const_int 250)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 3) (pc))
+ (const_int -16350))
+ (le (minus (match_dup 3) (pc))
+ (const_int 16350)))
+ (const_int 4)
+ (const_int 8)))
+ (const_int 8))
+ ;; Alternative 1
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350))
+ (le (minus (match_dup 3) (pc)) (const_int 16350)))
+ (const_int 4)
+ (const_int 10))
+ (const_int 10))
+ ])])
;; This pattern is dedicated to V3/V3M,
;; because V3/V3M DO HAVE beqc/bnec instruction.
-(define_insn "*cbranchsi4_equality_reg_or_const_int"
+(define_insn "cbranchsi4_equality_reg_or_const_int"
[(set (pc)
(if_then_else (match_operator 0 "nds32_equality_comparison_operator"
- [(match_operand:SI 1 "register_operand" "r, r")
- (match_operand:SI 2 "nds32_reg_constant_operand" "r, Is11")])
+ [(match_operand:SI 1 "register_operand" "v, r, r")
+ (match_operand:SI 2 "nds32_rimm11s_operand" "l, r, Is11")])
(label_ref (match_operand 3 "" ""))
(pc)))]
"TARGET_ISA_V3 || TARGET_ISA_V3M"
{
- enum rtx_code code;
-
- code = GET_CODE (operands[0]);
-
- /* This register-comparison conditional branch has one form:
- 32-bit instruction => beq/bne imm14s << 1
- 32-bit instruction => beqc/bnec imm8s << 1
-
- For 32-bit case, we assume it is always reachable.
- (but check range -16350 ~ 16350 and -250 ~ 250). */
-
- switch (code)
- {
- case EQ:
- if (which_alternative == 0)
- {
- /* r, r */
- switch (get_attr_length (insn))
- {
- case 4:
- return "beq\t%1, %2, %3";
- case 8:
- /* beq $r0, $r1, .L0
- =>
- bne $r0, $r1, .LCB0
- j .L0
- .LCB0:
- */
- return "bne\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
- }
- else
- {
- /* r, Is11 */
- switch (get_attr_length (insn))
- {
- case 4:
- return "beqc\t%1, %2, %3";
- case 8:
- /* beqc $r0, constant, .L0
- =>
- bnec $r0, constant, .LCB0
- j .L0
- .LCB0:
- */
- return "bnec\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
- }
- case NE:
- if (which_alternative == 0)
- {
- /* r, r */
- switch (get_attr_length (insn))
- {
- case 4:
- return "bne\t%1, %2, %3";
- case 8:
- /* bne $r0, $r1, .L0
- =>
- beq $r0, $r1, .LCB0
- j .L0
- .LCB0:
- */
- return "beq\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
- }
- else
- {
- /* r, Is11 */
- switch (get_attr_length (insn))
- {
- case 4:
- return "bnec\t%1, %2, %3";
- case 8:
- /* bnec $r0, constant, .L0
- =>
- beqc $r0, constant, .LCB0
- j .L0
- .LCB0:
- */
- return "beqc\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
- }
- default:
- gcc_unreachable ();
- }
+ return nds32_output_cbranchsi4_equality_reg_or_const_int (insn, operands);
}
[(set_attr "type" "branch")
+ (set_attr_alternative "enabled"
+ [
+ ;; Alternative 0
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_string "yes")
+ (const_string "no"))
+ ;; Alternative 1
+ (const_string "yes")
+ ;; Alternative 2
+ (const_string "yes")
+ ])
(set_attr_alternative "length"
[
;; Alternative 0
- (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350))
- (le (minus (match_dup 3) (pc)) (const_int 16350)))
- (const_int 4)
- (const_int 8))
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -250))
+ (le (minus (match_dup 3) (pc)) (const_int 250)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (match_dup 3) (pc))
+ (const_int -16350))
+ (le (minus (match_dup 3) (pc))
+ (const_int 16350)))
+ (const_int 4)
+ (const_int 8)))
+ (const_int 8))
;; Alternative 1
- (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -250))
- (le (minus (match_dup 3) (pc)) (const_int 250)))
- (const_int 4)
- (const_int 8))
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350))
+ (le (minus (match_dup 3) (pc)) (const_int 16350)))
+ (const_int 4)
+ (const_int 10))
+ (const_int 10))
+ ;; Alternative 2
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -250))
+ (le (minus (match_dup 3) (pc)) (const_int 250)))
+ (const_int 4)
+ (const_int 10))
+ (const_int 10))
])])
(pc)))]
""
{
- enum rtx_code code;
-
- code = GET_CODE (operands[0]);
-
- /* This zero-greater-less-comparison conditional branch has one form:
- 32-bit instruction => bgtz/bgez/bltz/blez imm16s << 1
-
- For 32-bit case, we assume it is always reachable.
- (but check range -65500 ~ 65500). */
-
- if (get_attr_length (insn) == 8)
- {
- /* The branch target is too far to simply use one
- bgtz/bgez/bltz/blez instruction.
- We need to reverse condition and use 'j' to jump to the target. */
- switch (code)
- {
- case GT:
- /* bgtz $r8, .L0
- =>
- blez $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "blez\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- case GE:
- /* bgez $r8, .L0
- =>
- bltz $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "bltz\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- case LT:
- /* bltz $r8, .L0
- =>
- bgez $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "bgez\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- case LE:
- /* blez $r8, .L0
- =>
- bgtz $r8, .LCB0
- j .L0
- .LCB0:
- */
- return "bgtz\t%1, .LCB%=\;j\t%2\n.LCB%=:";
- default:
- gcc_unreachable ();
- }
- }
-
- switch (code)
- {
- case GT:
- return "bgtz\t%1, %2";
- case GE:
- return "bgez\t%1, %2";
- case LT:
- return "bltz\t%1, %2";
- case LE:
- return "blez\t%1, %2";
- default:
- gcc_unreachable ();
- }
+ return nds32_output_cbranchsi4_greater_less_zero (insn, operands);
}
[(set_attr "type" "branch")
(set (attr "length")
- (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
- (le (minus (match_dup 2) (pc)) (const_int 65500)))
- (const_int 4)
- (const_int 8)))])
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500))
+ (le (minus (match_dup 2) (pc)) (const_int 65500)))
+ (const_int 4)
+ (const_int 10))
+ (const_int 10)))])
(define_expand "cstoresi4"
(match_operand:SI 3 "nonmemory_operand" "")]))]
""
{
- rtx tmp_reg;
- enum rtx_code code;
-
- code = GET_CODE (operands[1]);
-
- switch (code)
+ enum nds32_expand_result_type result = nds32_expand_cstore (operands);
+ switch (result)
{
- case EQ:
- if (GET_CODE (operands[3]) == CONST_INT)
- {
- /* reg_R = (reg_A == const_int_B)
- --> addi reg_C, reg_A, -const_int_B
- slti reg_R, reg_C, const_int_1 */
- tmp_reg = gen_reg_rtx (SImode);
- operands[3] = gen_int_mode (-INTVAL (operands[3]), SImode);
- /* If the integer value is not in the range of imm15s,
- we need to force register first because our addsi3 pattern
- only accept nds32_rimm15s_operand predicate. */
- if (!satisfies_constraint_Is15 (operands[3]))
- operands[3] = force_reg (SImode, operands[3]);
- emit_insn (gen_addsi3 (tmp_reg, operands[2], operands[3]));
- emit_insn (gen_slt_compare (operands[0], tmp_reg, const1_rtx));
-
- DONE;
- }
- else
- {
- /* reg_R = (reg_A == reg_B)
- --> xor reg_C, reg_A, reg_B
- slti reg_R, reg_C, const_int_1 */
- tmp_reg = gen_reg_rtx (SImode);
- emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3]));
- emit_insn (gen_slt_compare (operands[0], tmp_reg, const1_rtx));
-
- DONE;
- }
-
- case NE:
- if (GET_CODE (operands[3]) == CONST_INT)
- {
- /* reg_R = (reg_A != const_int_B)
- --> addi reg_C, reg_A, -const_int_B
- slti reg_R, const_int_0, reg_C */
- tmp_reg = gen_reg_rtx (SImode);
- operands[3] = gen_int_mode (-INTVAL (operands[3]), SImode);
- /* If the integer value is not in the range of imm15s,
- we need to force register first because our addsi3 pattern
- only accept nds32_rimm15s_operand predicate. */
- if (!satisfies_constraint_Is15 (operands[3]))
- operands[3] = force_reg (SImode, operands[3]);
- emit_insn (gen_addsi3 (tmp_reg, operands[2], operands[3]));
- emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg));
-
- DONE;
- }
- else
- {
- /* reg_R = (reg_A != reg_B)
- --> xor reg_C, reg_A, reg_B
- slti reg_R, const_int_0, reg_C */
- tmp_reg = gen_reg_rtx (SImode);
- emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3]));
- emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg));
-
- DONE;
- }
-
- case GT:
- case GTU:
- /* reg_R = (reg_A > reg_B) --> slt reg_R, reg_B, reg_A */
- /* reg_R = (reg_A > const_int_B) --> slt reg_R, const_int_B, reg_A */
- if (code == GT)
- {
- /* GT, use slts instruction */
- emit_insn (gen_slts_compare (operands[0], operands[3], operands[2]));
- }
- else
- {
- /* GTU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0], operands[3], operands[2]));
- }
-
+ case EXPAND_DONE:
DONE;
-
- case GE:
- case GEU:
- if (GET_CODE (operands[3]) == CONST_INT)
- {
- /* reg_R = (reg_A >= const_int_B)
- --> movi reg_C, const_int_B - 1
- slt reg_R, reg_C, reg_A */
- tmp_reg = gen_reg_rtx (SImode);
-
- emit_insn (gen_movsi (tmp_reg,
- gen_int_mode (INTVAL (operands[3]) - 1,
- SImode)));
- if (code == GE)
- {
- /* GE, use slts instruction */
- emit_insn (gen_slts_compare (operands[0], tmp_reg, operands[2]));
- }
- else
- {
- /* GEU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0], tmp_reg, operands[2]));
- }
-
- DONE;
- }
- else
- {
- /* reg_R = (reg_A >= reg_B)
- --> slt reg_R, reg_A, reg_B
- xori reg_R, reg_R, const_int_1 */
- if (code == GE)
- {
- /* GE, use slts instruction */
- emit_insn (gen_slts_compare (operands[0],
- operands[2], operands[3]));
- }
- else
- {
- /* GEU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0],
- operands[2], operands[3]));
- }
-
- /* perform 'not' behavior */
- emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
-
- DONE;
- }
-
- case LT:
- case LTU:
- /* reg_R = (reg_A < reg_B) --> slt reg_R, reg_A, reg_B */
- /* reg_R = (reg_A < const_int_B) --> slt reg_R, reg_A, const_int_B */
- if (code == LT)
- {
- /* LT, use slts instruction */
- emit_insn (gen_slts_compare (operands[0], operands[2], operands[3]));
- }
- else
- {
- /* LTU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0], operands[2], operands[3]));
- }
-
- DONE;
-
- case LE:
- case LEU:
- if (GET_CODE (operands[3]) == CONST_INT)
- {
- /* reg_R = (reg_A <= const_int_B)
- --> movi reg_C, const_int_B + 1
- slt reg_R, reg_A, reg_C */
- tmp_reg = gen_reg_rtx (SImode);
-
- emit_insn (gen_movsi (tmp_reg,
- gen_int_mode (INTVAL (operands[3]) + 1,
- SImode)));
- if (code == LE)
- {
- /* LE, use slts instruction */
- emit_insn (gen_slts_compare (operands[0], operands[2], tmp_reg));
- }
- else
- {
- /* LEU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0], operands[2], tmp_reg));
- }
-
- DONE;
- }
- else
- {
- /* reg_R = (reg_A <= reg_B) --> slt reg_R, reg_B, reg_A
- xori reg_R, reg_R, const_int_1 */
- if (code == LE)
- {
- /* LE, use slts instruction */
- emit_insn (gen_slts_compare (operands[0],
- operands[3], operands[2]));
- }
- else
- {
- /* LEU, use slt instruction */
- emit_insn (gen_slt_compare (operands[0],
- operands[3], operands[2]));
- }
-
- /* perform 'not' behavior */
- emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
-
- DONE;
- }
-
-
+ break;
+ case EXPAND_FAIL:
+ FAIL;
+ break;
+ case EXPAND_CREATE_TEMPLATE:
+ break;
default:
gcc_unreachable ();
}
})
-(define_insn "slts_compare"
- [(set (match_operand:SI 0 "register_operand" "=t, t, r, r")
- (lt:SI (match_operand:SI 1 "nonmemory_operand" " d, d, r, r")
- (match_operand:SI 2 "nonmemory_operand" " r, Iu05, r, Is15")))]
+(define_expand "slts_compare"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lt:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "general_operand" "")))]
+ ""
+{
+ if (!REG_P (operands[1]))
+ operands[1] = force_reg (SImode, operands[1]);
+
+ if (!REG_P (operands[2]) && !satisfies_constraint_Is15 (operands[2]))
+ operands[2] = force_reg (SImode, operands[2]);
+})
+
+(define_insn "slts_compare_impl"
+ [(set (match_operand:SI 0 "register_operand" "=t, t, r, r")
+ (lt:SI (match_operand:SI 1 "register_operand" " d, d, r, r")
+ (match_operand:SI 2 "nds32_rimm15s_operand" " r,Iu05, r, Is15")))]
""
"@
slts45\t%1, %2
sltsi45\t%1, %2
slts\t%0, %1, %2
sltsi\t%0, %1, %2"
- [(set_attr "type" " alu, alu, alu, alu")
- (set_attr "length" " 2, 2, 4, 4")])
+ [(set_attr "type" "alu, alu, alu, alu")
+ (set_attr "length" " 2, 2, 4, 4")])
+
+(define_insn "slt_eq0"
+ [(set (match_operand:SI 0 "register_operand" "=t, r")
+ (eq:SI (match_operand:SI 1 "register_operand" " d, r")
+ (const_int 0)))]
+ ""
+ "@
+ slti45\t%1, 1
+ slti\t%0, %1, 1"
+ [(set_attr "type" "alu, alu")
+ (set_attr "length" " 2, 4")])
+
+(define_expand "slt_compare"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ltu:SI (match_operand:SI 1 "general_operand" "")
+ (match_operand:SI 2 "general_operand" "")))]
+ ""
+{
+ if (!REG_P (operands[1]))
+ operands[1] = force_reg (SImode, operands[1]);
+
+ if (!REG_P (operands[2]) && !satisfies_constraint_Is15 (operands[2]))
+ operands[2] = force_reg (SImode, operands[2]);
+})
-(define_insn "slt_compare"
- [(set (match_operand:SI 0 "register_operand" "=t, t, r, r")
- (ltu:SI (match_operand:SI 1 "nonmemory_operand" " d, d, r, r")
- (match_operand:SI 2 "nonmemory_operand" " r, Iu05, r, Is15")))]
+(define_insn "slt_compare_impl"
+ [(set (match_operand:SI 0 "register_operand" "=t, t, r, r")
+ (ltu:SI (match_operand:SI 1 "register_operand" " d, d, r, r")
+ (match_operand:SI 2 "nds32_rimm15s_operand" " r, Iu05, r, Is15")))]
""
"@
slt45\t%1, %2
[(set_attr "type" "branch")
(set_attr "enabled" "yes")
(set (attr "length")
- (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
- (le (minus (match_dup 0) (pc)) (const_int 250)))
- (if_then_else (match_test "TARGET_16_BIT")
- (const_int 2)
+ (if_then_else (match_test "!CROSSING_JUMP_P (insn)")
+ (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250))
+ (le (minus (match_dup 0) (pc)) (const_int 250)))
+ (if_then_else (match_test "TARGET_16_BIT")
+ (const_int 2)
+ (const_int 4))
(const_int 4))
(const_int 4)))])