+2014-10-21 Joern Rennecke <joern.rennecke@embecosm.com>
+ Vidya Praveen <vidya.praveen@atmel.com>
+ Praveen Kumar Kaushik <Praveen_Kumar.Kaushik@atmel.com>
+ Senthil Kumar Selvaraj <Senthil_Kumar.Selvaraj@atmel.com>
+ Pitchumani Sivanupandi <Pitchumani.S@atmel.com>
+
+ * config/avr/avr-c.c (avr_cpu_cpp_builtins): Don't define
+ __MEMX for avrtiny.
+ * config/avr/avr.c (avr_insert_attributes): Reject __memx for avrtiny.
+ (avr_nonconst_pointer_addrspace): Likewise.
+ * config/avr/avr.h (AVR_HAVE_LPM): Define.
+
+ Added AVRTINY architecture to avr target.
+ * config/avr/avr-arch.h (avr_arch): Added AVRTINY architecture.
+ (base_arch_s): member added for AVRTINY architecture.
+ * config/avr/avr.c: Added TINY_ADIW, TINY_SBIW macros as AVRTINY
+ alternate for adiw/sbiw instructions. Added AVR_TMP_REGNO and
+ AVR_ZERO_REGNO macros for tmp and zero registers. Replaced TMP_REGNO
+ and ZERO_REGNO occurrences by AVR_TMP_REGNO and AVR_ZERO_REGNO
+ respectively. LAST_CALLEE_SAVED_REG macro added for the last register
+ in callee saved register list.
+ (avr_option_override): CCP address updated for AVRTINY.
+ (avr_init_expanders): tmp and zero rtx initialized as per arch.
+ Reset avr_have_dimode if AVRTINY.
+ (sequent_regs_live): Use LAST_CALLEE_SAVED_REG instead magic number.
+ (emit_push_sfr): Use AVR_TMP_REGNO for tmp register number.
+ (avr_prologue_setup_frame): Don't minimize prologue if AVRTINY.
+ Use LAST_CALLEE_SAVED_REG to refer last callee saved register.
+ (expand_epilogue): Likewise.
+ (avr_print_operand): Print CCP address in case of AVRTINY also.
+ <TBD>bad address
+ (function_arg_regno_p): Check different register list for arguments
+ if AVRTINY.
+ (init_cumulative_args): Check for AVRTINY to update number of argument
+ registers.
+ (tiny_valid_direct_memory_access_range): New function. Return false if
+ direct memory access range is not in accepted range for AVRTINY.
+ (avr_out_movqi_r_mr_reg_disp_tiny): New function to handle register
+ indirect load (with displacement) for AVRTINY.
+ (out_movqi_r_mr): Updated instruction length for AVRTINY. Call
+ avr_out_movqi_r_mr_reg_disp_tiny for load from reg+displacement.
+ (avr_out_movhi_r_mr_reg_no_disp_tiny): New function to handle register
+ indirect load (no displacement) for AVRTINY.
+ (avr_out_movhi_r_mr_reg_disp_tiny): New function to handle register
+ indirect load (with displacement) for AVRTINY.
+ (avr_out_movhi_r_mr_pre_dec_tiny): New function to handle register
+ indirect load for pre-decrement address.
+ (out_movhi_r_mr): In case of AVRTINY, call tiny register indirect load
+ functions. Update instruction length for AVRTINY.
+ (avr_out_movsi_r_mr_reg_no_disp_tiny): New function. Likewise, for
+ SImode.
+ (avr_out_movsi_r_mr_reg_disp_tiny): New function. Likewise, for SImode.
+ (out_movsi_r_mr): Likewise, for SImode.
+ (avr_out_movsi_mr_r_reg_no_disp_tiny): New function to handle register
+ indirect store (no displacement) for AVRTINY.
+ (avr_out_movsi_mr_r_reg_disp_tiny): New function to handle register
+ indirect store (with displacement) for AVRTINY.
+ (out_movsi_mr_r): Emit out insn for IO address store. Update store
+ instruction's size for AVRTINY. For AVRTINY, call tiny SImode indirect
+ store functions.
+ (avr_out_load_psi_reg_no_disp_tiny): New function to handle register
+ indirect load (no displacement) for PSImode in AVRTINY.
+ (avr_out_load_psi_reg_disp_tiny): New function to handle register
+ indirect load (with displacement) for PSImode in AVRTINY.
+ (avr_out_load_psi): Call PSImode register indirect load functions for
+ AVRTINY. Update instruction length for AVRTINY.
+ (avr_out_store_psi_reg_no_disp_tiny): New function to handle register
+ indirect store (no displacement) for PSImode in AVRTINY.
+ (avr_out_store_psi_reg_disp_tiny): New function to handle register
+ indirect store (with displacement) for PSImode in AVRTINY.
+ (avr_out_store_psi): Update instruction length for AVRTINY. Call tiny
+ register indirect store functions for AVRTINY.
+ (avr_out_movqi_mr_r_reg_disp_tiny): New function to handle QImode
+ register indirect store (with displacement) for AVRTINY.
+ (out_movqi_mr_r): Update instruction length for AVRTINY. Call tiny
+ register indirect store function for QImode in AVRTINY.
+ (avr_out_movhi_mr_r_xmega): Update instruction length for AVRTINY.
+ (avr_out_movhi_mr_r_reg_no_disp_tiny): New function to handle register
+ indirect store (no displacement) for HImode in AVRTINY.
+ (avr_out_movhi_mr_r_reg_disp_tiny): New function to handle register
+ indirect store (with displacement) for HImode in AVRTINY.
+ (avr_out_movhi_mr_r_post_inc_tiny): New function to handle register
+ indirect store for post-increment address in HImode.
+ (out_movhi_mr_r): Update instruction length for AVRTINY. Call tiny
+ register indirect store function for HImode in AVRTINY.
+ (avr_out_compare): Use TINY_SBIW/ TINY_ADIW in place of sbiw/adiw
+ in case of AVRTINY.
+ (order_regs_for_local_alloc): Updated register allocation order for
+ AVRTINY.
+ (avr_conditional_register_usage): New function. It is a target hook
+ (TARGET_CONDITIONAL_REGISTER_USAGE) function which updates fixed, call
+ used registers list and register allocation order for AVRTINY.
+ (avr_return_in_memory): Update return value size for AVRTINY.
+ * config/avr/avr-c.c (avr_cpu_cpp_builtins): Added builtin macros
+ for AVRTINY arch and tiny program memory base address.
+ * config/avr/avr-devices.c (avr_arch_types): Added AVRTINY arch.
+ (avr_texinfo): Added description for AVRTINY arch.
+ * config/avr/avr.h: Added macro to identify AVRTINY arch. Updated
+ STATIC_CHAIN_REGNUM for AVRTINY.
+ * config/avr/avr-mcus.def: Added AVRTINY arch devices.
+ * config/avr/avr.md: Added constants for tmp/ zero registers in
+ AVRTINY. Attributes for AVRTINY added.
+ (mov<mode>): Move src/ dest address to register if it is not in AVRTINY
+ memory access range.
+ (mov<mode>_insn): Avoid QImode direct load for AVRTINY if address not
+ in AVRTINY memory access range.
+ (*mov<mode>): Likewise for HImode and SImode.
+ (*movsf): Likewise for SFmode.
+ (delay_cycles_2): Updated instructions to be emitted as AVRTINY does
+ not have sbiw.
+ * config/avr/avr-protos.h: Added function prototype for
+ tiny_valid_direct_memory_access_range.
+ * config/avr/avr-tables.opt: Regenerate.
+ * gcc/config/avr/t-multilib: Regenerate.
+ * doc/avr-mmcu.texi: Regenerate.
+
2014-10-21 Andrew Pinski <apinski@cavium.com>
* doc/invoke.texi (AARCH64/mtune): Document thunderx as an
ARCH_AVR5,
ARCH_AVR51,
ARCH_AVR6,
+ ARCH_AVRTINY,
ARCH_AVRXMEGA2,
ARCH_AVRXMEGA4,
ARCH_AVRXMEGA5,
and thus also the RAMPX, RAMPY and RAMPZ registers. */
int have_rampd;
+ /* This is a TINY core. */
+ int tiny_p;
+
/* Default start of data section address for architecture. */
int default_data_section_start;
}
if (AVR_XMEGA)
cpp_define (pfile, "__AVR_XMEGA__");
+
+ if (AVR_TINY)
+ {
+ cpp_define (pfile, "__AVR_TINY__");
+
+ /* Define macro "__AVR_TINY_PM_BASE_ADDRESS__" with mapped program memory
+ start address. This macro shall be referred where mapped program memory
+ is accessed. (Eg. copying data section (do_copy_data) contents to data
+ memory region.
+ NOTE:
+ Program memory of AVR_TINY devices can not be accessed directly, it has
+ been mapped to the data memory. For AVR_TINY devices (ATtiny4/ 5/ 9/ 10/
+ 20 and 40) mapped program memory starts at 0x4000.
+ */
+ cpp_define (pfile, "__AVR_TINY_PM_BASE_ADDRESS__=0x4000");
+ }
+
if (avr_current_arch->have_eijmp_eicall)
{
cpp_define (pfile, "__AVR_HAVE_EIJMP_EICALL__");
/* Only supply __FLASH<n> macro if the address space is reasonable
for this target. The address space qualifier itself is still
supported, but using it will throw an error. */
- && avr_addrspace[i].segment < avr_n_flash)
+ && avr_addrspace[i].segment < avr_n_flash
+ /* Only support __MEMX macro if we have LPM. */
+ && (AVR_HAVE_LPM || avr_addrspace[i].pointer_size <= 2))
+
{
const char *name = avr_addrspace[i].name;
char *Name = (char*) alloca (1 + strlen (name));
avr_arch_types[] =
{
/* unknown device specified */
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, NULL, "avr2" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, NULL, "avr2" },
/*
- A M J LM E E E X R d S S O A
- S U M PO L L I M A a t F ff r
- M L P MV P P J E M t a R s c
- XW M M M G P a r e h
- X P A D t t ID */
- { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "1", "avr1" },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "2", "avr2" },
- { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "25", "avr25" },
- { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "3", "avr3" },
- { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 32, "31", "avr31" },
- { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "35", "avr35" },
- { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "4", "avr4" },
- { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "5", "avr5" },
- { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, "51", "avr51" },
- { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 32, "6", "avr6" },
+ A M J LM E E E X R T d S S O A
+ S U M PO L L I M A I a t F ff r
+ M L P MV P P J E M N t a R s c
+ XW M M M G P Y a r e h
+ X P A D t t ID */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "1", "avr1" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "2", "avr2" },
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "25", "avr25" },
+ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "3", "avr3" },
+ { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "31", "avr31" },
+ { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "35", "avr35" },
+ { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "4", "avr4" },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "5", "avr5" },
+ { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0x0060, 32, "51", "avr51" },
+ { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, "6", "avr6" },
- { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0x2000, 0, "102", "avrxmega2" },
- { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0x2000, 0, "104", "avrxmega4" },
- { 0, 1, 1, 1, 1, 1, 0, 1, 1, 0x2000, 0, "105", "avrxmega5" },
- { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0x2000, 0, "106", "avrxmega6" },
- { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0x2000, 0, "107", "avrxmega7" }
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0x0040, 0, "100", "avrtiny" },
+ { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0x2000, 0, "102", "avrxmega2" },
+ { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0x2000, 0, "104", "avrxmega4" },
+ { 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0x2000, 0, "105", "avrxmega5" },
+ { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0x2000, 0, "106", "avrxmega6" },
+ { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0x2000, 0, "107", "avrxmega7" }
};
const avr_arch_info_t
{ ARCH_AVR6,
"``Enhanced'' devices with 3-byte PC, i.e.@: with more than 128@tie{}KiB "
"of program memory." },
+ { ARCH_AVRTINY,
+ "``TINY'' Tiny core devices with 512@tie{}B up to 4@tie{}KiB of "
+ "program memory." },
{ ARCH_AVRXMEGA2,
"``XMEGA'' devices with more than 8@tie{}KiB and up to 64@tie{}KiB "
"of program memory." },
AVR_MCU ("atxmega128a1", ARCH_AVRXMEGA7, AVR_ISA_NONE, "__AVR_ATxmega128A1__", 0x2000, 0x0, 3, "x128a1")
AVR_MCU ("atxmega128a1u", ARCH_AVRXMEGA7, AVR_ISA_RMW, "__AVR_ATxmega128A1U__", 0x2000, 0x0, 3, "x128a1u")
AVR_MCU ("atxmega128a4u", ARCH_AVRXMEGA7, AVR_ISA_RMW, "__AVR_ATxmega128A4U__", 0x2000, 0x0, 3, "x128a4u")
+/* Tiny family */
+AVR_MCU ("avrtiny", ARCH_AVRTINY, AVR_ISA_NONE, NULL, 0x0040, 0x0, 1, "tn10")
+AVR_MCU ("attiny4", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny4__", 0x0040, 0x0, 1, "tn4")
+AVR_MCU ("attiny5", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny5__", 0x0040, 0x0, 1, "tn5")
+AVR_MCU ("attiny9", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny9__", 0x0040, 0x0, 1, "tn9")
+AVR_MCU ("attiny10", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny10__", 0x0040, 0x0, 1, "tn10")
+AVR_MCU ("attiny20", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny20__", 0x0040, 0x0, 1, "tn20")
+AVR_MCU ("attiny40", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny40__", 0x0040, 0x0, 1, "tn40")
/* Assembler only. */
AVR_MCU ("avr1", ARCH_AVR1, AVR_ISA_NONE, NULL, 0x0060, 0x0, 1, "s1200")
AVR_MCU ("at90s1200", ARCH_AVR1, AVR_ISA_NONE, "__AVR_AT90S1200__", 0x0060, 0x0, 1, "s1200")
#ifdef RTX_CODE
extern int avr_hard_regno_call_part_clobbered (unsigned, enum machine_mode);
+extern bool tiny_valid_direct_memory_access_range(rtx, enum machine_mode);
extern const char *output_movqi (rtx_insn *insn, rtx operands[], int *l);
extern const char *output_movhi (rtx_insn *insn, rtx operands[], int *l);
extern const char *output_movsisf (rtx_insn *insn, rtx operands[], int *l);
EnumValue
Enum(avr_arch) String(avrxmega7) Value(ARCH_AVRXMEGA7)
+EnumValue
+Enum(avr_arch) String(avrtiny) Value(ARCH_AVRTINY)
+
EnumValue
Enum(avr_arch) String(avr1) Value(ARCH_AVR1)
((SYMBOL_REF_FLAGS (sym) & AVR_SYMBOL_FLAG_PROGMEM) \
/ SYMBOL_FLAG_MACH_DEP)
+#define TINY_ADIW(REG1, REG2, I) \
+ "subi " #REG1 ",lo8(-(" #I "))" CR_TAB \
+ "sbci " #REG2 ",hi8(-(" #I "))"
+
+#define TINY_SBIW(REG1, REG2, I) \
+ "subi " #REG1 ",lo8((" #I "))" CR_TAB \
+ "sbci " #REG2 ",hi8((" #I "))"
+
+#define AVR_TMP_REGNO (AVR_TINY ? TMP_REGNO_TINY : TMP_REGNO)
+#define AVR_ZERO_REGNO (AVR_TINY ? ZERO_REGNO_TINY : ZERO_REGNO)
+
/* Known address spaces. The order must be the same as in the respective
enum from avr.h (or designated initialized must be used). */
const avr_addrspace_t avr_addrspace[ADDR_SPACE_COUNT] =
/* Allocate registers from r25 to r8 for parameters for function calls. */
#define FIRST_CUM_REG 26
+/* Last call saved register */
+#define LAST_CALLEE_SAVED_REG (AVR_TINY ? 19 : 17)
+
/* Implicit target register of LPM instruction (R0) */
extern GTY(()) rtx lpm_reg_rtx;
rtx lpm_reg_rtx;
avr_addr.rampy = 0x3A + avr_current_arch->sfr_offset;
avr_addr.rampx = 0x39 + avr_current_arch->sfr_offset;
avr_addr.rampd = 0x38 + avr_current_arch->sfr_offset;
- avr_addr.ccp = 0x34 + avr_current_arch->sfr_offset;
+ avr_addr.ccp = (AVR_TINY ? 0x3C : 0x34) + avr_current_arch->sfr_offset;
/* SP: Stack Pointer (SP_H:SP_L) */
avr_addr.sp_l = 0x3D + avr_current_arch->sfr_offset;
all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
lpm_reg_rtx = all_regs_rtx[LPM_REGNO];
- tmp_reg_rtx = all_regs_rtx[TMP_REGNO];
- zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
+ tmp_reg_rtx = all_regs_rtx[AVR_TMP_REGNO];
+ zero_reg_rtx = all_regs_rtx[AVR_ZERO_REGNO];
lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+
+ /* TINY core does not have regs r10-r16, but avr-dimode.md expects them
+ to be present */
+ if (AVR_TINY)
+ avr_have_dimode = false;
}
int live_seq = 0;
int cur_seq = 0;
- for (reg = 0; reg < 18; ++reg)
+ for (reg = 0; reg <= LAST_CALLEE_SAVED_REG; ++reg)
{
if (fixed_regs[reg])
{
RTX_FRAME_RELATED_P (insn) = 1;
/* PUSH __tmp_reg__ */
- emit_push_byte (TMP_REGNO, frame_related_p);
+ emit_push_byte (AVR_TMP_REGNO, frame_related_p);
if (clr_p)
{
&& live_seq
&& !isr_p
&& !cfun->machine->is_OS_task
- && !cfun->machine->is_OS_main);
+ && !cfun->machine->is_OS_main
+ && !AVR_TINY);
if (minimize
&& (frame_pointer_needed
/* Note that live_seq always contains r28+r29, but the other
registers to be saved are all below 18. */
- first_reg = 18 - (live_seq - 2);
+ first_reg = (LAST_CALLEE_SAVED_REG + 1) - (live_seq - 2);
for (reg = 29, offset = -live_seq + 1;
reg >= first_reg;
- reg = (reg == 28 ? 17 : reg - 1), ++offset)
+ reg = (reg == 28 ? LAST_CALLEE_SAVED_REG : reg - 1), ++offset)
{
rtx m, r;
emit_insn (gen_enable_interrupt ());
/* Push zero reg. */
- emit_push_byte (ZERO_REGNO, true);
+ emit_push_byte (AVR_ZERO_REGNO, true);
/* Push tmp reg. */
- emit_push_byte (TMP_REGNO, true);
+ emit_push_byte (AVR_TMP_REGNO, true);
/* Push SREG. */
/* ??? There's no dwarf2 column reserved for SREG. */
&& live_seq
&& !isr_p
&& !cfun->machine->is_OS_task
- && !cfun->machine->is_OS_main);
+ && !cfun->machine->is_OS_main
+ && !AVR_TINY);
if (minimize
&& (live_seq > 4
/* Restore SREG using tmp_reg as scratch. */
- emit_pop_byte (TMP_REGNO);
+ emit_pop_byte (AVR_TMP_REGNO);
emit_move_insn (sreg_rtx, tmp_reg_rtx);
/* Restore tmp REG. */
- emit_pop_byte (TMP_REGNO);
+ emit_pop_byte (AVR_TMP_REGNO);
/* Restore zero REG. */
- emit_pop_byte (ZERO_REGNO);
+ emit_pop_byte (AVR_ZERO_REGNO);
}
if (!sibcall_p)
static void
avr_print_operand (FILE *file, rtx x, int code)
{
- int abcd = 0;
+ int abcd = 0, ef = 0, ij = 0;
if (code >= 'A' && code <= 'D')
abcd = code - 'A';
+ else if (code == 'E' || code == 'F')
+ ef = code - 'E';
+ else if (code == 'I' || code == 'J')
+ ij = code - 'I';
if (code == '~')
{
else
fatal_insn ("operands to %T/%t must be reg + const_int:", x);
}
+ else if (code == 'E' || code == 'F')
+ {
+ rtx op = XEXP(x, 0);
+ fprintf (file, reg_names[REGNO (op) + ef]);
+ }
+ else if (code == 'I' || code == 'J')
+ {
+ rtx op = XEXP(XEXP(x, 0), 0);
+ fprintf (file, reg_names[REGNO (op) + ij]);
+ }
else if (REG_P (x))
{
if (x == zero_reg_rtx)
fprintf (file, "__RAMPX__");
else if (AVR_HAVE_RAMPD && ival == avr_addr.rampd)
fprintf (file, "__RAMPD__");
- else if (AVR_XMEGA && ival == avr_addr.ccp)
+ else if ((AVR_XMEGA || AVR_TINY) && ival == avr_addr.ccp)
fprintf (file, "__CCP__");
else if (ival == avr_addr.sreg) fprintf (file, "__SREG__");
else if (ival == avr_addr.sp_l) fprintf (file, "__SP_L__");
avr_print_operand (file, XEXP (addr, 1), 0);
}
+ else if (code == 'b')
+ {
+ if (GET_CODE (addr) != PLUS)
+ fatal_insn ("bad address, not (reg+disp):", addr);
+
+ avr_print_operand_address (file, XEXP (addr, 0));
+ }
else if (code == 'p' || code == 'r')
{
if (GET_CODE (addr) != POST_INC && GET_CODE (addr) != PRE_DEC)
int
avr_function_arg_regno_p(int r)
{
- return (r >= 8 && r <= 25);
+ return (AVR_TINY ? r >= 20 && r <= 25 : r >= 8 && r <= 25);
}
avr_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname,
tree fndecl ATTRIBUTE_UNUSED)
{
- cum->nregs = 18;
+ cum->nregs = AVR_TINY ? 6 : 18;
cum->regno = FIRST_CUM_REG;
if (!libname && stdarg_p (fntype))
cum->nregs = 0;
return "";
}
+/*
+AVRTC-579
+if operand is symbol or constant expression with value > 0xbf
+ return false, otherwise true
+This check is used to avoid lds/sts instruction with invalid memory
+access range (valid range 0x40..0xbf). For io operand range 0x0..0x3f,
+in/out instruction will be generated.
+*/
+bool tiny_valid_direct_memory_access_range(rtx op, enum machine_mode mode)
+{
+ rtx x;
+
+ if (!AVR_TINY)
+ return true;
+
+ x = XEXP(op,0);
+
+ if (MEM_P(op) && x && (GET_CODE(x) == SYMBOL_REF))
+ {
+ return false;
+ }
+ if (MEM_P(op) && x && (CONSTANT_ADDRESS_P (x)) &&
+ !(IN_RANGE (INTVAL (x), 0, 0xC0 - GET_MODE_SIZE (mode))))
+ {
+ return false;
+ }
+
+ return true;
+}
const char*
output_movqi (rtx_insn *insn, rtx operands[], int *plen)
return "";
}
+/* Same as out_movqi_r_mr, but TINY does not have ADIW or SBIW */
+static const char*
+avr_out_movqi_r_mr_reg_disp_tiny (rtx_insn *insn, rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx x = XEXP (src, 0);
+
+ avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %0,%b1" , op, plen, -3);
+
+ if (!reg_overlap_mentioned_p (dest, XEXP (x,0))
+ && !reg_unused_after (insn, XEXP (x,0)))
+ avr_asm_len (TINY_SBIW (%I1, %J1, %o1), op, plen, 2);
+
+ return "";
+}
+
static const char*
out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen)
{
if (CONSTANT_ADDRESS_P (x))
{
+ int n_words = AVR_TINY ? 1 : 2;
return optimize > 0 && io_address_operand (x, QImode)
? avr_asm_len ("in %0,%i1", op, plen, -1)
- : avr_asm_len ("lds %0,%m1", op, plen, -2);
+ : avr_asm_len ("lds %0,%m1", op, plen, -n_words);
}
- else if (GET_CODE (x) == PLUS
+
+ if (GET_CODE (x) == PLUS
&& REG_P (XEXP (x, 0))
&& CONST_INT_P (XEXP (x, 1)))
{
int disp = INTVAL (XEXP (x, 1));
+ if (AVR_TINY)
+ return avr_out_movqi_r_mr_reg_disp_tiny (insn, op, plen);
+
if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63)
{
if (REGNO (XEXP (x, 0)) != REG_Y)
return avr_asm_len ("ld %0,%1", op, plen, -1);
}
+/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */
+static const char*
+avr_out_movhi_r_mr_reg_no_disp_tiny (rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (base);
+
+ if (reg_dest == reg_base) /* R = (R) */
+ return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB
+ "ld %B0,%1" CR_TAB
+ "mov %A0,__tmp_reg__", op, plen, -3);
+
+ return avr_asm_len ("ld %A0,%1" CR_TAB
+ TINY_ADIW (%E1, %F1, 1) CR_TAB
+ "ld %B0,%1" CR_TAB
+ TINY_SBIW (%E1, %F1, 1), op, plen, -6);
+
+}
+
+/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */
+static const char*
+avr_out_movhi_r_mr_reg_disp_tiny (rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (XEXP (base, 0));
+
+ if (reg_base == reg_dest)
+ {
+ return avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld __tmp_reg__,%b1+" CR_TAB
+ "ld %B0,%b1" CR_TAB
+ "mov %A0,__tmp_reg__", op, plen, -5);
+ }
+ else
+ {
+ return avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %A0,%b1+" CR_TAB
+ "ld %B0,%b1" CR_TAB
+ TINY_SBIW (%I1, %J1, %o1+1), op, plen, -6);
+ }
+}
+
+/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */
+static const char*
+avr_out_movhi_r_mr_pre_dec_tiny (rtx_insn *insn, rtx op[], int *plen)
+{
+ int mem_volatile_p = 0;
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+
+ /* "volatile" forces reading low byte first, even if less efficient,
+ for correct operation with 16-bit I/O registers. */
+ mem_volatile_p = MEM_VOLATILE_P (src);
+
+ if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
+ fatal_insn ("incorrect insn:", insn);
+
+ if (!mem_volatile_p)
+ return avr_asm_len ("ld %B0,%1" CR_TAB
+ "ld %A0,%1", op, plen, -2);
+
+ return avr_asm_len (TINY_SBIW (%I1, %J1, 2) CR_TAB
+ "ld %A0,%p1+" CR_TAB
+ "ld %B0,%p1" CR_TAB
+ TINY_SBIW (%I1, %J1, 1), op, plen, -6);
+}
+
static const char*
out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen)
{
if (reg_base > 0)
{
+ if (AVR_TINY)
+ return avr_out_movhi_r_mr_reg_no_disp_tiny (op, plen);
+
if (reg_dest == reg_base) /* R = (R) */
return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB
"ld %B0,%1" CR_TAB
int disp = INTVAL (XEXP (base, 1));
int reg_base = true_regnum (XEXP (base, 0));
+ if (AVR_TINY)
+ return avr_out_movhi_r_mr_reg_disp_tiny (op, plen);
+
if (disp > MAX_LD_OFFSET (GET_MODE (src)))
{
if (REGNO (XEXP (base, 0)) != REG_Y)
"ldd %B0,Y+63" CR_TAB
"sbiw r28,%o1-62", op, plen, -4)
- : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+ : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
"sbci r29,hi8(-%o1)" CR_TAB
"ld %A0,Y" CR_TAB
"ldd %B0,Y+1" CR_TAB
}
else if (GET_CODE (base) == PRE_DEC) /* (--R) */
{
+ if (AVR_TINY)
+ return avr_out_movhi_r_mr_pre_dec_tiny (insn, op, plen);
+
if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
fatal_insn ("incorrect insn:", insn);
}
else if (CONSTANT_ADDRESS_P (base))
{
+ int n_words = AVR_TINY ? 2 : 4;
return optimize > 0 && io_address_operand (base, HImode)
? avr_asm_len ("in %A0,%i1" CR_TAB
"in %B0,%i1+1", op, plen, -2)
: avr_asm_len ("lds %A0,%m1" CR_TAB
- "lds %B0,%m1+1", op, plen, -4);
+ "lds %B0,%m1+1", op, plen, -n_words);
}
fatal_insn ("unknown move insn:",insn);
return "";
}
+static const char*
+avr_out_movsi_r_mr_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *l)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (base);
+
+ if (reg_dest == reg_base)
+ {
+ /* "ld r26,-X" is undefined */
+ return *l=9, (TINY_ADIW (%E1, %F1, 3) CR_TAB
+ "ld %D0,%1" CR_TAB
+ "ld %C0,-%1" CR_TAB
+ "ld __tmp_reg__,-%1" CR_TAB
+ TINY_SBIW (%E1, %F1, 1) CR_TAB
+ "ld %A0,%1" CR_TAB
+ "mov %B0,__tmp_reg__");
+ }
+ else if (reg_dest == reg_base - 2)
+ {
+ return *l=5, ("ld %A0,%1+" CR_TAB
+ "ld %B0,%1+" CR_TAB
+ "ld __tmp_reg__,%1+" CR_TAB
+ "ld %D0,%1" CR_TAB
+ "mov %C0,__tmp_reg__");
+ }
+ else if (reg_unused_after (insn, base))
+ {
+ return *l=4, ("ld %A0,%1+" CR_TAB
+ "ld %B0,%1+" CR_TAB
+ "ld %C0,%1+" CR_TAB
+ "ld %D0,%1");
+ }
+ else
+ {
+ return *l=6, ("ld %A0,%1+" CR_TAB
+ "ld %B0,%1+" CR_TAB
+ "ld %C0,%1+" CR_TAB
+ "ld %D0,%1" CR_TAB
+ TINY_SBIW (%E1, %F1, 3));
+ }
+}
+
+static const char*
+avr_out_movsi_r_mr_reg_disp_tiny (rtx_insn *insn, rtx op[], int *l)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (XEXP (base, 0));
+
+ if (reg_dest == reg_base)
+ {
+ /* "ld r26,-X" is undefined */
+ return *l=9, (TINY_ADIW (%I1, %J1, %o1+3) CR_TAB
+ "ld %D0,%b1" CR_TAB
+ "ld %C0,-%b1" CR_TAB
+ "ld __tmp_reg__,-%b1" CR_TAB
+ TINY_SBIW (%I1, %J1, 1) CR_TAB
+ "ld %A0,%b1" CR_TAB
+ "mov %B0,__tmp_reg__");
+ }
+ else if (reg_dest == reg_base - 2)
+ {
+ return *l=7, (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %A0,%b1+" CR_TAB
+ "ld %B0,%b1+" CR_TAB
+ "ld __tmp_reg__,%b1+" CR_TAB
+ "ld %D0,%b1" CR_TAB
+ "mov %C0,__tmp_reg__");
+ }
+ else if (reg_unused_after (insn, XEXP (base, 0)))
+ {
+ return *l=6, (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %A0,%b1+" CR_TAB
+ "ld %B0,%b1+" CR_TAB
+ "ld %C0,%b1+" CR_TAB
+ "ld %D0,%b1");
+ }
+ else
+ {
+ return *l=8, (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %A0,%b1+" CR_TAB
+ "ld %B0,%b1+" CR_TAB
+ "ld %C0,%b1+" CR_TAB
+ "ld %D0,%b1" CR_TAB
+ TINY_SBIW (%I1, %J1, %o1+3));
+ }
+}
+
static const char*
out_movsi_r_mr (rtx_insn *insn, rtx op[], int *l)
{
if (reg_base > 0)
{
+ if (AVR_TINY)
+ return avr_out_movsi_r_mr_reg_no_disp_tiny (insn, op, l);
+
if (reg_base == REG_X) /* (R26) */
{
if (reg_dest == REG_X)
{
int disp = INTVAL (XEXP (base, 1));
+ if (AVR_TINY)
+ return avr_out_movsi_r_mr_reg_disp_tiny (insn, op, l);
+
if (disp > MAX_LD_OFFSET (GET_MODE (src)))
{
if (REGNO (XEXP (base, 0)) != REG_Y)
"ld %C0,%1" CR_TAB
"ld %D0,%1");
else if (CONSTANT_ADDRESS_P (base))
- return *l=8, ("lds %A0,%m1" CR_TAB
+ {
+ if (io_address_operand (base, SImode))
+ {
+ *l = 4;
+ return ("in %A0,%i1" CR_TAB
+ "in %B0,%i1+1" CR_TAB
+ "in %C0,%i1+2" CR_TAB
+ "in %D0,%i1+3");
+ }
+ else
+ {
+ *l = AVR_TINY ? 4 : 8;
+ return ("lds %A0,%m1" CR_TAB
"lds %B0,%m1+1" CR_TAB
"lds %C0,%m1+2" CR_TAB
"lds %D0,%m1+3");
+ }
+ }
fatal_insn ("unknown move insn:",insn);
return "";
}
+static const char*
+avr_out_movsi_mr_r_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *l)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = true_regnum (base);
+ int reg_src = true_regnum (src);
+
+ if (reg_base == reg_src)
+ {
+ /* "ld r26,-X" is undefined */
+ if (reg_unused_after (insn, base))
+ {
+ return *l=7, ("mov __tmp_reg__, %B1" CR_TAB
+ "st %0,%A1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0+,__tmp_reg__" CR_TAB
+ "st %0+,%C1" CR_TAB
+ "st %0+,%D1");
+ }
+ else
+ {
+ return *l=9, ("mov __tmp_reg__, %B1" CR_TAB
+ "st %0,%A1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0+,__tmp_reg__" CR_TAB
+ "st %0+,%C1" CR_TAB
+ "st %0+,%D1" CR_TAB
+ TINY_SBIW (%E0, %F0, 3));
+ }
+ }
+ else if (reg_base == reg_src + 2)
+ {
+ if (reg_unused_after (insn, base))
+ return *l=7, ("mov __zero_reg__,%C1" CR_TAB
+ "mov __tmp_reg__,%D1" CR_TAB
+ "st %0+,%A1" CR_TAB
+ "st %0+,%B1" CR_TAB
+ "st %0+,__zero_reg__" CR_TAB
+ "st %0,__tmp_reg__" CR_TAB
+ "clr __zero_reg__");
+ else
+ return *l=9, ("mov __zero_reg__,%C1" CR_TAB
+ "mov __tmp_reg__,%D1" CR_TAB
+ "st %0+,%A1" CR_TAB
+ "st %0+,%B1" CR_TAB
+ "st %0+,__zero_reg__" CR_TAB
+ "st %0,__tmp_reg__" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ TINY_SBIW (%E0, %F0, 3));
+ }
+
+ return *l=6, ("st %0+,%A1" CR_TAB
+ "st %0+,%B1" CR_TAB
+ "st %0+,%C1" CR_TAB
+ "st %0,%D1" CR_TAB
+ TINY_SBIW (%E0, %F0, 3));
+}
+
+static const char*
+avr_out_movsi_mr_r_reg_disp_tiny (rtx op[], int *l)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = REGNO (XEXP (base, 0));
+ int reg_src =true_regnum (src);
+
+ if (reg_base == reg_src)
+ {
+ *l = 11;
+ return ("mov __tmp_reg__,%A2" CR_TAB
+ "mov __zero_reg__,%B2" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,__tmp_reg__" CR_TAB
+ "st %b0+,__zero_reg__" CR_TAB
+ "st %b0+,%C2" CR_TAB
+ "st %b0,%D2" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+3));
+ }
+ else if (reg_src == reg_base - 2)
+ {
+ *l = 11;
+ return ("mov __tmp_reg__,%C2" CR_TAB
+ "mov __zero_reg__,%D2" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,%A0" CR_TAB
+ "st %b0+,%B0" CR_TAB
+ "st %b0+,__tmp_reg__" CR_TAB
+ "st %b0,__zero_reg__" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+3));
+ }
+ *l = 8;
+ return (TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,%A1" CR_TAB
+ "st %b0+,%B1" CR_TAB
+ "st %b0+,%C1" CR_TAB
+ "st %b0,%D1" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+3));
+}
+
static const char*
out_movsi_mr_r (rtx_insn *insn, rtx op[], int *l)
{
l = &tmp;
if (CONSTANT_ADDRESS_P (base))
- return *l=8,("sts %m0,%A1" CR_TAB
- "sts %m0+1,%B1" CR_TAB
- "sts %m0+2,%C1" CR_TAB
- "sts %m0+3,%D1");
+ {
+ if (io_address_operand (base, SImode))
+ {
+ return *l=4,("out %i0, %A1" CR_TAB
+ "out %i0+1,%B1" CR_TAB
+ "out %i0+2,%C1" CR_TAB
+ "out %i0+3,%D1");
+ }
+ else
+ {
+ *l = AVR_TINY ? 4 : 8;
+ return ("sts %m0,%A1" CR_TAB
+ "sts %m0+1,%B1" CR_TAB
+ "sts %m0+2,%C1" CR_TAB
+ "sts %m0+3,%D1");
+ }
+ }
+
if (reg_base > 0) /* (r) */
{
+ if (AVR_TINY)
+ return avr_out_movsi_mr_r_reg_no_disp_tiny (insn, op, l);
+
if (reg_base == REG_X) /* (R26) */
{
if (reg_src == REG_X)
else if (GET_CODE (base) == PLUS) /* (R + i) */
{
int disp = INTVAL (XEXP (base, 1));
+
+ if (AVR_TINY)
+ return avr_out_movsi_mr_r_reg_disp_tiny (op, l);
+
reg_base = REGNO (XEXP (base, 0));
if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
{
/* Handle loads of 24-bit types from memory to register. */
+static const char*
+avr_out_load_psi_reg_no_disp_tiny (rtx_insn *insn, rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (base);
+
+ if (reg_base == reg_dest)
+ {
+ return avr_asm_len (TINY_ADIW (%E1, %F1, 2) CR_TAB
+ "ld %C0,%1" CR_TAB
+ "ld __tmp_reg__,-%1" CR_TAB
+ TINY_SBIW (%E1, %F1, 1) CR_TAB
+ "ld %A0,%1" CR_TAB
+ "mov %B0,__tmp_reg__", op, plen, -8);
+ }
+ else
+ {
+ return avr_asm_len ("ld %A0,%1+" CR_TAB
+ "ld %B0,%1+" CR_TAB
+ "ld %C0,%1", op, plen, -3);
+
+ if (reg_dest != reg_base - 2 &&
+ !reg_unused_after (insn, base))
+ {
+ avr_asm_len (TINY_SBIW (%E1, %F1, 2), op, plen, 2);
+ }
+ return "";
+ }
+}
+
+static const char*
+avr_out_load_psi_reg_disp_tiny (rtx_insn *insn, rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (src, 0);
+ int reg_dest = true_regnum (dest);
+ int reg_base = true_regnum (base);
+
+ reg_base = true_regnum (XEXP (base, 0));
+ if (reg_base == reg_dest)
+ {
+ return avr_asm_len (TINY_ADIW (%I1, %J1, %o1+2) CR_TAB
+ "ld %C0,%b1" CR_TAB
+ "ld __tmp_reg__,-%b1" CR_TAB
+ TINY_SBIW (%I1, %J1, 1) CR_TAB
+ "ld %A0,%b1" CR_TAB
+ "mov %B0,__tmp_reg__", op, plen, -8);
+ }
+ else
+ {
+ avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB
+ "ld %A0,%b1+" CR_TAB
+ "ld %B0,%b1+" CR_TAB
+ "ld %C0,%b1", op, plen, -5);
+
+ if (reg_dest != (reg_base - 2)
+ && !reg_unused_after (insn, XEXP (base, 0)))
+ avr_asm_len (TINY_SBIW (%I1, %J1, %o1+2), op, plen, 2);
+
+ return "";
+ }
+}
+
static const char*
avr_out_load_psi (rtx_insn *insn, rtx *op, int *plen)
{
if (reg_base > 0)
{
+ if (AVR_TINY)
+ return avr_out_load_psi_reg_no_disp_tiny (insn, op, plen);
+
if (reg_base == REG_X) /* (R26) */
{
if (reg_dest == REG_X)
{
int disp = INTVAL (XEXP (base, 1));
+ if (AVR_TINY)
+ return avr_out_load_psi_reg_disp_tiny (insn, op, plen);
+
if (disp > MAX_LD_OFFSET (GET_MODE (src)))
{
if (REGNO (XEXP (base, 0)) != REG_Y)
"ld %C0,%1", op, plen, -3);
else if (CONSTANT_ADDRESS_P (base))
- return avr_asm_len ("lds %A0,%m1" CR_TAB
- "lds %B0,%m1+1" CR_TAB
- "lds %C0,%m1+2", op, plen , -6);
+ {
+ int n_words = AVR_TINY ? 3 : 6;
+ return avr_asm_len ("lds %A0,%m1" CR_TAB
+ "lds %B0,%m1+1" CR_TAB
+ "lds %C0,%m1+2", op, plen , -n_words);
+ }
fatal_insn ("unknown move insn:",insn);
return "";
}
+
+static const char*
+avr_out_store_psi_reg_no_disp_tiny (rtx_insn *insn, rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = true_regnum (base);
+ int reg_src = true_regnum (src);
+
+ if (reg_base == reg_src)
+ {
+ avr_asm_len ("st %0,%A1" CR_TAB
+ "mov __tmp_reg__,%B1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB /* st X+, r27 is undefined */
+ "st %0+,__tmp_reg__" CR_TAB
+ "st %0,%C1", op, plen, -6);
+
+ }
+ else if (reg_src == reg_base - 2)
+ {
+ avr_asm_len ("st %0,%A1" CR_TAB
+ "mov __tmp_reg__,%C1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0+,%B1" CR_TAB
+ "st %0,__tmp_reg__", op, plen, 6);
+ }
+ else
+ {
+ avr_asm_len ("st %0+,%A1" CR_TAB
+ "st %0+,%B1" CR_TAB
+ "st %0,%C1", op, plen, -3);
+ }
+
+ if (!reg_unused_after (insn, base))
+ avr_asm_len (TINY_SBIW (%E0, %F0, 2), op, plen, 2);
+
+ return "";
+}
+
+static const char*
+avr_out_store_psi_reg_disp_tiny (rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = REGNO (XEXP (base, 0));
+ int reg_src = true_regnum (src);
+
+ if (reg_src == reg_base)
+ {
+ return avr_asm_len ("mov __tmp_reg__,%A1" CR_TAB
+ "mov __zero_reg__,%B1" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,__tmp_reg__" CR_TAB
+ "st %b0+,__zero_reg__" CR_TAB
+ "st %b0,%C1" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+2), op, plen, -10);
+ }
+ else if (reg_src == reg_base - 2)
+ {
+ return avr_asm_len ("mov __tmp_reg__,%C1" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,%A1" CR_TAB
+ "st %b0+,%B1" CR_TAB
+ "st %b0,__tmp_reg__" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+2), op, plen, -8);
+ }
+
+ return avr_asm_len (TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0+,%A1" CR_TAB
+ "st %b0+,%B1" CR_TAB
+ "st %b0,%C1" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0+2), op, plen, -7);
+}
+
/* Handle store of 24-bit type from register or zero to memory. */
static const char*
int reg_base = true_regnum (base);
if (CONSTANT_ADDRESS_P (base))
- return avr_asm_len ("sts %m0,%A1" CR_TAB
- "sts %m0+1,%B1" CR_TAB
- "sts %m0+2,%C1", op, plen, -6);
+ {
+ int n_words = AVR_TINY ? 3 : 6;
+ return avr_asm_len ("sts %m0,%A1" CR_TAB
+ "sts %m0+1,%B1" CR_TAB
+ "sts %m0+2,%C1", op, plen, -n_words);
+ }
if (reg_base > 0) /* (r) */
{
+ if (AVR_TINY)
+ return avr_out_store_psi_reg_no_disp_tiny (insn, op, plen);
+
if (reg_base == REG_X) /* (R26) */
{
gcc_assert (!reg_overlap_mentioned_p (base, src));
else if (GET_CODE (base) == PLUS) /* (R + i) */
{
int disp = INTVAL (XEXP (base, 1));
+
+ if (AVR_TINY)
+ return avr_out_store_psi_reg_disp_tiny (op, plen);
+
reg_base = REGNO (XEXP (base, 0));
if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
return "";
}
+static const char*
+avr_out_movqi_mr_r_reg_disp_tiny (rtx_insn *insn, rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx x = XEXP (dest, 0);
+
+ if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
+ {
+ avr_asm_len ("mov __tmp_reg__,%1" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0,__tmp_reg__", op, plen, -4);
+ }
+ else
+ {
+ avr_asm_len (TINY_ADIW (%I0, %J0, %o0) CR_TAB
+ "st %b0,%1" , op, plen, -3);
+ }
+
+ if (!reg_unused_after (insn, XEXP (x,0)))
+ avr_asm_len (TINY_SBIW (%I0, %J0, %o0), op, plen, 2);
+
+ return "";
+}
static const char*
out_movqi_mr_r (rtx_insn *insn, rtx op[], int *plen)
if (CONSTANT_ADDRESS_P (x))
{
+ int n_words = AVR_TINY ? 1 : 2;
return optimize > 0 && io_address_operand (x, QImode)
? avr_asm_len ("out %i0,%1", op, plen, -1)
- : avr_asm_len ("sts %m0,%1", op, plen, -2);
+ : avr_asm_len ("sts %m0,%1", op, plen, -n_words);
}
else if (GET_CODE (x) == PLUS
&& REG_P (XEXP (x, 0))
int disp = INTVAL (XEXP (x, 1));
+ if (AVR_TINY)
+ return avr_out_movqi_mr_r_reg_disp_tiny (insn, op, plen);
+
if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63)
{
if (REGNO (XEXP (x, 0)) != REG_Y)
int mem_volatile_p = MEM_VOLATILE_P (dest);
if (CONSTANT_ADDRESS_P (base))
- return optimize > 0 && io_address_operand (base, HImode)
- ? avr_asm_len ("out %i0,%A1" CR_TAB
- "out %i0+1,%B1", op, plen, -2)
+ {
+ int n_words = AVR_TINY ? 2 : 4;
+ return optimize > 0 && io_address_operand (base, HImode)
+ ? avr_asm_len ("out %i0,%A1" CR_TAB
+ "out %i0+1,%B1", op, plen, -2)
- : avr_asm_len ("sts %m0,%A1" CR_TAB
- "sts %m0+1,%B1", op, plen, -4);
+ : avr_asm_len ("sts %m0,%A1" CR_TAB
+ "sts %m0+1,%B1", op, plen, -n_words);
+ }
if (reg_base > 0)
{
return "";
}
+static const char*
+avr_out_movhi_mr_r_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = true_regnum (base);
+ int reg_src = true_regnum (src);
+ int mem_volatile_p = MEM_VOLATILE_P (dest);
+
+ if (reg_base == reg_src)
+ {
+ return !mem_volatile_p && reg_unused_after (insn, src)
+ ? avr_asm_len ("mov __tmp_reg__,%B1" CR_TAB
+ "st %0,%A1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0,__tmp_reg__", op, plen, -5)
+ : avr_asm_len ("mov __tmp_reg__,%B1" CR_TAB
+ TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0,__tmp_reg__" CR_TAB
+ TINY_SBIW (%E0, %F0, 1) CR_TAB
+ "st %0, %A1", op, plen, -7);
+ }
+
+ return !mem_volatile_p && reg_unused_after (insn, base)
+ ? avr_asm_len ("st %0+,%A1" CR_TAB
+ "st %0,%B1", op, plen, -2)
+ : avr_asm_len (TINY_ADIW (%E0, %F0, 1) CR_TAB
+ "st %0,%B1" CR_TAB
+ "st -%0,%A1", op, plen, -4);
+}
+
+static const char*
+avr_out_movhi_mr_r_reg_disp_tiny (rtx op[], int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ int reg_base = REGNO (XEXP (base, 0));
+ int reg_src = true_regnum (src);
+
+ return reg_src == reg_base
+ ? avr_asm_len ("mov __tmp_reg__,%A1" CR_TAB
+ "mov __zero_reg__,%B1" CR_TAB
+ TINY_ADIW (%I0, %J0, %o0+1) CR_TAB
+ "st %b0,__zero_reg__" CR_TAB
+ "st -%b0,__tmp_reg__" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0), op, plen, -9)
+
+ : avr_asm_len (TINY_ADIW (%I0, %J0, %o0+1) CR_TAB
+ "st %b0,%B1" CR_TAB
+ "st -%b0,%A1" CR_TAB
+ TINY_SBIW (%I0, %J0, %o0), op, plen, -6);
+}
+
+static const char*
+avr_out_movhi_mr_r_post_inc_tiny (rtx op[], int *plen)
+{
+ return avr_asm_len (TINY_ADIW (%I0, %J0, 1) CR_TAB
+ "st %p0,%B1" CR_TAB
+ "st -%p0,%A1" CR_TAB
+ TINY_ADIW (%I0, %J0, 2), op, plen, -6);
+}
static const char*
out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen)
mem_volatile_p = MEM_VOLATILE_P (dest);
if (CONSTANT_ADDRESS_P (base))
- return optimize > 0 && io_address_operand (base, HImode)
- ? avr_asm_len ("out %i0+1,%B1" CR_TAB
- "out %i0,%A1", op, plen, -2)
+ {
+ int n_words = AVR_TINY ? 2 : 4;
+ return optimize > 0 && io_address_operand (base, HImode)
+ ? avr_asm_len ("out %i0+1,%B1" CR_TAB
+ "out %i0,%A1", op, plen, -2)
- : avr_asm_len ("sts %m0+1,%B1" CR_TAB
- "sts %m0,%A1", op, plen, -4);
+ : avr_asm_len ("sts %m0+1,%B1" CR_TAB
+ "sts %m0,%A1", op, plen, -n_words);
+ }
if (reg_base > 0)
{
+ if (AVR_TINY)
+ return avr_out_movhi_mr_r_reg_no_disp_tiny (insn, op, plen);
+
if (reg_base != REG_X)
return avr_asm_len ("std %0+1,%B1" CR_TAB
"st %0,%A1", op, plen, -2);
else if (GET_CODE (base) == PLUS)
{
int disp = INTVAL (XEXP (base, 1));
+
+ if (AVR_TINY)
+ return avr_out_movhi_mr_r_reg_disp_tiny (op, plen);
+
reg_base = REGNO (XEXP (base, 0));
if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
{
return avr_asm_len ("st %0,%A1" CR_TAB
"st %0,%B1", op, plen, -2);
+ if (AVR_TINY)
+ return avr_out_movhi_mr_r_post_inc_tiny (op, plen);
+
return REGNO (XEXP (base, 0)) == REG_X
? avr_asm_len ("adiw r26,1" CR_TAB
"st X,%B1" CR_TAB
&& (val8 == 0
|| reg_unused_after (insn, xreg)))
{
- avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+ if (AVR_TINY)
+ avr_asm_len (TINY_SBIW (%A0, %B0, %1), xop, plen, 2);
+ else
+ avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+
i++;
continue;
}
&& compare_eq_p (insn)
&& reg_unused_after (insn, xreg))
{
- return avr_asm_len ("adiw %0,%n1", xop, plen, 1);
+ return AVR_TINY
+ ? avr_asm_len (TINY_ADIW (%A0, %B0, %n1), xop, plen, 2)
+ : avr_asm_len ("adiw %0,%n1", xop, plen, 1);
}
}
static bool
avr_class_likely_spilled_p (reg_class_t c)
{
- return (c != ALL_REGS && c != ADDW_REGS);
+ return (c != ALL_REGS &&
+ (AVR_TINY ? 1 : c != ADDW_REGS));
}
if (!ADDR_SPACE_GENERIC_P (as)
&& (!TYPE_READONLY (target)
- || avr_addrspace[as].segment >= avr_n_flash))
+ || avr_addrspace[as].segment >= avr_n_flash
+ /* Also refuse __memx address space if we can't support it. */
+ || (!AVR_HAVE_LPM && avr_addrspace[as].pointer_size > 2)))
{
return as;
}
" beyond flash of %qs",
node, avr_addrspace[as].name, avr_current_device->name);
}
+ else if (!AVR_HAVE_LPM && avr_addrspace[as].pointer_size > 2)
+ {
+ error ("variable %q+D located in address space %qs"
+ " which is not supported by %qs",
+ node, avr_addrspace[as].name, avr_current_arch->arch_name);
+ }
if (!TYPE_READONLY (node0)
&& !TREE_READONLY (node))
fprintf (asm_out_file, "__RAMPX__ = 0x%02x\n", avr_addr.rampx - sfr_offset);
if (AVR_HAVE_RAMPD)
fprintf (asm_out_file, "__RAMPD__ = 0x%02x\n", avr_addr.rampd - sfr_offset);
- if (AVR_XMEGA)
+ if (AVR_XMEGA || AVR_TINY)
fprintf (asm_out_file, "__CCP__ = 0x%02x\n", avr_addr.ccp - sfr_offset);
- fprintf (asm_out_file, "__tmp_reg__ = %d\n", TMP_REGNO);
- fprintf (asm_out_file, "__zero_reg__ = %d\n", ZERO_REGNO);
+ fprintf (asm_out_file, "__tmp_reg__ = %d\n", AVR_TMP_REGNO);
+ fprintf (asm_out_file, "__zero_reg__ = %d\n", AVR_ZERO_REGNO);
}
0, 1,
32, 33, 34, 35
};
+ static const int tiny_order_0[] = {
+ 20, 21,
+ 22, 23,
+ 24, 25,
+ 30, 31,
+ 26, 27,
+ 28, 29,
+ 19, 18,
+ 16, 17,
+ 32, 33, 34, 35,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+ };
static const int order_1[] =
{
18, 19, 20, 21, 22, 23, 24, 25,
0, 1,
32, 33, 34, 35
};
+ static const int tiny_order_1[] = {
+ 22, 23,
+ 24, 25,
+ 30, 31,
+ 26, 27,
+ 28, 29,
+ 21, 20, 19, 18,
+ 16, 17,
+ 32, 33, 34, 35,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+ };
static const int order_2[] =
{
25, 24, 23, 22, 21, 20, 19, 18,
32, 33, 34, 35
};
- const int *order = (TARGET_ORDER_1 ? order_1 :
- TARGET_ORDER_2 ? order_2 :
- order_0);
+ /*
+ Select specific register allocation order. Tiny Core (attiny4/5/9/10/20/40)
+ devices has only 16 registers, so different allocation order should be used
+ */
+ const int *order = (TARGET_ORDER_1 ? (AVR_TINY ? tiny_order_1 : order_1) :
+ TARGET_ORDER_2 ? (AVR_TINY ? tiny_order_0 : order_2) :
+ (AVR_TINY ? tiny_order_0 : order_0));
+
for (i = 0; i < ARRAY_SIZE (order_0); ++i)
reg_alloc_order[i] = order[i];
}
{
if (!clear_p)
avr_asm_len (ldreg_p ? "ldi %0,0"
- : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0"
+ : AVR_ZERO_REGNO == REGNO (xdest[n]) ? "clr %0"
: "mov %0,__zero_reg__",
&xdest[n], len, 1);
continue;
fprintf (stream, "\trjmp .L%d\n", value);
}
+static void
+avr_conditional_register_usage(void) {
+
+ if (AVR_TINY) {
+ unsigned int i;
+
+ const int tiny_reg_alloc_order[] = {
+ 24, 25,
+ 22, 23,
+ 30, 31,
+ 26, 27,
+ 28, 29,
+ 21, 20, 19, 18,
+ 16, 17,
+ 32, 33, 34, 35,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+ };
+
+ /* Set R0-R17 as fixed registers. Reset R0-R17 in call used register list
+ - R0-R15 are not available in Tiny Core devices
+ - R16 and R17 are fixed registers
+ */
+ for (i = 0; i <= 17; i++) {
+ fixed_regs[i] = 1;
+ call_used_regs[i] = 1;
+ }
+
+ /* Set R18 to R21 as callee saved registers
+ - R18, R19, R20 and R21 are the callee saved registers in Tiny Core devices
+ */
+ for (i = 18; i <= LAST_CALLEE_SAVED_REG; i++) {
+ call_used_regs[i] = 0;
+ }
+
+ /*update register allocation order for Tiny Core devices */
+ for (i=0; i < ARRAY_SIZE (tiny_reg_alloc_order); i++) {
+ reg_alloc_order[i] = tiny_reg_alloc_order[i];
+ }
+
+ CLEAR_HARD_REG_SET(reg_class_contents[(int)ADDW_REGS]);
+ CLEAR_HARD_REG_SET(reg_class_contents[(int)NO_LD_REGS]);
+ }
+}
/* Implement `TARGET_HARD_REGNO_SCRATCH_OK'. */
/* Returns true if SCRATCH are safe to be allocated as a scratch
static bool
avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
- if (TYPE_MODE (type) == BLKmode)
- {
- HOST_WIDE_INT size = int_size_in_bytes (type);
- return (size == -1 || size > 8);
- }
+ HOST_WIDE_INT size = int_size_in_bytes (type);
+ HOST_WIDE_INT ret_size_limit = AVR_TINY ? 4 : 8;
+
+ /* In avr, there are 8 return registers. But, for Tiny Core
+ (attiny4/5/9/10/20/40) devices, only 4 registers available.
+ Return true if size is unknown or greater than the limit */
+ if ((size == -1) || (size > ret_size_limit))
+ {
+ return true;
+ }
else
+ {
return false;
+ }
}
#undef TARGET_BUILTIN_SETJMP_FRAME_VALUE
#define TARGET_BUILTIN_SETJMP_FRAME_VALUE avr_builtin_setjmp_frame_value
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE avr_conditional_register_usage
+
#undef TARGET_HARD_REGNO_SCRATCH_OK
#define TARGET_HARD_REGNO_SCRATCH_OK avr_hard_regno_scratch_ok
#undef TARGET_CASE_VALUES_THRESHOLD
#define AVR_HAVE_JMP_CALL (avr_current_arch->have_jmp_call)
#define AVR_HAVE_MUL (avr_current_arch->have_mul)
#define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx)
+#define AVR_HAVE_LPM (!AVR_TINY)
#define AVR_HAVE_LPMX (avr_current_arch->have_movw_lpmx)
#define AVR_HAVE_ELPM (avr_current_arch->have_elpm)
#define AVR_HAVE_ELPMX (avr_current_arch->have_elpmx)
#define AVR_3_BYTE_PC (AVR_HAVE_EIJMP_EICALL)
#define AVR_XMEGA (avr_current_arch->xmega_p)
+#define AVR_TINY (avr_current_arch->tiny_p)
#define BITS_BIG_ENDIAN 0
#define BYTES_BIG_ENDIAN 0
#define ARG_POINTER_REGNUM 34
-#define STATIC_CHAIN_REGNUM 2
+#define STATIC_CHAIN_REGNUM ((AVR_TINY) ? 18 :2)
#define ELIMINABLE_REGS { \
{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
;; B Add 1 to REG number, MEM address or CONST_INT.
;; C Add 2.
;; D Add 3.
+;; E reg number in XEXP(x, 0).
+;; F Add 1 to reg number.
+;; I reg number in XEXP(XEXP(x, 0), 0).
+;; J Add 1 to reg number.
;; j Branch condition.
;; k Reverse branch condition.
;;..m..Constant Direct Data memory address.
(ZERO_REGNO 1) ; zero register r1
])
+(define_constants
+ [ (TMP_REGNO_TINY 16) ; r16 is temp register for AVR_TINY
+ (ZERO_REGNO_TINY 17) ; r17 is zero register for AVR_TINY
+ ])
+
(define_c_enum "unspec"
[UNSPEC_STRLEN
UNSPEC_MOVMEM
;; lpm : ISA has no LPMX lpmx : ISA has LPMX
;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX
;; no_xmega: non-XMEGA core xmega : XMEGA core
+;; no_tiny: non-TINY core tiny : TINY core
(define_attr "isa"
- "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, no_xmega,xmega,
+ "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, no_xmega,xmega, no_tiny,tiny,
standard"
(const_string "standard"))
(match_test "AVR_XMEGA"))
(const_int 1)
+ (and (eq_attr "isa" "tiny")
+ (match_test "AVR_TINY"))
+ (const_int 1)
+
(and (eq_attr "isa" "no_xmega")
(match_test "!AVR_XMEGA"))
(const_int 1)
+
+ (and (eq_attr "isa" "no_tiny")
+ (match_test "!AVR_TINY"))
+ (const_int 1)
+
] (const_int 0)))
emit_insn (gen_load<mode>_libgcc (dest, src));
DONE;
}
+
+ /* AVRTC-579
+ if the source operand expression is out of range for 'lds' instruction
+ copy source operand expression to register
+ For tiny core, LDS instruction's memory access range limited to 0x40..0xbf
+ */
+ if (!tiny_valid_direct_memory_access_range(src,<MODE>mode))
+ {
+ rtx srcx = XEXP(src,0);
+ operands[1] = src = replace_equiv_address (src,copy_to_mode_reg (GET_MODE(srcx),srcx));
+ emit_move_insn(dest,src);
+ DONE;
+ }
+
+ /* AVRTC-579
+ if the destination operand expression is out of range for 'sts' instruction
+ copy destination operand expression to register
+ For tiny core, STS instruction's memory access range limited to 0x40..0xbf
+ */
+ if (!tiny_valid_direct_memory_access_range(dest,<MODE>mode))
+ {
+ rtx destx = XEXP(dest,0);
+ operands[0] = dest = replace_equiv_address (dest,copy_to_mode_reg (GET_MODE(destx),destx));
+ emit_move_insn(dest,src);
+ DONE;
+ }
+
})
;;========================================================================
(define_insn "mov<mode>_insn"
[(set (match_operand:ALL1 0 "nonimmediate_operand" "=r ,d ,Qm ,r ,q,r,*r")
(match_operand:ALL1 1 "nox_general_operand" "r Y00,n Ynn,r Y00,Qm,r,q,i"))]
- "register_operand (operands[0], <MODE>mode)
- || reg_or_0_operand (operands[1], <MODE>mode)"
+ "(register_operand (operands[0], <MODE>mode)
+ || reg_or_0_operand (operands[1], <MODE>mode)) &&
+ /* skip if operands are out of lds/sts memory access range(0x40..0xbf)
+ though access range is checked during define_expand, it is required
+ here to avoid merging rtls during combine pass */
+ tiny_valid_direct_memory_access_range(operands[0],QImode) &&
+ tiny_valid_direct_memory_access_range(operands[1],QImode)"
{
return output_movqi (insn, operands, NULL);
}
(define_insn "*mov<mode>"
[(set (match_operand:ALL2 0 "nonimmediate_operand" "=r,r ,r,m ,d,*r,q,r")
(match_operand:ALL2 1 "nox_general_operand" "r,Y00,m,r Y00,i,i ,r,q"))]
- "register_operand (operands[0], <MODE>mode)
- || reg_or_0_operand (operands[1], <MODE>mode)"
+ "(register_operand (operands[0], <MODE>mode)
+ || reg_or_0_operand (operands[1], <MODE>mode)) &&
+ /* skip if operands are out of lds/sts memory access range(0x40..0xbf)
+ though access range is checked during define_expand, it is required
+ here to avoid merging rtls during combine pass */
+ tiny_valid_direct_memory_access_range(operands[0],HImode) &&
+ tiny_valid_direct_memory_access_range(operands[1],HImode)"
{
return output_movhi (insn, operands, NULL);
}
(define_insn "*mov<mode>"
[(set (match_operand:ALL4 0 "nonimmediate_operand" "=r,r ,r ,Qm ,!d,r")
(match_operand:ALL4 1 "nox_general_operand" "r,Y00,Qm,r Y00,i ,i"))]
- "register_operand (operands[0], <MODE>mode)
- || reg_or_0_operand (operands[1], <MODE>mode)"
+ "(register_operand (operands[0], <MODE>mode)
+ || reg_or_0_operand (operands[1], <MODE>mode)) &&
+ /* skip if operands are out of lds/sts memory access range(0x40..0xbf)
+ though access range is checked during define_expand, it is required
+ here to avoid merging rtls during combine pass */
+ tiny_valid_direct_memory_access_range(operands[0],SImode) &&
+ tiny_valid_direct_memory_access_range(operands[1],SImode)"
{
return output_movsisf (insn, operands, NULL);
}
(define_insn "*movsf"
[(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r")
(match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))]
- "register_operand (operands[0], SFmode)
- || reg_or_0_operand (operands[1], SFmode)"
+ "(register_operand (operands[0], SFmode)
+ || reg_or_0_operand (operands[1], SFmode)) &&
+ /* skip if operands are out of lds/sts memory access range(0x40..0xbf)
+ though access range is checked during define_expand, it is required
+ here to avoid merging rtls during combine pass */
+ tiny_valid_direct_memory_access_range(operands[0],SFmode) &&
+ tiny_valid_direct_memory_access_range(operands[1],SFmode)"
{
return output_movsisf (insn, operands, NULL);
}
(set_attr "cc" "clobber")])
(define_insn "delay_cycles_2"
- [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n")
+ [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n,n")
(const_int 2)]
UNSPECV_DELAY_CYCLES)
(set (match_operand:BLK 1 "" "")
(unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
- (clobber (match_scratch:HI 2 "=&w"))]
+ (clobber (match_scratch:HI 2 "=&w,&d"))]
""
- "ldi %A2,lo8(%0)
- ldi %B2,hi8(%0)
- 1: sbiw %A2,1
- brne 1b"
- [(set_attr "length" "4")
+ "@
+ ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\;1: sbiw %A2,1\;brne 1b
+ ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\;1: subi %A2,1\;sbci %B2,0\;brne 1b"
+ [(set_attr "length" "4,5")
+ (set_attr "isa" "no_tiny,tiny")
(set_attr "cc" "clobber")])
(define_insn "delay_cycles_3"
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
-MULTILIB_OPTIONS = march=avr2/march=avr25/march=avr3/march=avr31/march=avr35/march=avr4/march=avr5/march=avr51/march=avr6/march=avrxmega2/march=avrxmega4/march=avrxmega5/march=avrxmega6/march=avrxmega7 msp8
+MULTILIB_OPTIONS = march=avr2/march=avr25/march=avr3/march=avr31/march=avr35/march=avr4/march=avr5/march=avr51/march=avr6/march=avrxmega2/march=avrxmega4/march=avrxmega5/march=avrxmega6/march=avrxmega7/march=avrtiny msp8
-MULTILIB_DIRNAMES = avr2 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack avr25/tiny-stack
+MULTILIB_DIRNAMES = avr2 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 avrtiny tiny-stack avr25/tiny-stack
MULTILIB_EXCEPTIONS = \
march=avr3/msp8 \
march=avrxmega4/msp8 \
march=avrxmega5/msp8 \
march=avrxmega6/msp8 \
- march=avrxmega7/msp8
+ march=avrxmega7/msp8 \
+ march=avrtiny/msp8
``XMEGA'' devices with more than 128@tie{}KiB of program memory and more than 64@tie{}KiB of RAM.
@*@var{mcu}@tie{}= @code{atxmega128a1}, @code{atxmega128a1u}, @code{atxmega128a4u}.
+@item avrtiny
+``TINY'' Tiny core devices with 512@tie{}B up to 4@tie{}KiB of program memory.
+@*@var{mcu}@tie{}= @code{attiny10}, @code{attiny20}, @code{attiny4}, @code{attiny40}, @code{attiny5}, @code{attiny9}.
+
@item avr1
This ISA is implemented by the minimal AVR core and supported for assembler only.
@*@var{mcu}@tie{}= @code{attiny11}, @code{attiny12}, @code{attiny15}, @code{attiny28}, @code{at90s1200}.
+2014-10-21 Joern Rennecke <joern.rennecke@embecosm.com>
+
+ * gcc.target/avr/tiny-memx.c: New test.
+
+ * gcc.target/avr/tiny-caller-save.c: New test.
+
2014-10-21 Jiong Wang <jiong.wang@arm.com>
* gcc.target/arm/20031108-1.c (Proc_7): Add explicit declaration.
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=avrtiny -gdwarf -Os" } */
+
+/* This is a stripped down piece of libgcc2.c that triggerd an ICE for avr with
+ "-march=avrtiny -g -Os"; replace_reg_with_saved_mem would generate:
+ (concatn:SI [
+ (reg:SI 18 r18)
+ (reg:SI 19 r19)
+ (mem/c:QI (plus:HI (reg/f:HI 28 r28)
+ (const_int 43 [0x2b])) [6 S1 A8])
+ (mem/c:QI (plus:HI (reg/f:HI 28 r28)
+ (const_int 44 [0x2c])) [6 S1 A8])
+ ]) */
+
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+struct DWstruct
+{
+ SItype low, high;
+};
+typedef union
+{
+ struct DWstruct s;
+ DItype ll;
+} DWunion;
+
+UDItype
+__udivmoddi4 (UDItype n, UDItype d)
+{
+ const DWunion nn = {.ll = n };
+ const DWunion dd = {.ll = d };
+ USItype d0, d1, n2;
+ USItype q0;
+
+ d0 = dd.s.low;
+ d1 = dd.s.high;
+ n2 = nn.s.high;
+
+ USItype m0;
+
+ do
+ {
+ USItype __d1, __d0, __q1, __q0;
+ USItype __r1, __m;
+ __d1 = ((USItype) (d1) >> 16);
+ __d0 = ((USItype) (d1) & (((USItype) 1 << 16) - 1));
+ __r1 = (n2) % __d1;
+ __q1 = (n2) / __d1;
+ __m = (USItype) __q1 *__d0;
+ __r1 -= __m;
+ __q0 = __r1 / __d1;
+ (q0) = (USItype) __q1 *((USItype) 1 << 16) | __q0;
+ }
+ while (0);
+ do
+ {
+ USItype __x0, __x1, __x2;
+ USItype __ul, __vl, __uh, __vh;
+ __ul = ((USItype) (q0) & (((USItype) 1 << 16) - 1));
+ __uh = ((USItype) (q0) >> 16);
+ __vl = ((USItype) (d0) & (((USItype) 1 << 16) - 1));
+ __vh = ((USItype) (d0) >> 16);
+ __x0 = (USItype) __ul *__vl;
+ __x1 = (USItype) __ul *__vh;
+ __x2 = (USItype) __uh *__vl;
+ __x1 += ((USItype) (__x0) >> 16);
+ __x1 += __x2;
+ (m0) =
+ ((USItype) (__x1) & (((USItype) 1 << 16) - 1)) *
+ ((USItype) 1 << 16) +
+ ((USItype) (__x0) & (((USItype) 1 << 16) - 1));
+ }
+ while (0);
+
+return m0;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=avrtiny" } */
+
+const __memx char ascmonth[] = "Jan"; /* { dg-error "not supported" } */
+2014-10-21 Joern Rennecke <joern.rennecke@embecosm.com>
+ Vidya Praveen <vidya.praveen@atmel.com>
+ Praveen Kumar Kaushik <Praveen_Kumar.Kaushik@atmel.com>
+ Senthil Kumar Selvaraj <Senthil_Kumar.Selvaraj@atmel.com>
+ Pitchumani Sivanupandi <Pitchumani.S@atmel.com>
+
+ * config/avr/lib1funcs.S (__do_global_dtors): Go back to descending
+ order.
+
+ Updated library functions for AVRTINY arch.
+ * config/avr/lib1funcs.S: Updated zero/tmp regs for AVRTINY.
+ Replaced occurrences of r0/r1 with tmp/zero reg macros.
+ Added wsubi/ wadi macros that expands conditionally as sbiw/ adiw
+ or AVRTINY equivalent. Replaced occurrences of sbiw/adiw with
+ wsubi/wadi macors.
+ (__mulsi3_helper): Update stack, preserve callee saved regs and
+ argument from stack. Restore callee save registers.
+ (__mulpsi3): Likewise.
+ (__muldi3, __udivmodsi4, __divmodsi4, __negsi2, __umoddi3, __udivmod64,
+ __moddi3, __adddi3, __adddi3_s8, __subdi3, __cmpdi2, __cmpdi2_s8,
+ __negdi2, __prologue_saves__, __epilogue_restores__): Excluded for
+ AVRTINY.
+ (__tablejump2__): Added lpm equivalent instructions for AVRTINY.
+ (__do_copy_data): Added new definition for AVRTINY.
+ (__do_clear_bss): Replace r17 by r18 to preserve zero reg for AVRTINY.
+ (__load_3, __load_4, __xload_1, __xload_2, __xload_3,
+ __xload_4, __movmemx_qi, __movmemx_hi): Excluded for AVRTINY.
+ * config/avr/lib1funcs-fixed.S: Replaced occurrences of r0/r1 with
+ tmp/zero reg macros. Replaced occurrences of sbiw/adiw with wsubi/wadi
+ macors.
+ * config/avr/t-avr (LIB1ASMFUNCS): Remove unsupported functions for
+ AVRTINY.
+
+ Fix broken long multiplication on tiny arch.
+
2014-10-09 Joseph Myers <joseph@codesourcery.com>
* soft-fp/double.h: Update from glibc.
;; Fixed point library routines for AVR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+#if defined __AVR_TINY__
+#define __zero_reg__ r17
+#define __tmp_reg__ r16
+#else
+#define __zero_reg__ r1
+#define __tmp_reg__ r0
+#endif
+
.section .text.libgcc.fixed, "ax", @progbits
+#ifndef __AVR_TINY__
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Conversions to float
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ret
ENDF __ret
#endif /* L_ret */
+
+#endif /* if not __AVR_TINY__ */
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
+#if defined (__AVR_TINY__)
+#define __zero_reg__ r17
+#define __tmp_reg__ r16
+#else
#define __zero_reg__ r1
#define __tmp_reg__ r0
+#endif
#define __SREG__ 0x3f
#if defined (__AVR_HAVE_SPH__)
#define __SP_H__ 0x3e
;; Support function entry and exit for convenience
+.macro wsubi r_arg1, i_arg2
+#if defined (__AVR_TINY__)
+ subi \r_arg1, lo8(\i_arg2)
+ sbci \r_arg1+1, hi8(\i_arg2)
+#else
+ sbiw \r_arg1, \i_arg2
+#endif
+.endm
+
+.macro waddi r_arg1, i_arg2
+#if defined (__AVR_TINY__)
+ subi \r_arg1, lo8(-\i_arg2)
+ sbci \r_arg1+1, hi8(-\i_arg2)
+#else
+ adiw \r_arg1, \i_arg2
+#endif
+.endm
+
.macro DEFUN name
.global \name
.func \name
.endm
;; Skip next instruction, typically a jump target
+#if defined(__AVR_TINY__)
#define skip cpse 0,0
+#else
+#define skip cpse 16,16
+#endif
;; Negate a 2-byte value held in consecutive registers
.macro NEG2 reg
Multiplication 16 x 16 without MUL
*******************************************************/
-#define A0 r22
-#define A1 r23
-#define B0 r24
-#define BB0 r20
-#define B1 r25
+#define A0 22
+#define A1 23
+#define B0 24
+#define BB0 20
+#define B1 25
;; Output overlaps input, thus expand result in CC0/1
-#define C0 r24
-#define C1 r25
+#define C0 24
+#define C1 25
#define CC0 __tmp_reg__
-#define CC1 R21
+#define CC1 21
#if defined (L_umulqihi3)
;;; R25:R24 = (unsigned int) R22 * (unsigned int) R24
rol B1
3:
;; If B == 0 we are ready
- sbiw B0, 0
+ wsubi B0, 0
breq 9f
;; Carry = n-th bit of A
#if defined (L_mulsi3)
DEFUN __mulsi3
+#if defined (__AVR_TINY__)
+ in r26, __SP_L__ ; safe to use X, as it is CC0/CC1
+ in r27, __SP_H__
+ subi r26, lo8(-3) ; Add 3 to point past return address
+ sbci r27, hi8(-3)
+ push B0 ; save callee saved regs
+ push B1
+ ld B0, X+ ; load from caller stack
+ ld B1, X+
+ ld B2, X+
+ ld B3, X
+#endif
;; Clear result
clr CC2
clr CC3
;; Only continue if A != 0
sbci A1, 0
brne 2b
- sbiw A2, 0
+ wsubi A2, 0
brne 2b
;; All bits of A are consumed: Copy result to return register C
wmov C0, CC0
wmov C2, CC2
+#if defined (__AVR_TINY__)
+ pop B1 ; restore callee saved regs
+ pop B0
+#endif /* defined (__AVR_TINY__) */
+
ret
ENDF __mulsi3_helper
#endif /* L_mulsi3 */
#undef C0
#else /* !HAVE_MUL */
-
;; C[0..2]: Expand Result
+#if defined (__AVR_TINY__)
+#define C0 16
+#else
#define C0 0
+#endif /* defined (__AVR_TINY__) */
#define C1 C0+1
#define C2 21
;; Clobbers: __tmp_reg__, R18, R19, R20, R21
DEFUN __mulpsi3
+#if defined (__AVR_TINY__)
+ in r26,__SP_L__
+ in r27,__SP_H__
+ subi r26, lo8(-3) ; Add 3 to point past return address
+ sbci r27, hi8(-3)
+ push B0 ; save callee saved regs
+ push B1
+ ld B0,X+ ; load from caller stack
+ ld B1,X+
+ ld B2,X+
+#endif /* defined (__AVR_TINY__) */
;; C[] = 0
clr __tmp_reg__
mov A2, C2
clr __zero_reg__
+#if defined (__AVR_TINY__)
+ pop B1
+ pop B0
+#endif /* (__AVR_TINY__) */
ret
ENDF __mulpsi3
#define B6 B0+6
#define B7 B0+7
+#ifndef __AVR_TINY__
#if defined (__AVR_HAVE_MUL__)
-
;; Define C[] for convenience
;; Notice that parts of C[] overlap A[] respective B[]
#define C0 16
#endif /* L_muldi3 */
#endif /* HAVE_MUL */
+#endif /* if not __AVR_TINY__ */
#undef B7
#undef B6
/**********************************************************
Widening Multiplication 64 = 32 x 32 without MUL
**********************************************************/
-
+#ifndef __AVR_TINY__ /* if not __AVR_TINY__ */
#if defined (L_mulsidi3) && !defined (__AVR_HAVE_MUL__)
#define A0 18
#define A1 A0+1
#undef BB3
#undef Mask
#endif /* L_mulsidi3 && !HAVE_MUL */
-
+#endif /* if not __AVR_TINY__ */
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
\f
#define r_cnt 21
#if defined (L_udivmodpsi4)
-;; R24:R22 = R24:R22 udiv R20:R18
+;; R24:R22 = R24:R24 udiv R20:R18
;; R20:R18 = R24:R22 umod R20:R18
;; Clobbers: R21, R25, R26
#undef r_arg2L
#undef r_cnt
+/* *di routines use registers below R19 and won't work with tiny arch
+ right now. */
+
+#if !defined (__AVR_TINY__)
/*******************************************************
Division 64 / 64
Modulo 64 % 64
#undef A1
#undef A0
+#endif /* !defined (__AVR_TINY__) */
+
\f
.section .text.libgcc.prologue, "ax", @progbits
/**********************************
* This is a prologue subroutine
**********************************/
+#if !defined (__AVR_TINY__)
#if defined (L_prologue)
;; This function does not clobber T-flag; 64-bit division relies on it
ret
ENDF __epilogue_restores__
#endif /* defined (L_epilogue) */
+#endif /* !defined (__AVR_TINY__) */
#ifdef L_exit
.section .fini9,"ax",@progbits
lpm r31, Z
mov r30, __tmp_reg__
ijmp
+#elif defined (__AVR_TINY__)
+ wsubi 30, -(__AVR_TINY_PM_BASE_ADDRESS__) ; Add PM offset to Z
+ ld __tmp_reg__, Z+
+ ld r31, Z ; Use ld instead of lpm to load Z
+ mov r30, __tmp_reg__
+ ijmp
#else
lpm
push r0
ENDF __tablejump2__
#endif /* L_tablejump2 */
+#if defined(__AVR_TINY__)
+#ifdef L_copy_data
+ .section .init4,"ax",@progbits
+ .global __do_copy_data
+__do_copy_data:
+ ldi r18, hi8(__data_end)
+ ldi r26, lo8(__data_start)
+ ldi r27, hi8(__data_start)
+ ldi r30, lo8(__data_load_start + __AVR_TINY_PM_BASE_ADDRESS__)
+ ldi r31, hi8(__data_load_start + __AVR_TINY_PM_BASE_ADDRESS__)
+ rjmp .L__do_copy_data_start
+.L__do_copy_data_loop:
+ ld r19, z+
+ st X+, r19
+.L__do_copy_data_start:
+ cpi r26, lo8(__data_end)
+ cpc r27, r18
+ brne .L__do_copy_data_loop
+#endif
+#else
#ifdef L_copy_data
.section .init4,"ax",@progbits
DEFUN __do_copy_data
#endif /* ELPM && RAMPD */
ENDF __do_copy_data
#endif /* L_copy_data */
+#endif /* !defined (__AVR_TINY__) */
/* __do_clear_bss is only necessary if there is anything in .bss section. */
#ifdef L_clear_bss
.section .init4,"ax",@progbits
DEFUN __do_clear_bss
- ldi r17, hi8(__bss_end)
+ ldi r18, hi8(__bss_end)
ldi r26, lo8(__bss_start)
ldi r27, hi8(__bss_start)
rjmp .do_clear_bss_start
st X+, __zero_reg__
.do_clear_bss_start:
cpi r26, lo8(__bss_end)
- cpc r27, r17
+ cpc r27, r18
brne .do_clear_bss_loop
ENDF __do_clear_bss
#endif /* L_clear_bss */
/* __do_global_ctors and __do_global_dtors are only necessary
if there are any constructors/destructors. */
+#if defined(__AVR_TINY__)
+#define cdtors_tst_reg r18
+#else
+#define cdtors_tst_reg r17
+#endif
+
#ifdef L_ctors
.section .init6,"ax",@progbits
DEFUN __do_global_ctors
- ldi r17, pm_hi8(__ctors_start)
+ ldi cdtors_tst_reg, pm_hi8(__ctors_start)
ldi r28, pm_lo8(__ctors_end)
ldi r29, pm_hi8(__ctors_end)
#ifdef __AVR_HAVE_EIJMP_EICALL__
#endif /* HAVE_EIJMP */
rjmp .L__do_global_ctors_start
.L__do_global_ctors_loop:
- sbiw r28, 1
+ wsubi 28, 1
#ifdef __AVR_HAVE_EIJMP_EICALL__
sbc r16, __zero_reg__
mov r24, r16
XCALL __tablejump2__
.L__do_global_ctors_start:
cpi r28, pm_lo8(__ctors_start)
- cpc r29, r17
+ cpc r29, cdtors_tst_reg
#ifdef __AVR_HAVE_EIJMP_EICALL__
ldi r24, pm_hh8(__ctors_start)
cpc r16, r24
#ifdef L_dtors
.section .fini6,"ax",@progbits
DEFUN __do_global_dtors
- ldi r17, pm_hi8(__dtors_start)
- ldi r28, pm_lo8(__dtors_end)
- ldi r29, pm_hi8(__dtors_end)
+ ldi cdtors_tst_reg, pm_hi8(__dtors_end)
+ ldi r28, pm_lo8(__dtors_start)
+ ldi r29, pm_hi8(__dtors_start)
#ifdef __AVR_HAVE_EIJMP_EICALL__
- ldi r16, pm_hh8(__dtors_end)
+ ldi r16, pm_hh8(__dtors_start)
#endif /* HAVE_EIJMP */
rjmp .L__do_global_dtors_start
.L__do_global_dtors_loop:
- sbiw r28, 1
+ waddi 28, 1
#ifdef __AVR_HAVE_EIJMP_EICALL__
- sbc r16, __zero_reg__
+ adc r16, __zero_reg__
mov r24, r16
#endif /* HAVE_EIJMP */
mov_h r31, r29
mov_l r30, r28
XCALL __tablejump2__
.L__do_global_dtors_start:
- cpi r28, pm_lo8(__dtors_start)
- cpc r29, r17
+ cpi r28, pm_lo8(__dtors_end)
+ cpc r29, cdtors_tst_reg
#ifdef __AVR_HAVE_EIJMP_EICALL__
- ldi r24, pm_hh8(__dtors_start)
+ ldi r24, pm_hh8(__dtors_end)
cpc r16, r24
#endif /* HAVE_EIJMP */
brne .L__do_global_dtors_loop
.section .text.libgcc, "ax", @progbits
+#if !defined (__AVR_TINY__)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loading n bytes from Flash; n = 3,4
;; R22... = Flash[Z]
#endif /* L_load_4 */
#endif /* L_load_3 || L_load_3 */
+#endif /* !defined (__AVR_TINY__) */
+#if !defined (__AVR_TINY__)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loading n bytes from Flash or RAM; n = 1,2,3,4
;; R22... = Flash[R21:Z] or RAM[Z] depending on R21.7
#endif /* L_xload_4 */
#endif /* L_xload_{1|2|3|4} */
+#endif /* if !defined (__AVR_TINY__) */
+#if !defined (__AVR_TINY__)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; memcopy from Address Space __pgmx to RAM
;; R23:Z = Source Address
#undef LOOP
#endif /* L_movmemx */
+#endif /* !defined (__AVR_TINY__) */
\f
.section .text.libgcc.builtins, "ax", @progbits
_mulqi3 \
_mulhi3 \
_mulqihi3 _umulqihi3 \
- _mulpsi3 _mulsqipsi3 \
- _mulhisi3 \
- _umulhisi3 \
- _usmulhisi3 \
- _muluhisi3 \
- _mulshisi3 \
+ _mulpsi3 \
_mulsi3 \
_udivmodqi4 \
_divmodqi4 \
_divmodpsi4 _udivmodpsi4 \
_udivmodsi4 \
_divmodsi4 \
- _divdi3 _udivdi3 \
- _muldi3 _muldi3_6 \
- _mulsidi3 _umulsidi3 \
- _udivmod64 \
- _negsi2 _negdi2 \
- _prologue \
- _epilogue \
+ _negsi2 \
_exit \
_cleanup \
_tablejump2 \
- _load_3 _load_4 \
- _xload_1 _xload_2 _xload_3 _xload_4 \
- _movmemx \
_copy_data \
_clear_bss \
_ctors \
_loop_ffsqi2 \
_ctzsi2 \
_ctzhi2 \
- _clzdi2 \
_clzsi2 \
_clzhi2 \
- _paritydi2 \
_paritysi2 \
_parityhi2 \
_popcounthi2 \
_popcountsi2 \
- _popcountdi2 \
_popcountqi2 \
_bswapsi2 \
+ _fmul _fmuls _fmulsu
+
+# The below functions either use registers that are not present
+# in tiny core, or use a different register conventions (don't save
+# callee saved regs, for example)
+# _mulhisi3 and variations - clobber R18, R19
+# All *di funcs - use regs < R16 or expect args in regs < R20
+# _prologue and _epilogue save registers < R16
+# _load ad _xload variations - expect lpm and elpm support
+# _movmemx - expects elpm/lpm
+
+ifneq ($(MULTIFLAGS),-mmcu=avrtiny)
+LIB1ASMFUNCS += \
+ _mulsqipsi3 \
+ _mulhisi3 \
+ _umulhisi3 \
+ _usmulhisi3 \
+ _muluhisi3 \
+ _mulshisi3 \
+ _muldi3 _muldi3_6 \
+ _mulsidi3 _umulsidi3 \
+ _divdi3 _udivdi3 \
+ _udivmod64 \
+ _negdi2 \
+ _prologue \
+ _epilogue \
+ _load_3 _load_4 \
+ _xload_1 _xload_2 _xload_3 _xload_4 \
+ _movmemx \
+ _clzdi2 \
+ _paritydi2 \
+ _popcountdi2 \
_bswapdi2 \
_ashldi3 _ashrdi3 _lshrdi3 _rotldi3 \
_adddi3 _adddi3_s8 _subdi3 \
- _cmpdi2 _cmpdi2_s8 \
- _fmul _fmuls _fmulsu
+ _cmpdi2 _cmpdi2_s8
+endif
# Fixed point routines in avr/lib1funcs-fixed.S
+ifneq ($(MULTIFLAGS),-mmcu=avrtiny)
LIB1ASMFUNCS += \
_fractqqsf _fractuqqsf \
_fracthqsf _fractuhqsf _fracthasf _fractuhasf \
_round_x8 \
_rounddq3 _roundudq3 \
_roundda3 _rounduda3 \
- _roundta3 _rounduta3 \
-
+ _roundta3 _rounduta3
+endif
LIB2FUNCS_EXCLUDE = \
_moddi3 _umoddi3 \