static int sequent_regs_live PARAMS ((void));
static char * ptrreg_to_str PARAMS ((int));
static char * cond_string PARAMS ((enum rtx_code));
+static int avr_num_arg_regs PARAMS ((enum machine_mode, tree));
static int out_adj_frame_ptr PARAMS ((FILE *, int));
static int out_set_stack_ptr PARAMS ((FILE *, int, int));
if (adj)
{
- /* For -mtiny-stack, the high byte (r29) does not change -
- prefer "subi" (1 cycle) over "sbiw" (2 cycles). */
-
- if (adj < -63 || adj > 63 || TARGET_TINY_STACK)
+ if (TARGET_TINY_STACK)
{
- fprintf (file, (AS2 (subi, r28, lo8(%d)) CR_TAB), adj);
- size++;
+ if (adj < -63 || adj > 63)
+ warning ("large frame pointer change (%d) with -mtiny-stack", adj);
- if (TARGET_TINY_STACK)
- {
- /* In addition to any local data, each level of function calls
- needs at least 4 more bytes of stack space for the saved
- frame pointer and return address. So, (255 - 16) leaves
- room for 4 levels of function calls. */
-
- if (adj < -(255 - 16) || adj > (255 - 16))
- fatal ("Frame pointer change (%d) too big for -mtiny-stack",
- adj);
- }
- else
- {
- fprintf (file, (AS2 (sbci, r29, hi8(%d)) CR_TAB), adj);
- size++;
- }
+ /* The high byte (r29) doesn't change - prefer "subi" (1 cycle)
+ over "sbiw" (2 cycles, same size). */
+
+ fprintf (file, (AS2 (subi, r28, %d) CR_TAB), adj);
+ size++;
+ }
+ else if (adj < -63 || adj > 63)
+ {
+ fprintf (file, (AS2 (subi, r28, lo8(%d)) CR_TAB
+ AS2 (sbci, r29, hi8(%d)) CR_TAB),
+ adj, adj);
+ size += 2;
}
else if (adj < 0)
{
int before;
int after;
{
- int do_sph, do_cli, do_save, size;
+ int do_sph, do_cli, do_save, do_sei, lock_sph, size;
- if (TARGET_NO_INTERRUPTS)
- {
- before = 0;
- after = 0;
- }
+ /* The logic here is so that -mno-interrupts actually means
+ "it is safe to write SPH in one instruction, then SPL in the
+ next instruction, without disabling interrupts first".
+ The after != -1 case (interrupt/signal) is not affected. */
do_sph = !TARGET_TINY_STACK;
- do_cli = (before != 0 && (after == 0 || do_sph));
- do_save = (before == -1 && after == -1 && do_cli);
+ lock_sph = do_sph && !TARGET_NO_INTERRUPTS;
+ do_cli = (before != 0 && (after == 0 || lock_sph));
+ do_save = (do_cli && before == -1 && after == -1);
+ do_sei = ((do_cli || before != 1) && after == 1);
size = 1;
if (do_save)
}
/* Do SPH first - maybe this will disable interrupts for one instruction
- someday, much like x86 does when changing SS (a suggestion has been
- sent to avr@atmel.com for consideration in future devices). */
+ someday (a suggestion has been sent to avr@atmel.com for consideration
+ in future devices - that would make -mno-interrupts always safe). */
if (do_sph)
{
fprintf (file, AS2 (out, __SP_H__, r29) CR_TAB);
fprintf (file, AS2 (out, __SREG__, __tmp_reg__) CR_TAB);
size++;
}
- else if (after == 1 && (before != 1 || do_cli))
+ else if (do_sei)
{
fprintf (file, "sei" CR_TAB);
size++;
fprintf (file, ("\t"
AS2 (ldi, r28, lo8(%s - %d)) CR_TAB
AS2 (ldi, r29, hi8(%s - %d)) CR_TAB
- AS2 (out,__SP_L__,r28) CR_TAB
- AS2 (out,__SP_H__,r29) "\n"),
+ AS2 (out, __SP_H__, r29) CR_TAB
+ AS2 (out, __SP_L__, r28) "\n"),
initial_stack, size, initial_stack, size);
prologue_size += 4;
if (interrupt_func_p)
{
- prologue_size += out_set_stack_ptr (file, -1, 1);
+ prologue_size += out_set_stack_ptr (file, 1, 1);
}
else if (signal_func_p)
{
}
}
+/* Returns the number of registers to allocate for a function argument. */
+
+static int
+avr_num_arg_regs (mode, type)
+ enum machine_mode mode;
+ tree type;
+{
+ int size;
+
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ /* Align all function arguments to start in even-numbered registers,
+ for "movw" on the enhanced core (to keep call conventions the same
+ on all devices, do it even if "movw" is not available). Odd-sized
+ arguments leave holes above them - registers still available for
+ other uses. Use -mpack-args for compatibility with old asm code
+ (the new convention will still be used for libgcc calls). */
+
+ if (!(type && TARGET_PACK_ARGS))
+ size += size & 1;
+
+ return size;
+}
+
/* Controls whether a function argument is passed
in a register, and which register. */
tree type;
int named ATTRIBUTE_UNUSED;
{
- int bytes;
-
- bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ int bytes = avr_num_arg_regs (mode, type);
if (cum->nregs && bytes <= cum->nregs)
return gen_rtx (REG, mode, cum->regno - bytes);
+
return NULL_RTX;
}
tree type; /* type of the argument or 0 if lib support */
int named ATTRIBUTE_UNUSED; /* whether or not the argument was named */
{
- int bytes;
+ int bytes = avr_num_arg_regs (mode, type);
- bytes = (mode == BLKmode ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
cum->nregs -= bytes;
cum->regno -= bytes;
cum->nregs = 0;
cum->regno = FIRST_CUM_REG;
}
-
- return;
}
/***********************************************************************
{
case 0: /* mov r,r */
if (true_regnum (operands[0]) > true_regnum (operands[1]))
- return (AS2 (mov,%D0,%D1) CR_TAB
- AS2 (mov,%C0,%C1) CR_TAB
- AS2 (mov,%B0,%B1) CR_TAB
- AS2 (mov,%A0,%A1));
+ {
+ if (TARGET_ENHANCED)
+ return (AS2 (movw,%C0,%C1) CR_TAB
+ AS2 (movw,%A0,%A1)); /* FIXME: length = 4 -> 2 */
+ else
+ return (AS2 (mov,%D0,%D1) CR_TAB
+ AS2 (mov,%C0,%C1) CR_TAB
+ AS2 (mov,%B0,%B1) CR_TAB
+ AS2 (mov,%A0,%A1));
+ }
else
- return (AS2 (mov,%A0,%A1) CR_TAB
- AS2 (mov,%B0,%B1) CR_TAB
- AS2 (mov,%C0,%C1) CR_TAB
- AS2 (mov,%D0,%D1));
+ {
+ if (TARGET_ENHANCED)
+ return (AS2 (movw,%A0,%A1) CR_TAB
+ AS2 (movw,%C0,%C1)); /* FIXME: length = 4 -> 2 */
+ else
+ return (AS2 (mov,%A0,%A1) CR_TAB
+ AS2 (mov,%B0,%B1) CR_TAB
+ AS2 (mov,%C0,%C1) CR_TAB
+ AS2 (mov,%D0,%D1));
+ }
case 1: /* mov r,L */
+ if (TARGET_ENHANCED)
+ return (AS1 (clr,%A0) CR_TAB
+ AS1 (clr,%B0) CR_TAB
+ AS2 (movw,%C0,%A0)); /* FIXME: length = 4 -> 3 */
+
return (AS1 (clr,%A0) CR_TAB
AS1 (clr,%B0) CR_TAB
AS1 (clr,%C0) CR_TAB
if (l) *l = 1;
return AS1 (tst,%B0);
}
- if (TEST_HARD_REG_CLASS (ADDW_REGS, true_regnum (SET_SRC (PATTERN (insn)))))
+ if (test_hard_reg_class (ADDW_REGS, SET_SRC (PATTERN (insn))))
{
if (l) *l = 1;
return AS2 (sbiw,%0,0);
if (l) *l = 1;
return AS1 (tst,%D0);
}
- if (TEST_HARD_REG_CLASS (ADDW_REGS, true_regnum (SET_SRC (PATTERN (insn)))))
+ if (test_hard_reg_class (ADDW_REGS, SET_SRC (PATTERN (insn))))
{
if (l) *l = 3;
return (AS2 (sbiw,%A0,0) CR_TAB
*len = mov_len + 1;
}
}
- else if (register_operand (operands[2],QImode))
+ else if (register_operand (operands[2], QImode))
{
if (reg_unused_after (insn, operands[2]))
op[3] = op[2];
AS1 (lsl,%0));
case 4:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len = 2;
return (AS1 (swap,%0) CR_TAB
AS1 (lsl,%0));
case 5:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len = 3;
return (AS1 (swap,%0) CR_TAB
AS1 (lsl,%0));
case 6:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len = 4;
return (AS1 (swap,%0) CR_TAB
int reg0 = true_regnum (operands[0]);
int reg1 = true_regnum (operands[1]);
*len = 4;
+ if (TARGET_ENHANCED && (reg0 + 2 != reg1))
+ {
+ *len = 3;
+ return (AS2 (movw,%C0,%A1) CR_TAB
+ AS1 (clr,%B0) CR_TAB
+ AS1 (clr,%A0));
+ }
if (reg0 + 1 >= reg1)
return (AS2 (mov,%D0,%B1) CR_TAB
AS2 (mov,%C0,%A1) CR_TAB
int reg0 = true_regnum (operands[0]);
int reg1 = true_regnum (operands[1]);
*len=6;
+ if (TARGET_ENHANCED && (reg0 != reg1 + 2))
+ {
+ *len = 5;
+ return (AS2 (movw,%A0,%C1) CR_TAB
+ AS1 (clr,%D0) CR_TAB
+ AS2 (sbrc,%B0,7) CR_TAB
+ AS1 (com,%D0) CR_TAB
+ AS2 (mov,%C0,%D0));
+ }
if (reg0 <= reg1 + 1)
return (AS2 (mov,%A0,%C1) CR_TAB
AS2 (mov,%B0,%D1) CR_TAB
AS1 (lsr,%0));
case 4:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len=2;
return (AS1 (swap,%0) CR_TAB
AS1 (lsr,%0));
case 5:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len = 3;
return (AS1 (swap,%0) CR_TAB
AS1 (lsr,%0));
case 6:
- if (TEST_HARD_REG_CLASS (LD_REGS, true_regnum (operands[0])))
+ if (test_hard_reg_class (LD_REGS, operands[0]))
{
*len = 4;
return (AS1 (swap,%0) CR_TAB
int reg0 = true_regnum (operands[0]);
int reg1 = true_regnum (operands[1]);
*len = 4;
+ if (TARGET_ENHANCED && (reg0 != reg1 + 2))
+ {
+ *len = 3;
+ return (AS2 (movw,%A0,%C1) CR_TAB
+ AS1 (clr,%C0) CR_TAB
+ AS1 (clr,%D0));
+ }
if (reg0 <= reg1 + 1)
return (AS2 (mov,%A0,%C1) CR_TAB
AS2 (mov,%B0,%D1) CR_TAB
const char *name;
int reloc ATTRIBUTE_UNUSED;
{
- fprintf (file, ".section\t%s,\"%s\",@progbits\n", name, \
+ fprintf (file, ".section %s, \"%s\", @progbits\n", name,
decl && TREE_CODE (decl) == FUNCTION_DECL ? "ax" :
decl && TREE_READONLY (decl) ? "a" : "aw");
}
Valid attributes:
progmem - put data to program memory;
signal - make a function to be hardware interrupt. After function
- epilogue interrupts are disabled;
+ prologue interrupts are disabled;
interrupt - make a function to be hardware interrupt. After function
- epilogue interrupts are enabled;
+ prologue interrupts are enabled;
naked - don't generate function prologue/epilogue and `ret' command. */
int
/* Look for attribute `progmem' in DECL
- founded - 1 otherwise 0 */
+ if found return 1, otherwise 0. */
int
avr_progmem_p (decl)
{
char * dsec = ".progmem.data";
DECL_SECTION_NAME (decl) = build_string (strlen (dsec), dsec);
+ TREE_READONLY (decl) = 1;
}
}
return gen_rtx (REG, BLKmode, RET_REGISTER + 2 - offs);
}
-/* Returns non-zero if number MASK have only one setted bit */
+/* Returns non-zero if the number MASK has only one bit set. */
int
mask_one_bit_p (mask)
in class CLASS. */
enum reg_class
-preferred_reload_class(x,class)
+preferred_reload_class (x, class)
rtx x;
enum reg_class class;
{
}
int
-test_hard_reg_class(class, x)
+test_hard_reg_class (class, x)
enum reg_class class;
rtx x;
{
}
/* Returns 1 if a value of mode MODE can be stored starting with hard
- register number REGNO. On the enhanced core, it should be a win to
- align modes larger than QI on even register numbers (even if < 24).
- so that the "movw" instruction can be used on them. */
+ register number REGNO. On the enhanced core, anything larger than
+ 1 byte must start in even numbered register for "movw" to work
+ (this way we don't have to check for odd registers everywhere). */
int
avr_hard_regno_mode_ok (regno, mode)
{
if (mode == QImode)
return 1;
- if (regno < 24 /* && !TARGET_ENHANCED */ )
+ if (regno < 24 && !TARGET_ENHANCED)
return 1;
return !(regno & 1);
}
switch (which_alternative)
{
case 0: /* mov r,r */
+ if (TARGET_ENHANCED)
+ return (AS2 (movw,%0,%1)); /* FIXME: length = 2 -> 1 */
+
if (true_regnum (operands[0]) > true_regnum (operands[1]))
return (AS2 (mov,%B0,%B1) CR_TAB
AS2 (mov,%A0,%A1));
[(set_attr "length" "4,4")
(set_attr "cc" "set_czn,set_czn")])
+;******************************************************************************
+; mul
+
+(define_insn "mulqi3"
+ [(set (match_operand:QI 0 "register_operand" "=r")
+ (mult:QI (match_operand:QI 1 "register_operand" "r")
+ (match_operand:QI 2 "register_operand" "r")))]
+ "TARGET_ENHANCED"
+ "mul %1,%2
+ mov %0,r0
+ clr r1"
+ [(set_attr "length" "3")
+ (set_attr "cc" "clobber")])
+
+(define_insn "mulqihi3"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "d"))
+ (sign_extend:HI (match_operand:QI 2 "register_operand" "d"))))]
+ "TARGET_ENHANCED"
+ "muls %1,%2
+ movw %0,r0
+ clr r1"
+ [(set_attr "length" "3")
+ (set_attr "cc" "clobber")])
+
+(define_insn "umulqihi3"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r"))
+ (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
+ "TARGET_ENHANCED"
+ "mul %1,%2
+ movw %0,r0
+ clr r1"
+ [(set_attr "length" "3")
+ (set_attr "cc" "clobber")])
+
+(define_insn "mulhi3"
+ [(set (match_operand:HI 0 "register_operand" "=&r")
+ (mult:HI (match_operand:HI 1 "register_operand" "r")
+ (match_operand:HI 2 "register_operand" "r")))]
+ "TARGET_ENHANCED"
+ "mul %A1,%A2
+ movw %0,r0
+ mul %A1,%B2
+ add %B0,r0
+ mul %B1,%A2
+ add %B0,r0
+ clr r1"
+ [(set_attr "length" "7")
+ (set_attr "cc" "clobber")])
+
;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
; and
if (which_alternative==0)
return \"icall\";
else if (which_alternative==1)
- return (AS2 (mov, r30,%A0) CR_TAB
- AS2 (mov, r31,%B0) CR_TAB
- \"icall\");
+ {
+ if (TARGET_ENHANCED)
+ return (AS2 (movw, r30, %0) CR_TAB
+ \"icall\");
+ else
+ return (AS2 (mov, r30, %A0) CR_TAB
+ AS2 (mov, r31, %B0) CR_TAB
+ \"icall\");
+ }
else if (!AVR_MEGA)
return AS1(rcall,%c0);
return AS1(call,%c0);
if (which_alternative==0)
return \"icall\";
else if (which_alternative==1)
- return (AS2 (mov, r30,%A1) CR_TAB
- AS2 (mov, r31,%B1) CR_TAB
- \"icall\");
+ {
+ if (TARGET_ENHANCED)
+ return (AS2 (movw, r30, %1) CR_TAB
+ \"icall\");
+ else
+ return (AS2 (mov, r30, %A1) CR_TAB
+ AS2 (mov, r31, %B1) CR_TAB
+ \"icall\");
+ }
else if (!AVR_MEGA)
return AS1(rcall,%c1);
return AS1(call,%c1);
"optimize"
"")
+(define_insn "*tablejump_enh"
+ [(set (pc) (mem:HI
+ (plus:HI (match_operand:HI 0 "register_operand" "=&z")
+ (label_ref (match_operand 2 "" "")))))
+ (use (label_ref (match_operand 1 "" "")))]
+ "TARGET_ENHANCED"
+ "subi r30,lo8(-(%2))
+ sbci r31,hi8(-(%2))
+ lpm __tmp_reg__,Z+
+ lpm r31,Z
+ mov r30,__tmp_reg__
+ ijmp"
+ [(set_attr "length" "6")
+ (set_attr "cc" "clobber")])
+
(define_insn "*tablejump"
[(set (pc) (mem:HI
(plus:HI (match_operand:HI 0 "register_operand" "=&z")
sbci r31,hi8(-(%2))
lpm
push r0
- adiw r30,1
+ adiw r30,1
lpm
push r0
- ret"
+ ret"
[(set_attr "length" "8")
(set_attr "cc" "clobber")])