+2015-07-14 Sandra Loosemore <sandra@codesourcery.com>
+ Cesar Philippidis <cesar@codesourcery.com>
+ Chung-Lin Tang <cltang@codesourcery.com>
+
+ * config/nios2/nios2.h (LABEL_ALIGN): Define.
+ (REG_ALLOC_ORDER): Define.
+ (ADJUST_REG_ALLOC_ORDER): Define.
+ (HONOR_REG_ALLOC_ORDER): Define.
+ (CDX_REG_P): Define.
+ (ANDCLEAR_INT): Define.
+ * config/nios2/nios2-protos.h (nios2_add_insn_asm): Declare.
+ (nios2_label_align): Declare.
+ (nios2_cdx_narrow_form_p): Declare.
+ (nios2_adjust_reg_alloc_order): Declare.
+ * config/nios2/nios2.c (nios2_rtx_costs): Adjust for BMX zero-extract
+ operation.
+ (nios2_large_unspec_reloc_p): New function, split from...
+ (nios2_legitimate_pic_operand_p): ...here.
+ (nios2_emit_move_sequence): Add *high/*lo_sum constant expand code.
+ (nios2_print_operand_punct_valid_p): New.
+ (nios2_print_operand): Add %., %!, %x, %y, %A. Remove %U.
+ (split_mem_address): New.
+ (split_alu_insn): New.
+ (cdxreg): New.
+ (cdx_add_immed, cdx_and_immed, cdx_mov_immed, cdx_shift_immed): New.
+ (enum nios2_add_insn_kind): New.
+ (nios2_add_insn_names, nios2_add_insn_narrow): New.
+ (nios2_add_insn_classify): New.
+ (nios2_add_insn_asm): New.
+ (nios2_cdx_narrow_form_p): New.
+ (label_align, min_labelno, max_labelno): New.
+ (nios2_reorg): New.
+ (nios2_label_align): New.
+ (nios2_adjust_reg_alloc_order): New.
+ (TARGET_PRINT_OPERAND_PUNCT_VALID_P): Define.
+ (TARGET_MACHINE_DEPENDENT_REORG): Define.
+ * config/nios2/constraints.md (P): New constraint.
+ * config/nios2/predicates.md (const_and_operand): New.
+ (and_operand): New.
+ (stack_memory_operand): New.
+ * config/nios2/nios2.md (SP_REGNO): Define stack pointer regno.
+ (length): Update to use nios2_cdx_narrow_form_p().
+ (type): Add new insn type values.
+ (control, alu, st, ld, shift): Update insn reservations with
+ new insn type values.
+ (*high, *lo_sum): Define new insn patterns for constant generation.
+ (movqi_internal, movhi_internal, movsi_internal): Reduce
+ alternatives, update asm template to handle CDX variants, update
+ type attributes.
+ (zero_extendhisi2, zero_extendqi<mode>2): Add CDX variants to asm
+ template, update type attributes.
+ (extendhisi2, extendqi<mode>2): Likewise.
+ (addsi3): Change to use function for asm string.
+ (subsi3): Add CDX notation to asm template, update type attributes.
+ (negsi3, one_cmplsi3): Likewise.
+ (andsi3): New pattern, specialized from logical patterns.
+ (<code>si3): Remove and case, combine alternatives, update asm
+ template.
+ (<shift_op>si3): Add CDX notation, update type attributes.
+ (rotrsi3): Update type attribute.
+ (*merge, extzv, insv): New insn patterns.
+ (return): Change to define_expand.
+ (simple_return): Add CDX notation, update type attributes.
+ (indirect_jump): Add CDX notation.
+ (jump): Update asm cases, update length attribute expression.
+ (*call, *call_value, *sibcall, *sibcall_value): Add CDX variant.
+ (nios2_cbranch): Update asm cases and length attribute expression
+ to handle CDX variants.
+ (nios2_cmp<code>): Update asm template.
+ (nop): Add CDX notation, update type attributes.
+ (trap): Add CDX notation.
+ (ctrapsi4): Update asm cases and length attribute expression to
+ handle CDX variant.
+ * doc/md.texi (Machine Constraints): Document P constraint.
+
2015-07-14 Sandra Loosemore <sandra@codesourcery.com>
Cesar Philippidis <cesar@codesourcery.com>
Chung-Lin Tang <cltang@codesourcery.com>
;; We use the following constraint letters for constants
;;
-;; I: -32768 to -32767
+;; I: -32768 to 32767
;; J: 0 to 65535
;; K: $nnnn0000 for some nnnn
+;; P: Under R2, $nnnnffff or $ffffnnnn for some nnnn
;; L: 0 to 31 (for shift counts)
;; M: 0
;; N: 0 to 255 (for custom instruction numbers)
(and (match_code "const_int")
(match_test "ival >= 0 && ival <= 31")))
+(define_constraint "P"
+ "An immediate operand for R2 andchi/andci instructions."
+ (and (match_code "const_int")
+ (match_test "TARGET_ARCH_R2 && ANDCLEAR_INT (ival)")))
+
(define_constraint "S"
"An immediate stored in small data, accessible by GP."
(match_test "gprel_constant_p (op)"))
extern bool nios2_fpu_insn_enabled (enum n2fpu_code);
extern const char * nios2_fpu_insn_asm (enum n2fpu_code);
+extern const char * nios2_add_insn_asm (rtx_insn *, rtx *);
extern bool nios2_legitimate_pic_operand_p (rtx);
extern bool gprel_constant_p (rtx);
extern bool nios2_regno_ok_for_base_p (int, bool);
extern bool nios2_unspec_reloc_p (rtx);
+extern int nios2_label_align (rtx);
+extern bool nios2_cdx_narrow_form_p (rtx_insn *);
+
+extern void nios2_adjust_reg_alloc_order (void);
+
#ifdef TREE_CODE
#ifdef ARGS_SIZE_RTX
/* expr.h defines both ARGS_SIZE_RTX and `enum direction' */
return false;
}
+ case ZERO_EXTRACT:
+ if (TARGET_HAS_BMX)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+
default:
return false;
}
&& ! nios2_large_offset_p (XINT (XEXP (op, 0), 1)));
}
+static bool
+nios2_large_unspec_reloc_p (rtx op)
+{
+ return (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == UNSPEC
+ && nios2_large_offset_p (XINT (XEXP (op, 0), 1)));
+}
+
/* Helper to generate unspec constant. */
static rtx
nios2_unspec_offset (rtx loc, int unspec)
bool
nios2_legitimate_pic_operand_p (rtx x)
{
- if (GET_CODE (x) == CONST
- && GET_CODE (XEXP (x, 0)) == UNSPEC
- && nios2_large_offset_p (XINT (XEXP (x, 0), 1)))
+ if (nios2_large_unspec_reloc_p (x))
return true;
return ! (GET_CODE (x) == SYMBOL_REF
from = copy_to_mode_reg (mode, from);
}
- if (GET_CODE (from) == SYMBOL_REF || GET_CODE (from) == LABEL_REF
- || (GET_CODE (from) == CONST
- && GET_CODE (XEXP (from, 0)) != UNSPEC))
- from = nios2_legitimize_constant_address (from);
+ if (CONSTANT_P (from))
+ {
+ if (CONST_INT_P (from))
+ {
+ if (!SMALL_INT (INTVAL (from))
+ && !SMALL_INT_UNSIGNED (INTVAL (from))
+ && !UPPER16_INT (INTVAL (from)))
+ {
+ HOST_WIDE_INT high = (INTVAL (from) + 0x8000) & ~0xffff;
+ HOST_WIDE_INT low = INTVAL (from) & 0xffff;
+ emit_move_insn (to, gen_int_mode (high, SImode));
+ emit_insn (gen_add2_insn (to, gen_int_mode (low, HImode)));
+ set_unique_reg_note (get_last_insn (), REG_EQUAL,
+ copy_rtx (from));
+ return true;
+ }
+ }
+ else if (!gprel_constant_p (from))
+ {
+ if (!nios2_large_unspec_reloc_p (from))
+ from = nios2_legitimize_constant_address (from);
+ if (CONSTANT_P (from))
+ {
+ emit_insn (gen_rtx_SET (to, gen_rtx_HIGH (Pmode, from)));
+ emit_insn (gen_rtx_SET (to, gen_rtx_LO_SUM (Pmode, to, from)));
+ set_unique_reg_note (get_last_insn (), REG_EQUAL,
+ copy_rtx (operands[1]));
+ return true;
+ }
+ }
+ }
operands[0] = to;
operands[1] = from;
\f
/* Output assembly language related definitions. */
+/* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P. */
+static bool
+nios2_print_operand_punct_valid_p (unsigned char code)
+{
+ return (code == '.' || code == '!');
+}
+
+
/* Print the operand OP to file stream FILE modified by LETTER.
LETTER can be one of:
- i: print "i" if OP is an immediate, except 0
- o: print "io" if OP is volatile
- z: for const0_rtx print $0 instead of 0
+ i: print i/hi/ui suffixes (used for mov instruction variants),
+ when OP is the appropriate immediate operand.
+
+ u: like 'i', except without "ui" suffix case (used for cmpgeu/cmpltu)
+
+ o: print "io" if OP needs volatile access (due to TARGET_BYPASS_CACHE
+ or TARGET_BYPASS_CACHE_VOLATILE).
+
+ x: print i/hi/ci/chi suffixes for the and instruction,
+ when OP is the appropriate immediate operand.
+
+ z: prints the third register immediate operand in assembly
+ instructions. Outputs const0_rtx as the 'zero' register
+ instead of '0'.
+
+ y: same as 'z', but for specifically for logical instructions,
+ where the processing for immediates are slightly different.
+
H: for %hiadj
L: for %lo
- U: for upper half of 32 bit value
D: for the upper 32-bits of a 64-bit double value
R: prints reverse condition.
+ A: prints (reg) operand for ld[s]ex and st[s]ex.
+
+ .: print .n suffix for 16-bit instructions.
+ !: print r.n suffix for 16-bit instructions. Used for jmpr.n.
*/
static void
nios2_print_operand (FILE *file, rtx op, int letter)
{
+ /* First take care of the format letters that just insert a string
+ into the output stream. */
switch (letter)
{
+ case '.':
+ if (current_output_insn && get_attr_length (current_output_insn) == 2)
+ fprintf (file, ".n");
+ return;
+
+ case '!':
+ if (current_output_insn && get_attr_length (current_output_insn) == 2)
+ fprintf (file, "r.n");
+ return;
+
+ case 'x':
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT val = INTVAL (op);
+ HOST_WIDE_INT low = val & 0xffff;
+ HOST_WIDE_INT high = (val >> 16) & 0xffff;
+
+ if (val != 0)
+ {
+ if (high != 0)
+ {
+ if (low != 0)
+ {
+ gcc_assert (TARGET_ARCH_R2);
+ if (high == 0xffff)
+ fprintf (file, "c");
+ else if (low == 0xffff)
+ fprintf (file, "ch");
+ else
+ gcc_unreachable ();
+ }
+ else
+ fprintf (file, "h");
+ }
+ fprintf (file, "i");
+ }
+ }
+ return;
+
+ case 'u':
case 'i':
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT val = INTVAL (op);
+ HOST_WIDE_INT low = val & 0xffff;
+ HOST_WIDE_INT high = (val >> 16) & 0xffff;
+ if (val != 0)
+ {
+ if (low == 0 && high != 0)
+ fprintf (file, "h");
+ else if (high == 0 && (low & 0x8000) != 0 && letter != 'u')
+ fprintf (file, "u");
+ }
+ }
if (CONSTANT_P (op) && op != const0_rtx)
fprintf (file, "i");
return;
if (GET_CODE (op) == MEM
&& ((MEM_VOLATILE_P (op) && TARGET_BYPASS_CACHE_VOLATILE)
|| TARGET_BYPASS_CACHE))
- fprintf (file, "io");
+ {
+ gcc_assert (current_output_insn
+ && get_attr_length (current_output_insn) == 4);
+ fprintf (file, "io");
+ }
return;
default:
break;
}
+ /* Handle comparison operator names. */
if (comparison_operator (op, VOIDmode))
{
enum rtx_code cond = GET_CODE (op);
}
}
+ /* Now handle the cases where we actually need to format an operand. */
switch (GET_CODE (op))
{
case REG:
- if (letter == 0 || letter == 'z')
+ if (letter == 0 || letter == 'z' || letter == 'y')
{
fprintf (file, "%s", reg_names[REGNO (op)]);
return;
break;
case CONST_INT:
- if (INTVAL (op) == 0 && letter == 'z')
- {
- fprintf (file, "zero");
- return;
- }
+ {
+ rtx int_rtx = op;
+ HOST_WIDE_INT val = INTVAL (int_rtx);
+ HOST_WIDE_INT low = val & 0xffff;
+ HOST_WIDE_INT high = (val >> 16) & 0xffff;
+
+ if (letter == 'y')
+ {
+ if (val == 0)
+ fprintf (file, "zero");
+ else
+ {
+ if (high != 0)
+ {
+ if (low != 0)
+ {
+ gcc_assert (TARGET_ARCH_R2);
+ if (high == 0xffff)
+ /* andci. */
+ int_rtx = gen_int_mode (low, SImode);
+ else if (low == 0xffff)
+ /* andchi. */
+ int_rtx = gen_int_mode (high, SImode);
+ else
+ gcc_unreachable ();
+ }
+ else
+ /* andhi. */
+ int_rtx = gen_int_mode (high, SImode);
+ }
+ else
+ /* andi. */
+ int_rtx = gen_int_mode (low, SImode);
+ output_addr_const (file, int_rtx);
+ }
+ return;
+ }
+ else if (letter == 'z')
+ {
+ if (val == 0)
+ fprintf (file, "zero");
+ else
+ {
+ if (low == 0 && high != 0)
+ int_rtx = gen_int_mode (high, SImode);
+ else if (low != 0)
+ {
+ gcc_assert (high == 0 || high == 0xffff);
+ int_rtx = gen_int_mode (low, high == 0 ? SImode : HImode);
+ }
+ else
+ gcc_unreachable ();
+ output_addr_const (file, int_rtx);
+ }
+ return;
+ }
+ }
- if (letter == 'U')
- {
- HOST_WIDE_INT val = INTVAL (op);
- val = (val >> 16) & 0xFFFF;
- output_addr_const (file, gen_int_mode (val, SImode));
- return;
- }
/* Else, fall through. */
case CONST:
case SUBREG:
case MEM:
+ if (letter == 'A')
+ {
+ /* Address of '(reg)' form, with no index. */
+ fprintf (file, "(%s)", reg_names[REGNO (XEXP (op, 0))]);
+ return;
+ }
if (letter == 0)
{
output_address (op);
reload_completed = 0;
}
+
+/* Utility function to break a memory address into
+ base register + constant offset. Return false if something
+ unexpected is seen. */
+static bool
+split_mem_address (rtx addr, rtx *base_reg, rtx *offset)
+{
+ if (REG_P (addr))
+ {
+ *base_reg = addr;
+ *offset = const0_rtx;
+ return true;
+ }
+ else if (GET_CODE (addr) == PLUS)
+ {
+ *base_reg = XEXP (addr, 0);
+ *offset = XEXP (addr, 1);
+ return true;
+ }
+ return false;
+}
+
+/* Splits out the operands of an ALU insn, places them in *LHS, *RHS1, *RHS2. */
+static void
+split_alu_insn (rtx_insn *insn, rtx *lhs, rtx *rhs1, rtx *rhs2)
+{
+ rtx pat = PATTERN (insn);
+ gcc_assert (GET_CODE (pat) == SET);
+ *lhs = SET_DEST (pat);
+ *rhs1 = XEXP (SET_SRC (pat), 0);
+ if (GET_RTX_CLASS (GET_CODE (SET_SRC (pat))) != RTX_UNARY)
+ *rhs2 = XEXP (SET_SRC (pat), 1);
+ return;
+}
+
+/* Returns true if OP is a REG and assigned a CDX reg. */
+static bool
+cdxreg (rtx op)
+{
+ return REG_P (op) && (!reload_completed || CDX_REG_P (REGNO (op)));
+}
+
+/* Returns true if OP is within range of CDX addi.n immediates. */
+static bool
+cdx_add_immed (rtx op)
+{
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT ival = INTVAL (op);
+ return ival <= 128 && ival > 0 && (ival & (ival - 1)) == 0;
+ }
+ return false;
+}
+
+/* Returns true if OP is within range of CDX andi.n immediates. */
+static bool
+cdx_and_immed (rtx op)
+{
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT ival = INTVAL (op);
+ return (ival == 1 || ival == 2 || ival == 3 || ival == 4
+ || ival == 8 || ival == 0xf || ival == 0x10
+ || ival == 0x10 || ival == 0x1f || ival == 0x20
+ || ival == 0x3f || ival == 0x3f || ival == 0x7f
+ || ival == 0x80 || ival == 0xff || ival == 0x7ff
+ || ival == 0xff00 || ival == 0xffff);
+ }
+ return false;
+}
+
+/* Returns true if OP is within range of CDX movi.n immediates. */
+static bool
+cdx_mov_immed (rtx op)
+{
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT ival = INTVAL (op);
+ return ((ival >= 0 && ival <= 124)
+ || ival == 0xff || ival == -2 || ival == -1);
+ }
+ return false;
+}
+
+/* Returns true if OP is within range of CDX slli.n/srli.n immediates. */
+static bool
+cdx_shift_immed (rtx op)
+{
+ if (CONST_INT_P (op))
+ {
+ HOST_WIDE_INT ival = INTVAL (op);
+ return (ival == 1 || ival == 2 || ival == 3 || ival == 8
+ || ival == 12 || ival == 16 || ival == 24
+ || ival == 31);
+ }
+ return false;
+}
+
+
+
+/* Classification of different kinds of add instructions. */
+enum nios2_add_insn_kind {
+ nios2_add_n_kind,
+ nios2_addi_n_kind,
+ nios2_subi_n_kind,
+ nios2_spaddi_n_kind,
+ nios2_spinci_n_kind,
+ nios2_spdeci_n_kind,
+ nios2_add_kind,
+ nios2_addi_kind
+};
+
+static const char *nios2_add_insn_names[] = {
+ "add.n", "addi.n", "subi.n", "spaddi.n", "spinci.n", "spdeci.n",
+ "add", "addi" };
+static bool nios2_add_insn_narrow[] = {
+ true, true, true, true, true, true,
+ false, false};
+
+/* Function to classify kinds of add instruction patterns. */
+static enum nios2_add_insn_kind
+nios2_add_insn_classify (rtx_insn *insn ATTRIBUTE_UNUSED,
+ rtx lhs, rtx rhs1, rtx rhs2)
+{
+ if (TARGET_HAS_CDX)
+ {
+ if (cdxreg (lhs) && cdxreg (rhs1))
+ {
+ if (cdxreg (rhs2))
+ return nios2_add_n_kind;
+ if (CONST_INT_P (rhs2))
+ {
+ HOST_WIDE_INT ival = INTVAL (rhs2);
+ if (ival > 0 && cdx_add_immed (rhs2))
+ return nios2_addi_n_kind;
+ if (ival < 0 && cdx_add_immed (GEN_INT (-ival)))
+ return nios2_subi_n_kind;
+ }
+ }
+ else if (rhs1 == stack_pointer_rtx
+ && CONST_INT_P (rhs2))
+ {
+ HOST_WIDE_INT imm7 = INTVAL (rhs2) >> 2;
+ HOST_WIDE_INT rem = INTVAL (rhs2) & 3;
+ if (rem == 0 && (imm7 & ~0x7f) == 0)
+ {
+ if (cdxreg (lhs))
+ return nios2_spaddi_n_kind;
+ if (lhs == stack_pointer_rtx)
+ return nios2_spinci_n_kind;
+ }
+ imm7 = -INTVAL(rhs2) >> 2;
+ rem = -INTVAL (rhs2) & 3;
+ if (lhs == stack_pointer_rtx
+ && rem == 0 && (imm7 & ~0x7f) == 0)
+ return nios2_spdeci_n_kind;
+ }
+ }
+ return ((REG_P (rhs2) || rhs2 == const0_rtx)
+ ? nios2_add_kind : nios2_addi_kind);
+}
+
+/* Emit assembly language for the different kinds of add instructions. */
+const char*
+nios2_add_insn_asm (rtx_insn *insn, rtx *operands)
+{
+ static char buf[256];
+ int ln = 256;
+ enum nios2_add_insn_kind kind
+ = nios2_add_insn_classify (insn, operands[0], operands[1], operands[2]);
+ if (kind == nios2_subi_n_kind)
+ snprintf (buf, ln, "subi.n\t%%0, %%1, %d", (int) -INTVAL (operands[2]));
+ else if (kind == nios2_spaddi_n_kind)
+ snprintf (buf, ln, "spaddi.n\t%%0, %%2");
+ else if (kind == nios2_spinci_n_kind)
+ snprintf (buf, ln, "spinci.n\t%%2");
+ else if (kind == nios2_spdeci_n_kind)
+ snprintf (buf, ln, "spdeci.n\t%d", (int) -INTVAL (operands[2]));
+ else
+ snprintf (buf, ln, "%s\t%%0, %%1, %%z2", nios2_add_insn_names[(int)kind]);
+ return buf;
+}
+
+/* This routine, which the default "length" attribute computation is
+ based on, encapsulates information about all the cases where CDX
+ provides a narrow 2-byte instruction form. */
+bool
+nios2_cdx_narrow_form_p (rtx_insn *insn)
+{
+ rtx pat, lhs, rhs1, rhs2;
+ enum attr_type type;
+ if (!TARGET_HAS_CDX)
+ return false;
+ type = get_attr_type (insn);
+ pat = PATTERN (insn);
+ gcc_assert (reload_completed);
+ switch (type)
+ {
+ case TYPE_CONTROL:
+ if (GET_CODE (pat) == SIMPLE_RETURN)
+ return true;
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ if (GET_CODE (pat) == SET)
+ pat = SET_SRC (pat);
+ if (GET_CODE (pat) == IF_THEN_ELSE)
+ {
+ /* Conditional branch patterns; for these we
+ only check the comparison to find beqz.n/bnez.n cases.
+ For the 'nios2_cbranch' pattern, we cannot also check
+ the branch range here. That will be done at the md
+ pattern "length" attribute computation. */
+ rtx cmp = XEXP (pat, 0);
+ return ((GET_CODE (cmp) == EQ || GET_CODE (cmp) == NE)
+ && cdxreg (XEXP (cmp, 0))
+ && XEXP (cmp, 1) == const0_rtx);
+ }
+ if (GET_CODE (pat) == TRAP_IF)
+ /* trap.n is always usable. */
+ return true;
+ if (GET_CODE (pat) == CALL)
+ pat = XEXP (XEXP (pat, 0), 0);
+ if (REG_P (pat))
+ /* Control instructions taking a register operand are indirect
+ jumps and calls. The CDX instructions have a 5-bit register
+ field so any reg is valid. */
+ return true;
+ else
+ {
+ gcc_assert (!insn_variable_length_p (insn));
+ return false;
+ }
+ case TYPE_ADD:
+ {
+ enum nios2_add_insn_kind kind;
+ split_alu_insn (insn, &lhs, &rhs1, &rhs2);
+ kind = nios2_add_insn_classify (insn, lhs, rhs1, rhs2);
+ return nios2_add_insn_narrow[(int)kind];
+ }
+ case TYPE_LD:
+ {
+ bool ret;
+ HOST_WIDE_INT offset, rem = 0;
+ rtx addr, reg = SET_DEST (pat), mem = SET_SRC (pat);
+ if (GET_CODE (mem) == SIGN_EXTEND)
+ /* No CDX form for sign-extended load. */
+ return false;
+ if (GET_CODE (mem) == ZERO_EXTEND)
+ /* The load alternatives in the zero_extend* patterns. */
+ mem = XEXP (mem, 0);
+ if (MEM_P (mem))
+ {
+ /* ldxio. */
+ if ((MEM_VOLATILE_P (mem) && TARGET_BYPASS_CACHE_VOLATILE)
+ || TARGET_BYPASS_CACHE)
+ return false;
+ addr = XEXP (mem, 0);
+ /* GP-based references are never narrow. */
+ if (gprel_constant_p (addr))
+ return false;
+ ret = split_mem_address (addr, &rhs1, &rhs2);
+ gcc_assert (ret);
+ }
+ else
+ return false;
+
+ offset = INTVAL (rhs2);
+ if (GET_MODE (mem) == SImode)
+ {
+ rem = offset & 3;
+ offset >>= 2;
+ /* ldwsp.n case. */
+ if (rtx_equal_p (rhs1, stack_pointer_rtx)
+ && rem == 0 && (offset & ~0x1f) == 0)
+ return true;
+ }
+ else if (GET_MODE (mem) == HImode)
+ {
+ rem = offset & 1;
+ offset >>= 1;
+ }
+ /* ldbu.n, ldhu.n, ldw.n cases. */
+ return (cdxreg (reg) && cdxreg (rhs1)
+ && rem == 0 && (offset & ~0xf) == 0);
+ }
+ case TYPE_ST:
+ if (GET_CODE (pat) == PARALLEL)
+ /* stex, stsex. */
+ return false;
+ else
+ {
+ bool ret;
+ HOST_WIDE_INT offset, rem = 0;
+ rtx addr, reg = SET_SRC (pat), mem = SET_DEST (pat);
+ if (!MEM_P (mem))
+ return false;
+ /* stxio. */
+ if ((MEM_VOLATILE_P (mem) && TARGET_BYPASS_CACHE_VOLATILE)
+ || TARGET_BYPASS_CACHE)
+ return false;
+ addr = XEXP (mem, 0);
+ /* GP-based references are never narrow. */
+ if (gprel_constant_p (addr))
+ return false;
+ ret = split_mem_address (addr, &rhs1, &rhs2);
+ gcc_assert (ret);
+ offset = INTVAL (rhs2);
+ if (GET_MODE (mem) == SImode)
+ {
+ rem = offset & 3;
+ offset >>= 2;
+ /* stwsp.n case. */
+ if (rtx_equal_p (rhs1, stack_pointer_rtx)
+ && rem == 0 && (offset & ~0x1f) == 0)
+ return true;
+ /* stwz.n case. */
+ else if (reg == const0_rtx && cdxreg (rhs1)
+ && rem == 0 && (offset & ~0x3f) == 0)
+ return true;
+ }
+ else if (GET_MODE (mem) == HImode)
+ {
+ rem = offset & 1;
+ offset >>= 1;
+ }
+ else
+ {
+ gcc_assert (GET_MODE (mem) == QImode);
+ /* stbz.n case. */
+ if (reg == const0_rtx && cdxreg (rhs1)
+ && (offset & ~0x3f) == 0)
+ return true;
+ }
+
+ /* stbu.n, sthu.n, stw.n cases. */
+ return (cdxreg (reg) && cdxreg (rhs1)
+ && rem == 0 && (offset & ~0xf) == 0);
+ }
+ case TYPE_MOV:
+ lhs = SET_DEST (pat);
+ rhs1 = SET_SRC (pat);
+ if (CONST_INT_P (rhs1))
+ return (cdxreg (lhs) && cdx_mov_immed (rhs1));
+ gcc_assert (REG_P (lhs) && REG_P (rhs1));
+ return true;
+
+ case TYPE_AND:
+ /* Some zero_extend* alternatives are and insns. */
+ if (GET_CODE (SET_SRC (pat)) == ZERO_EXTEND)
+ return (cdxreg (SET_DEST (pat))
+ && cdxreg (XEXP (SET_SRC (pat), 0)));
+ split_alu_insn (insn, &lhs, &rhs1, &rhs2);
+ if (CONST_INT_P (rhs2))
+ return (cdxreg (lhs) && cdxreg (rhs1) && cdx_and_immed (rhs2));
+ return (cdxreg (lhs) && cdxreg (rhs2)
+ && (!reload_completed || rtx_equal_p (lhs, rhs1)));
+
+ case TYPE_OR:
+ case TYPE_XOR:
+ /* Note the two-address limitation for CDX form. */
+ split_alu_insn (insn, &lhs, &rhs1, &rhs2);
+ return (cdxreg (lhs) && cdxreg (rhs2)
+ && (!reload_completed || rtx_equal_p (lhs, rhs1)));
+
+ case TYPE_SUB:
+ split_alu_insn (insn, &lhs, &rhs1, &rhs2);
+ return (cdxreg (lhs) && cdxreg (rhs1) && cdxreg (rhs2));
+
+ case TYPE_NEG:
+ case TYPE_NOT:
+ split_alu_insn (insn, &lhs, &rhs1, NULL);
+ return (cdxreg (lhs) && cdxreg (rhs1));
+
+ case TYPE_SLL:
+ case TYPE_SRL:
+ split_alu_insn (insn, &lhs, &rhs1, &rhs2);
+ return (cdxreg (lhs)
+ && ((cdxreg (rhs1) && cdx_shift_immed (rhs2))
+ || (cdxreg (rhs2)
+ && (!reload_completed || rtx_equal_p (lhs, rhs1)))));
+ case TYPE_NOP:
+ case TYPE_PUSH:
+ case TYPE_POP:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Implement TARGET_MACHINE_DEPENDENT_REORG:
+ We use this hook when emitting CDX code to enforce the 4-byte
+ alignment requirement for labels that are used as the targets of
+ jmpi instructions. CDX code can otherwise contain a mix of 16-bit
+ and 32-bit instructions aligned on any 16-bit boundary, but functions
+ and jmpi labels have to be 32-bit aligned because of the way the address
+ is encoded in the instruction. */
+
+static unsigned char *label_align;
+static int min_labelno, max_labelno;
+
+static void
+nios2_reorg (void)
+{
+ bool changed = true;
+ rtx_insn *insn;
+
+ if (!TARGET_HAS_CDX)
+ return;
+
+ /* Initialize the data structures. */
+ if (label_align)
+ free (label_align);
+ max_labelno = max_label_num ();
+ min_labelno = get_first_label_num ();
+ label_align = XCNEWVEC (unsigned char, max_labelno - min_labelno + 1);
+
+ /* Iterate on inserting alignment and adjusting branch lengths until
+ no more changes. */
+ while (changed)
+ {
+ changed = false;
+ shorten_branches (get_insns ());
+
+ for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
+ if (JUMP_P (insn) && insn_variable_length_p (insn))
+ {
+ rtx label = JUMP_LABEL (insn);
+ /* We use the current fact that all cases of 'jmpi'
+ doing the actual branch in the machine description
+ has a computed length of 6 or 8. Length 4 and below
+ are all PC-relative 'br' branches without the jump-align
+ problem. */
+ if (label && LABEL_P (label) && get_attr_length (insn) > 4)
+ {
+ int index = CODE_LABEL_NUMBER (label) - min_labelno;
+ if (label_align[index] != 2)
+ {
+ label_align[index] = 2;
+ changed = true;
+ }
+ }
+ }
+ }
+}
+
+/* Implement LABEL_ALIGN, using the information gathered in nios2_reorg. */
+int
+nios2_label_align (rtx label)
+{
+ int n = CODE_LABEL_NUMBER (label);
+
+ if (label_align && n >= min_labelno && n <= max_labelno)
+ return MAX (label_align[n - min_labelno], align_labels_log);
+ return align_labels_log;
+}
+
+/* Implement ADJUST_REG_ALLOC_ORDER. We use the default ordering
+ for R1 and non-CDX R2 code; for CDX we tweak thing to prefer
+ the registers that can be used as operands to instructions that
+ have 3-bit register fields. */
+void
+nios2_adjust_reg_alloc_order (void)
+{
+ const int cdx_reg_alloc_order[] =
+ {
+ /* Call-clobbered GPRs within CDX 3-bit encoded range. */
+ 2, 3, 4, 5, 6, 7,
+ /* Call-saved GPRs within CDX 3-bit encoded range. */
+ 16, 17,
+ /* Other call-clobbered GPRs. */
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ /* Other call-saved GPRs. RA placed first since it is always saved. */
+ 31, 18, 19, 20, 21, 22, 23, 28,
+ /* Fixed GPRs, not used by the register allocator. */
+ 0, 1, 24, 25, 26, 27, 29, 30, 32, 33, 34, 35, 36, 37, 38, 39
+ };
+
+ if (TARGET_HAS_CDX)
+ memcpy (reg_alloc_order, cdx_reg_alloc_order,
+ sizeof (int) * FIRST_PSEUDO_REGISTER);
+}
+
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_FUNCTION_PROLOGUE
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
#define TARGET_ASM_OUTPUT_DWARF_DTPREL nios2_output_dwarf_dtprel
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P nios2_print_operand_punct_valid_p
+
#undef TARGET_PRINT_OPERAND
#define TARGET_PRINT_OPERAND nios2_print_operand
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK nios2_asm_output_mi_thunk
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG nios2_reorg
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-nios2.h"
((TREE_CODE (EXP) == STRING_CST) \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+#define LABEL_ALIGN(LABEL) nios2_label_align (LABEL)
+
/* Layout of source language data types. */
#define INT_TYPE_SIZE 32
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+/* Order in which to allocate registers. Each register must be
+ listed once. This is the default ordering for R1 and non-CDX R2
+ code. For CDX, we overwrite this in ADJUST_REG_ALLOC_ORDER. */
+#define REG_ALLOC_ORDER \
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, \
+ 37, 38, 39 }
+
+#define ADJUST_REG_ALLOC_ORDER nios2_adjust_reg_alloc_order ()
+
+/* Caller-save costs can be less emphasized under R2 CDX, where we can
+ use push.n/pop.n. */
+#define HONOR_REG_ALLOC_ORDER (TARGET_HAS_CDX)
+
/* Register Classes. */
enum reg_class
#define CLASS_MAX_NREGS(CLASS, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+#define CDX_REG_P(REGNO) \
+ ((REGNO) == 16 || (REGNO) == 17 || (2 <= (REGNO) && (REGNO) <= 7))
+
/* Tests for various kinds of constants used in the Nios II port. */
#define SMALL_INT(X) ((unsigned HOST_WIDE_INT)(X) + 0x8000 < 0x10000)
#define SHIFT_INT(X) ((X) >= 0 && (X) <= 31)
#define RDWRCTL_INT(X) ((X) >= 0 && (X) <= 31)
#define CUSTOM_INSN_OPCODE(X) ((X) >= 0 && (X) <= 255)
+#define ANDCLEAR_INT(X) \
+ (((X) & 0xffff) == 0xffff || (((X) >> 16) & 0xffff) == 0xffff)
/* Say that the epilogue uses the return address register. Note that
in the case of sibcalls, the values "used by the epilogue" are
(TP_REGNO 23) ; Thread pointer register
(GP_REGNO 26) ; Global pointer register
+ (SP_REGNO 27) ; Stack pointer register
(FP_REGNO 28) ; Frame pointer register
(EA_REGNO 29) ; Exception return address register
(RA_REGNO 31) ; Return address register
; incuring a stall.
; length of an instruction (in bytes)
-(define_attr "length" "" (const_int 4))
+(define_attr "length" ""
+ (if_then_else (match_test "nios2_cdx_narrow_form_p (insn)")
+ (const_int 2)
+ (const_int 4)))
+
(define_attr "type"
- "unknown,complex,control,alu,cond_alu,st,ld,shift,mul,div,custom"
+ "unknown,complex,control,alu,cond_alu,st,ld,stwm,ldwm,push,pop,mul,div,\
+ custom,add,sub,mov,and,or,xor,neg,not,sll,srl,sra,rol,ror,nop"
(const_string "complex"))
(define_asm_attributes
"cpu")
(define_insn_reservation "control" 1
- (eq_attr "type" "control")
+ (eq_attr "type" "control,pop")
"cpu")
(define_insn_reservation "alu" 1
- (eq_attr "type" "alu")
+ (eq_attr "type" "alu,add,sub,mov,and,or,xor,neg,not")
"cpu")
(define_insn_reservation "cond_alu" 1
"cpu")
(define_insn_reservation "st" 1
- (eq_attr "type" "st")
+ (eq_attr "type" "st,stwm,push")
"cpu")
(define_insn_reservation "custom" 1
; shifts, muls and lds have three cycle latency
(define_insn_reservation "ld" 3
- (eq_attr "type" "ld")
+ (eq_attr "type" "ld,ldwm")
"cpu")
(define_insn_reservation "shift" 3
- (eq_attr "type" "shift")
+ (eq_attr "type" "sll,srl,sra,rol,ror")
"cpu")
(define_insn_reservation "mul" 3
DONE;
})
+(define_insn "*high"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (high:SI (match_operand:SI 1 "immediate_operand" "i")))]
+ ""
+ "movhi\\t%0, %H1"
+ [(set_attr "type" "alu")])
+
+(define_insn "*lo_sum"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "immediate_operand" "i")))]
+ ""
+ "addi\\t%0, %1, %L2"
+ [(set_attr "type" "alu")])
+
(define_insn "movqi_internal"
- [(set (match_operand:QI 0 "nonimmediate_operand" "=m, r,r, r")
- (match_operand:QI 1 "general_operand" "rM,m,rM,I"))]
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=m, r,r")
+ (match_operand:QI 1 "general_operand" "rM,m,rI"))]
"(register_operand (operands[0], QImode)
|| reg_or_0_operand (operands[1], QImode))"
- "@
- stb%o0\\t%z1, %0
- ldbu%o1\\t%0, %1
- mov\\t%0, %z1
- movi\\t%0, %1"
- [(set_attr "type" "st,ld,alu,alu")])
+ {
+ switch (which_alternative)
+ {
+ case 0:
+ if (get_attr_length (insn) != 2)
+ return "stb%o0\\t%z1, %0";
+ else if (const_0_operand (operands[1], QImode))
+ return "stbz.n\\t%z1, %0";
+ else
+ return "stb.n\\t%z1, %0";
+ case 1:
+ return "ldbu%o1%.\\t%0, %1";
+ case 2:
+ return "mov%i1%.\\t%0, %z1";
+ default:
+ gcc_unreachable ();
+ }
+ }
+ [(set_attr "type" "st,ld,mov")])
(define_insn "movhi_internal"
- [(set (match_operand:HI 0 "nonimmediate_operand" "=m, r,r, r")
- (match_operand:HI 1 "general_operand" "rM,m,rM,I"))]
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=m, r,r")
+ (match_operand:HI 1 "general_operand" "rM,m,rI"))]
"(register_operand (operands[0], HImode)
|| reg_or_0_operand (operands[1], HImode))"
"@
- sth%o0\\t%z1, %0
- ldhu%o1\\t%0, %1
- mov\\t%0, %z1
- movi\\t%0, %1"
- [(set_attr "type" "st,ld,alu,alu")])
+ sth%o0%.\\t%z1, %0
+ ldhu%o1%.\\t%0, %1
+ mov%i1%.\\t%0, %z1"
+ [(set_attr "type" "st,ld,mov")])
(define_insn "movsi_internal"
- [(set (match_operand:SI 0 "nonimmediate_operand" "=m, r,r, r,r,r,r,r")
- (match_operand:SI 1 "general_operand" "rM,m,rM,I,J,K,S,i"))]
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=m, r,r, r")
+ (match_operand:SI 1 "general_operand" "rM,m,rIJK,S"))]
"(register_operand (operands[0], SImode)
|| reg_or_0_operand (operands[1], SImode))"
- "@
- stw%o0\\t%z1, %0
- ldw%o1\\t%0, %1
- mov\\t%0, %z1
- movi\\t%0, %1
- movui\\t%0, %1
- movhi\\t%0, %H1
- addi\\t%0, gp, %%gprel(%1)
- movhi\\t%0, %H1\;addi\\t%0, %0, %L1"
- [(set_attr "type" "st,ld,alu,alu,alu,alu,alu,alu")
- (set_attr "length" "4,4,4,4,4,4,4,8")])
+ {
+ switch (which_alternative)
+ {
+ case 0:
+ if (get_attr_length (insn) != 2)
+ return "stw%o0\\t%z1, %0";
+ else if (stack_memory_operand (operands[0], SImode))
+ return "stwsp.n\\t%z1, %0";
+ else if (const_0_operand (operands[1], SImode))
+ return "stwz.n\\t%z1, %0";
+ else
+ return "stw.n\\t%z1, %0";
+ case 1:
+ if (get_attr_length (insn) != 2)
+ return "ldw%o1\\t%0, %1";
+ else if (stack_memory_operand (operands[1], SImode))
+ return "ldwsp.n\\t%0, %1";
+ else
+ return "ldw.n\\t%0, %1";
+ case 2:
+ return "mov%i1%.\\t%0, %z1";
+ case 3:
+ return "addi\\t%0, gp, %%gprel(%1)";
+ default:
+ gcc_unreachable ();
+ }
+ }
+ [(set_attr "type" "st,ld,mov,alu")])
(define_mode_iterator BH [QI HI])
(define_mode_iterator BHW [QI HI SI])
(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
""
"@
- andi\\t%0, %1, 0xffff
- ldhu%o1\\t%0, %1"
- [(set_attr "type" "alu,ld")])
+ andi%.\\t%0, %1, 0xffff
+ ldhu%o1%.\\t%0, %1"
+ [(set_attr "type" "and,ld")])
(define_insn "zero_extendqi<mode>2"
[(set (match_operand:QX 0 "register_operand" "=r,r")
(zero_extend:QX (match_operand:QI 1 "nonimmediate_operand" "r,m")))]
""
"@
- andi\\t%0, %1, 0xff
- ldbu%o1\\t%0, %1"
- [(set_attr "type" "alu,ld")])
+ andi%.\\t%0, %1, 0xff
+ ldbu%o1%.\\t%0, %1"
+ [(set_attr "type" "and,ld")])
;; Sign extension patterns
""
"@
#
- ldh%o1\\t%0, %1"
+ ldh%o1%.\\t%0, %1"
[(set_attr "type" "alu,ld")])
(define_insn "extendqi<mode>2"
""
"@
#
- ldb%o1\\t%0, %1"
+ ldb%o1%.\\t%0, %1"
[(set_attr "type" "alu,ld")])
;; Split patterns for register alternative cases.
(plus:SI (match_operand:SI 1 "register_operand" "%r")
(match_operand:SI 2 "add_regimm_operand" "rIT")))]
""
- "add%i2\\t%0, %1, %z2"
- [(set_attr "type" "alu")])
+{
+ return nios2_add_insn_asm (insn, operands);
+}
+ [(set_attr "type" "add")])
(define_insn "subsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 1 "reg_or_0_operand" "rM")
(match_operand:SI 2 "register_operand" "r")))]
""
- "sub\\t%0, %z1, %2"
- [(set_attr "type" "alu")])
+ "sub%.\\t%0, %z1, %2"
+ [(set_attr "type" "sub")])
(define_insn "mulsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (match_operand:SI 1 "register_operand" "r")))]
""
- "sub\\t%0, zero, %1"
- [(set_attr "type" "alu")])
+{
+ if (get_attr_length (insn) == 2)
+ return "neg.n\\t%0, %1";
+ else
+ return "sub\\t%0, zero, %1";
+}
+ [(set_attr "type" "neg")])
(define_insn "one_cmplsi2"
[(set (match_operand:SI 0 "register_operand" "=r")
(not:SI (match_operand:SI 1 "register_operand" "r")))]
""
- "nor\\t%0, zero, %1"
- [(set_attr "type" "alu")])
+{
+ if (get_attr_length (insn) == 2)
+ return "not.n\\t%0, %1";
+ else
+ return "nor\\t%0, zero, %1";
+}
+ [(set_attr "type" "not")])
\f
;; Integer logical Operations
-(define_code_iterator LOGICAL [and ior xor])
-(define_code_attr logical_asm [(and "and") (ior "or") (xor "xor")])
+(define_insn "andsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (and:SI (match_operand:SI 1 "register_operand" "%r")
+ (match_operand:SI 2 "and_operand" "rJKP")))]
+ ""
+ "and%x2%.\\t%0, %1, %y2"
+ [(set_attr "type" "and")])
+
+(define_code_iterator LOGICAL [ior xor])
+(define_code_attr logical_asm [(ior "or") (xor "xor")])
(define_insn "<code>si3"
- [(set (match_operand:SI 0 "register_operand" "=r,r,r")
- (LOGICAL:SI (match_operand:SI 1 "register_operand" "%r,r,r")
- (match_operand:SI 2 "logical_operand" "rM,J,K")))]
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (LOGICAL:SI (match_operand:SI 1 "register_operand" "%r")
+ (match_operand:SI 2 "logical_operand" "rJK")))]
""
- "@
- <logical_asm>\\t%0, %1, %z2
- <logical_asm>%i2\\t%0, %1, %2
- <logical_asm>h%i2\\t%0, %1, %U2"
- [(set_attr "type" "alu")])
+ "<logical_asm>%x2%.\\t%0, %1, %y2"
+ [(set_attr "type" "<logical_asm>")])
(define_insn "*norsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(SHIFT:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "shift_operand" "rL")))]
""
- "<shift_asm>%i2\\t%0, %1, %z2"
- [(set_attr "type" "shift")])
+ "<shift_asm>%i2%.\\t%0, %1, %z2"
+ [(set_attr "type" "<shift_asm>")])
(define_insn "rotrsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 2 "register_operand" "r")))]
""
"ror\\t%0, %1, %2"
- [(set_attr "type" "shift")])
+ [(set_attr "type" "ror")])
+
+;; Nios II R2 Bit Manipulation Extension (BMX), provides
+;; bit merge/insertion/extraction instructions.
+
+(define_insn "*merge"
+ [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
+ (match_operand:SI 1 "const_shift_operand" "L")
+ (match_operand:SI 2 "const_shift_operand" "L"))
+ (zero_extract:SI (match_operand:SI 3 "register_operand" "r")
+ (match_dup 1) (match_dup 2)))]
+ "TARGET_HAS_BMX"
+{
+ operands[4] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1);
+ return "merge\\t%0, %3, %4, %2";
+}
+ [(set_attr "type" "alu")])
+
+(define_insn "extzv"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (zero_extract:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "const_shift_operand" "L")
+ (match_operand:SI 3 "const_shift_operand" "L")))]
+ "TARGET_HAS_BMX"
+{
+ operands[4] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1);
+ return "extract\\t%0, %1, %4, %3";
+}
+ [(set_attr "type" "alu")])
+
+(define_insn "insv"
+ [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
+ (match_operand:SI 1 "const_shift_operand" "L")
+ (match_operand:SI 2 "const_shift_operand" "L"))
+ (match_operand:SI 3 "reg_or_0_operand" "rM"))]
+ "TARGET_HAS_BMX"
+{
+ operands[4] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1);
+ return "insert\\t%0, %z3, %4, %2";
+}
+ [(set_attr "type" "alu")])
+
\f
;; Floating point instructions
DONE;
})
-(define_insn "return"
+(define_expand "return"
[(simple_return)]
"nios2_can_use_return_insn ()"
- "ret")
+ "")
(define_insn "simple_return"
[(simple_return)]
""
- "ret")
+ "ret%."
+ [(set_attr "type" "control")])
;; Block any insns from being moved before this point, since the
;; profiling call to mcount can use various registers that aren't
(define_insn "indirect_jump"
[(set (pc) (match_operand:SI 0 "register_operand" "c"))]
""
- "jmp\\t%0"
+ "jmp%!\\t%0"
[(set_attr "type" "control")])
(define_insn "jump"
(label_ref (match_operand 0 "" "")))]
""
{
- if (flag_pic || get_attr_length (insn) == 4)
+ if (get_attr_length (insn) == 2)
+ return "br.n\\t%0";
+ else if (get_attr_length (insn) == 4)
return "br\\t%0";
else
return "jmpi\\t%0";
[(set_attr "type" "control")
(set (attr "length")
(if_then_else
- (and (ge (minus (match_dup 0) (pc)) (const_int -32768))
- (le (minus (match_dup 0) (pc)) (const_int 32764)))
- (const_int 4)
- (const_int 8)))])
-
+ (and (match_test "TARGET_HAS_CDX")
+ (and (ge (minus (match_dup 0) (pc)) (const_int -1022))
+ (le (minus (match_dup 0) (pc)) (const_int 1022))))
+ (const_int 2)
+ (if_then_else
+ (ior (match_test "flag_pic")
+ (and (ge (minus (match_dup 0) (pc)) (const_int -32764))
+ (le (minus (match_dup 0) (pc)) (const_int 32764))))
+ (const_int 4)
+ (const_int 8))))])
(define_expand "call"
[(parallel [(call (match_operand 0 "" "")
""
"@
call\\t%0
- callr\\t%0"
+ callr%.\\t%0"
[(set_attr "type" "control")])
(define_insn "*call_value"
""
"@
call\\t%1
- callr\\t%1"
+ callr%.\\t%1"
[(set_attr "type" "control")])
(define_expand "sibcall"
""
"@
jmpi\\t%0
- jmp\\t%0"
+ jmp%!\\t%0"
[(set_attr "type" "control")])
(define_insn "sibcall_value_internal"
""
"@
jmpi\\t%1
- jmp\\t%1"
+ jmp%!\\t%1"
[(set_attr "type" "control")])
(define_expand "tablejump"
(match_operand:SI 0 "register_operand" "c"))
(use (label_ref (match_operand 1 "" "")))]
""
- "jmp\\t%0"
+ "jmp%!\\t%0"
[(set_attr "type" "control")])
\f
(label_ref (match_operand 3 "" ""))
(pc)))]
""
- {
- if (flag_pic || get_attr_length (insn) == 4)
- return "b%0\t%z1, %z2, %l3";
- else
- return "b%R0\t%z1, %z2, .+8;jmpi\t%l3";
- }
+{
+ if (get_attr_length (insn) == 2)
+ return "b%0z.n\t%z1, %l3";
+ else if (get_attr_length (insn) == 4)
+ return "b%0\t%z1, %z2, %l3";
+ else if (get_attr_length (insn) == 6)
+ return "b%R0z.n\t%z1, .+6;jmpi\t%l3";
+ else
+ return "b%R0\t%z1, %z2, .+8;jmpi\t%l3";
+}
[(set_attr "type" "control")
(set (attr "length")
- (if_then_else
- (and (ge (minus (match_dup 3) (pc)) (const_int -32768))
- (le (minus (match_dup 3) (pc)) (const_int 32764)))
- (const_int 4) (const_int 8)))])
+ (cond
+ [(and (match_test "nios2_cdx_narrow_form_p (insn)")
+ (ge (minus (match_dup 3) (pc)) (const_int -126))
+ (le (minus (match_dup 3) (pc)) (const_int 126)))
+ (const_int 2)
+ (ior (match_test "flag_pic")
+ (and (ge (minus (match_dup 3) (pc)) (const_int -32764))
+ (le (minus (match_dup 3) (pc)) (const_int 32764))))
+ (const_int 4)
+ (match_test "nios2_cdx_narrow_form_p (insn)")
+ (const_int 6)]
+ (const_int 8)))])
;; Floating point comparisons
(define_code_iterator FCMP [eq ne gt ge le lt])
(UCMP:SI (match_operand:SI 1 "reg_or_0_operand" "rM")
(match_operand:SI 2 "uns_arith_operand" "rJ")))]
""
- "cmp<code>%i2\\t%0, %z1, %z2"
+ "cmp<code>%u2\\t%0, %z1, %z2"
[(set_attr "type" "alu")])
(define_insn "nop"
[(const_int 0)]
""
- "nop"
- [(set_attr "type" "alu")])
+ "nop%."
+ [(set_attr "type" "nop")])
;; Connect 'sync' to 'memory_barrier' standard expand name
(define_expand "memory_barrier"
(define_insn "trap"
[(trap_if (const_int 1) (const_int 3))]
""
- "trap\\t3"
+ "trap%.\\t3"
[(set_attr "type" "control")])
(define_insn "ctrapsi4"
(match_operand:SI 2 "reg_or_0_operand" "rM")])
(match_operand 3 "const_int_operand" "i"))]
""
- "b%R0\\t%z1, %z2, 1f\;trap\\t%3\;1:"
+{
+ if (get_attr_length (insn) == 6)
+ return "b%R0\\t%z1, %z2, 1f\;trap.n\\t%3\;1:";
+ else
+ return "b%R0\\t%z1, %z2, 1f\;trap\\t%3\;1:";
+}
[(set_attr "type" "control")
- (set_attr "length" "8")])
+ (set (attr "length")
+ (if_then_else (match_test "nios2_cdx_narrow_form_p (insn)")
+ (const_int 6) (const_int 8)))])
;; Load the GOT register.
(define_insn "load_got_register"
(ior (match_operand 0 "const_logical_operand")
(match_operand 0 "register_operand")))
+(define_predicate "const_and_operand"
+ (and (match_code "const_int")
+ (match_test "SMALL_INT_UNSIGNED (INTVAL (op))
+ || UPPER16_INT (INTVAL (op))
+ || (TARGET_ARCH_R2 && ANDCLEAR_INT (INTVAL (op)))")))
+
+(define_predicate "and_operand"
+ (ior (match_operand 0 "const_and_operand")
+ (match_operand 0 "register_operand")))
+
(define_predicate "const_shift_operand"
(and (match_code "const_int")
(match_test "SHIFT_INT (INTVAL (op))")))
false));
})
+(define_predicate "stack_memory_operand"
+ (match_code "mem")
+{
+ rtx addr = XEXP (op, 0);
+ return ((REG_P (addr) && REGNO (addr) == SP_REGNO)
+ || (GET_CODE (addr) == PLUS
+ && REG_P (XEXP (addr, 0)) && REGNO (XEXP (addr, 0)) == SP_REGNO
+ && CONST_INT_P (XEXP (addr, 1))));
+})
+
(define_predicate "ldstio_memory_operand"
(match_code "mem")
{
Integer that is valid as an immediate operand for
a custom instruction opcode. Range 0 to 255.
+@item P
+An immediate operand for R2 andchi/andci instructions.
+
@item S
Matches immediates which are addresses in the small
data section and therefore can be added to @code{gp}
+2015-07-14 Sandra Loosemore <sandra@codesourcery.com>
+ Cesar Philippidis <cesar@codesourcery.com>
+ Chung-Lin Tang <cltang@codesourcery.com>
+
+ * gcc.target/nios2/andci.c: New.
+ * gcc.target/nios2/bmx.c: New.
+ * gcc.target/nios2/cdx-add.c: New.
+ * gcc.target/nios2/cdx-branch.c: New.
+ * gcc.target/nios2/cdx-callret.c: New.
+ * gcc.target/nios2/cdx-loadstore.c: New.
+ * gcc.target/nios2/cdx-logical.c: New.
+ * gcc.target/nios2/cdx-mov.c: New.
+ * gcc.target/nios2/cdx-shift.c: New.
+ * gcc.target/nios2/cdx-sub.c: New.
+ * gcc.target/nios2/nios2-trap-insn.c: Adjust pattern.
+
2015-07-14 Andrea Azzarone <azzaronea@gmail.com>
PR c++/65071
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2" } */
+
+/* Test generation of Nios II R2 "andci" and "andchi" instructions. */
+
+unsigned int f (unsigned int a)
+{
+ return a & 0xfffffff0;
+}
+
+unsigned int g (unsigned int b)
+{
+ return b & 0xfff0ffff;
+}
+
+/* { dg-final { scan-assembler "\tandci\t.*" } } */
+/* { dg-final { scan-assembler "\tandchi\t.*" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mbmx" } */
+
+/* Test generation of Nios II R2 BMX instructions. */
+
+struct s {
+ unsigned int pad1 : 3;
+ unsigned int bitfield : 20;
+ unsigned int intfield;
+};
+
+void f (struct s *a, struct s *b)
+{
+ a->bitfield = b->bitfield;
+}
+
+void g (struct s *a, struct s *b)
+{
+ a->bitfield = b->intfield;
+}
+
+void h (struct s *a, struct s *b)
+{
+ a->intfield = b->bitfield;
+}
+
+/* { dg-final { scan-assembler "\tmerge\t.*, 22, 3" } } */
+/* { dg-final { scan-assembler "\tinsert\t.*, 22, 3" } } */
+/* { dg-final { scan-assembler "\textract\t.*, 22, 3" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX add.n and addi.n instructions. */
+
+int f (int a, int b)
+{
+ return a + b;
+}
+
+int g (int a)
+{
+ return a + 32;
+}
+
+int h (int a)
+{
+ return a + 33;
+}
+
+/* { dg-final { scan-assembler "\tadd\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\taddi\\.n\t.*, 32" } } */
+/* { dg-final { scan-assembler "\taddi\t.*, 33" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-Os -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX br.n, beqz.n, bnez.n instructions. */
+
+int f (int a, int b, int c)
+{
+ if (a == 0)
+ return b;
+ else
+ return c;
+}
+
+int g (int a, int b, int c)
+{
+ if (a != 0)
+ return b;
+ else
+ return c;
+}
+
+extern int i (int);
+extern int j (int);
+extern int k (int);
+
+int h (int a)
+{
+ int x;
+
+ /* As well as the conditional branch for the "if", there has to be
+ an unconditional branch from one branch of the "if" to
+ the return statement. We compile this testcase with -Os to
+ avoid insertion of a duplicate epilogue in place of the branch. */
+ if (a == 1)
+ x = i (37);
+ else
+ x = j (42);
+ return x + a + k (x);
+}
+
+/* { dg-final { scan-assembler "\tbeqz\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tbnez\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tbeq\t|\tbne\t" } } */
+/* { dg-final { scan-assembler "\tbr\\.n\t.*" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX callr.n, jmpr.n, ret.n instructions. */
+
+typedef int (*F) (void);
+
+int x (F f)
+{
+ f ();
+
+ /* Note that the compiler might generate a return via pop.n or ldwm;
+ the test below is to make sure that it doesn't generate a 32-bit
+ return instruction. */
+ return 3;
+}
+
+int y (F f)
+{
+ return f ();
+}
+
+/* { dg-final { scan-assembler "\tcallr\\.n\t.*" } } */
+/* { dg-final { scan-assembler-not "\tret$" } } */
+/* { dg-final { scan-assembler "\tjmpr\\.n\t.*" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX load/store instructions. */
+
+unsigned char ldb (unsigned char *p)
+{
+ return p[7];
+}
+
+unsigned short ldh (unsigned short *p)
+{
+ return p[7];
+}
+
+unsigned int ldw (unsigned int *p)
+{
+ return p[7];
+}
+
+void stb (unsigned char *p, unsigned char x)
+{
+ p[15] = x;
+}
+
+void sth (unsigned short *p, unsigned short x)
+{
+ p[15] = x;
+}
+
+void stw (unsigned int *p, unsigned int x)
+{
+ p[15] = x;
+}
+
+void no_cdx_stb (unsigned char *p, unsigned char x)
+{
+ p[16] = x;
+}
+
+void no_cdx_sth (unsigned short *p, unsigned short x)
+{
+ p[16] = x;
+}
+
+void no_cdx_stw (unsigned int *p, unsigned int x)
+{
+ p[16] = x;
+}
+
+/* { dg-final { scan-assembler "\tldbu\\.n\t.*, 7\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tldhu\\.n\t.*, 14\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tldw\\.n\t.*, 28\\(.*\\)" } } */
+
+/* { dg-final { scan-assembler "\tstb\\.n\t.*, 15\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tsth\\.n\t.*, 30\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tstw\\.n\t.*, 60\\(.*\\)" } } */
+
+/* { dg-final { scan-assembler "\tstb\t.*, 16\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tsth\t.*, 32\\(.*\\)" } } */
+/* { dg-final { scan-assembler "\tstw\t.*, 64\\(.*\\)" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX and.n, andi.n, or.n, xor.n, and not.n
+ instructions.
+
+ and.n, or.n, and x.n require one of the input registers to be the same
+ as the output register. Since the tests below want to put the result
+ in the return value register, they use this function to make sure that
+ one of the input operands is also already in the return register. */
+
+extern unsigned int x (unsigned int a);
+
+unsigned int f (unsigned int a, unsigned int b)
+{
+ return x (a) & b;
+}
+
+unsigned int g (unsigned int a)
+{
+ return a & 31;
+}
+
+unsigned int h (unsigned int a, unsigned int b)
+{
+ return x (a) | b;
+}
+
+unsigned int i (unsigned int a, unsigned int b)
+{
+ return x (a) ^ b;
+}
+
+unsigned int j (unsigned int a)
+{
+ return ~a;
+}
+
+/* { dg-final { scan-assembler "\tand\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tandi\\.n\t.*, 31" } } */
+/* { dg-final { scan-assembler "\tor\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\txor\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tnot\\.n\t.*" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX mov.n and movi.n instructions. */
+
+extern void f (int a, int b, int c, int d);
+
+int g (int x, int y, int z)
+{
+ f (100, x, y, z);
+ return -1;
+}
+
+/* We should always get mov.n and never mov when compiling with -mcdx. */
+/* { dg-final { scan-assembler "\tmov\\.n\t.*" } } */
+/* { dg-final { scan-assembler-not "\tmov\t.*" } } */
+
+/* Both of the constant loads are expressible with movi.n. */
+/* { dg-final { scan-assembler "\tmovi\\.n\t.*, 100" } } */
+/* { dg-final { scan-assembler "\tmovi\\.n\t.*, -1" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX and.n, andi.n, or.n, xor.n, and not.n
+ instructions. */
+
+extern unsigned int x (unsigned int a);
+
+unsigned int f (unsigned int a, unsigned int b)
+{
+ return x (a) << b;
+}
+
+unsigned int g (unsigned int a)
+{
+ return x (a) << 24;
+}
+
+unsigned int h (unsigned int a, unsigned int b)
+{
+ return x (a) >> b;
+}
+
+unsigned int i (unsigned int a, unsigned int b)
+{
+ return x (a) >> 24;
+}
+
+/* { dg-final { scan-assembler "\tsll\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tslli\\.n\t.*, 24" } } */
+/* { dg-final { scan-assembler "\tsrl\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tsrli\\.n\t.*, 24" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=r2 -mcdx" } */
+
+/* Check generation of R2 CDX sub.n, subi.n, and neg.n instructions. */
+
+int f (int a, int b)
+{
+ return a - b;
+}
+
+int g (int a)
+{
+ return a - 32;
+}
+
+int h (int a)
+{
+ return -a;
+}
+
+/* { dg-final { scan-assembler "\tsub\\.n\t.*" } } */
+/* { dg-final { scan-assembler "\tsubi\\.n\t.*, 32" } } */
+/* { dg-final { scan-assembler "\tneg\\.n\t.*" } } */
/* { dg-do compile } */
-/* { dg-final { scan-assembler "trap\\t3" } } */
+/* { dg-final { scan-assembler "trap\\t3|trap.n\\t3" } } */
/* Test the nios2 trap instruction */
void foo(void){