#include "tree.h"
#include "output.h"
#include "expr.h"
+#include "c-family/c-common.h"
#include "diagnostic-core.h"
#include "obstack.h"
#include "function.h"
/* Maximal allowed offset for an address in the LD command */
#define MAX_LD_OFFSET(MODE) (64 - (signed)GET_MODE_SIZE (MODE))
-static void avr_option_override (void);
+/* Return true if STR starts with PREFIX and false, otherwise. */
+#define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX)))
+
+/* The 4 bits starting at SECTION_MACH_DEP are reserved to store the
+ address space where data is to be located.
+ As the only non-generic address spaces are all located in Flash,
+ this can be used to test if data shall go into some .progmem* section.
+ This must be the rightmost field of machine dependent section flags. */
+#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP)
+
+/* 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_RAM, 0, 2, "" , 0 },
+ { ADDR_SPACE_PGM, 1, 2, "__pgm", 0 },
+ { ADDR_SPACE_PGM1, 1, 2, "__pgm1", 1 },
+ { ADDR_SPACE_PGM2, 1, 2, "__pgm2", 2 },
+ { ADDR_SPACE_PGM3, 1, 2, "__pgm3", 3 },
+ { ADDR_SPACE_PGM4, 1, 2, "__pgm4", 4 },
+ { ADDR_SPACE_PGM5, 1, 2, "__pgm5", 5 },
+ { ADDR_SPACE_PGMX, 1, 3, "__pgmx", 0 },
+ { 0 , 0, 0, NULL, 0 }
+};
+
+/* Map 64-k Flash segment to section prefix. */
+static const char* const progmem_section_prefix[6] =
+ {
+ ".progmem.data",
+ ".progmem1.data",
+ ".progmem2.data",
+ ".progmem3.data",
+ ".progmem4.data",
+ ".progmem5.data"
+ };
+
+
+/* Prototypes for local helper functions. */
+
+static const char* out_movqi_r_mr (rtx, rtx[], int*);
+static const char* out_movhi_r_mr (rtx, rtx[], int*);
+static const char* out_movsi_r_mr (rtx, rtx[], int*);
+static const char* out_movqi_mr_r (rtx, rtx[], int*);
+static const char* out_movhi_mr_r (rtx, rtx[], int*);
+static const char* out_movsi_mr_r (rtx, rtx[], int*);
+
static int avr_naked_function_p (tree);
static int interrupt_function_p (tree);
static int signal_function_p (tree);
static const char *ptrreg_to_str (int);
static const char *cond_string (enum rtx_code);
static int avr_num_arg_regs (enum machine_mode, const_tree);
-
-static RTX_CODE compare_condition (rtx insn);
-static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
-static int compare_sign_p (rtx insn);
-static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *);
-static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
-static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *);
-static bool avr_assemble_integer (rtx, unsigned int, int);
-static void avr_file_start (void);
-static void avr_file_end (void);
-static bool avr_legitimate_address_p (enum machine_mode, rtx, bool);
-static void avr_asm_function_end_prologue (FILE *);
-static void avr_asm_function_begin_epilogue (FILE *);
-static bool avr_cannot_modify_jumps_p (void);
-static rtx avr_function_value (const_tree, const_tree, bool);
-static rtx avr_libcall_value (enum machine_mode, const_rtx);
-static bool avr_function_value_regno_p (const unsigned int);
-static void avr_insert_attributes (tree, tree *);
-static void avr_asm_init_sections (void);
-static unsigned int avr_section_type_flags (tree, const char *, int);
-
-static void avr_reorg (void);
-static void avr_asm_out_ctor (rtx, int);
-static void avr_asm_out_dtor (rtx, int);
-static int avr_register_move_cost (enum machine_mode, reg_class_t, reg_class_t);
-static int avr_memory_move_cost (enum machine_mode, reg_class_t, bool);
-static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, bool);
-static bool avr_rtx_costs (rtx, int, int, int *, bool);
-static int avr_address_cost (rtx, bool);
-static bool avr_return_in_memory (const_tree, const_tree);
+static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code,
+ int, bool);
+static void output_reload_in_const (rtx*, rtx, int*, bool);
static struct machine_function * avr_init_machine_status (void);
-static void avr_init_builtins (void);
-static rtx avr_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
-static rtx avr_builtin_setjmp_frame_value (void);
-static bool avr_hard_regno_scratch_ok (unsigned int);
-static unsigned int avr_case_values_threshold (void);
-static bool avr_frame_pointer_required_p (void);
-static bool avr_can_eliminate (const int, const int);
-static bool avr_class_likely_spilled_p (reg_class_t c);
-static rtx avr_function_arg (cumulative_args_t , enum machine_mode,
- const_tree, bool);
-static void avr_function_arg_advance (cumulative_args_t, enum machine_mode,
- const_tree, bool);
-static bool avr_function_ok_for_sibcall (tree, tree);
-static void avr_asm_named_section (const char *name, unsigned int flags, tree decl);
-static void avr_encode_section_info (tree, rtx, int);
+
+
+/* Prototypes for hook implementors if needed before their implementation. */
+
+static bool avr_rtx_costs (rtx, int, int, int, int *, bool);
+
/* Allocate registers from r25 to r8 for parameters for function calls. */
#define FIRST_CUM_REG 26
+/* Implicit target register of LPM instruction (R0) */
+static GTY(()) rtx lpm_reg_rtx;
+
+/* (Implicit) address register of LPM instruction (R31:R30 = Z) */
+static GTY(()) rtx lpm_addr_reg_rtx;
+
/* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */
static GTY(()) rtx tmp_reg_rtx;
/* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
static GTY(()) rtx zero_reg_rtx;
+/* RAMPZ special function register */
+static GTY(()) rtx rampz_rtx;
+
+/* RTX containing the strings "" and "e", respectively */
+static GTY(()) rtx xstring_empty;
+static GTY(()) rtx xstring_e;
+
+/* RTXs for all general purpose registers as QImode */
+static GTY(()) rtx all_regs_rtx[32];
+
/* AVR register names {"r0", "r1", ..., "r31"} */
static const char *const avr_regnames[] = REGISTER_NAMES;
/* Current device. */
const struct mcu_type_s *avr_current_device;
-section *progmem_section;
+/* Section to put switch tables in. */
+static GTY(()) section *progmem_swtable_section;
+
+/* Unnamed sections associated to __attribute__((progmem)) aka. PROGMEM
+ or to address space __pgm*. */
+static GTY(()) section *progmem_section[6];
+
+/* Condition for insns/expanders from avr-dimode.md. */
+bool avr_have_dimode = true;
/* To track if code will use .bss and/or .data. */
bool avr_need_clear_bss_p = false;
bool avr_need_copy_data_p = false;
-/* AVR attributes. */
-static const struct attribute_spec avr_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
- affects_type_identity } */
- { "progmem", 0, 0, false, false, false, avr_handle_progmem_attribute,
- false },
- { "signal", 0, 0, true, false, false, avr_handle_fndecl_attribute,
- false },
- { "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute,
- false },
- { "naked", 0, 0, false, true, true, avr_handle_fntype_attribute,
- false },
- { "OS_task", 0, 0, false, true, true, avr_handle_fntype_attribute,
- false },
- { "OS_main", 0, 0, false, true, true, avr_handle_fntype_attribute,
- false },
- { NULL, 0, 0, false, false, false, NULL, false }
-};
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE avr_attribute_table
-#undef TARGET_ASM_FUNCTION_RODATA_SECTION
-#define TARGET_ASM_FUNCTION_RODATA_SECTION default_no_function_rodata_section
#undef TARGET_INSERT_ATTRIBUTES
#define TARGET_INSERT_ATTRIBUTES avr_insert_attributes
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS avr_section_type_flags
-/* `TARGET_ASM_NAMED_SECTION' must be defined in avr.h. */
-
+#undef TARGET_ASM_NAMED_SECTION
+#define TARGET_ASM_NAMED_SECTION avr_asm_named_section
#undef TARGET_ASM_INIT_SECTIONS
#define TARGET_ASM_INIT_SECTIONS avr_asm_init_sections
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info
+#undef TARGET_ASM_SELECT_SECTION
+#define TARGET_ASM_SELECT_SECTION avr_asm_select_section
#undef TARGET_REGISTER_MOVE_COST
#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
#undef TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance
-#undef TARGET_LEGITIMIZE_ADDRESS
-#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address
-
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY avr_return_in_memory
#undef TARGET_CASE_VALUES_THRESHOLD
#define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold
-#undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p
-
#undef TARGET_FRAME_POINTER_REQUIRED
#define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p
#undef TARGET_CAN_ELIMINATE
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN avr_expand_builtin
+#undef TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p
+
+#undef TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p
+
+#undef TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert
+
+#undef TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode
+
+#undef TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode
+
+#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p
+
+#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
+#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
+
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND avr_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p
-struct gcc_target targetm = TARGET_INITIALIZER;
\f
+
+/* Custom function to replace string prefix.
+
+ Return a ggc-allocated string with strlen (OLD_PREFIX) characters removed
+ from the start of OLD_STR and then prepended with NEW_PREFIX. */
+
+static inline const char*
+avr_replace_prefix (const char *old_str,
+ const char *old_prefix, const char *new_prefix)
+{
+ char *new_str;
+ size_t len = strlen (old_str) + strlen (new_prefix) - strlen (old_prefix);
+
+ gcc_assert (strlen (old_prefix) <= strlen (old_str));
+
+ /* Unfortunately, ggc_alloc_string returns a const char* and thus cannot be
+ used here. */
+
+ new_str = (char*) ggc_alloc_atomic (1 + len);
+
+ strcat (stpcpy (new_str, new_prefix), old_str + strlen (old_prefix));
+
+ return (const char*) new_str;
+}
+
+
+/* Custom function to count number of set bits. */
+
+static inline int
+avr_popcount (unsigned int val)
+{
+ int pop = 0;
+
+ while (val)
+ {
+ val &= val-1;
+ pop++;
+ }
+
+ return pop;
+}
+
+
+/* Constraint helper function. XVAL is a CONST_INT or a CONST_DOUBLE.
+ Return true if the least significant N_BYTES bytes of XVAL all have a
+ popcount in POP_MASK and false, otherwise. POP_MASK represents a subset
+ of integers which contains an integer N iff bit N of POP_MASK is set. */
+
+bool
+avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask)
+{
+ int i;
+
+ enum machine_mode mode = GET_MODE (xval);
+
+ if (VOIDmode == mode)
+ mode = SImode;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+ unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+ if (0 == (pop_mask & (1 << avr_popcount (val8))))
+ return false;
+ }
+
+ return true;
+}
+
static void
avr_option_override (void)
{
flag_delete_null_pointer_checks = 0;
+ /* caller-save.c looks for call-clobbered hard registers that are assigned
+ to pseudos that cross calls and tries so save-restore them around calls
+ in order to reduce the number of stack slots needed.
+
+ This might leads to situations where reload is no more able to cope
+ with the challenge of AVR's very few address registers and fails to
+ perform the requested spills. */
+
+ if (avr_strict_X)
+ flag_caller_saves = 0;
+
+ /* Unwind tables currently require a frame pointer for correctness,
+ see toplev.c:process_options(). */
+
+ if ((flag_unwind_tables
+ || flag_non_call_exceptions
+ || flag_asynchronous_unwind_tables)
+ && !ACCUMULATE_OUTGOING_ARGS)
+ {
+ flag_omit_frame_pointer = 0;
+ }
+
avr_current_device = &avr_mcu_types[avr_mcu_index];
avr_current_arch = &avr_arch_types[avr_current_device->arch];
avr_extra_arch_macro = avr_current_device->macro;
- tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO);
- zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO);
-
init_machine_status = avr_init_machine_status;
-}
-/* return register class from register number. */
-
-static const enum reg_class reg_class_tab[]={
- GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
- GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
- GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,GENERAL_REGS,
- GENERAL_REGS, /* r0 - r15 */
- LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,
- LD_REGS, /* r16 - 23 */
- ADDW_REGS,ADDW_REGS, /* r24,r25 */
- POINTER_X_REGS,POINTER_X_REGS, /* r26,27 */
- POINTER_Y_REGS,POINTER_Y_REGS, /* r28,r29 */
- POINTER_Z_REGS,POINTER_Z_REGS, /* r30,r31 */
- STACK_REG,STACK_REG /* SPL,SPH */
-};
+ avr_log_set_avr_log();
+}
/* Function to set up the backend function structure. */
return ggc_alloc_cleared_machine_function ();
}
+
+/* Implement `INIT_EXPANDERS'. */
+/* The function works like a singleton. */
+
+void
+avr_init_expanders (void)
+{
+ int regno;
+
+ static bool done = false;
+
+ if (done)
+ return;
+ else
+ done = true;
+
+ for (regno = 0; regno < 32; regno ++)
+ 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];
+
+ lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
+
+ rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR));
+
+ xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
+ xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+}
+
+
/* Return register class for register R. */
enum reg_class
avr_regno_reg_class (int r)
{
+ static const enum reg_class reg_class_tab[] =
+ {
+ R0_REG,
+ /* r1 - r15 */
+ NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+ NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+ NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+ NO_LD_REGS, NO_LD_REGS, NO_LD_REGS, NO_LD_REGS,
+ /* r16 - r23 */
+ SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS,
+ SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS, SIMPLE_LD_REGS,
+ /* r24, r25 */
+ ADDW_REGS, ADDW_REGS,
+ /* X: r26, 27 */
+ POINTER_X_REGS, POINTER_X_REGS,
+ /* Y: r28, r29 */
+ POINTER_Y_REGS, POINTER_Y_REGS,
+ /* Z: r30, r31 */
+ POINTER_Z_REGS, POINTER_Z_REGS,
+ /* SP: SPL, SPH */
+ STACK_REG, STACK_REG
+ };
+
if (r <= 33)
return reg_class_tab[r];
+
return ALL_REGS;
}
+
+static bool
+avr_scalar_mode_supported_p (enum machine_mode mode)
+{
+ if (PSImode == mode)
+ return true;
+
+ return default_scalar_mode_supported_p (mode);
+}
+
+
+/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */
+
+static bool
+avr_decl_pgm_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL
+ || TREE_TYPE (decl) == error_mark_node)
+ {
+ return false;
+ }
+
+ return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash
+ address space and FALSE, otherwise. */
+
+static bool
+avr_decl_pgmx_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL
+ || TREE_TYPE (decl) == error_mark_node)
+ {
+ return false;
+ }
+
+ return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */
+
+bool
+avr_mem_pgm_p (rtx x)
+{
+ return (MEM_P (x)
+ && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)));
+}
+
+
+/* Return TRUE if X is a MEM rtx located in the 24-bit Flash
+ address space and FALSE, otherwise. */
+
+bool
+avr_mem_pgmx_p (rtx x)
+{
+ return (MEM_P (x)
+ && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+}
+
+
/* A helper for the subsequent function attribute used to dig for
attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
return avr_lookup_function_attribute1 (func, "signal");
}
-/* Return nonzero if FUNC is a OS_task function. */
+/* Return nonzero if FUNC is an OS_task function. */
static int
avr_OS_task_function_p (tree func)
return avr_lookup_function_attribute1 (func, "OS_task");
}
-/* Return nonzero if FUNC is a OS_main function. */
+/* Return nonzero if FUNC is an OS_main function. */
static int
avr_OS_main_function_p (tree func)
return avr_lookup_function_attribute1 (func, "OS_main");
}
+
+/* Implement `ACCUMULATE_OUTGOING_ARGS'. */
+bool
+avr_accumulate_outgoing_args (void)
+{
+ if (!cfun)
+ return TARGET_ACCUMULATE_OUTGOING_ARGS;
+
+ /* FIXME: For setjmp and in avr_builtin_setjmp_frame_value we don't know
+ what offset is correct. In some cases it is relative to
+ virtual_outgoing_args_rtx and in others it is relative to
+ virtual_stack_vars_rtx. For example code see
+ gcc.c-torture/execute/built-in-setjmp.c
+ gcc.c-torture/execute/builtins/sprintf-chk.c */
+
+ return (TARGET_ACCUMULATE_OUTGOING_ARGS
+ && !(cfun->calls_setjmp
+ || cfun->has_nonlocal_label));
+}
+
+
+/* Report contribution of accumulated outgoing arguments to stack size. */
+
+static inline int
+avr_outgoing_args_size (void)
+{
+ return ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0;
+}
+
+
+/* Implement `STARTING_FRAME_OFFSET'. */
+/* This is the offset from the frame pointer register to the first stack slot
+ that contains a variable living in the frame. */
+
+int
+avr_starting_frame_offset (void)
+{
+ return 1 + avr_outgoing_args_size ();
+}
+
+
/* Return the number of hard registers to push/pop in the prologue/epilogue
of the current function, and optionally store these registers in SET. */
{
int reg, count;
int int_or_sig_p = (interrupt_function_p (current_function_decl)
- || signal_function_p (current_function_decl));
+ || signal_function_p (current_function_decl));
if (set)
CLEAR_HARD_REG_SET (*set);
count = 0;
/* No need to save any registers if the function never returns or
- is have "OS_task" or "OS_main" attribute. */
+ has the "OS_task" or "OS_main" attribute. */
if (TREE_THIS_VOLATILE (current_function_decl)
|| cfun->machine->is_OS_task
|| cfun->machine->is_OS_main)
for (reg = 0; reg < 32; reg++)
{
/* Do not push/pop __tmp_reg__, __zero_reg__, as well as
- any global register variables. */
+ any global register variables. */
if (fixed_regs[reg])
- continue;
+ continue;
if ((int_or_sig_p && !current_function_is_leaf && call_used_regs[reg])
- || (df_regs_ever_live_p (reg)
- && (int_or_sig_p || !call_used_regs[reg])
- && !(frame_pointer_needed
- && (reg == REG_Y || reg == (REG_Y+1)))))
- {
- if (set)
- SET_HARD_REG_BIT (*set, reg);
- count++;
- }
+ || (df_regs_ever_live_p (reg)
+ && (int_or_sig_p || !call_used_regs[reg])
+ /* Don't record frame pointer registers here. They are treated
+ indivitually in prologue. */
+ && !(frame_pointer_needed
+ && (reg == REG_Y || reg == (REG_Y+1)))))
+ {
+ if (set)
+ SET_HARD_REG_BIT (*set, reg);
+ count++;
+ }
}
return count;
}
/* Return true if register FROM can be eliminated via register TO. */
-bool
+static bool
avr_can_eliminate (const int from, const int to)
{
return ((from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
- || ((from == FRAME_POINTER_REGNUM
- || from == FRAME_POINTER_REGNUM + 1)
- && !frame_pointer_needed));
+ || (frame_pointer_needed && to == FRAME_POINTER_REGNUM)
+ || ((from == FRAME_POINTER_REGNUM
+ || from == FRAME_POINTER_REGNUM + 1)
+ && !frame_pointer_needed));
}
/* Compute offset between arg_pointer and frame_pointer. */
{
int offset = frame_pointer_needed ? 2 : 0;
int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2;
-
+
offset += avr_regs_to_save (NULL);
- return get_frame_size () + (avr_pc_size) + 1 + offset;
+ return (get_frame_size () + avr_outgoing_args_size()
+ + avr_pc_size + 1 + offset);
}
}
Using saved frame = virtual_stack_vars_rtx - STARTING_FRAME_OFFSET
avoids creating add/sub of offset in nonlocal goto and setjmp. */
-rtx avr_builtin_setjmp_frame_value (void)
+static rtx
+avr_builtin_setjmp_frame_value (void)
{
return gen_rtx_MINUS (Pmode, virtual_stack_vars_rtx,
- gen_int_mode (STARTING_FRAME_OFFSET, Pmode));
+ gen_int_mode (STARTING_FRAME_OFFSET, Pmode));
}
/* Return contents of MEM at frame pointer + stack size + 1 (+2 if 3 byte PC).
{
rtx r;
- /* Can only return this functions return address. Others not supported. */
+ /* Can only return this function's return address. Others not supported. */
if (count)
return NULL;
avr_simple_epilogue (void)
{
return (! frame_pointer_needed
- && get_frame_size () == 0
- && avr_regs_to_save (NULL) == 0
- && ! interrupt_function_p (current_function_decl)
- && ! signal_function_p (current_function_decl)
- && ! avr_naked_function_p (current_function_decl)
- && ! TREE_THIS_VOLATILE (current_function_decl));
+ && get_frame_size () == 0
+ && avr_outgoing_args_size() == 0
+ && avr_regs_to_save (NULL) == 0
+ && ! interrupt_function_p (current_function_decl)
+ && ! signal_function_p (current_function_decl)
+ && ! avr_naked_function_p (current_function_decl)
+ && ! TREE_THIS_VOLATILE (current_function_decl));
}
/* This function checks sequence of live registers. */
for (reg = 0; reg < 18; ++reg)
{
+ if (fixed_regs[reg])
+ {
+ /* Don't recognize sequences that contain global register
+ variables. */
+
+ if (live_seq != 0)
+ return 0;
+ else
+ continue;
+ }
+
if (!call_used_regs[reg])
{
if (df_regs_ever_live_p (reg))
cfun->machine->stack_usage++;
}
-
-/* Output function prologue. */
-
-void
-expand_prologue (void)
+static void
+avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
{
- int live_seq;
- HARD_REG_SET set;
- int minimize;
- HOST_WIDE_INT size = get_frame_size();
rtx insn;
+ bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
+ int live_seq = sequent_regs_live ();
+
+ bool minimize = (TARGET_CALL_PROLOGUES
+ && live_seq
+ && !isr_p
+ && !cfun->machine->is_OS_task
+ && !cfun->machine->is_OS_main);
- /* Init cfun->machine. */
- cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
- cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
- cfun->machine->is_signal = signal_function_p (current_function_decl);
- cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
- cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
- cfun->machine->stack_usage = 0;
-
- /* Prologue: naked. */
- if (cfun->machine->is_naked)
- {
- return;
- }
-
- avr_regs_to_save (&set);
- live_seq = sequent_regs_live ();
- minimize = (TARGET_CALL_PROLOGUES
- && !cfun->machine->is_interrupt
- && !cfun->machine->is_signal
- && !cfun->machine->is_OS_task
- && !cfun->machine->is_OS_main
- && live_seq);
-
- if (cfun->machine->is_interrupt || cfun->machine->is_signal)
- {
- /* Enable interrupts. */
- if (cfun->machine->is_interrupt)
- emit_insn (gen_enable_interrupt ());
-
- /* Push zero reg. */
- emit_push_byte (ZERO_REGNO, true);
-
- /* Push tmp reg. */
- emit_push_byte (TMP_REGNO, true);
-
- /* Push SREG. */
- /* ??? There's no dwarf2 column reserved for SREG. */
- emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
- emit_push_byte (TMP_REGNO, false);
-
- /* Push RAMPZ. */
- /* ??? There's no dwarf2 column reserved for RAMPZ. */
- if (AVR_HAVE_RAMPZ
- && TEST_HARD_REG_BIT (set, REG_Z)
- && TEST_HARD_REG_BIT (set, REG_Z + 1))
- {
- emit_move_insn (tmp_reg_rtx,
- gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
- emit_push_byte (TMP_REGNO, false);
- }
-
- /* Clear zero reg. */
- emit_move_insn (zero_reg_rtx, const0_rtx);
-
- /* Prevent any attempt to delete the setting of ZERO_REG! */
- emit_use (zero_reg_rtx);
- }
- if (minimize && (frame_pointer_needed
- || (AVR_2_BYTE_PC && live_seq > 6)
- || live_seq > 7))
+ if (minimize
+ && (frame_pointer_needed
+ || avr_outgoing_args_size() > 8
+ || (AVR_2_BYTE_PC && live_seq > 6)
+ || live_seq > 7))
{
+ rtx pattern;
int first_reg, reg, offset;
emit_move_insn (gen_rtx_REG (HImode, REG_X),
gen_int_mode (size, HImode));
- insn = emit_insn (gen_call_prologue_saves
- (gen_int_mode (live_seq, HImode),
- gen_int_mode (size + live_seq, HImode)));
+ pattern = gen_call_prologue_saves (gen_int_mode (live_seq, HImode),
+ gen_int_mode (live_seq+size, HImode));
+ insn = emit_insn (pattern);
RTX_FRAME_RELATED_P (insn) = 1;
/* Describe the effect of the unspec_volatile call to prologue_saves.
- Note that this formulation assumes that add_reg_note pushes the
- notes to the front. Thus we build them in the reverse order of
- how we want dwarf2out to process them. */
+ Note that this formulation assumes that add_reg_note pushes the
+ notes to the front. Thus we build them in the reverse order of
+ how we want dwarf2out to process them. */
/* The function does always set frame_pointer_rtx, but whether that
- is going to be permanent in the function is frame_pointer_needed. */
+ is going to be permanent in the function is frame_pointer_needed. */
+
add_reg_note (insn, REG_CFA_ADJUST_CFA,
- gen_rtx_SET (VOIDmode,
- (frame_pointer_needed
- ? frame_pointer_rtx : stack_pointer_rtx),
- plus_constant (stack_pointer_rtx,
- -(size + live_seq))));
+ gen_rtx_SET (VOIDmode, (frame_pointer_needed
+ ? frame_pointer_rtx
+ : stack_pointer_rtx),
+ plus_constant (stack_pointer_rtx,
+ -(size + live_seq))));
/* Note that live_seq always contains r28+r29, but the other
- registers to be saved are all below 18. */
+ registers to be saved are all below 18. */
+
first_reg = 18 - (live_seq - 2);
for (reg = 29, offset = -live_seq + 1;
- reg >= first_reg;
- reg = (reg == 28 ? 17 : reg - 1), ++offset)
- {
- rtx m, r;
+ reg >= first_reg;
+ reg = (reg == 28 ? 17 : reg - 1), ++offset)
+ {
+ rtx m, r;
- m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset));
- r = gen_rtx_REG (QImode, reg);
- add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r));
- }
+ m = gen_rtx_MEM (QImode, plus_constant (stack_pointer_rtx, offset));
+ r = gen_rtx_REG (QImode, reg);
+ add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, m, r));
+ }
cfun->machine->stack_usage += size + live_seq;
}
- else
+ else /* !minimize */
{
int reg;
+
for (reg = 0; reg < 32; ++reg)
if (TEST_HARD_REG_BIT (set, reg))
- emit_push_byte (reg, true);
+ emit_push_byte (reg, true);
- if (frame_pointer_needed)
+ if (frame_pointer_needed
+ && (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main)))
{
- if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
- {
- /* Push frame pointer. Always be consistent about the
- ordering of pushes -- epilogue_restores expects the
- register pair to be pushed low byte first. */
- emit_push_byte (REG_Y, true);
- emit_push_byte (REG_Y + 1, true);
- }
+ /* Push frame pointer. Always be consistent about the
+ ordering of pushes -- epilogue_restores expects the
+ register pair to be pushed low byte first. */
+
+ emit_push_byte (REG_Y, true);
+ emit_push_byte (REG_Y + 1, true);
+ }
+
+ if (frame_pointer_needed
+ && size == 0)
+ {
+ insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ if (size != 0)
+ {
+ /* Creating a frame can be done by direct manipulation of the
+ stack or via the frame pointer. These two methods are:
+ fp = sp
+ fp -= size
+ sp = fp
+ or
+ sp -= size
+ fp = sp (*)
+ the optimum method depends on function type, stack and
+ frame size. To avoid a complex logic, both methods are
+ tested and shortest is selected.
+
+ There is also the case where SIZE != 0 and no frame pointer is
+ needed; this can occur if ACCUMULATE_OUTGOING_ARGS is on.
+ In that case, insn (*) is not needed in that case.
+ We use the X register as scratch. This is save because in X
+ is call-clobbered.
+ In an interrupt routine, the case of SIZE != 0 together with
+ !frame_pointer_needed can only occur if the function is not a
+ leaf function and thus X has already been saved. */
+
+ rtx fp_plus_insns, fp, my_fp;
+ rtx sp_minus_size = plus_constant (stack_pointer_rtx, -size);
- if (!size)
+ gcc_assert (frame_pointer_needed
+ || !isr_p
+ || !current_function_is_leaf);
+
+ fp = my_fp = (frame_pointer_needed
+ ? frame_pointer_rtx
+ : gen_rtx_REG (Pmode, REG_X));
+
+ if (AVR_HAVE_8BIT_SP)
{
- insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
- RTX_FRAME_RELATED_P (insn) = 1;
+ /* The high byte (r29) does not change:
+ Prefer SUBI (1 cycle) over ABIW (2 cycles, same size). */
+
+ my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
+ }
+
+ /************ Method 1: Adjust frame pointer ************/
+
+ start_sequence ();
+
+ /* Normally, the dwarf2out frame-related-expr interpreter does
+ not expect to have the CFA change once the frame pointer is
+ set up. Thus, we avoid marking the move insn below and
+ instead indicate that the entire operation is complete after
+ the frame pointer subtraction is done. */
+
+ insn = emit_move_insn (fp, stack_pointer_rtx);
+ if (!frame_pointer_needed)
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ insn = emit_move_insn (my_fp, plus_constant (my_fp, -size));
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ if (frame_pointer_needed)
+ {
+ add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (VOIDmode, fp, sp_minus_size));
+ }
+
+ /* Copy to stack pointer. Note that since we've already
+ changed the CFA to the frame pointer this operation
+ need not be annotated if frame pointer is needed. */
+
+ if (AVR_HAVE_8BIT_SP)
+ {
+ insn = emit_move_insn (stack_pointer_rtx, fp);
+ }
+ else if (TARGET_NO_INTERRUPTS
+ || isr_p
+ || cfun->machine->is_OS_main)
+ {
+ rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
+
+ insn = emit_insn (gen_movhi_sp_r (stack_pointer_rtx,
+ fp, irqs_are_on));
}
else
{
- /* Creating a frame can be done by direct manipulation of the
- stack or via the frame pointer. These two methods are:
- fp=sp
- fp-=size
- sp=fp
- OR
- sp-=size
- fp=sp
- the optimum method depends on function type, stack and frame size.
- To avoid a complex logic, both methods are tested and shortest
- is selected. */
- rtx myfp;
- rtx fp_plus_insns;
+ insn = emit_move_insn (stack_pointer_rtx, fp);
+ }
- if (AVR_HAVE_8BIT_SP)
- {
- /* The high byte (r29) doesn't change. Prefer 'subi'
- (1 cycle) over 'sbiw' (2 cycles, same size). */
- myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
- }
- else
+ if (!frame_pointer_needed)
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ fp_plus_insns = get_insns ();
+ end_sequence ();
+
+ /************ Method 2: Adjust Stack pointer ************/
+
+ /* Stack adjustment by means of RCALL . and/or PUSH __TMP_REG__
+ can only handle specific offsets. */
+
+ if (avr_sp_immediate_operand (gen_int_mode (-size, HImode), HImode))
+ {
+ rtx sp_plus_insns;
+
+ start_sequence ();
+
+ insn = emit_move_insn (stack_pointer_rtx, sp_minus_size);
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ if (frame_pointer_needed)
{
- /* Normal sized addition. */
- myfp = frame_pointer_rtx;
+ insn = emit_move_insn (fp, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
}
- /* Method 1-Adjust frame pointer. */
- start_sequence ();
+ sp_plus_insns = get_insns ();
+ end_sequence ();
- /* Normally the dwarf2out frame-related-expr interpreter does
- not expect to have the CFA change once the frame pointer is
- set up. Thus we avoid marking the move insn below and
- instead indicate that the entire operation is complete after
- the frame pointer subtraction is done. */
+ /************ Use shortest method ************/
+
+ emit_insn (get_sequence_length (sp_plus_insns)
+ < get_sequence_length (fp_plus_insns)
+ ? sp_plus_insns
+ : fp_plus_insns);
+ }
+ else
+ {
+ emit_insn (fp_plus_insns);
+ }
- emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+ cfun->machine->stack_usage += size;
+ } /* !minimize && size != 0 */
+ } /* !minimize */
+}
- insn = emit_move_insn (myfp, plus_constant (myfp, -size));
- RTX_FRAME_RELATED_P (insn) = 1;
- add_reg_note (insn, REG_CFA_ADJUST_CFA,
- gen_rtx_SET (VOIDmode, frame_pointer_rtx,
- plus_constant (stack_pointer_rtx,
- -size)));
-
- /* Copy to stack pointer. Note that since we've already
- changed the CFA to the frame pointer this operation
- need not be annotated at all. */
- if (AVR_HAVE_8BIT_SP)
- {
- emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
- }
- else if (TARGET_NO_INTERRUPTS
- || cfun->machine->is_signal
- || cfun->machine->is_OS_main)
- {
- emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx,
- frame_pointer_rtx));
- }
- else if (cfun->machine->is_interrupt)
- {
- emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx,
- frame_pointer_rtx));
- }
- else
- {
- emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
- }
- fp_plus_insns = get_insns ();
- end_sequence ();
+/* Output function prologue. */
- /* Method 2-Adjust Stack pointer. */
- if (size <= 6)
- {
- rtx sp_plus_insns;
+void
+expand_prologue (void)
+{
+ HARD_REG_SET set;
+ HOST_WIDE_INT size;
+
+ size = get_frame_size() + avr_outgoing_args_size();
+
+ /* Init cfun->machine. */
+ cfun->machine->is_naked = avr_naked_function_p (current_function_decl);
+ cfun->machine->is_interrupt = interrupt_function_p (current_function_decl);
+ cfun->machine->is_signal = signal_function_p (current_function_decl);
+ cfun->machine->is_OS_task = avr_OS_task_function_p (current_function_decl);
+ cfun->machine->is_OS_main = avr_OS_main_function_p (current_function_decl);
+ cfun->machine->stack_usage = 0;
+
+ /* Prologue: naked. */
+ if (cfun->machine->is_naked)
+ {
+ return;
+ }
- start_sequence ();
+ avr_regs_to_save (&set);
- insn = plus_constant (stack_pointer_rtx, -size);
- insn = emit_move_insn (stack_pointer_rtx, insn);
- RTX_FRAME_RELATED_P (insn) = 1;
-
- insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
- RTX_FRAME_RELATED_P (insn) = 1;
+ if (cfun->machine->is_interrupt || cfun->machine->is_signal)
+ {
+ /* Enable interrupts. */
+ if (cfun->machine->is_interrupt)
+ emit_insn (gen_enable_interrupt ());
+
+ /* Push zero reg. */
+ emit_push_byte (ZERO_REGNO, true);
- sp_plus_insns = get_insns ();
- end_sequence ();
+ /* Push tmp reg. */
+ emit_push_byte (TMP_REGNO, true);
- /* Use shortest method. */
- if (get_sequence_length (sp_plus_insns)
- < get_sequence_length (fp_plus_insns))
- emit_insn (sp_plus_insns);
- else
- emit_insn (fp_plus_insns);
- }
- else
- emit_insn (fp_plus_insns);
+ /* Push SREG. */
+ /* ??? There's no dwarf2 column reserved for SREG. */
+ emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)));
+ emit_push_byte (TMP_REGNO, false);
- cfun->machine->stack_usage += size;
- }
+ /* Push RAMPZ. */
+ /* ??? There's no dwarf2 column reserved for RAMPZ. */
+ if (AVR_HAVE_RAMPZ
+ && TEST_HARD_REG_BIT (set, REG_Z)
+ && TEST_HARD_REG_BIT (set, REG_Z + 1))
+ {
+ emit_move_insn (tmp_reg_rtx, rampz_rtx);
+ emit_push_byte (TMP_REGNO, false);
}
+
+ /* Clear zero reg. */
+ emit_move_insn (zero_reg_rtx, const0_rtx);
+
+ /* Prevent any attempt to delete the setting of ZERO_REG! */
+ emit_use (zero_reg_rtx);
}
+ avr_prologue_setup_frame (size, set);
+
if (flag_stack_usage_info)
current_function_static_stack_size = cfun->machine->stack_usage;
}
else
fputs ("/* prologue: function */\n", file);
}
+
+ if (ACCUMULATE_OUTGOING_ARGS)
+ fprintf (file, "/* outgoing args size = %d */\n",
+ avr_outgoing_args_size());
+
fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n",
get_frame_size());
fprintf (file, "/* stack size = %d */\n",
int live_seq;
HARD_REG_SET set;
int minimize;
- HOST_WIDE_INT size = get_frame_size();
+ HOST_WIDE_INT size;
+ bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
+
+ size = get_frame_size() + avr_outgoing_args_size();
/* epilogue: naked */
if (cfun->machine->is_naked)
avr_regs_to_save (&set);
live_seq = sequent_regs_live ();
+
minimize = (TARGET_CALL_PROLOGUES
- && !cfun->machine->is_interrupt
- && !cfun->machine->is_signal
- && !cfun->machine->is_OS_task
- && !cfun->machine->is_OS_main
- && live_seq);
+ && live_seq
+ && !isr_p
+ && !cfun->machine->is_OS_task
+ && !cfun->machine->is_OS_main);
- if (minimize && (frame_pointer_needed || live_seq > 4))
+ if (minimize
+ && (live_seq > 4
+ || frame_pointer_needed
+ || size))
{
- if (frame_pointer_needed)
- {
- /* Get rid of frame. */
- emit_move_insn(frame_pointer_rtx,
- gen_rtx_PLUS (HImode, frame_pointer_rtx,
- gen_int_mode (size, HImode)));
- }
- else
- {
+ /* Get rid of frame. */
+
+ if (!frame_pointer_needed)
+ {
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
- }
-
+ }
+
+ if (size)
+ {
+ emit_move_insn (frame_pointer_rtx,
+ plus_constant (frame_pointer_rtx, size));
+ }
+
emit_insn (gen_epilogue_restores (gen_int_mode (live_seq, HImode)));
+ return;
}
- else
+
+ if (size)
{
- if (frame_pointer_needed)
- {
- if (size)
- {
- /* Try two methods to adjust stack and select shortest. */
- rtx myfp;
- rtx fp_plus_insns;
+ /* Try two methods to adjust stack and select shortest. */
+
+ rtx fp, my_fp;
+ rtx fp_plus_insns;
- if (AVR_HAVE_8BIT_SP)
- {
- /* The high byte (r29) doesn't change - prefer 'subi'
- (1 cycle) over 'sbiw' (2 cycles, same size). */
- myfp = gen_rtx_REG (QImode, FRAME_POINTER_REGNUM);
- }
- else
- {
- /* Normal sized addition. */
- myfp = frame_pointer_rtx;
- }
-
- /* Method 1-Adjust frame pointer. */
- start_sequence ();
+ gcc_assert (frame_pointer_needed
+ || !isr_p
+ || !current_function_is_leaf);
+
+ fp = my_fp = (frame_pointer_needed
+ ? frame_pointer_rtx
+ : gen_rtx_REG (Pmode, REG_X));
- emit_move_insn (myfp, plus_constant (myfp, size));
+ if (AVR_HAVE_8BIT_SP)
+ {
+ /* The high byte (r29) does not change:
+ Prefer SUBI (1 cycle) over SBIW (2 cycles). */
+
+ my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
+ }
+
+ /********** Method 1: Adjust fp register **********/
+
+ start_sequence ();
- /* Copy to stack pointer. */
- if (AVR_HAVE_8BIT_SP)
- {
- emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
- }
- else if (TARGET_NO_INTERRUPTS
- || cfun->machine->is_signal)
- {
- emit_insn (gen_movhi_sp_r_irq_off (stack_pointer_rtx,
- frame_pointer_rtx));
- }
- else if (cfun->machine->is_interrupt)
- {
- emit_insn (gen_movhi_sp_r_irq_on (stack_pointer_rtx,
- frame_pointer_rtx));
- }
- else
- {
- emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
- }
+ if (!frame_pointer_needed)
+ emit_move_insn (fp, stack_pointer_rtx);
- fp_plus_insns = get_insns ();
- end_sequence ();
+ emit_move_insn (my_fp, plus_constant (my_fp, size));
- /* Method 2-Adjust Stack pointer. */
- if (size <= 5)
- {
- rtx sp_plus_insns;
+ /* Copy to stack pointer. */
+
+ if (AVR_HAVE_8BIT_SP)
+ {
+ emit_move_insn (stack_pointer_rtx, fp);
+ }
+ else if (TARGET_NO_INTERRUPTS
+ || isr_p
+ || cfun->machine->is_OS_main)
+ {
+ rtx irqs_are_on = GEN_INT (!!cfun->machine->is_interrupt);
+
+ emit_insn (gen_movhi_sp_r (stack_pointer_rtx, fp, irqs_are_on));
+ }
+ else
+ {
+ emit_move_insn (stack_pointer_rtx, fp);
+ }
+
+ fp_plus_insns = get_insns ();
+ end_sequence ();
+
+ /********** Method 2: Adjust Stack pointer **********/
+
+ if (avr_sp_immediate_operand (gen_int_mode (size, HImode), HImode))
+ {
+ rtx sp_plus_insns;
- start_sequence ();
+ start_sequence ();
- emit_move_insn (stack_pointer_rtx,
- plus_constant (stack_pointer_rtx, size));
+ emit_move_insn (stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx, size));
- sp_plus_insns = get_insns ();
- end_sequence ();
+ sp_plus_insns = get_insns ();
+ end_sequence ();
- /* Use shortest method. */
- if (get_sequence_length (sp_plus_insns)
- < get_sequence_length (fp_plus_insns))
- emit_insn (sp_plus_insns);
- else
- emit_insn (fp_plus_insns);
- }
- else
- emit_insn (fp_plus_insns);
- }
- if (!(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
- {
- /* Restore previous frame_pointer. See expand_prologue for
- rationale for not using pophi. */
- emit_pop_byte (REG_Y + 1);
- emit_pop_byte (REG_Y);
- }
- }
+ /************ Use shortest method ************/
+
+ emit_insn (get_sequence_length (sp_plus_insns)
+ < get_sequence_length (fp_plus_insns)
+ ? sp_plus_insns
+ : fp_plus_insns);
+ }
+ else
+ emit_insn (fp_plus_insns);
+ } /* size != 0 */
+
+ if (frame_pointer_needed
+ && !(cfun->machine->is_OS_task || cfun->machine->is_OS_main))
+ {
+ /* Restore previous frame_pointer. See expand_prologue for
+ rationale for not using pophi. */
+
+ emit_pop_byte (REG_Y + 1);
+ emit_pop_byte (REG_Y);
+ }
- /* Restore used registers. */
- for (reg = 31; reg >= 0; --reg)
- if (TEST_HARD_REG_BIT (set, reg))
- emit_pop_byte (reg);
+ /* Restore used registers. */
+
+ for (reg = 31; reg >= 0; --reg)
+ if (TEST_HARD_REG_BIT (set, reg))
+ emit_pop_byte (reg);
- if (cfun->machine->is_interrupt || cfun->machine->is_signal)
+ if (isr_p)
+ {
+ /* Restore RAMPZ using tmp reg as scratch. */
+
+ if (AVR_HAVE_RAMPZ
+ && TEST_HARD_REG_BIT (set, REG_Z)
+ && TEST_HARD_REG_BIT (set, REG_Z + 1))
{
- /* Restore RAMPZ using tmp reg as scratch. */
- if (AVR_HAVE_RAMPZ
- && TEST_HARD_REG_BIT (set, REG_Z)
- && TEST_HARD_REG_BIT (set, REG_Z + 1))
- {
- emit_pop_byte (TMP_REGNO);
- emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)),
- tmp_reg_rtx);
- }
-
- /* Restore SREG using tmp reg as scratch. */
emit_pop_byte (TMP_REGNO);
-
- emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)),
- tmp_reg_rtx);
+ emit_move_insn (rampz_rtx, tmp_reg_rtx);
+ }
- /* Restore tmp REG. */
- emit_pop_byte (TMP_REGNO);
+ /* Restore SREG using tmp reg as scratch. */
+
+ emit_pop_byte (TMP_REGNO);
+ emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (SREG_ADDR)),
+ tmp_reg_rtx);
- /* Restore zero REG. */
- emit_pop_byte (ZERO_REGNO);
- }
+ /* Restore tmp REG. */
+ emit_pop_byte (TMP_REGNO);
- if (!sibcall_p)
- emit_jump_insn (gen_return ());
+ /* Restore zero REG. */
+ emit_pop_byte (ZERO_REGNO);
}
+
+ if (!sibcall_p)
+ emit_jump_insn (gen_return ());
}
/* Output summary messages at beginning of function epilogue. */
}
+/* Helper function for `avr_legitimate_address_p'. */
+
+static inline bool
+avr_reg_ok_for_addr_p (rtx reg, addr_space_t as,
+ RTX_CODE outer_code, bool strict)
+{
+ return (REG_P (reg)
+ && (avr_regno_mode_code_ok_for_base_p (REGNO (reg), QImode,
+ as, outer_code, UNKNOWN)
+ || (!strict
+ && REGNO (reg) >= FIRST_PSEUDO_REGISTER)));
+}
+
+
/* Return nonzero if X (an RTX) is a legitimate memory address on the target
machine for a memory operand of mode MODE. */
-bool
+static bool
avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
{
- enum reg_class r = NO_REGS;
+ bool ok = CONSTANT_ADDRESS_P (x);
- if (TARGET_ALL_DEBUG)
- {
- fprintf (stderr, "mode: (%s) %s %s %s %s:",
- GET_MODE_NAME(mode),
- strict ? "(strict)": "",
- reload_completed ? "(reload_completed)": "",
- reload_in_progress ? "(reload_in_progress)": "",
- reg_renumber ? "(reg_renumber)" : "");
- if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) >= 0
- && INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode)
- && reg_renumber
- )
- fprintf (stderr, "(r%d ---> r%d)", REGNO (XEXP (x, 0)),
- true_regnum (XEXP (x, 0)));
- debug_rtx (x);
- }
-
- if (REG_P (x) && (strict ? REG_OK_FOR_BASE_STRICT_P (x)
- : REG_OK_FOR_BASE_NOSTRICT_P (x)))
- r = POINTER_REGS;
- else if (CONSTANT_ADDRESS_P (x))
- r = ALL_REGS;
- else if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) >= 0)
- {
- int fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode);
- if (fit)
- {
- if (! strict
- || REGNO (XEXP (x,0)) == REG_X
- || REGNO (XEXP (x,0)) == REG_Y
- || REGNO (XEXP (x,0)) == REG_Z)
- r = BASE_POINTER_REGS;
- if (XEXP (x,0) == frame_pointer_rtx
- || XEXP (x,0) == arg_pointer_rtx)
- r = BASE_POINTER_REGS;
- }
- else if (frame_pointer_needed && XEXP (x,0) == frame_pointer_rtx)
- r = POINTER_Y_REGS;
- }
- else if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC)
- && REG_P (XEXP (x, 0))
- && (strict ? REG_OK_FOR_BASE_STRICT_P (XEXP (x, 0))
- : REG_OK_FOR_BASE_NOSTRICT_P (XEXP (x, 0))))
+ switch (GET_CODE (x))
{
- r = POINTER_REGS;
+ case REG:
+ ok = avr_reg_ok_for_addr_p (x, ADDR_SPACE_GENERIC,
+ MEM, strict);
+
+ if (strict
+ && DImode == mode
+ && REG_X == REGNO (x))
+ {
+ ok = false;
+ }
+ break;
+
+ case POST_INC:
+ case PRE_DEC:
+ ok = avr_reg_ok_for_addr_p (XEXP (x, 0), ADDR_SPACE_GENERIC,
+ GET_CODE (x), strict);
+ break;
+
+ case PLUS:
+ {
+ rtx reg = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+
+ if (REG_P (reg)
+ && CONST_INT_P (op1)
+ && INTVAL (op1) >= 0)
+ {
+ bool fit = IN_RANGE (INTVAL (op1), 0, MAX_LD_OFFSET (mode));
+
+ if (fit)
+ {
+ ok = (! strict
+ || avr_reg_ok_for_addr_p (reg, ADDR_SPACE_GENERIC,
+ PLUS, strict));
+
+ if (reg == frame_pointer_rtx
+ || reg == arg_pointer_rtx)
+ {
+ ok = true;
+ }
+ }
+ else if (frame_pointer_needed
+ && reg == frame_pointer_rtx)
+ {
+ ok = true;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
}
- if (TARGET_ALL_DEBUG)
+
+ if (avr_log.legitimate_address_p)
{
- fprintf (stderr, " ret = %c\n", r + '0');
+ avr_edump ("\n%?: ret=%d, mode=%m strict=%d "
+ "reload_completed=%d reload_in_progress=%d %s:",
+ ok, mode, strict, reload_completed, reload_in_progress,
+ reg_renumber ? "(reg_renumber)" : "");
+
+ if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1))
+ && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
+ && reg_renumber)
+ {
+ avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
+ true_regnum (XEXP (x, 0)));
+ }
+
+ avr_edump ("\n%r\n", x);
}
- return r == NO_REGS ? 0 : (int)r;
+
+ return ok;
}
+
+/* Former implementation of TARGET_LEGITIMIZE_ADDRESS,
+ now only a helper for avr_addr_space_legitimize_address. */
/* Attempts to replace X with a valid
memory address for an operand of mode MODE */
-rtx
+static rtx
avr_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
{
+ bool big_offset_p = false;
+
x = oldx;
- if (TARGET_ALL_DEBUG)
+
+ if (GET_CODE (oldx) == PLUS
+ && REG_P (XEXP (oldx, 0)))
{
- fprintf (stderr, "legitimize_address mode: %s", GET_MODE_NAME(mode));
- debug_rtx (oldx);
+ if (REG_P (XEXP (oldx, 1)))
+ x = force_reg (GET_MODE (oldx), oldx);
+ else if (CONST_INT_P (XEXP (oldx, 1)))
+ {
+ int offs = INTVAL (XEXP (oldx, 1));
+ if (frame_pointer_rtx != XEXP (oldx, 0)
+ && offs > MAX_LD_OFFSET (mode))
+ {
+ big_offset_p = true;
+ x = force_reg (GET_MODE (oldx), oldx);
+ }
+ }
}
- if (GET_CODE (oldx) == PLUS
- && REG_P (XEXP (oldx,0)))
+ if (avr_log.legitimize_address)
{
- if (REG_P (XEXP (oldx,1)))
- x = force_reg (GET_MODE (oldx), oldx);
- else if (GET_CODE (XEXP (oldx, 1)) == CONST_INT)
- {
- int offs = INTVAL (XEXP (oldx,1));
- if (frame_pointer_rtx != XEXP (oldx,0))
- if (offs > MAX_LD_OFFSET (mode))
- {
- if (TARGET_ALL_DEBUG)
- fprintf (stderr, "force_reg (big offset)\n");
- x = force_reg (GET_MODE (oldx), oldx);
- }
- }
+ avr_edump ("\n%?: mode=%m\n %r\n", mode, oldx);
+
+ if (x != oldx)
+ avr_edump (" %s --> %r\n", big_offset_p ? "(big offset)" : "", x);
}
+
return x;
}
+/* Implement `LEGITIMIZE_RELOAD_ADDRESS'. */
+/* This will allow register R26/27 to be used where it is no worse than normal
+ base pointers R28/29 or R30/31. For example, if base offset is greater
+ than 63 bytes or for R++ or --R addressing. */
+
+rtx
+avr_legitimize_reload_address (rtx *px, enum machine_mode mode,
+ int opnum, int type, int addr_type,
+ int ind_levels ATTRIBUTE_UNUSED,
+ rtx (*mk_memloc)(rtx,int))
+{
+ rtx x = *px;
+
+ if (avr_log.legitimize_reload_address)
+ avr_edump ("\n%?:%m %r\n", mode, x);
+
+ if (1 && (GET_CODE (x) == POST_INC
+ || GET_CODE (x) == PRE_DEC))
+ {
+ push_reload (XEXP (x, 0), XEXP (x, 0), &XEXP (x, 0), &XEXP (x, 0),
+ POINTER_REGS, GET_MODE (x), GET_MODE (x), 0, 0,
+ opnum, RELOAD_OTHER);
+
+ if (avr_log.legitimize_reload_address)
+ avr_edump (" RCLASS.1 = %R\n IN = %r\n OUT = %r\n",
+ POINTER_REGS, XEXP (x, 0), XEXP (x, 0));
+
+ return x;
+ }
+
+ if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0))
+ && 0 == reg_equiv_constant (REGNO (XEXP (x, 0)))
+ && CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) >= 1)
+ {
+ bool fit = INTVAL (XEXP (x, 1)) <= MAX_LD_OFFSET (mode);
+
+ if (fit)
+ {
+ if (reg_equiv_address (REGNO (XEXP (x, 0))) != 0)
+ {
+ int regno = REGNO (XEXP (x, 0));
+ rtx mem = mk_memloc (x, regno);
+
+ push_reload (XEXP (mem, 0), NULL_RTX, &XEXP (mem, 0), NULL,
+ POINTER_REGS, Pmode, VOIDmode, 0, 0,
+ 1, addr_type);
+
+ if (avr_log.legitimize_reload_address)
+ avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n",
+ POINTER_REGS, XEXP (mem, 0), NULL_RTX);
+
+ push_reload (mem, NULL_RTX, &XEXP (x, 0), NULL,
+ BASE_POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, type);
+
+ if (avr_log.legitimize_reload_address)
+ avr_edump (" RCLASS.2 = %R\n IN = %r\n OUT = %r\n",
+ BASE_POINTER_REGS, mem, NULL_RTX);
+
+ return x;
+ }
+ }
+ else if (! (frame_pointer_needed
+ && XEXP (x, 0) == frame_pointer_rtx))
+ {
+ push_reload (x, NULL_RTX, px, NULL,
+ POINTER_REGS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, type);
+
+ if (avr_log.legitimize_reload_address)
+ avr_edump (" RCLASS.3 = %R\n IN = %r\n OUT = %r\n",
+ POINTER_REGS, x, NULL_RTX);
+
+ return x;
+ }
+ }
+
+ return NULL_RTX;
+}
+
+
/* Helper function to print assembler resp. track instruction
- sequence lengths.
+ sequence lengths. Always return "".
If PLEN == NULL:
Output assembler code from template TPL with operands supplied
by OPERANDS. This is just forwarding to output_asm_insn.
If PLEN != NULL:
- Add N_WORDS to *PLEN.
+ If N_WORDS >= 0 Add N_WORDS to *PLEN.
+ If N_WORDS < 0 Set *PLEN to -N_WORDS.
Don't output anything.
*/
-static void
+static const char*
avr_asm_len (const char* tpl, rtx* operands, int* plen, int n_words)
{
if (NULL == plen)
}
else
{
- *plen += n_words;
+ if (n_words < 0)
+ *plen = -n_words;
+ else
+ *plen += n_words;
}
+
+ return "";
}
case REG_Y: return "Y";
case REG_Z: return "Z";
default:
- output_operand_lossage ("address operand requires constraint for X, Y, or Z register");
+ output_operand_lossage ("address operand requires constraint for"
+ " X, Y, or Z register");
}
return NULL;
}
default:
gcc_unreachable ();
}
+
+ return "";
}
+
+/* Implement `TARGET_PRINT_OPERAND_ADDRESS'. */
/* Output ADDR to FILE as address. */
-void
-print_operand_address (FILE *file, rtx addr)
+static void
+avr_print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x,1)) == CONST_INT)
{
/* Assembler gs() will implant word address. Make offset
- a byte offset inside gs() for assembler. This is
- needed because the more logical (constant+gs(sym)) is not
- accepted by gas. For 128K and lower devices this is ok. For
- large devices it will create a Trampoline to offset from symbol
- which may not be what the user really wanted. */
+ a byte offset inside gs() for assembler. This is
+ needed because the more logical (constant+gs(sym)) is not
+ accepted by gas. For 128K and lower devices this is ok.
+ For large devices it will create a Trampoline to offset
+ from symbol which may not be what the user really wanted. */
fprintf (file, "gs(");
output_addr_const (file, XEXP (x,0));
- fprintf (file,"+" HOST_WIDE_INT_PRINT_DEC ")", 2 * INTVAL (XEXP (x,1)));
+ fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC ")",
+ 2 * INTVAL (XEXP (x, 1)));
if (AVR_3_BYTE_PC)
if (warning (0, "pointer offset from symbol maybe incorrect"))
{
}
-/* Output X as assembler operand to file FILE. */
-
-void
-print_operand (FILE *file, rtx x, int code)
+/* Implement `TARGET_PRINT_OPERAND_PUNCT_VALID_P'. */
+
+static bool
+avr_print_operand_punct_valid_p (unsigned char code)
+{
+ return code == '~' || code == '!';
+}
+
+
+/* Implement `TARGET_PRINT_OPERAND'. */
+/* Output X as assembler operand to file FILE.
+ For a description of supported %-codes, see top of avr.md. */
+
+static void
+avr_print_operand (FILE *file, rtx x, int code)
{
int abcd = 0;
if (AVR_HAVE_EIJMP_EICALL)
fputc ('e', file);
}
+ else if (code == 't'
+ || code == 'T')
+ {
+ static int t_regno = -1;
+ static int t_nbits = -1;
+
+ if (REG_P (x) && t_regno < 0 && code == 'T')
+ {
+ t_regno = REGNO (x);
+ t_nbits = GET_MODE_BITSIZE (GET_MODE (x));
+ }
+ else if (CONST_INT_P (x) && t_regno >= 0
+ && IN_RANGE (INTVAL (x), 0, t_nbits - 1))
+ {
+ int bpos = INTVAL (x);
+
+ fprintf (file, "%s", reg_names[t_regno + bpos / 8]);
+ if (code == 'T')
+ fprintf (file, ",%d", bpos % 8);
+
+ t_regno = -1;
+ }
+ else
+ fatal_insn ("operands to %T/%t must be reg + const_int:", x);
+ }
else if (REG_P (x))
{
if (x == zero_reg_rtx)
else
fprintf (file, reg_names[true_regnum (x) + abcd]);
}
- else if (GET_CODE (x) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) + abcd);
- else if (GET_CODE (x) == MEM)
+ else if (CONST_INT_P (x))
+ {
+ HOST_WIDE_INT ival = INTVAL (x);
+
+ if ('i' != code)
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival + abcd);
+ else if (low_io_address_operand (x, VOIDmode)
+ || high_io_address_operand (x, VOIDmode))
+ {
+ switch (ival)
+ {
+ case RAMPZ_ADDR: fprintf (file, "__RAMPZ__"); break;
+ case SREG_ADDR: fprintf (file, "__SREG__"); break;
+ case SP_ADDR: fprintf (file, "__SP_L__"); break;
+ case SP_ADDR+1: fprintf (file, "__SP_H__"); break;
+
+ default:
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX,
+ ival - avr_current_arch->sfr_offset);
+ break;
+ }
+ }
+ else
+ fatal_insn ("bad address, not an I/O address:", x);
+ }
+ else if (MEM_P (x))
{
- rtx addr = XEXP (x,0);
+ rtx addr = XEXP (x, 0);
+
if (code == 'm')
{
- if (!CONSTANT_P (addr))
- fatal_insn ("bad address, not a constant):", addr);
- /* Assembler template with m-code is data - not progmem section */
- if (text_segment_operand (addr, VOIDmode))
- if (warning ( 0, "accessing data memory with program memory address"))
- {
- output_addr_const (stderr, addr);
- fprintf(stderr,"\n");
- }
- output_addr_const (file, addr);
+ if (!CONSTANT_P (addr))
+ fatal_insn ("bad address, not a constant:", addr);
+ /* Assembler template with m-code is data - not progmem section */
+ if (text_segment_operand (addr, VOIDmode))
+ if (warning (0, "accessing data memory with"
+ " program memory address"))
+ {
+ output_addr_const (stderr, addr);
+ fprintf(stderr,"\n");
+ }
+ output_addr_const (file, addr);
}
+ else if (code == 'i')
+ {
+ avr_print_operand (file, addr, 'i');
+ }
else if (code == 'o')
{
if (GET_CODE (addr) != PLUS)
fatal_insn ("bad address, not (reg+disp):", addr);
- print_operand (file, XEXP (addr, 1), 0);
+ avr_print_operand (file, XEXP (addr, 1), 0);
}
else if (code == 'p' || code == 'r')
{
fatal_insn ("bad address, not post_inc or pre_dec:", addr);
if (code == 'p')
- print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */
+ avr_print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */
else
- print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */
+ avr_print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */
}
else if (GET_CODE (addr) == PLUS)
{
- print_operand_address (file, XEXP (addr,0));
+ avr_print_operand_address (file, XEXP (addr,0));
if (REGNO (XEXP (addr, 0)) == REG_X)
fatal_insn ("internal compiler error. Bad address:"
,addr);
fputc ('+', file);
- print_operand (file, XEXP (addr,1), code);
+ avr_print_operand (file, XEXP (addr,1), code);
}
else
- print_operand_address (file, addr);
+ avr_print_operand_address (file, addr);
+ }
+ else if (code == 'i')
+ {
+ fatal_insn ("bad address, not an I/O address:", x);
}
else if (code == 'x')
{
/* Constant progmem address - like used in jmp or call */
if (0 == text_segment_operand (x, VOIDmode))
- if (warning ( 0, "accessing program memory with data memory address"))
+ if (warning (0, "accessing program memory"
+ " with data memory address"))
{
output_addr_const (stderr, x);
fprintf(stderr,"\n");
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "0x%lx", val);
}
+ else if (GET_CODE (x) == CONST_STRING)
+ fputs (XSTR (x, 0), file);
else if (code == 'j')
fputs (cond_string (GET_CODE (x)), file);
else if (code == 'k')
fputs (cond_string (reverse_condition (GET_CODE (x))), file);
else
- print_operand_address (file, x);
+ avr_print_operand_address (file, x);
}
/* Update the condition code in the INSN. */
notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
{
rtx set;
+ enum attr_cc cc = get_attr_cc (insn);
- switch (get_attr_cc (insn))
+ switch (cc)
+ {
+ default:
+ break;
+
+ case CC_OUT_PLUS:
+ case CC_OUT_PLUS_NOCLOBBER:
+ case CC_LDI:
+ {
+ rtx *op = recog_data.operand;
+ int len_dummy, icc;
+
+ /* Extract insn's operands. */
+ extract_constrain_insn_cached (insn);
+
+ switch (cc)
+ {
+ default:
+ gcc_unreachable();
+
+ case CC_OUT_PLUS:
+ avr_out_plus (op, &len_dummy, &icc);
+ cc = (enum attr_cc) icc;
+ break;
+
+ case CC_OUT_PLUS_NOCLOBBER:
+ avr_out_plus_noclobber (op, &len_dummy, &icc);
+ cc = (enum attr_cc) icc;
+ break;
+
+ case CC_LDI:
+
+ cc = (op[1] == CONST0_RTX (GET_MODE (op[0]))
+ && reg_overlap_mentioned_p (op[0], zero_reg_rtx))
+ /* Loading zero-reg with 0 uses CLI and thus clobbers cc0. */
+ ? CC_CLOBBER
+ /* Any other "r,rL" combination does not alter cc0. */
+ : CC_NONE;
+
+ break;
+ } /* inner switch */
+
+ break;
+ }
+ } /* outer swicth */
+
+ switch (cc)
{
+ default:
+ /* Special values like CC_OUT_PLUS from above have been
+ mapped to "standard" CC_* values so we never come here. */
+
+ gcc_unreachable();
+ break;
+
case CC_NONE:
/* Insn does not affect CC at all. */
break;
case CC_CLOBBER:
/* Insn doesn't leave CC in a usable state. */
CC_STATUS_INIT;
-
- /* Correct CC for the ashrqi3 with the shift count as CONST_INT != 6 */
- set = single_set (insn);
- if (set)
- {
- rtx src = SET_SRC (set);
-
- if (GET_CODE (src) == ASHIFTRT
- && GET_MODE (src) == QImode)
- {
- rtx x = XEXP (src, 1);
-
- if (CONST_INT_P (x)
- && IN_RANGE (INTVAL (x), 1, 5))
- {
- cc_status.value1 = SET_DEST (set);
- cc_status.flags |= CC_OVERFLOW_UNUSABLE;
- }
- }
- }
break;
}
}
-/* Return maximum number of consecutive registers of
- class CLASS needed to hold a value of mode MODE. */
-
-int
-class_max_nregs (enum reg_class rclass ATTRIBUTE_UNUSED,enum machine_mode mode)
-{
- return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
-}
-
/* Choose mode for jump insn:
1 - relative jump in range -63 <= x <= 62 ;
2 - relative jump in range -2046 <= x <= 2045 ;
return "";
}
-/* Predicate function for immediate operand which fits to byte (8bit) */
-
-int
-byte_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) <= 0xff && INTVAL (op) >= 0);
-}
-
/* Output insn cost for next insn. */
void
final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
- int num_operands ATTRIBUTE_UNUSED)
+ int num_operands ATTRIBUTE_UNUSED)
{
- if (TARGET_ALL_DEBUG)
+ if (avr_log.rtx_costs)
{
- fprintf (asm_out_file, "/* DEBUG: cost = %d. */\n",
- rtx_cost (PATTERN (insn), INSN, !optimize_size));
+ rtx set = single_set (insn);
+
+ if (set)
+ fprintf (asm_out_file, "/* DEBUG: cost = %d. */\n",
+ set_src_cost (SET_SRC (set), optimize_insn_for_speed_p ()));
+ else
+ fprintf (asm_out_file, "/* DEBUG: pattern-cost = %d. */\n",
+ rtx_cost (PATTERN (insn), INSN, 0,
+ optimize_insn_for_speed_p()));
}
}
{
unsigned int max = (mode == QImode ? 0xff :
mode == HImode ? 0xffff :
+ mode == PSImode ? 0xffffff :
mode == SImode ? 0xffffffff : 0);
if (max && op && GET_CODE (x) == CONST_INT)
{
/* Test if all registers needed by the ABI are actually available. If the
user has fixed a GPR needed to pass an argument, an (implicit) function
- call would clobber that fixed register. See PR45099 for an example. */
+ call will clobber that fixed register. See PR45099 for an example. */
if (cum->regno >= 8
&& cum->nregs >= 0)
for (regno = cum->regno; regno < cum->regno + bytes; regno++)
if (fixed_regs[regno])
- error ("Register %s is needed to pass a parameter but is fixed",
- reg_names[regno]);
+ warning (0, "fixed register %s used to pass parameter to function",
+ reg_names[regno]);
}
if (cum->nregs <= 0)
/***********************************************************************
Functions for outputting various mov's for a various modes
************************************************************************/
-const char *
-output_movqi (rtx insn, rtx operands[], int *l)
+
+/* Return true if a value of mode MODE is read from flash by
+ __load_* function from libgcc. */
+
+bool
+avr_load_libgcc_p (rtx op)
{
- int dummy;
- rtx dest = operands[0];
- rtx src = operands[1];
- int *real_l = l;
-
- if (!l)
- l = &dummy;
+ enum machine_mode mode = GET_MODE (op);
+ int n_bytes = GET_MODE_SIZE (mode);
+
+ return (n_bytes > 2
+ && !AVR_HAVE_LPMX
+ && avr_mem_pgm_p (op));
+}
- *l = 1;
+/* Return true if a value of mode MODE is read by __xload_* function. */
+
+bool
+avr_xload_libgcc_p (enum machine_mode mode)
+{
+ int n_bytes = GET_MODE_SIZE (mode);
- if (register_operand (dest, QImode))
+ return (n_bytes > 1
+ && avr_current_arch->n_segments > 1
+ && !AVR_HAVE_ELPMX);
+}
+
+
+/* Find an unused d-register to be used as scratch in INSN.
+ EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
+ is a register, skip all possible return values that overlap EXCLUDE.
+ The policy for the returned register is similar to that of
+ `reg_unused_after', i.e. the returned register may overlap the SET_DEST
+ of INSN.
+
+ Return a QImode d-register or NULL_RTX if nothing found. */
+
+static rtx
+avr_find_unused_d_reg (rtx insn, rtx exclude)
+{
+ int regno;
+ bool isr_p = (interrupt_function_p (current_function_decl)
+ || signal_function_p (current_function_decl));
+
+ for (regno = 16; regno < 32; regno++)
{
- if (register_operand (src, QImode)) /* mov r,r */
- {
- if (test_hard_reg_class (STACK_REG, dest))
- return AS2 (out,%0,%1);
- else if (test_hard_reg_class (STACK_REG, src))
- return AS2 (in,%0,%1);
-
- return AS2 (mov,%0,%1);
- }
- else if (CONSTANT_P (src))
- {
- if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
- return AS2 (ldi,%0,lo8(%1));
-
- if (GET_CODE (src) == CONST_INT)
- {
- if (src == const0_rtx) /* mov r,L */
- return AS1 (clr,%0);
- else if (src == const1_rtx)
- {
- *l = 2;
- return (AS1 (clr,%0) CR_TAB
- AS1 (inc,%0));
- }
- else if (src == constm1_rtx)
- {
- /* Immediate constants -1 to any register */
- *l = 2;
- return (AS1 (clr,%0) CR_TAB
- AS1 (dec,%0));
- }
- else
- {
- int bit_nr = exact_log2 (INTVAL (src));
+ rtx reg = all_regs_rtx[regno];
+
+ if ((exclude
+ && reg_overlap_mentioned_p (exclude, reg))
+ || fixed_regs[regno])
+ {
+ continue;
+ }
- if (bit_nr >= 0)
- {
- *l = 3;
- if (!real_l)
- output_asm_insn ((AS1 (clr,%0) CR_TAB
- "set"), operands);
- if (!real_l)
- avr_output_bld (operands, bit_nr);
-
- return "";
- }
- }
- }
-
- /* Last resort, larger than loading from memory. */
- *l = 4;
- return (AS2 (mov,__tmp_reg__,r31) CR_TAB
- AS2 (ldi,r31,lo8(%1)) CR_TAB
- AS2 (mov,%0,r31) CR_TAB
- AS2 (mov,r31,__tmp_reg__));
- }
- else if (GET_CODE (src) == MEM)
- return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
+ /* Try non-live register */
+
+ if (!df_regs_ever_live_p (regno)
+ && (TREE_THIS_VOLATILE (current_function_decl)
+ || cfun->machine->is_OS_task
+ || cfun->machine->is_OS_main
+ || (!isr_p && call_used_regs[regno])))
+ {
+ return reg;
+ }
+
+ /* Any live register can be used if it is unused after.
+ Prologue/epilogue will care for it as needed. */
+
+ if (df_regs_ever_live_p (regno)
+ && reg_unused_after (insn, reg))
+ {
+ return reg;
+ }
}
- else if (GET_CODE (dest) == MEM)
+
+ return NULL_RTX;
+}
+
+
+/* Helper function for the next function in the case where only restricted
+ version of LPM instruction is available. */
+
+static const char*
+avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
+{
+ rtx dest = xop[0];
+ rtx addr = xop[1];
+ int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+ int regno_dest;
+
+ regno_dest = REGNO (dest);
+
+ /* The implicit target register of LPM. */
+ xop[3] = lpm_reg_rtx;
+
+ switch (GET_CODE (addr))
{
- const char *templ;
+ default:
+ gcc_unreachable();
+
+ case REG:
- if (src == const0_rtx)
- operands[1] = zero_reg_rtx;
+ gcc_assert (REG_Z == REGNO (addr));
- templ = out_movqi_mr_r (insn, operands, real_l);
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
- if (!real_l)
- output_asm_insn (templ, operands);
+ case 1:
+ avr_asm_len ("%4lpm", xop, plen, 1);
- operands[1] = src;
- }
+ if (regno_dest != LPM_REGNO)
+ avr_asm_len ("mov %0,%3", xop, plen, 1);
+
+ return "";
+
+ case 2:
+ if (REGNO (dest) == REG_Z)
+ return avr_asm_len ("%4lpm" CR_TAB
+ "push %3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "%4lpm" CR_TAB
+ "mov %B0,%3" CR_TAB
+ "pop %A0", xop, plen, 6);
+
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "%4lpm" CR_TAB
+ "mov %B0,%3", xop, plen, 5);
+
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,1", xop, plen, 1);
+
+ break; /* 2 */
+ }
+
+ break; /* REG */
+
+ case POST_INC:
+
+ gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+ && n_bytes <= 4);
+
+ if (regno_dest == LPM_REGNO)
+ avr_asm_len ("%4lpm" CR_TAB
+ "adiw %2,1", xop, plen, 2);
+ else
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
+ if (n_bytes >= 2)
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %B0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
+ if (n_bytes >= 3)
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %C0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
+ if (n_bytes >= 4)
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %D0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
+ break; /* POST_INC */
+
+ } /* switch CODE (addr) */
+
return "";
}
-const char *
-output_movhi (rtx insn, rtx operands[], int *l)
+/* If PLEN == NULL: Ouput instructions to load a value from a memory location
+ OP[1] in AS1 to register OP[0].
+ If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
+ Return "". */
+
+static const char*
+avr_out_lpm (rtx insn, rtx *op, int *plen)
{
- int dummy;
- rtx dest = operands[0];
- rtx src = operands[1];
- int *real_l = l;
+ rtx xop[6];
+ rtx dest = op[0];
+ rtx src = SET_SRC (single_set (insn));
+ rtx addr;
+ int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
+ int regno_dest;
+ int segment;
+ RTX_CODE code;
- if (!l)
- l = &dummy;
+ addr_space_t as;
+
+ if (plen)
+ *plen = 0;
- if (register_operand (dest, HImode))
+ if (MEM_P (dest))
{
- if (register_operand (src, HImode)) /* mov r,r */
- {
- if (test_hard_reg_class (STACK_REG, dest))
- {
- if (AVR_HAVE_8BIT_SP)
- return *l = 1, AS2 (out,__SP_L__,%A1);
- /* Use simple load of stack pointer if no interrupts are
- used. */
- else if (TARGET_NO_INTERRUPTS)
- return *l = 2, (AS2 (out,__SP_H__,%B1) CR_TAB
- AS2 (out,__SP_L__,%A1));
- *l = 5;
- return (AS2 (in,__tmp_reg__,__SREG__) CR_TAB
- "cli" CR_TAB
- AS2 (out,__SP_H__,%B1) CR_TAB
- AS2 (out,__SREG__,__tmp_reg__) CR_TAB
- AS2 (out,__SP_L__,%A1));
- }
- else if (test_hard_reg_class (STACK_REG, src))
- {
- *l = 2;
- return (AS2 (in,%A0,__SP_L__) CR_TAB
- AS2 (in,%B0,__SP_H__));
- }
+ warning (0, "writing to address space %qs not supported",
+ avr_addrspace[MEM_ADDR_SPACE (dest)].name);
+
+ return "";
+ }
- if (AVR_HAVE_MOVW)
- {
- *l = 1;
- return (AS2 (movw,%0,%1));
- }
- else
- {
- *l = 2;
- return (AS2 (mov,%A0,%A1) CR_TAB
- AS2 (mov,%B0,%B1));
- }
- }
- else if (CONSTANT_P (src))
- {
- if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
- {
- *l = 2;
- return (AS2 (ldi,%A0,lo8(%1)) CR_TAB
- AS2 (ldi,%B0,hi8(%1)));
- }
-
- if (GET_CODE (src) == CONST_INT)
- {
- if (src == const0_rtx) /* mov r,L */
- {
- *l = 2;
- return (AS1 (clr,%A0) CR_TAB
- AS1 (clr,%B0));
- }
- else if (src == const1_rtx)
- {
- *l = 3;
- return (AS1 (clr,%A0) CR_TAB
- AS1 (clr,%B0) CR_TAB
- AS1 (inc,%A0));
- }
- else if (src == constm1_rtx)
- {
- /* Immediate constants -1 to any register */
- *l = 3;
- return (AS1 (clr,%0) CR_TAB
- AS1 (dec,%A0) CR_TAB
- AS2 (mov,%B0,%A0));
- }
- else
- {
- int bit_nr = exact_log2 (INTVAL (src));
+ as = MEM_ADDR_SPACE (src);
- if (bit_nr >= 0)
- {
- *l = 4;
- if (!real_l)
- output_asm_insn ((AS1 (clr,%A0) CR_TAB
- AS1 (clr,%B0) CR_TAB
- "set"), operands);
- if (!real_l)
- avr_output_bld (operands, bit_nr);
-
- return "";
- }
- }
+ addr = XEXP (src, 0);
+ code = GET_CODE (addr);
- if ((INTVAL (src) & 0xff) == 0)
- {
- *l = 5;
- return (AS2 (mov,__tmp_reg__,r31) CR_TAB
- AS1 (clr,%A0) CR_TAB
- AS2 (ldi,r31,hi8(%1)) CR_TAB
- AS2 (mov,%B0,r31) CR_TAB
- AS2 (mov,r31,__tmp_reg__));
- }
- else if ((INTVAL (src) & 0xff00) == 0)
- {
- *l = 5;
- return (AS2 (mov,__tmp_reg__,r31) CR_TAB
- AS2 (ldi,r31,lo8(%1)) CR_TAB
- AS2 (mov,%A0,r31) CR_TAB
- AS1 (clr,%B0) CR_TAB
- AS2 (mov,r31,__tmp_reg__));
- }
- }
+ gcc_assert (REG_P (dest));
+
+ if (as == ADDR_SPACE_PGMX)
+ {
+ /* We are called from avr_out_xload because someone wrote
+ __pgmx on a device with just one flash segment. */
+
+ gcc_assert (LO_SUM == code);
+
+ addr = XEXP (addr, 1);
+ }
+ else
+ gcc_assert (REG == code || POST_INC == code);
+
+ xop[0] = dest;
+ xop[1] = addr;
+ xop[2] = lpm_addr_reg_rtx;
+ xop[4] = xstring_empty;
+ xop[5] = tmp_reg_rtx;
+
+ regno_dest = REGNO (dest);
+
+ /* Cut down segment number to a number the device actually supports.
+ We do this late to preserve the address space's name for diagnostics. */
+
+ segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+
+ /* Set RAMPZ as needed. */
+
+ if (segment)
+ {
+ xop[4] = GEN_INT (segment);
+
+ if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx),
+ xop[3])
+ {
+ avr_asm_len ("ldi %3,%4" CR_TAB
+ "out __RAMPZ__,%3", xop, plen, 2);
+ }
+ else if (segment == 1)
+ {
+ avr_asm_len ("clr %5" CR_TAB
+ "inc %5" CR_TAB
+ "out __RAMPZ__,%5", xop, plen, 3);
+ }
+ else
+ {
+ avr_asm_len ("mov %5,%2" CR_TAB
+ "ldi %2,%4" CR_TAB
+ "out __RAMPZ__,%2" CR_TAB
+ "mov %2,%5", xop, plen, 4);
+ }
+
+ xop[4] = xstring_e;
+
+ if (!AVR_HAVE_ELPMX)
+ return avr_out_lpm_no_lpmx (insn, xop, plen);
+ }
+ else if (!AVR_HAVE_LPMX)
+ {
+ return avr_out_lpm_no_lpmx (insn, xop, plen);
+ }
+
+ /* We have [E]LPMX: Output reading from Flash the comfortable way. */
+
+ switch (GET_CODE (addr))
+ {
+ default:
+ gcc_unreachable();
+
+ case REG:
+
+ gcc_assert (REG_Z == REGNO (addr));
+
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
+
+ case 1:
+ return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
+
+ case 2:
+ if (REGNO (dest) == REG_Z)
+ return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+ "%4lpm %B0,%a2" CR_TAB
+ "mov %A0,%5", xop, plen, 3);
+ else
+ {
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2", xop, plen, 2);
+
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,1", xop, plen, 1);
+ }
+
+ break; /* 2 */
+
+ case 3:
+
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2+" CR_TAB
+ "%4lpm %C0,%a2", xop, plen, 3);
+
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,2", xop, plen, 1);
+
+ break; /* 3 */
+
+ case 4:
+
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2+", xop, plen, 2);
+
+ if (REGNO (dest) == REG_Z - 2)
+ return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+ "%4lpm %C0,%a2" CR_TAB
+ "mov %D0,%5", xop, plen, 3);
+ else
+ {
+ avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
+ "%4lpm %D0,%a2", xop, plen, 2);
+
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,3", xop, plen, 1);
+ }
+
+ break; /* 4 */
+ } /* n_bytes */
+
+ break; /* REG */
+
+ case POST_INC:
+
+ gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
+ && n_bytes <= 4);
+
+ avr_asm_len ("%4lpm %A0,%a2+", xop, plen, 1);
+ if (n_bytes >= 2) avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
+ if (n_bytes >= 3) avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
+ if (n_bytes >= 4) avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
+
+ break; /* POST_INC */
+
+ } /* switch CODE (addr) */
+
+ return "";
+}
+
+
+/* Worker function for xload_<mode> and xload_8 insns. */
+
+const char*
+avr_out_xload (rtx insn, rtx *op, int *plen)
+{
+ rtx xop[5];
+ rtx reg = op[0];
+ int n_bytes = GET_MODE_SIZE (GET_MODE (reg));
+ unsigned int regno = REGNO (reg);
+
+ if (avr_current_arch->n_segments == 1)
+ return avr_out_lpm (insn, op, plen);
+
+ xop[0] = reg;
+ xop[1] = op[1];
+ xop[2] = lpm_addr_reg_rtx;
+ xop[3] = lpm_reg_rtx;
+ xop[4] = tmp_reg_rtx;
+
+ avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1);
+
+ if (1 == n_bytes)
+ {
+ if (AVR_HAVE_ELPMX)
+ return avr_asm_len ("elpm %0,%a2", xop, plen, 1);
+ else
+ return avr_asm_len ("elpm" CR_TAB
+ "mov %0,%3", xop, plen, 2);
+ }
+
+ gcc_assert (AVR_HAVE_ELPMX);
+
+ if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx))
+ {
+ /* Insn clobbers the Z-register so we can use post-increment. */
+
+ avr_asm_len ("elpm %A0,%a2+", xop, plen, 1);
+ if (n_bytes >= 2) avr_asm_len ("elpm %B0,%a2+", xop, plen, 1);
+ if (n_bytes >= 3) avr_asm_len ("elpm %C0,%a2+", xop, plen, 1);
+ if (n_bytes >= 4) avr_asm_len ("elpm %D0,%a2+", xop, plen, 1);
+
+ return "";
+ }
+
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
+
+ case 2:
+ gcc_assert (regno == REGNO (lpm_addr_reg_rtx));
+
+ return avr_asm_len ("elpm %4,%a2+" CR_TAB
+ "elpm %B0,%a2" CR_TAB
+ "mov %A0,%4", xop, plen, 3);
+
+ case 3:
+ case 4:
+ gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx));
+
+ avr_asm_len ("elpm %A0,%a2+" CR_TAB
+ "elpm %B0,%a2+", xop, plen, 2);
+
+ if (n_bytes == 3)
+ return avr_asm_len ("elpm %C0,%a2", xop, plen, 1);
+ else
+ return avr_asm_len ("elpm %4,%a2+" CR_TAB
+ "elpm %D0,%a2" CR_TAB
+ "mov %C0,%4", xop, plen, 3);
+ }
+
+ return "";
+}
+
+
+const char *
+output_movqi (rtx insn, rtx operands[], int *l)
+{
+ int dummy;
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ int *real_l = l;
+
+ if (avr_mem_pgm_p (src)
+ || avr_mem_pgm_p (dest))
+ {
+ return avr_out_lpm (insn, operands, real_l);
+ }
+
+ if (!l)
+ l = &dummy;
+
+ *l = 1;
+
+ if (register_operand (dest, QImode))
+ {
+ if (register_operand (src, QImode)) /* mov r,r */
+ {
+ if (test_hard_reg_class (STACK_REG, dest))
+ return AS2 (out,%0,%1);
+ else if (test_hard_reg_class (STACK_REG, src))
+ return AS2 (in,%0,%1);
- /* Last resort, equal to loading from memory. */
- *l = 6;
- return (AS2 (mov,__tmp_reg__,r31) CR_TAB
- AS2 (ldi,r31,lo8(%1)) CR_TAB
- AS2 (mov,%A0,r31) CR_TAB
- AS2 (ldi,r31,hi8(%1)) CR_TAB
- AS2 (mov,%B0,r31) CR_TAB
- AS2 (mov,r31,__tmp_reg__));
+ return AS2 (mov,%0,%1);
}
+ else if (CONSTANT_P (src))
+ {
+ output_reload_in_const (operands, NULL_RTX, real_l, false);
+ return "";
+ }
else if (GET_CODE (src) == MEM)
- return out_movhi_r_mr (insn, operands, real_l); /* mov r,m */
+ return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
}
else if (GET_CODE (dest) == MEM)
{
- const char *templ;
+ rtx xop[2];
- if (src == const0_rtx)
- operands[1] = zero_reg_rtx;
+ xop[0] = dest;
+ xop[1] = src == const0_rtx ? zero_reg_rtx : src;
- templ = out_movhi_mr_r (insn, operands, real_l);
+ return out_movqi_mr_r (insn, xop, real_l);
+ }
+ return "";
+}
- if (!real_l)
- output_asm_insn (templ, operands);
- operands[1] = src;
- return "";
+const char *
+output_movhi (rtx insn, rtx xop[], int *plen)
+{
+ rtx dest = xop[0];
+ rtx src = xop[1];
+
+ gcc_assert (GET_MODE_SIZE (GET_MODE (dest)) == 2);
+
+ if (avr_mem_pgm_p (src)
+ || avr_mem_pgm_p (dest))
+ {
+ return avr_out_lpm (insn, xop, plen);
}
+
+ if (REG_P (dest))
+ {
+ if (REG_P (src)) /* mov r,r */
+ {
+ if (test_hard_reg_class (STACK_REG, dest))
+ {
+ if (AVR_HAVE_8BIT_SP)
+ return avr_asm_len ("out __SP_L__,%A1", xop, plen, -1);
+
+ /* Use simple load of SP if no interrupts are used. */
+
+ return TARGET_NO_INTERRUPTS
+ ? avr_asm_len ("out __SP_H__,%B1" CR_TAB
+ "out __SP_L__,%A1", xop, plen, -2)
+
+ : avr_asm_len ("in __tmp_reg__,__SREG__" CR_TAB
+ "cli" CR_TAB
+ "out __SP_H__,%B1" CR_TAB
+ "out __SREG__,__tmp_reg__" CR_TAB
+ "out __SP_L__,%A1", xop, plen, -5);
+ }
+ else if (test_hard_reg_class (STACK_REG, src))
+ {
+ return AVR_HAVE_8BIT_SP
+ ? avr_asm_len ("in %A0,__SP_L__" CR_TAB
+ "clr %B0", xop, plen, -2)
+
+ : avr_asm_len ("in %A0,__SP_L__" CR_TAB
+ "in %B0,__SP_H__", xop, plen, -2);
+ }
+
+ return AVR_HAVE_MOVW
+ ? avr_asm_len ("movw %0,%1", xop, plen, -1)
+
+ : avr_asm_len ("mov %A0,%A1" CR_TAB
+ "mov %B0,%B1", xop, plen, -2);
+ } /* REG_P (src) */
+ else if (CONSTANT_P (src))
+ {
+ return output_reload_inhi (xop, NULL, plen);
+ }
+ else if (MEM_P (src))
+ {
+ return out_movhi_r_mr (insn, xop, plen); /* mov r,m */
+ }
+ }
+ else if (MEM_P (dest))
+ {
+ rtx xop[2];
+
+ xop[0] = dest;
+ xop[1] = src == const0_rtx ? zero_reg_rtx : src;
+
+ return out_movhi_mr_r (insn, xop, plen);
+ }
+
fatal_insn ("invalid insn:", insn);
+
return "";
}
-const char *
-out_movqi_r_mr (rtx insn, rtx op[], int *l)
+static const char*
+out_movqi_r_mr (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
rtx x = XEXP (src, 0);
- int dummy;
-
- if (!l)
- l = &dummy;
if (CONSTANT_ADDRESS_P (x))
{
- if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
- {
- *l = 1;
- return AS2 (in,%0,__SREG__);
- }
- if (optimize > 0 && io_address_operand (x, QImode))
- {
- *l = 1;
- return AS2 (in,%0,%m1-0x20);
- }
- *l = 2;
- return AS2 (lds,%0,%m1);
+ 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);
}
- /* memory access by reg+disp */
else if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x,0))
- && GET_CODE (XEXP (x,1)) == CONST_INT)
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
{
- if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63)
- {
- int disp = INTVAL (XEXP (x,1));
- if (REGNO (XEXP (x,0)) != REG_Y)
- fatal_insn ("incorrect insn:",insn);
+ /* memory access by reg+disp */
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
- return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB
- AS2 (ldd,%0,Y+63) CR_TAB
- AS2 (sbiw,r28,%o1-63));
+ int disp = INTVAL (XEXP (x, 1));
+
+ if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63)
+ {
+ if (REGNO (XEXP (x, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+ return avr_asm_len ("adiw r28,%o1-63" CR_TAB
+ "ldd %0,Y+63" CR_TAB
+ "sbiw r28,%o1-63", op, plen, -3);
+
+ return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+ "sbci r29,hi8(-%o1)" CR_TAB
+ "ld %0,Y" CR_TAB
+ "subi r28,lo8(%o1)" CR_TAB
+ "sbci r29,hi8(%o1)", op, plen, -5);
+ }
+ else if (REGNO (XEXP (x, 0)) == REG_X)
+ {
+ /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
+ it but I have this situation with extremal optimizing options. */
+
+ avr_asm_len ("adiw r26,%o1" CR_TAB
+ "ld %0,X", op, plen, -2);
+
+ if (!reg_overlap_mentioned_p (dest, XEXP (x,0))
+ && !reg_unused_after (insn, XEXP (x,0)))
+ {
+ avr_asm_len ("sbiw r26,%o1", op, plen, 1);
+ }
- return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
- AS2 (sbci,r29,hi8(-%o1)) CR_TAB
- AS2 (ld,%0,Y) CR_TAB
- AS2 (subi,r28,lo8(%o1)) CR_TAB
- AS2 (sbci,r29,hi8(%o1)));
- }
- else if (REGNO (XEXP (x,0)) == REG_X)
- {
- /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
- it but I have this situation with extremal optimizing options. */
- if (reg_overlap_mentioned_p (dest, XEXP (x,0))
- || reg_unused_after (insn, XEXP (x,0)))
- return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,%0,X));
-
- return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,%0,X) CR_TAB
- AS2 (sbiw,r26,%o1));
- }
- *l = 1;
- return AS2 (ldd,%0,%1);
+ return "";
+ }
+
+ return avr_asm_len ("ldd %0,%1", op, plen, -1);
}
- *l = 1;
- return AS2 (ld,%0,%1);
+
+ return avr_asm_len ("ld %0,%1", op, plen, -1);
}
-const char *
-out_movhi_r_mr (rtx insn, rtx op[], int *l)
+static const char*
+out_movhi_r_mr (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
/* "volatile" forces reading low byte first, even if less efficient,
for correct operation with 16-bit I/O registers. */
int mem_volatile_p = MEM_VOLATILE_P (src);
- int tmp;
-
- if (!l)
- l = &tmp;
if (reg_base > 0)
{
if (reg_dest == reg_base) /* R = (R) */
- {
- *l = 3;
- return (AS2 (ld,__tmp_reg__,%1+) CR_TAB
- AS2 (ld,%B0,%1) CR_TAB
- AS2 (mov,%A0,__tmp_reg__));
- }
- else if (reg_base == REG_X) /* (R26) */
- {
- if (reg_unused_after (insn, base))
- {
- *l = 2;
- return (AS2 (ld,%A0,X+) CR_TAB
- AS2 (ld,%B0,X));
- }
- *l = 3;
- return (AS2 (ld,%A0,X+) CR_TAB
- AS2 (ld,%B0,X) CR_TAB
- AS2 (sbiw,r26,1));
- }
- else /* (R) */
- {
- *l = 2;
- return (AS2 (ld,%A0,%1) CR_TAB
- AS2 (ldd,%B0,%1+1));
- }
+ return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB
+ "ld %B0,%1" CR_TAB
+ "mov %A0,__tmp_reg__", op, plen, -3);
+
+ if (reg_base != REG_X)
+ return avr_asm_len ("ld %A0,%1" CR_TAB
+ "ldd %B0,%1+1", op, plen, -2);
+
+ avr_asm_len ("ld %A0,X+" CR_TAB
+ "ld %B0,X", op, plen, -2);
+
+ if (!reg_unused_after (insn, base))
+ avr_asm_len ("sbiw r26,1", op, plen, 1);
+
+ return "";
}
else if (GET_CODE (base) == PLUS) /* (R + i) */
{
int reg_base = true_regnum (XEXP (base, 0));
if (disp > MAX_LD_OFFSET (GET_MODE (src)))
- {
- if (REGNO (XEXP (base, 0)) != REG_Y)
- fatal_insn ("incorrect insn:",insn);
-
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
- return *l = 4, (AS2 (adiw,r28,%o1-62) CR_TAB
- AS2 (ldd,%A0,Y+62) CR_TAB
- AS2 (ldd,%B0,Y+63) CR_TAB
- AS2 (sbiw,r28,%o1-62));
-
- return *l = 6, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
- AS2 (sbci,r29,hi8(-%o1)) CR_TAB
- AS2 (ld,%A0,Y) CR_TAB
- AS2 (ldd,%B0,Y+1) CR_TAB
- AS2 (subi,r28,lo8(%o1)) CR_TAB
- AS2 (sbci,r29,hi8(%o1)));
- }
- if (reg_base == REG_X)
- {
- /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude
- it but I have this situation with extremal
- optimization options. */
-
- *l = 4;
- if (reg_base == reg_dest)
- return (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,__tmp_reg__,X+) CR_TAB
- AS2 (ld,%B0,X) CR_TAB
- AS2 (mov,%A0,__tmp_reg__));
+ {
+ if (REGNO (XEXP (base, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ return disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))
+ ? avr_asm_len ("adiw r28,%o1-62" CR_TAB
+ "ldd %A0,Y+62" CR_TAB
+ "ldd %B0,Y+63" CR_TAB
+ "sbiw r28,%o1-62", op, plen, -4)
+
+ : 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
+ "subi r28,lo8(%o1)" CR_TAB
+ "sbci r29,hi8(%o1)", op, plen, -6);
+ }
- return (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,%A0,X+) CR_TAB
- AS2 (ld,%B0,X) CR_TAB
- AS2 (sbiw,r26,%o1+1));
- }
+ /* This is a paranoid case. LEGITIMIZE_RELOAD_ADDRESS must exclude
+ it but I have this situation with extremal
+ optimization options. */
- if (reg_base == reg_dest)
- {
- *l = 3;
- return (AS2 (ldd,__tmp_reg__,%A1) CR_TAB
- AS2 (ldd,%B0,%B1) CR_TAB
- AS2 (mov,%A0,__tmp_reg__));
- }
-
- *l = 2;
- return (AS2 (ldd,%A0,%A1) CR_TAB
- AS2 (ldd,%B0,%B1));
+ if (reg_base == REG_X)
+ return reg_base == reg_dest
+ ? avr_asm_len ("adiw r26,%o1" CR_TAB
+ "ld __tmp_reg__,X+" CR_TAB
+ "ld %B0,X" CR_TAB
+ "mov %A0,__tmp_reg__", op, plen, -4)
+
+ : avr_asm_len ("adiw r26,%o1" CR_TAB
+ "ld %A0,X+" CR_TAB
+ "ld %B0,X" CR_TAB
+ "sbiw r26,%o1+1", op, plen, -4);
+
+ return reg_base == reg_dest
+ ? avr_asm_len ("ldd __tmp_reg__,%A1" CR_TAB
+ "ldd %B0,%B1" CR_TAB
+ "mov %A0,__tmp_reg__", op, plen, -3)
+
+ : avr_asm_len ("ldd %A0,%A1" CR_TAB
+ "ldd %B0,%B1", op, plen, -2);
}
else if (GET_CODE (base) == PRE_DEC) /* (--R) */
{
if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
- fatal_insn ("incorrect insn:", insn);
+ fatal_insn ("incorrect insn:", insn);
- if (mem_volatile_p)
- {
- if (REGNO (XEXP (base, 0)) == REG_X)
- {
- *l = 4;
- return (AS2 (sbiw,r26,2) CR_TAB
- AS2 (ld,%A0,X+) CR_TAB
- AS2 (ld,%B0,X) CR_TAB
- AS2 (sbiw,r26,1));
- }
- else
- {
- *l = 3;
- return (AS2 (sbiw,%r1,2) CR_TAB
- AS2 (ld,%A0,%p1) CR_TAB
- AS2 (ldd,%B0,%p1+1));
- }
- }
-
- *l = 2;
- return (AS2 (ld,%B0,%1) CR_TAB
- AS2 (ld,%A0,%1));
+ if (!mem_volatile_p)
+ return avr_asm_len ("ld %B0,%1" CR_TAB
+ "ld %A0,%1", op, plen, -2);
+
+ return REGNO (XEXP (base, 0)) == REG_X
+ ? avr_asm_len ("sbiw r26,2" CR_TAB
+ "ld %A0,X+" CR_TAB
+ "ld %B0,X" CR_TAB
+ "sbiw r26,1", op, plen, -4)
+
+ : avr_asm_len ("sbiw %r1,2" CR_TAB
+ "ld %A0,%p1" CR_TAB
+ "ldd %B0,%p1+1", op, plen, -3);
}
else if (GET_CODE (base) == POST_INC) /* (R++) */
{
if (reg_overlap_mentioned_p (dest, XEXP (base, 0)))
- fatal_insn ("incorrect insn:", insn);
+ fatal_insn ("incorrect insn:", insn);
- *l = 2;
- return (AS2 (ld,%A0,%1) CR_TAB
- AS2 (ld,%B0,%1));
+ return avr_asm_len ("ld %A0,%1" CR_TAB
+ "ld %B0,%1", op, plen, -2);
}
else if (CONSTANT_ADDRESS_P (base))
{
- if (optimize > 0 && io_address_operand (base, HImode))
- {
- *l = 2;
- return (AS2 (in,%A0,%m1-0x20) CR_TAB
- AS2 (in,%B0,%m1+1-0x20));
- }
- *l = 4;
- return (AS2 (lds,%A0,%m1) CR_TAB
- AS2 (lds,%B0,%m1+1));
+ 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);
}
fatal_insn ("unknown move insn:",insn);
return "";
}
-const char *
+static const char*
out_movsi_r_mr (rtx insn, rtx op[], int *l)
{
rtx dest = op[0];
return "";
}
-const char *
+static const char*
out_movsi_mr_r (rtx insn, rtx op[], int *l)
{
rtx dest = op[0];
}
const char *
-output_movsisf (rtx insn, rtx operands[], rtx clobber_reg, int *l)
+output_movsisf (rtx insn, rtx operands[], int *l)
{
int dummy;
rtx dest = operands[0];
rtx src = operands[1];
int *real_l = l;
+ if (avr_mem_pgm_p (src)
+ || avr_mem_pgm_p (dest))
+ {
+ return avr_out_lpm (insn, operands, real_l);
+ }
+
if (!l)
l = &dummy;
AS2 (mov,%D0,%D1));
}
}
- else if (CONST_INT_P (src)
- || CONST_DOUBLE_P (src))
- {
- return output_reload_insisf (insn, operands, clobber_reg, real_l);
- }
else if (CONSTANT_P (src))
{
- if (test_hard_reg_class (LD_REGS, dest)) /* ldi d,i */
- {
- *l = 4;
- return (AS2 (ldi,%A0,lo8(%1)) CR_TAB
- AS2 (ldi,%B0,hi8(%1)) CR_TAB
- AS2 (ldi,%C0,hlo8(%1)) CR_TAB
- AS2 (ldi,%D0,hhi8(%1)));
- }
- /* Last resort, better than loading from memory. */
- *l = 10;
- return (AS2 (mov,__tmp_reg__,r31) CR_TAB
- AS2 (ldi,r31,lo8(%1)) CR_TAB
- AS2 (mov,%A0,r31) CR_TAB
- AS2 (ldi,r31,hi8(%1)) CR_TAB
- AS2 (mov,%B0,r31) CR_TAB
- AS2 (ldi,r31,hlo8(%1)) CR_TAB
- AS2 (mov,%C0,r31) CR_TAB
- AS2 (ldi,r31,hhi8(%1)) CR_TAB
- AS2 (mov,%D0,r31) CR_TAB
- AS2 (mov,r31,__tmp_reg__));
- }
+ return output_reload_insisf (operands, NULL_RTX, real_l);
+ }
else if (GET_CODE (src) == MEM)
return out_movsi_r_mr (insn, operands, real_l); /* mov r,m */
}
- else if (GET_CODE (dest) == MEM)
+ else if (GET_CODE (dest) == MEM)
+ {
+ const char *templ;
+
+ if (src == CONST0_RTX (GET_MODE (dest)))
+ operands[1] = zero_reg_rtx;
+
+ templ = out_movsi_mr_r (insn, operands, real_l);
+
+ if (!real_l)
+ output_asm_insn (templ, operands);
+
+ operands[1] = src;
+ return "";
+ }
+ fatal_insn ("invalid insn:", insn);
+ return "";
+}
+
+
+/* Handle loads of 24-bit types from memory to register. */
+
+static const char*
+avr_out_load_psi (rtx 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 > 0)
+ {
+ if (reg_base == REG_X) /* (R26) */
+ {
+ if (reg_dest == REG_X)
+ /* "ld r26,-X" is undefined */
+ return avr_asm_len ("adiw r26,2" CR_TAB
+ "ld r28,X" CR_TAB
+ "ld __tmp_reg__,-X" CR_TAB
+ "sbiw r26,1" CR_TAB
+ "ld r26,X" CR_TAB
+ "mov r27,__tmp_reg__", op, plen, -6);
+ else
+ {
+ avr_asm_len ("ld %A0,X+" CR_TAB
+ "ld %B0,X+" CR_TAB
+ "ld %C0,X", op, plen, -3);
+
+ if (reg_dest != REG_X - 2
+ && !reg_unused_after (insn, base))
+ {
+ avr_asm_len ("sbiw r26,2", op, plen, 1);
+ }
+
+ return "";
+ }
+ }
+ else /* reg_base != REG_X */
+ {
+ if (reg_dest == reg_base)
+ return avr_asm_len ("ldd %C0,%1+2" CR_TAB
+ "ldd __tmp_reg__,%1+1" CR_TAB
+ "ld %A0,%1" CR_TAB
+ "mov %B0,__tmp_reg__", op, plen, -4);
+ else
+ return avr_asm_len ("ld %A0,%1" CR_TAB
+ "ldd %B0,%1+1" CR_TAB
+ "ldd %C0,%1+2", op, plen, -3);
+ }
+ }
+ else if (GET_CODE (base) == PLUS) /* (R + i) */
+ {
+ int disp = INTVAL (XEXP (base, 1));
+
+ if (disp > MAX_LD_OFFSET (GET_MODE (src)))
+ {
+ if (REGNO (XEXP (base, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+ return avr_asm_len ("adiw r28,%o1-61" CR_TAB
+ "ldd %A0,Y+61" CR_TAB
+ "ldd %B0,Y+62" CR_TAB
+ "ldd %C0,Y+63" CR_TAB
+ "sbiw r28,%o1-61", op, plen, -5);
+
+ return 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
+ "ldd %C0,Y+2" CR_TAB
+ "subi r28,lo8(%o1)" CR_TAB
+ "sbci r29,hi8(%o1)", op, plen, -7);
+ }
+
+ reg_base = true_regnum (XEXP (base, 0));
+ if (reg_base == REG_X)
+ {
+ /* R = (X + d) */
+ if (reg_dest == REG_X)
+ {
+ /* "ld r26,-X" is undefined */
+ return avr_asm_len ("adiw r26,%o1+2" CR_TAB
+ "ld r28,X" CR_TAB
+ "ld __tmp_reg__,-X" CR_TAB
+ "sbiw r26,1" CR_TAB
+ "ld r26,X" CR_TAB
+ "mov r27,__tmp_reg__", op, plen, -6);
+ }
+
+ avr_asm_len ("adiw r26,%o1" CR_TAB
+ "ld r24,X+" CR_TAB
+ "ld r25,X+" CR_TAB
+ "ld r26,X", op, plen, -4);
+
+ if (reg_dest != REG_X - 2)
+ avr_asm_len ("sbiw r26,%o1+2", op, plen, 1);
+
+ return "";
+ }
+
+ if (reg_dest == reg_base)
+ return avr_asm_len ("ldd %C0,%C1" CR_TAB
+ "ldd __tmp_reg__,%B1" CR_TAB
+ "ldd %A0,%A1" CR_TAB
+ "mov %B0,__tmp_reg__", op, plen, -4);
+
+ return avr_asm_len ("ldd %A0,%A1" CR_TAB
+ "ldd %B0,%B1" CR_TAB
+ "ldd %C0,%C1", op, plen, -3);
+ }
+ else if (GET_CODE (base) == PRE_DEC) /* (--R) */
+ return avr_asm_len ("ld %C0,%1" CR_TAB
+ "ld %B0,%1" CR_TAB
+ "ld %A0,%1", op, plen, -3);
+ else if (GET_CODE (base) == POST_INC) /* (R++) */
+ return avr_asm_len ("ld %A0,%1" CR_TAB
+ "ld %B0,%1" CR_TAB
+ "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);
+
+ fatal_insn ("unknown move insn:",insn);
+ return "";
+}
+
+/* Handle store of 24-bit type from register or zero to memory. */
+
+static const char*
+avr_out_store_psi (rtx insn, rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+ rtx base = XEXP (dest, 0);
+ 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);
+
+ if (reg_base > 0) /* (r) */
+ {
+ if (reg_base == REG_X) /* (R26) */
+ {
+ gcc_assert (!reg_overlap_mentioned_p (base, src));
+
+ 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 ("sbiw r26,2", op, plen, 1);
+
+ return "";
+ }
+ else
+ return avr_asm_len ("st %0,%A1" CR_TAB
+ "std %0+1,%B1" CR_TAB
+ "std %0+2,%C1", op, plen, -3);
+ }
+ else if (GET_CODE (base) == PLUS) /* (R + i) */
+ {
+ int disp = INTVAL (XEXP (base, 1));
+ reg_base = REGNO (XEXP (base, 0));
+
+ if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
+ {
+ if (reg_base != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+ return avr_asm_len ("adiw r28,%o0-61" CR_TAB
+ "std Y+61,%A1" CR_TAB
+ "std Y+62,%B1" CR_TAB
+ "std Y+63,%C1" CR_TAB
+ "sbiw r28,%o0-60", op, plen, -5);
+
+ return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+ "sbci r29,hi8(-%o0)" CR_TAB
+ "st Y,%A1" CR_TAB
+ "std Y+1,%B1" CR_TAB
+ "std Y+2,%C1" CR_TAB
+ "subi r28,lo8(%o0)" CR_TAB
+ "sbci r29,hi8(%o0)", op, plen, -7);
+ }
+ if (reg_base == REG_X)
+ {
+ /* (X + d) = R */
+ gcc_assert (!reg_overlap_mentioned_p (XEXP (base, 0), src));
+
+ avr_asm_len ("adiw r26,%o0" CR_TAB
+ "st X+,%A1" CR_TAB
+ "st X+,%B1" CR_TAB
+ "st X,%C1", op, plen, -4);
+
+ if (!reg_unused_after (insn, XEXP (base, 0)))
+ avr_asm_len ("sbiw r26,%o0+2", op, plen, 1);
+
+ return "";
+ }
+
+ return avr_asm_len ("std %A0,%A1" CR_TAB
+ "std %B0,%B1" CR_TAB
+ "std %C0,%C1", op, plen, -3);
+ }
+ else if (GET_CODE (base) == PRE_DEC) /* (--R) */
+ return avr_asm_len ("st %0,%C1" CR_TAB
+ "st %0,%B1" CR_TAB
+ "st %0,%A1", op, plen, -3);
+ else if (GET_CODE (base) == POST_INC) /* (R++) */
+ return avr_asm_len ("st %0,%A1" CR_TAB
+ "st %0,%B1" CR_TAB
+ "st %0,%C1", op, plen, -3);
+
+ fatal_insn ("unknown move insn:",insn);
+ return "";
+}
+
+
+/* Move around 24-bit stuff. */
+
+const char *
+avr_out_movpsi (rtx insn, rtx *op, int *plen)
+{
+ rtx dest = op[0];
+ rtx src = op[1];
+
+ if (avr_mem_pgm_p (src)
+ || avr_mem_pgm_p (dest))
+ {
+ return avr_out_lpm (insn, op, plen);
+ }
+
+ if (register_operand (dest, VOIDmode))
+ {
+ if (register_operand (src, VOIDmode)) /* mov r,r */
+ {
+ if (true_regnum (dest) > true_regnum (src))
+ {
+ avr_asm_len ("mov %C0,%C1", op, plen, -1);
+
+ if (AVR_HAVE_MOVW)
+ return avr_asm_len ("movw %A0,%A1", op, plen, 1);
+ else
+ return avr_asm_len ("mov %B0,%B1" CR_TAB
+ "mov %A0,%A1", op, plen, 2);
+ }
+ else
+ {
+ if (AVR_HAVE_MOVW)
+ avr_asm_len ("movw %A0,%A1", op, plen, -1);
+ else
+ avr_asm_len ("mov %A0,%A1" CR_TAB
+ "mov %B0,%B1", op, plen, -2);
+
+ return avr_asm_len ("mov %C0,%C1", op, plen, 1);
+ }
+ }
+ else if (CONSTANT_P (src))
+ {
+ return avr_out_reload_inpsi (op, NULL_RTX, plen);
+ }
+ else if (MEM_P (src))
+ return avr_out_load_psi (insn, op, plen); /* mov r,m */
+ }
+ else if (MEM_P (dest))
{
- const char *templ;
-
- if (src == CONST0_RTX (GET_MODE (dest)))
- operands[1] = zero_reg_rtx;
-
- templ = out_movsi_mr_r (insn, operands, real_l);
-
- if (!real_l)
- output_asm_insn (templ, operands);
+ rtx xop[2];
- operands[1] = src;
- return "";
+ xop[0] = dest;
+ xop[1] = src == CONST0_RTX (GET_MODE (dest)) ? zero_reg_rtx : src;
+
+ return avr_out_store_psi (insn, xop, plen);
}
+
fatal_insn ("invalid insn:", insn);
return "";
}
-const char *
-out_movqi_mr_r (rtx insn, rtx op[], int *l)
+
+static const char*
+out_movqi_mr_r (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
rtx x = XEXP (dest, 0);
- int dummy;
-
- if (!l)
- l = &dummy;
if (CONSTANT_ADDRESS_P (x))
{
- if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
- {
- *l = 1;
- return AS2 (out,__SREG__,%1);
- }
- if (optimize > 0 && io_address_operand (x, QImode))
- {
- *l = 1;
- return AS2 (out,%m0-0x20,%1);
- }
- *l = 2;
- return AS2 (sts,%m0,%1);
+ 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);
}
- /* memory access by reg+disp */
- else if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x,0))
- && GET_CODE (XEXP (x,1)) == CONST_INT)
+ else if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
{
- if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63)
- {
- int disp = INTVAL (XEXP (x,1));
- if (REGNO (XEXP (x,0)) != REG_Y)
- fatal_insn ("incorrect insn:",insn);
+ /* memory access by reg+disp */
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
- return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB
- AS2 (std,Y+63,%1) CR_TAB
- AS2 (sbiw,r28,%o0-63));
+ int disp = INTVAL (XEXP (x, 1));
- return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
- AS2 (sbci,r29,hi8(-%o0)) CR_TAB
- AS2 (st,Y,%1) CR_TAB
- AS2 (subi,r28,lo8(%o0)) CR_TAB
- AS2 (sbci,r29,hi8(%o0)));
- }
+ if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63)
+ {
+ if (REGNO (XEXP (x, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+ return avr_asm_len ("adiw r28,%o0-63" CR_TAB
+ "std Y+63,%1" CR_TAB
+ "sbiw r28,%o0-63", op, plen, -3);
+
+ return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+ "sbci r29,hi8(-%o0)" CR_TAB
+ "st Y,%1" CR_TAB
+ "subi r28,lo8(%o0)" CR_TAB
+ "sbci r29,hi8(%o0)", op, plen, -5);
+ }
else if (REGNO (XEXP (x,0)) == REG_X)
- {
- if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
- {
- if (reg_unused_after (insn, XEXP (x,0)))
- return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB
- AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,__tmp_reg__));
-
- return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB
- AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,__tmp_reg__) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- else
- {
- if (reg_unused_after (insn, XEXP (x,0)))
- return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,%1));
+ {
+ if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
+ {
+ avr_asm_len ("mov __tmp_reg__,%1" CR_TAB
+ "adiw r26,%o0" CR_TAB
+ "st X,__tmp_reg__", op, plen, -3);
+ }
+ else
+ {
+ avr_asm_len ("adiw r26,%o0" CR_TAB
+ "st X,%1", op, plen, -2);
+ }
+
+ if (!reg_unused_after (insn, XEXP (x,0)))
+ avr_asm_len ("sbiw r26,%o0", op, plen, 1);
- return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,%1) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- }
- *l = 1;
- return AS2 (std,%0,%1);
+ return "";
+ }
+
+ return avr_asm_len ("std %0,%1", op, plen, 1);
}
- *l = 1;
- return AS2 (st,%0,%1);
+
+ return avr_asm_len ("st %0,%1", op, plen, 1);
}
-const char *
-out_movhi_mr_r (rtx insn, rtx op[], int *l)
+static const char*
+out_movhi_mr_r (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
/* "volatile" forces writing high byte first, even if less efficient,
for correct operation with 16-bit I/O registers. */
int mem_volatile_p = MEM_VOLATILE_P (dest);
- int tmp;
- if (!l)
- l = &tmp;
if (CONSTANT_ADDRESS_P (base))
- {
- if (optimize > 0 && io_address_operand (base, HImode))
- {
- *l = 2;
- return (AS2 (out,%m0+1-0x20,%B1) CR_TAB
- AS2 (out,%m0-0x20,%A1));
- }
- return *l = 4, (AS2 (sts,%m0+1,%B1) CR_TAB
- AS2 (sts,%m0,%A1));
- }
+ 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);
+
if (reg_base > 0)
{
- if (reg_base == REG_X)
- {
- if (reg_src == REG_X)
- {
- /* "st X+,r26" and "st -X,r26" are undefined. */
- if (!mem_volatile_p && reg_unused_after (insn, src))
- return *l=4, (AS2 (mov,__tmp_reg__,r27) CR_TAB
- AS2 (st,X,r26) CR_TAB
- AS2 (adiw,r26,1) CR_TAB
- AS2 (st,X,__tmp_reg__));
- else
- return *l=5, (AS2 (mov,__tmp_reg__,r27) CR_TAB
- AS2 (adiw,r26,1) CR_TAB
- AS2 (st,X,__tmp_reg__) CR_TAB
- AS2 (sbiw,r26,1) CR_TAB
- AS2 (st,X,r26));
- }
- else
- {
- if (!mem_volatile_p && reg_unused_after (insn, base))
- return *l=2, (AS2 (st,X+,%A1) CR_TAB
- AS2 (st,X,%B1));
- else
- return *l=3, (AS2 (adiw,r26,1) CR_TAB
- AS2 (st,X,%B1) CR_TAB
- AS2 (st,-X,%A1));
- }
- }
- else
- return *l=2, (AS2 (std,%0+1,%B1) CR_TAB
- AS2 (st,%0,%A1));
+ if (reg_base != REG_X)
+ return avr_asm_len ("std %0+1,%B1" CR_TAB
+ "st %0,%A1", op, plen, -2);
+
+ if (reg_src == REG_X)
+ /* "st X+,r26" and "st -X,r26" are undefined. */
+ return !mem_volatile_p && reg_unused_after (insn, src)
+ ? avr_asm_len ("mov __tmp_reg__,r27" CR_TAB
+ "st X,r26" CR_TAB
+ "adiw r26,1" CR_TAB
+ "st X,__tmp_reg__", op, plen, -4)
+
+ : avr_asm_len ("mov __tmp_reg__,r27" CR_TAB
+ "adiw r26,1" CR_TAB
+ "st X,__tmp_reg__" CR_TAB
+ "sbiw r26,1" CR_TAB
+ "st X,r26", op, plen, -5);
+
+ return !mem_volatile_p && reg_unused_after (insn, base)
+ ? avr_asm_len ("st X+,%A1" CR_TAB
+ "st X,%B1", op, plen, -2)
+ : avr_asm_len ("adiw r26,1" CR_TAB
+ "st X,%B1" CR_TAB
+ "st -X,%A1", op, plen, -3);
}
else if (GET_CODE (base) == PLUS)
{
int disp = INTVAL (XEXP (base, 1));
reg_base = REGNO (XEXP (base, 0));
if (disp > MAX_LD_OFFSET (GET_MODE (dest)))
- {
- if (reg_base != REG_Y)
- fatal_insn ("incorrect insn:",insn);
-
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
- return *l = 4, (AS2 (adiw,r28,%o0-62) CR_TAB
- AS2 (std,Y+63,%B1) CR_TAB
- AS2 (std,Y+62,%A1) CR_TAB
- AS2 (sbiw,r28,%o0-62));
-
- return *l = 6, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
- AS2 (sbci,r29,hi8(-%o0)) CR_TAB
- AS2 (std,Y+1,%B1) CR_TAB
- AS2 (st,Y,%A1) CR_TAB
- AS2 (subi,r28,lo8(%o0)) CR_TAB
- AS2 (sbci,r29,hi8(%o0)));
- }
- if (reg_base == REG_X)
- {
- /* (X + d) = R */
- if (reg_src == REG_X)
- {
- *l = 7;
- return (AS2 (mov,__tmp_reg__,r26) CR_TAB
- AS2 (mov,__zero_reg__,r27) CR_TAB
- AS2 (adiw,r26,%o0+1) CR_TAB
- AS2 (st,X,__zero_reg__) CR_TAB
- AS2 (st,-X,__tmp_reg__) CR_TAB
- AS1 (clr,__zero_reg__) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- *l = 4;
- return (AS2 (adiw,r26,%o0+1) CR_TAB
- AS2 (st,X,%B1) CR_TAB
- AS2 (st,-X,%A1) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- return *l=2, (AS2 (std,%B0,%B1) CR_TAB
- AS2 (std,%A0,%A1));
+ {
+ if (reg_base != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ return disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))
+ ? avr_asm_len ("adiw r28,%o0-62" CR_TAB
+ "std Y+63,%B1" CR_TAB
+ "std Y+62,%A1" CR_TAB
+ "sbiw r28,%o0-62", op, plen, -4)
+
+ : avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+ "sbci r29,hi8(-%o0)" CR_TAB
+ "std Y+1,%B1" CR_TAB
+ "st Y,%A1" CR_TAB
+ "subi r28,lo8(%o0)" CR_TAB
+ "sbci r29,hi8(%o0)", op, plen, -6);
+ }
+
+ if (reg_base != REG_X)
+ return avr_asm_len ("std %B0,%B1" CR_TAB
+ "std %A0,%A1", op, plen, -2);
+ /* (X + d) = R */
+ return reg_src == REG_X
+ ? avr_asm_len ("mov __tmp_reg__,r26" CR_TAB
+ "mov __zero_reg__,r27" CR_TAB
+ "adiw r26,%o0+1" CR_TAB
+ "st X,__zero_reg__" CR_TAB
+ "st -X,__tmp_reg__" CR_TAB
+ "clr __zero_reg__" CR_TAB
+ "sbiw r26,%o0", op, plen, -7)
+
+ : avr_asm_len ("adiw r26,%o0+1" CR_TAB
+ "st X,%B1" CR_TAB
+ "st -X,%A1" CR_TAB
+ "sbiw r26,%o0", op, plen, -4);
}
else if (GET_CODE (base) == PRE_DEC) /* (--R) */
- return *l=2, (AS2 (st,%0,%B1) CR_TAB
- AS2 (st,%0,%A1));
+ {
+ return avr_asm_len ("st %0,%B1" CR_TAB
+ "st %0,%A1", op, plen, -2);
+ }
else if (GET_CODE (base) == POST_INC) /* (R++) */
{
- if (mem_volatile_p)
- {
- if (REGNO (XEXP (base, 0)) == REG_X)
- {
- *l = 4;
- return (AS2 (adiw,r26,1) CR_TAB
- AS2 (st,X,%B1) CR_TAB
- AS2 (st,-X,%A1) CR_TAB
- AS2 (adiw,r26,2));
- }
- else
- {
- *l = 3;
- return (AS2 (std,%p0+1,%B1) CR_TAB
- AS2 (st,%p0,%A1) CR_TAB
- AS2 (adiw,%r0,2));
- }
- }
-
- *l = 2;
- return (AS2 (st,%0,%A1) CR_TAB
- AS2 (st,%0,%B1));
+ if (!mem_volatile_p)
+ return avr_asm_len ("st %0,%A1" CR_TAB
+ "st %0,%B1", op, plen, -2);
+
+ return REGNO (XEXP (base, 0)) == REG_X
+ ? avr_asm_len ("adiw r26,1" CR_TAB
+ "st X,%B1" CR_TAB
+ "st -X,%A1" CR_TAB
+ "adiw r26,2", op, plen, -4)
+
+ : avr_asm_len ("std %p0+1,%B1" CR_TAB
+ "st %p0,%A1" CR_TAB
+ "adiw %r0,2", op, plen, -3);
}
fatal_insn ("unknown move insn:",insn);
return "";
/* Return 1 if frame pointer for current function required. */
-bool
+static bool
avr_frame_pointer_required_p (void)
{
return (cfun->calls_alloca
- || crtl->args.info.nregs == 0
- || get_frame_size () > 0);
+ || cfun->calls_setjmp
+ || cfun->has_nonlocal_label
+ || crtl->args.info.nregs == 0
+ || get_frame_size () > 0);
}
/* Returns the condition of compare insn INSN, or UNKNOWN. */
compare_condition (rtx insn)
{
rtx next = next_real_insn (insn);
- RTX_CODE cond = UNKNOWN;
- if (next && GET_CODE (next) == JUMP_INSN)
+
+ if (next && JUMP_P (next))
{
rtx pat = PATTERN (next);
rtx src = SET_SRC (pat);
- rtx t = XEXP (src, 0);
- cond = GET_CODE (t);
+
+ if (IF_THEN_ELSE == GET_CODE (src))
+ return GET_CODE (XEXP (src, 0));
}
- return cond;
+
+ return UNKNOWN;
}
-/* Returns nonzero if INSN is a tst insn that only tests the sign. */
-static int
+/* Returns true iff INSN is a tst insn that only tests the sign. */
+
+static bool
compare_sign_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
return (cond == GE || cond == LT);
}
-/* Returns nonzero if the next insn is a JUMP_INSN with a condition
+
+/* Returns true iff the next insn is a JUMP_INSN with a condition
that needs to be swapped (GT, GTU, LE, LEU). */
-int
+static bool
compare_diff_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0;
}
-/* Returns nonzero if INSN is a compare insn with the EQ or NE condition. */
+/* Returns true iff INSN is a compare insn with the EQ or NE condition. */
-int
+static bool
compare_eq_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
}
+/* Output compare instruction
+
+ compare (XOP[0], XOP[1])
+
+ for an HI/SI register XOP[0] and an integer XOP[1]. Return "".
+ XOP[2] is an 8-bit scratch register as needed.
+
+ PLEN == NULL: Output instructions.
+ PLEN != NULL: Set *PLEN to the length (in words) of the sequence.
+ Don't output anything. */
+
+const char*
+avr_out_compare (rtx insn, rtx *xop, int *plen)
+{
+ /* Register to compare and value to compare against. */
+ rtx xreg = xop[0];
+ rtx xval = xop[1];
+
+ /* MODE of the comparison. */
+ enum machine_mode mode = GET_MODE (xreg);
+
+ /* Number of bytes to operate on. */
+ int i, n_bytes = GET_MODE_SIZE (mode);
+
+ /* Value (0..0xff) held in clobber register xop[2] or -1 if unknown. */
+ int clobber_val = -1;
+
+ gcc_assert (REG_P (xreg));
+ gcc_assert ((CONST_INT_P (xval) && n_bytes <= 4)
+ || (const_double_operand (xval, VOIDmode) && n_bytes == 8));
+
+ if (plen)
+ *plen = 0;
+
+ /* Comparisons == +/-1 and != +/-1 can be done similar to camparing
+ against 0 by ORing the bytes. This is one instruction shorter.
+ Notice that DImode comparisons are always against reg:DI 18
+ and therefore don't use this. */
+
+ if (!test_hard_reg_class (LD_REGS, xreg)
+ && compare_eq_p (insn)
+ && reg_unused_after (insn, xreg))
+ {
+ if (xval == const1_rtx)
+ {
+ avr_asm_len ("dec %A0" CR_TAB
+ "or %A0,%B0", xop, plen, 2);
+
+ if (n_bytes >= 3)
+ avr_asm_len ("or %A0,%C0", xop, plen, 1);
+
+ if (n_bytes >= 4)
+ avr_asm_len ("or %A0,%D0", xop, plen, 1);
+
+ return "";
+ }
+ else if (xval == constm1_rtx)
+ {
+ if (n_bytes >= 4)
+ avr_asm_len ("and %A0,%D0", xop, plen, 1);
+
+ if (n_bytes >= 3)
+ avr_asm_len ("and %A0,%C0", xop, plen, 1);
+
+ return avr_asm_len ("and %A0,%B0" CR_TAB
+ "com %A0", xop, plen, 2);
+ }
+ }
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ /* We compare byte-wise. */
+ rtx reg8 = simplify_gen_subreg (QImode, xreg, mode, i);
+ rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+ /* 8-bit value to compare with this byte. */
+ unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+ /* Registers R16..R31 can operate with immediate. */
+ bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+ xop[0] = reg8;
+ xop[1] = gen_int_mode (val8, QImode);
+
+ /* Word registers >= R24 can use SBIW/ADIW with 0..63. */
+
+ if (i == 0
+ && test_hard_reg_class (ADDW_REGS, reg8))
+ {
+ int val16 = trunc_int_for_mode (INTVAL (xval), HImode);
+
+ if (IN_RANGE (val16, 0, 63)
+ && (val8 == 0
+ || reg_unused_after (insn, xreg)))
+ {
+ avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+ i++;
+ continue;
+ }
+
+ if (n_bytes == 2
+ && IN_RANGE (val16, -63, -1)
+ && compare_eq_p (insn)
+ && reg_unused_after (insn, xreg))
+ {
+ return avr_asm_len ("adiw %0,%n1", xop, plen, 1);
+ }
+ }
+
+ /* Comparing against 0 is easy. */
+
+ if (val8 == 0)
+ {
+ avr_asm_len (i == 0
+ ? "cp %0,__zero_reg__"
+ : "cpc %0,__zero_reg__", xop, plen, 1);
+ continue;
+ }
+
+ /* Upper registers can compare and subtract-with-carry immediates.
+ Notice that compare instructions do the same as respective subtract
+ instruction; the only difference is that comparisons don't write
+ the result back to the target register. */
+
+ if (ld_reg_p)
+ {
+ if (i == 0)
+ {
+ avr_asm_len ("cpi %0,%1", xop, plen, 1);
+ continue;
+ }
+ else if (reg_unused_after (insn, xreg))
+ {
+ avr_asm_len ("sbci %0,%1", xop, plen, 1);
+ continue;
+ }
+ }
+
+ /* Must load the value into the scratch register. */
+
+ gcc_assert (REG_P (xop[2]));
+
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", xop, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len (i == 0
+ ? "cp %0,%2"
+ : "cpc %0,%2", xop, plen, 1);
+ }
+
+ return "";
+}
+
+
+/* Prepare operands of compare_const_di2 to be used with avr_out_compare. */
+
+const char*
+avr_out_compare64 (rtx insn, rtx *op, int *plen)
+{
+ rtx xop[3];
+
+ xop[0] = gen_rtx_REG (DImode, 18);
+ xop[1] = op[0];
+ xop[2] = op[1];
+
+ return avr_out_compare (insn, xop, plen);
+}
+
/* Output test instruction for HImode. */
-const char *
-out_tsthi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tsthi (rtx insn, rtx *op, int *plen)
+{
+ if (compare_sign_p (insn))
+ {
+ avr_asm_len ("tst %B0", op, plen, -1);
+ }
+ else if (reg_unused_after (insn, op[0])
+ && compare_eq_p (insn))
+ {
+ /* Faster than sbiw if we can clobber the operand. */
+ avr_asm_len ("or %A0,%B0", op, plen, -1);
+ }
+ else
+ {
+ avr_out_compare (insn, op, plen);
+ }
+
+ return "";
+}
+
+
+/* Output test instruction for PSImode. */
+
+const char*
+avr_out_tstpsi (rtx insn, rtx *op, int *plen)
{
if (compare_sign_p (insn))
{
- if (l) *l = 1;
- return AS1 (tst,%B0);
+ avr_asm_len ("tst %C0", op, plen, -1);
}
- if (reg_unused_after (insn, op)
- && compare_eq_p (insn))
+ else if (reg_unused_after (insn, op[0])
+ && compare_eq_p (insn))
{
/* Faster than sbiw if we can clobber the operand. */
- if (l) *l = 1;
- return "or %A0,%B0";
+ avr_asm_len ("or %A0,%B0" CR_TAB
+ "or %A0,%C0", op, plen, -2);
}
- if (test_hard_reg_class (ADDW_REGS, op))
+ else
{
- if (l) *l = 1;
- return AS2 (sbiw,%0,0);
+ avr_out_compare (insn, op, plen);
}
- if (l) *l = 2;
- return (AS2 (cp,%A0,__zero_reg__) CR_TAB
- AS2 (cpc,%B0,__zero_reg__));
+
+ return "";
}
/* Output test instruction for SImode. */
-const char *
-out_tstsi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tstsi (rtx insn, rtx *op, int *plen)
{
if (compare_sign_p (insn))
{
- if (l) *l = 1;
- return AS1 (tst,%D0);
+ avr_asm_len ("tst %D0", op, plen, -1);
+ }
+ else if (reg_unused_after (insn, op[0])
+ && compare_eq_p (insn))
+ {
+ /* Faster than sbiw if we can clobber the operand. */
+ avr_asm_len ("or %A0,%B0" CR_TAB
+ "or %A0,%C0" CR_TAB
+ "or %A0,%D0", op, plen, -3);
}
- if (test_hard_reg_class (ADDW_REGS, op))
+ else
{
- if (l) *l = 3;
- return (AS2 (sbiw,%A0,0) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
+ avr_out_compare (insn, op, plen);
}
- if (l) *l = 4;
- return (AS2 (cp,%A0,__zero_reg__) CR_TAB
- AS2 (cpc,%B0,__zero_reg__) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
+
+ return "";
}
-/* Generate asm equivalent for various shifts.
- Shift count is a CONST_INT, MEM or REG.
- This only handles cases that are not already
- carefully hand-optimized in ?sh??i3_out. */
+/* Generate asm equivalent for various shifts. This only handles cases
+ that are not already carefully hand-optimized in ?sh??i3_out.
+
+ OPERANDS[0] resp. %0 in TEMPL is the operand to be shifted.
+ OPERANDS[2] is the shift count as CONST_INT, MEM or REG.
+ OPERANDS[3] is a QImode scratch register from LD regs if
+ available and SCRATCH, otherwise (no scratch available)
+
+ TEMPL is an assembler template that shifts by one position.
+ T_LEN is the length of this template. */
void
out_shift_with_cnt (const char *templ, rtx insn, rtx operands[],
- int *len, int t_len)
+ int *plen, int t_len)
{
- rtx op[10];
- char str[500];
- int second_label = 1;
- int saved_in_tmp = 0;
- int use_zero_reg = 0;
+ bool second_label = true;
+ bool saved_in_tmp = false;
+ bool use_zero_reg = false;
+ rtx op[5];
op[0] = operands[0];
op[1] = operands[1];
op[2] = operands[2];
op[3] = operands[3];
- str[0] = 0;
- if (len)
- *len = 1;
+ if (plen)
+ *plen = 0;
- if (GET_CODE (operands[2]) == CONST_INT)
+ if (CONST_INT_P (operands[2]))
{
- int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL);
+ bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL
+ && REG_P (operands[3]));
int count = INTVAL (operands[2]);
int max_len = 10; /* If larger than this, always use a loop. */
if (count <= 0)
- {
- if (len)
- *len = 0;
- return;
- }
+ return;
if (count < 8 && !scratch)
- use_zero_reg = 1;
+ use_zero_reg = true;
if (optimize_size)
- max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5));
+ max_len = t_len + (scratch ? 3 : (use_zero_reg ? 4 : 5));
if (t_len * count <= max_len)
- {
- /* Output shifts inline with no loop - faster. */
- if (len)
- *len = t_len * count;
- else
- {
- while (count-- > 0)
- output_asm_insn (templ, op);
- }
+ {
+ /* Output shifts inline with no loop - faster. */
+
+ while (count-- > 0)
+ avr_asm_len (templ, op, plen, t_len);
- return;
- }
+ return;
+ }
if (scratch)
- {
- if (!len)
- strcat (str, AS2 (ldi,%3,%2));
- }
+ {
+ avr_asm_len ("ldi %3,%2", op, plen, 1);
+ }
else if (use_zero_reg)
- {
- /* Hack to save one word: use __zero_reg__ as loop counter.
- Set one bit, then shift in a loop until it is 0 again. */
+ {
+ /* Hack to save one word: use __zero_reg__ as loop counter.
+ Set one bit, then shift in a loop until it is 0 again. */
- op[3] = zero_reg_rtx;
- if (len)
- *len = 2;
- else
- strcat (str, ("set" CR_TAB
- AS2 (bld,%3,%2-1)));
- }
+ op[3] = zero_reg_rtx;
+
+ avr_asm_len ("set" CR_TAB
+ "bld %3,%2-1", op, plen, 2);
+ }
else
- {
- /* No scratch register available, use one from LD_REGS (saved in
- __tmp_reg__) that doesn't overlap with registers to shift. */
+ {
+ /* No scratch register available, use one from LD_REGS (saved in
+ __tmp_reg__) that doesn't overlap with registers to shift. */
- op[3] = gen_rtx_REG (QImode,
- ((true_regnum (operands[0]) - 1) & 15) + 16);
- op[4] = tmp_reg_rtx;
- saved_in_tmp = 1;
+ op[3] = all_regs_rtx[((REGNO (op[0]) - 1) & 15) + 16];
+ op[4] = tmp_reg_rtx;
+ saved_in_tmp = true;
- if (len)
- *len = 3; /* Includes "mov %3,%4" after the loop. */
- else
- strcat (str, (AS2 (mov,%4,%3) CR_TAB
- AS2 (ldi,%3,%2)));
- }
+ avr_asm_len ("mov %4,%3" CR_TAB
+ "ldi %3,%2", op, plen, 2);
+ }
- second_label = 0;
+ second_label = false;
}
- else if (GET_CODE (operands[2]) == MEM)
+ else if (MEM_P (op[2]))
{
- rtx op_mov[10];
+ rtx op_mov[2];
- op[3] = op_mov[0] = tmp_reg_rtx;
+ op_mov[0] = op[3] = tmp_reg_rtx;
op_mov[1] = op[2];
- if (len)
- out_movqi_r_mr (insn, op_mov, len);
- else
- output_asm_insn (out_movqi_r_mr (insn, op_mov, NULL), op_mov);
+ out_movqi_r_mr (insn, op_mov, plen);
}
- else if (register_operand (operands[2], QImode))
+ else if (register_operand (op[2], QImode))
{
- if (reg_unused_after (insn, operands[2]))
- op[3] = op[2];
- else
- {
- op[3] = tmp_reg_rtx;
- if (!len)
- strcat (str, (AS2 (mov,%3,%2) CR_TAB));
- }
+ op[3] = op[2];
+
+ if (!reg_unused_after (insn, op[2])
+ || reg_overlap_mentioned_p (op[0], op[2]))
+ {
+ op[3] = tmp_reg_rtx;
+ avr_asm_len ("mov %3,%2", op, plen, 1);
+ }
}
else
fatal_insn ("bad shift insn:", insn);
if (second_label)
- {
- if (len)
- ++*len;
- else
- strcat (str, AS1 (rjmp,2f));
- }
+ avr_asm_len ("rjmp 2f", op, plen, 1);
- if (len)
- *len += t_len + 2; /* template + dec + brXX */
- else
- {
- strcat (str, "\n1:\t");
- strcat (str, templ);
- strcat (str, second_label ? "\n2:\t" : "\n\t");
- strcat (str, use_zero_reg ? AS1 (lsr,%3) : AS1 (dec,%3));
- strcat (str, CR_TAB);
- strcat (str, second_label ? AS1 (brpl,1b) : AS1 (brne,1b));
- if (saved_in_tmp)
- strcat (str, (CR_TAB AS2 (mov,%3,%4)));
- output_asm_insn (str, op);
- }
+ avr_asm_len ("1:", op, plen, 0);
+ avr_asm_len (templ, op, plen, t_len);
+
+ if (second_label)
+ avr_asm_len ("2:", op, plen, 0);
+
+ avr_asm_len (use_zero_reg ? "lsr %3" : "dec %3", op, plen, 1);
+ avr_asm_len (second_label ? "brpl 1b" : "brne 1b", op, plen, 1);
+
+ if (saved_in_tmp)
+ avr_asm_len ("mov %3,%4", op, plen, 1);
}
}
+/* 24-bit shift left */
+
+const char*
+avr_out_ashlpsi3 (rtx insn, rtx *op, int *plen)
+{
+ if (plen)
+ *plen = 0;
+
+ if (CONST_INT_P (op[2]))
+ {
+ switch (INTVAL (op[2]))
+ {
+ default:
+ if (INTVAL (op[2]) < 24)
+ break;
+
+ return avr_asm_len ("clr %A0" CR_TAB
+ "clr %B0" CR_TAB
+ "clr %C0", op, plen, 3);
+
+ case 8:
+ {
+ int reg0 = REGNO (op[0]);
+ int reg1 = REGNO (op[1]);
+
+ if (reg0 >= reg1)
+ return avr_asm_len ("mov %C0,%B1" CR_TAB
+ "mov %B0,%A1" CR_TAB
+ "clr %A0", op, plen, 3);
+ else
+ return avr_asm_len ("clr %A0" CR_TAB
+ "mov %B0,%A1" CR_TAB
+ "mov %C0,%B1", op, plen, 3);
+ }
+
+ case 16:
+ {
+ int reg0 = REGNO (op[0]);
+ int reg1 = REGNO (op[1]);
+
+ if (reg0 + 2 != reg1)
+ avr_asm_len ("mov %C0,%A0", op, plen, 1);
+
+ return avr_asm_len ("clr %B0" CR_TAB
+ "clr %A0", op, plen, 2);
+ }
+
+ case 23:
+ return avr_asm_len ("clr %C0" CR_TAB
+ "lsr %A0" CR_TAB
+ "ror %C0" CR_TAB
+ "clr %B0" CR_TAB
+ "clr %A0", op, plen, 5);
+ }
+ }
+
+ out_shift_with_cnt ("lsl %A0" CR_TAB
+ "rol %B0" CR_TAB
+ "rol %C0", insn, op, plen, 3);
+ return "";
+}
+
+
/* 32bit shift left ((long)x << i) */
const char *
}
+/* 24-bit arithmetic shift right */
+
+const char*
+avr_out_ashrpsi3 (rtx insn, rtx *op, int *plen)
+{
+ int dest = REGNO (op[0]);
+ int src = REGNO (op[1]);
+
+ if (CONST_INT_P (op[2]))
+ {
+ if (plen)
+ *plen = 0;
+
+ switch (INTVAL (op[2]))
+ {
+ case 8:
+ if (dest <= src)
+ return avr_asm_len ("mov %A0,%B1" CR_TAB
+ "mov %B0,%C1" CR_TAB
+ "clr %C0" CR_TAB
+ "sbrc %B0,7" CR_TAB
+ "dec %C0", op, plen, 5);
+ else
+ return avr_asm_len ("clr %C0" CR_TAB
+ "sbrc %C1,7" CR_TAB
+ "dec %C0" CR_TAB
+ "mov %B0,%C1" CR_TAB
+ "mov %A0,%B1", op, plen, 5);
+
+ case 16:
+ if (dest != src + 2)
+ avr_asm_len ("mov %A0,%C1", op, plen, 1);
+
+ return avr_asm_len ("clr %B0" CR_TAB
+ "sbrc %A0,7" CR_TAB
+ "com %B0" CR_TAB
+ "mov %C0,%B0", op, plen, 4);
+
+ default:
+ if (INTVAL (op[2]) < 24)
+ break;
+
+ /* fall through */
+
+ case 23:
+ return avr_asm_len ("lsl %C0" CR_TAB
+ "sbc %A0,%A0" CR_TAB
+ "mov %B0,%A0" CR_TAB
+ "mov %C0,%A0", op, plen, 4);
+ } /* switch */
+ }
+
+ out_shift_with_cnt ("asr %C0" CR_TAB
+ "ror %B0" CR_TAB
+ "ror %A0", insn, op, plen, 3);
+ return "";
+}
+
+
/* 32bit arithmetic shift right ((signed long)x >> i) */
const char *
return "";
}
+
+/* 24-bit logic shift right */
+
+const char*
+avr_out_lshrpsi3 (rtx insn, rtx *op, int *plen)
+{
+ int dest = REGNO (op[0]);
+ int src = REGNO (op[1]);
+
+ if (CONST_INT_P (op[2]))
+ {
+ if (plen)
+ *plen = 0;
+
+ switch (INTVAL (op[2]))
+ {
+ case 8:
+ if (dest <= src)
+ return avr_asm_len ("mov %A0,%B1" CR_TAB
+ "mov %B0,%C1" CR_TAB
+ "clr %C0", op, plen, 3);
+ else
+ return avr_asm_len ("clr %C0" CR_TAB
+ "mov %B0,%C1" CR_TAB
+ "mov %A0,%B1", op, plen, 3);
+
+ case 16:
+ if (dest != src + 2)
+ avr_asm_len ("mov %A0,%C1", op, plen, 1);
+
+ return avr_asm_len ("clr %B0" CR_TAB
+ "clr %C0", op, plen, 2);
+
+ default:
+ if (INTVAL (op[2]) < 24)
+ break;
+
+ /* fall through */
+
+ case 23:
+ return avr_asm_len ("clr %A0" CR_TAB
+ "sbrc %C0,7" CR_TAB
+ "inc %A0" CR_TAB
+ "clr %B0" CR_TAB
+ "clr %C0", op, plen, 5);
+ } /* switch */
+ }
+
+ out_shift_with_cnt ("lsr %C0" CR_TAB
+ "ror %B0" CR_TAB
+ "ror %A0", insn, op, plen, 3);
+ return "";
+}
+
+
/* 32bit logic shift right ((unsigned int)x >> i) */
const char *
AS1 (clr,%C0) CR_TAB
AS1 (clr,%D0));
- case 31:
- *len = 6;
- return (AS1 (clr,%A0) CR_TAB
- AS2 (sbrc,%D0,7) CR_TAB
- AS1 (inc,%A0) CR_TAB
- AS1 (clr,%B0) CR_TAB
- AS1 (clr,%C0) CR_TAB
- AS1 (clr,%D0));
- }
- len = t;
+ case 31:
+ *len = 6;
+ return (AS1 (clr,%A0) CR_TAB
+ AS2 (sbrc,%D0,7) CR_TAB
+ AS1 (inc,%A0) CR_TAB
+ AS1 (clr,%B0) CR_TAB
+ AS1 (clr,%C0) CR_TAB
+ AS1 (clr,%D0));
+ }
+ len = t;
+ }
+ out_shift_with_cnt ((AS1 (lsr,%D0) CR_TAB
+ AS1 (ror,%C0) CR_TAB
+ AS1 (ror,%B0) CR_TAB
+ AS1 (ror,%A0)),
+ insn, operands, len, 4);
+ return "";
+}
+
+
+/* Output addition of register XOP[0] and compile time constant XOP[2]:
+
+ XOP[0] = XOP[0] + XOP[2]
+
+ and return "". If PLEN == NULL, print assembler instructions to perform the
+ addition; otherwise, set *PLEN to the length of the instruction sequence (in
+ words) printed with PLEN == NULL. XOP[3] is an 8-bit scratch register.
+ CODE == PLUS: perform addition by using ADD instructions.
+ CODE == MINUS: perform addition by using SUB instructions.
+ Set *PCC to effect on cc0 according to respective CC_* insn attribute. */
+
+static void
+avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
+{
+ /* MODE of the operation. */
+ enum machine_mode mode = GET_MODE (xop[0]);
+
+ /* Number of bytes to operate on. */
+ int i, n_bytes = GET_MODE_SIZE (mode);
+
+ /* Value (0..0xff) held in clobber register op[3] or -1 if unknown. */
+ int clobber_val = -1;
+
+ /* op[0]: 8-bit destination register
+ op[1]: 8-bit const int
+ op[2]: 8-bit scratch register */
+ rtx op[3];
+
+ /* Started the operation? Before starting the operation we may skip
+ adding 0. This is no more true after the operation started because
+ carry must be taken into account. */
+ bool started = false;
+
+ /* Value to add. There are two ways to add VAL: R += VAL and R -= -VAL. */
+ rtx xval = xop[2];
+
+ /* Except in the case of ADIW with 16-bit register (see below)
+ addition does not set cc0 in a usable way. */
+
+ *pcc = (MINUS == code) ? CC_SET_CZN : CC_CLOBBER;
+
+ if (MINUS == code)
+ xval = simplify_unary_operation (NEG, mode, xval, mode);
+
+ op[2] = xop[3];
+
+ if (plen)
+ *plen = 0;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ /* We operate byte-wise on the destination. */
+ rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i);
+ rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+ /* 8-bit value to operate with this byte. */
+ unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+ /* Registers R16..R31 can operate with immediate. */
+ bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+ op[0] = reg8;
+ op[1] = gen_int_mode (val8, QImode);
+
+ /* To get usable cc0 no low-bytes must have been skipped. */
+
+ if (i && !started)
+ *pcc = CC_CLOBBER;
+
+ if (!started
+ && i % 2 == 0
+ && i + 2 <= n_bytes
+ && test_hard_reg_class (ADDW_REGS, reg8))
+ {
+ rtx xval16 = simplify_gen_subreg (HImode, xval, mode, i);
+ unsigned int val16 = UINTVAL (xval16) & GET_MODE_MASK (HImode);
+
+ /* Registers R24, X, Y, Z can use ADIW/SBIW with constants < 64
+ i.e. operate word-wise. */
+
+ if (val16 < 64)
+ {
+ if (val16 != 0)
+ {
+ started = true;
+ avr_asm_len (code == PLUS ? "adiw %0,%1" : "sbiw %0,%1",
+ op, plen, 1);
+
+ if (n_bytes == 2 && PLUS == code)
+ *pcc = CC_SET_ZN;
+ }
+
+ i++;
+ continue;
+ }
+ }
+
+ if (val8 == 0)
+ {
+ if (started)
+ avr_asm_len (code == PLUS
+ ? "adc %0,__zero_reg__" : "sbc %0,__zero_reg__",
+ op, plen, 1);
+ continue;
+ }
+ else if ((val8 == 1 || val8 == 0xff)
+ && !started
+ && i == n_bytes - 1)
+ {
+ avr_asm_len ((code == PLUS) ^ (val8 == 1) ? "dec %0" : "inc %0",
+ op, plen, 1);
+ break;
+ }
+
+ switch (code)
+ {
+ case PLUS:
+
+ gcc_assert (plen != NULL || REG_P (op[2]));
+
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", op, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len (started ? "adc %0,%2" : "add %0,%2", op, plen, 1);
+
+ break; /* PLUS */
+
+ case MINUS:
+
+ if (ld_reg_p)
+ avr_asm_len (started ? "sbci %0,%1" : "subi %0,%1", op, plen, 1);
+ else
+ {
+ gcc_assert (plen != NULL || REG_P (op[2]));
+
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", op, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len (started ? "sbc %0,%2" : "sub %0,%2", op, plen, 1);
+ }
+
+ break; /* MINUS */
+
+ default:
+ /* Unknown code */
+ gcc_unreachable();
+ }
+
+ started = true;
+
+ } /* for all sub-bytes */
+
+ /* No output doesn't change cc0. */
+
+ if (plen && *plen == 0)
+ *pcc = CC_NONE;
+}
+
+
+/* Output addition of register XOP[0] and compile time constant XOP[2]:
+
+ XOP[0] = XOP[0] + XOP[2]
+
+ and return "". If PLEN == NULL, print assembler instructions to perform the
+ addition; otherwise, set *PLEN to the length of the instruction sequence (in
+ words) printed with PLEN == NULL.
+ If PCC != 0 then set *PCC to the the instruction sequence's effect on the
+ condition code (with respect to XOP[0]). */
+
+const char*
+avr_out_plus (rtx *xop, int *plen, int *pcc)
+{
+ int len_plus, len_minus;
+ int cc_plus, cc_minus, cc_dummy;
+
+ if (!pcc)
+ pcc = &cc_dummy;
+
+ /* Work out if XOP[0] += XOP[2] is better or XOP[0] -= -XOP[2]. */
+
+ avr_out_plus_1 (xop, &len_plus, PLUS, &cc_plus);
+ avr_out_plus_1 (xop, &len_minus, MINUS, &cc_minus);
+
+ /* Prefer MINUS over PLUS if size is equal because it sets cc0. */
+
+ if (plen)
+ {
+ *plen = (len_minus <= len_plus) ? len_minus : len_plus;
+ *pcc = (len_minus <= len_plus) ? cc_minus : cc_plus;
+ }
+ else if (len_minus <= len_plus)
+ avr_out_plus_1 (xop, NULL, MINUS, pcc);
+ else
+ avr_out_plus_1 (xop, NULL, PLUS, pcc);
+
+ return "";
+}
+
+
+/* Same as above but XOP has just 3 entries.
+ Supply a dummy 4th operand. */
+
+const char*
+avr_out_plus_noclobber (rtx *xop, int *plen, int *pcc)
+{
+ rtx op[4];
+
+ op[0] = xop[0];
+ op[1] = xop[1];
+ op[2] = xop[2];
+ op[3] = NULL_RTX;
+
+ return avr_out_plus (op, plen, pcc);
+}
+
+
+/* Prepare operands of adddi3_const_insn to be used with avr_out_plus_1. */
+
+const char*
+avr_out_plus64 (rtx addend, int *plen)
+{
+ int cc_dummy;
+ rtx op[4];
+
+ op[0] = gen_rtx_REG (DImode, 18);
+ op[1] = op[0];
+ op[2] = addend;
+ op[3] = NULL_RTX;
+
+ avr_out_plus_1 (op, plen, MINUS, &cc_dummy);
+
+ return "";
+}
+
+/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile
+ time constant XOP[2]:
+
+ XOP[0] = XOP[0] <op> XOP[2]
+
+ and return "". If PLEN == NULL, print assembler instructions to perform the
+ operation; otherwise, set *PLEN to the length of the instruction sequence
+ (in words) printed with PLEN == NULL. XOP[3] is either an 8-bit clobber
+ register or SCRATCH if no clobber register is needed for the operation. */
+
+const char*
+avr_out_bitop (rtx insn, rtx *xop, int *plen)
+{
+ /* CODE and MODE of the operation. */
+ enum rtx_code code = GET_CODE (SET_SRC (single_set (insn)));
+ enum machine_mode mode = GET_MODE (xop[0]);
+
+ /* Number of bytes to operate on. */
+ int i, n_bytes = GET_MODE_SIZE (mode);
+
+ /* Value of T-flag (0 or 1) or -1 if unknow. */
+ int set_t = -1;
+
+ /* Value (0..0xff) held in clobber register op[3] or -1 if unknown. */
+ int clobber_val = -1;
+
+ /* op[0]: 8-bit destination register
+ op[1]: 8-bit const int
+ op[2]: 8-bit clobber register or SCRATCH
+ op[3]: 8-bit register containing 0xff or NULL_RTX */
+ rtx op[4];
+
+ op[2] = xop[3];
+ op[3] = NULL_RTX;
+
+ if (plen)
+ *plen = 0;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ /* We operate byte-wise on the destination. */
+ rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i);
+ rtx xval8 = simplify_gen_subreg (QImode, xop[2], mode, i);
+
+ /* 8-bit value to operate with this byte. */
+ unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+ /* Number of bits set in the current byte of the constant. */
+ int pop8 = avr_popcount (val8);
+
+ /* Registers R16..R31 can operate with immediate. */
+ bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+ op[0] = reg8;
+ op[1] = GEN_INT (val8);
+
+ switch (code)
+ {
+ case IOR:
+
+ if (0 == pop8)
+ continue;
+ else if (ld_reg_p)
+ avr_asm_len ("ori %0,%1", op, plen, 1);
+ else if (1 == pop8)
+ {
+ if (set_t != 1)
+ avr_asm_len ("set", op, plen, 1);
+ set_t = 1;
+
+ op[1] = GEN_INT (exact_log2 (val8));
+ avr_asm_len ("bld %0,%1", op, plen, 1);
+ }
+ else if (8 == pop8)
+ {
+ if (op[3] != NULL_RTX)
+ avr_asm_len ("mov %0,%3", op, plen, 1);
+ else
+ avr_asm_len ("clr %0" CR_TAB
+ "dec %0", op, plen, 2);
+
+ op[3] = op[0];
+ }
+ else
+ {
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", op, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len ("or %0,%2", op, plen, 1);
+ }
+
+ continue; /* IOR */
+
+ case AND:
+
+ if (8 == pop8)
+ continue;
+ else if (0 == pop8)
+ avr_asm_len ("clr %0", op, plen, 1);
+ else if (ld_reg_p)
+ avr_asm_len ("andi %0,%1", op, plen, 1);
+ else if (7 == pop8)
+ {
+ if (set_t != 0)
+ avr_asm_len ("clt", op, plen, 1);
+ set_t = 0;
+
+ op[1] = GEN_INT (exact_log2 (GET_MODE_MASK (QImode) & ~val8));
+ avr_asm_len ("bld %0,%1", op, plen, 1);
+ }
+ else
+ {
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", op, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len ("and %0,%2", op, plen, 1);
+ }
+
+ continue; /* AND */
+
+ case XOR:
+
+ if (0 == pop8)
+ continue;
+ else if (8 == pop8)
+ avr_asm_len ("com %0", op, plen, 1);
+ else if (ld_reg_p && val8 == (1 << 7))
+ avr_asm_len ("subi %0,%1", op, plen, 1);
+ else
+ {
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", op, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len ("eor %0,%2", op, plen, 1);
+ }
+
+ continue; /* XOR */
+
+ default:
+ /* Unknown rtx_code */
+ gcc_unreachable();
+ }
+ } /* for all sub-bytes */
+
+ return "";
+}
+
+
+/* PLEN == NULL: Output code to add CONST_INT OP[0] to SP.
+ PLEN != NULL: Set *PLEN to the length of that sequence.
+ Return "". */
+
+const char*
+avr_out_addto_sp (rtx *op, int *plen)
+{
+ int pc_len = AVR_2_BYTE_PC ? 2 : 3;
+ int addend = INTVAL (op[0]);
+
+ if (plen)
+ *plen = 0;
+
+ if (addend < 0)
+ {
+ if (flag_verbose_asm || flag_print_asm_name)
+ avr_asm_len (ASM_COMMENT_START "SP -= %n0", op, plen, 0);
+
+ while (addend <= -pc_len)
+ {
+ addend += pc_len;
+ avr_asm_len ("rcall .", op, plen, 1);
+ }
+
+ while (addend++ < 0)
+ avr_asm_len ("push __zero_reg__", op, plen, 1);
}
- out_shift_with_cnt ((AS1 (lsr,%D0) CR_TAB
- AS1 (ror,%C0) CR_TAB
- AS1 (ror,%B0) CR_TAB
- AS1 (ror,%A0)),
- insn, operands, len, 4);
+ else if (addend > 0)
+ {
+ if (flag_verbose_asm || flag_print_asm_name)
+ avr_asm_len (ASM_COMMENT_START "SP += %0", op, plen, 0);
+
+ while (addend-- > 0)
+ avr_asm_len ("pop __tmp_reg__", op, plen, 1);
+ }
+
return "";
}
+
/* Create RTL split patterns for byte sized rotate expressions. This
produces a series of move instructions and considers overlap situations.
Overlapping non-HImode operands need a scratch register. */
if (mode == DImode)
move_mode = QImode;
/* Make scratch smaller if needed. */
- if (GET_MODE (scratch) == HImode && move_mode == QImode)
+ if (SCRATCH != GET_CODE (scratch)
+ && HImode == GET_MODE (scratch)
+ && QImode == move_mode)
scratch = simplify_gen_subreg (move_mode, scratch, HImode, 0);
move_size = GET_MODE_SIZE (move_mode);
When this move occurs, it will break chain deadlock.
The scratch register is substituted for real move. */
+ gcc_assert (SCRATCH != GET_CODE (scratch));
+
move[size].src = move[blocked].dst;
move[size].dst = scratch;
/* Scratch move is never blocked. */
}
/* Modifies the length assigned to instruction INSN
- LEN is the initially computed length of the insn. */
+ LEN is the initially computed length of the insn. */
int
adjust_insn_length (rtx insn, int len)
{
- rtx patt = PATTERN (insn);
- rtx set;
+ rtx *op = recog_data.operand;
+ enum attr_adjust_len adjust_len;
- if (GET_CODE (patt) == SET)
+ /* Some complex insns don't need length adjustment and therefore
+ the length need not/must not be adjusted for these insns.
+ It is easier to state this in an insn attribute "adjust_len" than
+ to clutter up code here... */
+
+ if (-1 == recog_memoized (insn))
{
- rtx op[10];
- op[1] = SET_SRC (patt);
- op[0] = SET_DEST (patt);
- if (general_operand (op[1], VOIDmode)
- && general_operand (op[0], VOIDmode))
- {
- switch (GET_MODE (op[0]))
- {
- case QImode:
- output_movqi (insn, op, &len);
- break;
- case HImode:
- output_movhi (insn, op, &len);
- break;
- case SImode:
- case SFmode:
- output_movsisf (insn, op, NULL_RTX, &len);
- break;
- default:
- break;
- }
- }
- else if (op[0] == cc0_rtx && REG_P (op[1]))
- {
- switch (GET_MODE (op[1]))
- {
- case HImode: out_tsthi (insn, op[1], &len); break;
- case SImode: out_tstsi (insn, op[1], &len); break;
- default: break;
- }
- }
- else if (GET_CODE (op[1]) == AND)
- {
- if (GET_CODE (XEXP (op[1],1)) == CONST_INT)
- {
- HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1));
- if (GET_MODE (op[1]) == SImode)
- len = (((mask & 0xff) != 0xff)
- + ((mask & 0xff00) != 0xff00)
- + ((mask & 0xff0000L) != 0xff0000L)
- + ((mask & 0xff000000L) != 0xff000000L));
- else if (GET_MODE (op[1]) == HImode)
- len = (((mask & 0xff) != 0xff)
- + ((mask & 0xff00) != 0xff00));
- }
- }
- else if (GET_CODE (op[1]) == IOR)
- {
- if (GET_CODE (XEXP (op[1],1)) == CONST_INT)
- {
- HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1));
- if (GET_MODE (op[1]) == SImode)
- len = (((mask & 0xff) != 0)
- + ((mask & 0xff00) != 0)
- + ((mask & 0xff0000L) != 0)
- + ((mask & 0xff000000L) != 0));
- else if (GET_MODE (op[1]) == HImode)
- len = (((mask & 0xff) != 0)
- + ((mask & 0xff00) != 0));
- }
- }
+ return len;
}
- set = single_set (insn);
- if (set)
+
+ /* Read from insn attribute "adjust_len" if/how length is to be adjusted. */
+
+ adjust_len = get_attr_adjust_len (insn);
+
+ if (adjust_len == ADJUST_LEN_NO)
+ {
+ /* Nothing to adjust: The length from attribute "length" is fine.
+ This is the default. */
+
+ return len;
+ }
+
+ /* Extract insn's operands. */
+
+ extract_constrain_insn_cached (insn);
+
+ /* Dispatch to right function. */
+
+ switch (adjust_len)
{
- rtx op[10];
+ case ADJUST_LEN_RELOAD_IN16: output_reload_inhi (op, op[2], &len); break;
+ case ADJUST_LEN_RELOAD_IN24: avr_out_reload_inpsi (op, op[2], &len); break;
+ case ADJUST_LEN_RELOAD_IN32: output_reload_insisf (op, op[2], &len); break;
+
+ case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break;
+
+ case ADJUST_LEN_OUT_PLUS: avr_out_plus (op, &len, NULL); break;
+ case ADJUST_LEN_PLUS64: avr_out_plus64 (op[0], &len); break;
+ case ADJUST_LEN_OUT_PLUS_NOCLOBBER:
+ avr_out_plus_noclobber (op, &len, NULL); break;
- op[1] = SET_SRC (set);
- op[0] = SET_DEST (set);
+ case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break;
+
+ case ADJUST_LEN_MOV8: output_movqi (insn, op, &len); break;
+ case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break;
+ case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break;
+ case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
+ case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
+ case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+
+ case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
+ case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
+ case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
+ case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break;
+ case ADJUST_LEN_COMPARE64: avr_out_compare64 (insn, op, &len); break;
+
+ case ADJUST_LEN_LSHRQI: lshrqi3_out (insn, op, &len); break;
+ case ADJUST_LEN_LSHRHI: lshrhi3_out (insn, op, &len); break;
+ case ADJUST_LEN_LSHRSI: lshrsi3_out (insn, op, &len); break;
+
+ case ADJUST_LEN_ASHRQI: ashrqi3_out (insn, op, &len); break;
+ case ADJUST_LEN_ASHRHI: ashrhi3_out (insn, op, &len); break;
+ case ADJUST_LEN_ASHRSI: ashrsi3_out (insn, op, &len); break;
+
+ case ADJUST_LEN_ASHLQI: ashlqi3_out (insn, op, &len); break;
+ case ADJUST_LEN_ASHLHI: ashlhi3_out (insn, op, &len); break;
+ case ADJUST_LEN_ASHLSI: ashlsi3_out (insn, op, &len); break;
+
+ case ADJUST_LEN_ASHLPSI: avr_out_ashlpsi3 (insn, op, &len); break;
+ case ADJUST_LEN_ASHRPSI: avr_out_ashrpsi3 (insn, op, &len); break;
+ case ADJUST_LEN_LSHRPSI: avr_out_lshrpsi3 (insn, op, &len); break;
- if (GET_CODE (patt) == PARALLEL
- && general_operand (op[1], VOIDmode)
- && general_operand (op[0], VOIDmode))
- {
- if (XVECLEN (patt, 0) == 2)
- op[2] = XVECEXP (patt, 0, 1);
+ case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break;
- switch (GET_MODE (op[0]))
- {
- case QImode:
- len = 2;
- break;
- case HImode:
- output_reload_inhi (insn, op, &len);
- break;
- case SImode:
- case SFmode:
- output_reload_insisf (insn, op, XEXP (op[2], 0), &len);
- break;
- default:
- break;
- }
- }
- else if (GET_CODE (op[1]) == ASHIFT
- || GET_CODE (op[1]) == ASHIFTRT
- || GET_CODE (op[1]) == LSHIFTRT)
- {
- rtx ops[10];
- ops[0] = op[0];
- ops[1] = XEXP (op[1],0);
- ops[2] = XEXP (op[1],1);
- switch (GET_CODE (op[1]))
- {
- case ASHIFT:
- switch (GET_MODE (op[0]))
- {
- case QImode: ashlqi3_out (insn,ops,&len); break;
- case HImode: ashlhi3_out (insn,ops,&len); break;
- case SImode: ashlsi3_out (insn,ops,&len); break;
- default: break;
- }
- break;
- case ASHIFTRT:
- switch (GET_MODE (op[0]))
- {
- case QImode: ashrqi3_out (insn,ops,&len); break;
- case HImode: ashrhi3_out (insn,ops,&len); break;
- case SImode: ashrsi3_out (insn,ops,&len); break;
- default: break;
- }
- break;
- case LSHIFTRT:
- switch (GET_MODE (op[0]))
- {
- case QImode: lshrqi3_out (insn,ops,&len); break;
- case HImode: lshrhi3_out (insn,ops,&len); break;
- case SImode: lshrsi3_out (insn,ops,&len); break;
- default: break;
- }
- break;
- default:
- break;
- }
- }
+ case ADJUST_LEN_MAP_BITS: avr_out_map_bits (insn, op, &len); break;
+
+ default:
+ gcc_unreachable();
}
+
return len;
}
return 1;
}
+
+/* Return RTX that represents the lower 16 bits of a constant address.
+ Unfortunately, simplify_gen_subreg does not handle this case. */
+
+static rtx
+avr_const_address_lo16 (rtx x)
+{
+ rtx lo16;
+
+ switch (GET_CODE (x))
+ {
+ default:
+ break;
+
+ case CONST:
+ if (PLUS == GET_CODE (XEXP (x, 0))
+ && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ {
+ HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1));
+ const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+
+ lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset));
+
+ return lo16;
+ }
+
+ break;
+
+ case SYMBOL_REF:
+ {
+ const char *name = XSTR (x, 0);
+
+ return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ }
+ }
+
+ avr_edump ("\n%?: %r\n", x);
+ gcc_unreachable();
+}
+
+
/* Target hook for assembling integer objects. The AVR version needs
special handling for references to certain labels. */
fputs ("\t.word\tgs(", asm_out_file);
output_addr_const (asm_out_file, x);
fputs (")\n", asm_out_file);
+
+ return true;
+ }
+ else if (GET_MODE (x) == PSImode)
+ {
+ default_assemble_integer (avr_const_address_lo16 (x),
+ GET_MODE_SIZE (HImode), aligned_p);
+
+ fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(",
+ asm_out_file);
+ output_addr_const (asm_out_file, x);
+ fputs (")\"\n", asm_out_file);
+
+ fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file);
+ output_addr_const (asm_out_file, x);
+ fputs (")\n", asm_out_file);
+
return true;
}
+
return default_assemble_integer (x, size, aligned_p);
}
+
/* Worker function for ASM_DECLARE_FUNCTION_NAME. */
void
if (cfun->machine->is_interrupt)
{
- if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+ if (!STR_PREFIX_P (name, "__vector"))
{
warning_at (DECL_SOURCE_LOCATION (decl), 0,
"%qs appears to be a misspelled interrupt handler",
}
else if (cfun->machine->is_signal)
{
- if (strncmp (name, "__vector", strlen ("__vector")) != 0)
+ if (!STR_PREFIX_P (name, "__vector"))
{
warning_at (DECL_SOURCE_LOCATION (decl), 0,
"%qs appears to be a misspelled signal handler",
return NULL_TREE;
}
-/* Look for attribute `progmem' in DECL
- if found return 1, otherwise 0. */
+
+/* AVR attributes. */
+static const struct attribute_spec
+avr_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
+ affects_type_identity } */
+ { "progmem", 0, 0, false, false, false, avr_handle_progmem_attribute,
+ false },
+ { "signal", 0, 0, true, false, false, avr_handle_fndecl_attribute,
+ false },
+ { "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute,
+ false },
+ { "naked", 0, 0, false, true, true, avr_handle_fntype_attribute,
+ false },
+ { "OS_task", 0, 0, false, true, true, avr_handle_fntype_attribute,
+ false },
+ { "OS_main", 0, 0, false, true, true, avr_handle_fntype_attribute,
+ false },
+ { NULL, 0, 0, false, false, false, NULL, false }
+};
+
+
+/* Look if DECL shall be placed in program memory space by
+ means of attribute `progmem' or some address-space qualifier.
+ Return non-zero if DECL is data that must end up in Flash and
+ zero if the data lives in RAM (.bss, .data, .rodata, ...).
+
+ Return 2 if DECL is located in 24-bit flash address-space
+ Return 1 if DECL is located in 16-bit flash address-space
+ Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES
+ Return 0 otherwise */
int
avr_progmem_p (tree decl, tree attributes)
if (TREE_CODE (decl) != VAR_DECL)
return 0;
+ if (avr_decl_pgmx_p (decl))
+ return 2;
+
+ if (avr_decl_pgm_p (decl))
+ return 1;
+
if (NULL_TREE
!= lookup_attribute ("progmem", attributes))
- return 1;
+ return -1;
- a=decl;
+ a = decl;
+
do
a = TREE_TYPE(a);
while (TREE_CODE (a) == ARRAY_TYPE);
return 0;
if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a)))
- return 1;
+ return -1;
return 0;
}
+
+/* Scan type TYP for pointer references to address space ASn.
+ Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting
+ the AS are also declared to be CONST.
+ Otherwise, return the respective addres space, i.e. a value != 0. */
+
+static addr_space_t
+avr_nonconst_pointer_addrspace (tree typ)
+{
+ while (ARRAY_TYPE == TREE_CODE (typ))
+ typ = TREE_TYPE (typ);
+
+ if (POINTER_TYPE_P (typ))
+ {
+ tree target = TREE_TYPE (typ);
+
+ /* Pointer to function: Test the function's return type. */
+
+ if (FUNCTION_TYPE == TREE_CODE (target))
+ return avr_nonconst_pointer_addrspace (TREE_TYPE (target));
+
+ /* "Ordinary" pointers... */
+
+ while (TREE_CODE (target) == ARRAY_TYPE)
+ target = TREE_TYPE (target);
+
+ if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target))
+ && !TYPE_READONLY (target))
+ {
+ /* Pointers to non-generic address space must be const. */
+
+ return TYPE_ADDR_SPACE (target);
+ }
+
+ /* Scan pointer's target type. */
+
+ return avr_nonconst_pointer_addrspace (target);
+ }
+
+ return ADDR_SPACE_GENERIC;
+}
+
+
+/* Sanity check NODE so that all pointers targeting address space AS1
+ go along with CONST qualifier. Writing to this address space should
+ be detected and complained about as early as possible. */
+
+static bool
+avr_pgm_check_var_decl (tree node)
+{
+ const char *reason = NULL;
+
+ addr_space_t as = ADDR_SPACE_GENERIC;
+
+ gcc_assert (as == 0);
+
+ if (avr_log.progmem)
+ avr_edump ("%?: %t\n", node);
+
+ switch (TREE_CODE (node))
+ {
+ default:
+ break;
+
+ case VAR_DECL:
+ if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+ reason = "variable";
+ break;
+
+ case PARM_DECL:
+ if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+ reason = "function parameter";
+ break;
+
+ case FIELD_DECL:
+ if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as)
+ reason = "structure field";
+ break;
+
+ case FUNCTION_DECL:
+ if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))),
+ as)
+ reason = "return type of function";
+ break;
+
+ case POINTER_TYPE:
+ if (as = avr_nonconst_pointer_addrspace (node), as)
+ reason = "pointer";
+ break;
+ }
+
+ if (reason)
+ {
+ if (TYPE_P (node))
+ error ("pointer targeting address space %qs must be const in %qT",
+ avr_addrspace[as].name, node);
+ else
+ error ("pointer targeting address space %qs must be const in %s %q+D",
+ avr_addrspace[as].name, reason, node);
+ }
+
+ return reason == NULL;
+}
+
+
/* Add the section attribute if the variable is in progmem. */
static void
avr_insert_attributes (tree node, tree *attributes)
{
+ avr_pgm_check_var_decl (node);
+
if (TREE_CODE (node) == VAR_DECL
&& (TREE_STATIC (node) || DECL_EXTERNAL (node))
&& avr_progmem_p (node, *attributes))
if (error_mark_node == node0)
return;
- if (TYPE_READONLY (node0))
+ if (!TYPE_READONLY (node0)
+ && !TREE_READONLY (node))
{
- static const char dsec[] = ".progmem.data";
+ addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node));
+ const char *reason = "__attribute__((progmem))";
- *attributes = tree_cons (get_identifier ("section"),
- build_tree_list (NULL, build_string (strlen (dsec), dsec)),
- *attributes);
- }
- else
- {
+ if (!ADDR_SPACE_GENERIC_P (as))
+ reason = avr_addrspace[as].name;
+
+ if (avr_log.progmem)
+ avr_edump ("\n%?: %t\n%t\n", node, node0);
+
error ("variable %q+D must be const in order to be put into"
- " read-only section by means of %<__attribute__((progmem))%>",
- node);
+ " read-only section by means of %qs", node, reason);
}
}
}
-/* A get_unnamed_section callback for switching to progmem_section. */
-
-static void
-avr_output_progmem_section_asm_op (const void *arg ATTRIBUTE_UNUSED)
-{
- fprintf (asm_out_file,
- "\t.section .progmem.gcc_sw_table, \"%s\", @progbits\n",
- AVR_HAVE_JMP_CALL ? "a" : "ax");
- /* Should already be aligned, this is just to be safe if it isn't. */
- fprintf (asm_out_file, "\t.p2align 1\n");
-}
-
/* Implement `ASM_OUTPUT_ALIGNED_DECL_LOCAL'. */
/* Implement `ASM_OUTPUT_ALIGNED_DECL_COMMON'. */
/* Track need of __do_clear_bss. */
void
-avr_asm_output_aligned_decl_common (FILE * stream, const_tree decl ATTRIBUTE_UNUSED,
- const char *name, unsigned HOST_WIDE_INT size,
+avr_asm_output_aligned_decl_common (FILE * stream,
+ const_tree decl ATTRIBUTE_UNUSED,
+ const char *name,
+ unsigned HOST_WIDE_INT size,
unsigned int align, bool local_p)
{
avr_need_clear_bss_p = true;
}
-/* Unnamed section callback for bss_section
- to track need of __do_clear_bss. */
+/* Unnamed section callback for bss_section
+ to track need of __do_clear_bss. */
+
+static void
+avr_output_bss_section_asm_op (const void *data)
+{
+ avr_need_clear_bss_p = true;
+
+ /* Dispatch to default. */
+ output_section_asm_op (data);
+}
+
+
+/* Unnamed section callback for progmem*.data sections. */
+
+static void
+avr_output_progmem_section_asm_op (const void *data)
+{
+ fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n",
+ (const char*) data);
+}
+
+
+/* Implement `TARGET_ASM_INIT_SECTIONS'. */
+
+static void
+avr_asm_init_sections (void)
+{
+ unsigned int n;
+
+ /* Set up a section for jump tables. Alignment is handled by
+ ASM_OUTPUT_BEFORE_CASE_LABEL. */
+
+ if (AVR_HAVE_JMP_CALL)
+ {
+ progmem_swtable_section
+ = get_unnamed_section (0, output_section_asm_op,
+ "\t.section\t.progmem.gcc_sw_table"
+ ",\"a\",@progbits");
+ }
+ else
+ {
+ progmem_swtable_section
+ = get_unnamed_section (SECTION_CODE, output_section_asm_op,
+ "\t.section\t.progmem.gcc_sw_table"
+ ",\"ax\",@progbits");
+ }
+
+ for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++)
+ {
+ progmem_section[n]
+ = get_unnamed_section (0, avr_output_progmem_section_asm_op,
+ progmem_section_prefix[n]);
+ }
+
+ /* Override section callbacks to keep track of `avr_need_clear_bss_p'
+ resp. `avr_need_copy_data_p'. */
+
+ readonly_data_section->unnamed.callback = avr_output_data_section_asm_op;
+ data_section->unnamed.callback = avr_output_data_section_asm_op;
+ bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+}
+
+
+/* Implement `TARGET_ASM_FUNCTION_RODATA_SECTION'. */
-static void
-avr_output_bss_section_asm_op (const void *data)
+static section*
+avr_asm_function_rodata_section (tree decl)
{
- avr_need_clear_bss_p = true;
-
- /* Dispatch to default. */
- output_section_asm_op (data);
-}
+ /* If a function is unused and optimized out by -ffunction-sections
+ and --gc-sections, ensure that the same will happen for its jump
+ tables by putting them into individual sections. */
+ unsigned int flags;
+ section * frodata;
-/* Implement `TARGET_ASM_INIT_SECTIONS'. */
+ /* Get the frodata section from the default function in varasm.c
+ but treat function-associated data-like jump tables as code
+ rather than as user defined data. AVR has no constant pools. */
+ {
+ int fdata = flag_data_sections;
-static void
-avr_asm_init_sections (void)
-{
- progmem_section = get_unnamed_section (AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE,
- avr_output_progmem_section_asm_op,
- NULL);
- readonly_data_section = data_section;
+ flag_data_sections = flag_function_sections;
+ frodata = default_function_rodata_section (decl);
+ flag_data_sections = fdata;
+ flags = frodata->common.flags;
+ }
- data_section->unnamed.callback = avr_output_data_section_asm_op;
- bss_section->unnamed.callback = avr_output_bss_section_asm_op;
+ if (frodata != readonly_data_section
+ && flags & SECTION_NAMED)
+ {
+ /* Adjust section flags and replace section name prefix. */
+
+ unsigned int i;
+
+ static const char* const prefix[] =
+ {
+ ".rodata", ".progmem.gcc_sw_table",
+ ".gnu.linkonce.r.", ".gnu.linkonce.t."
+ };
+
+ for (i = 0; i < sizeof (prefix) / sizeof (*prefix); i += 2)
+ {
+ const char * old_prefix = prefix[i];
+ const char * new_prefix = prefix[i+1];
+ const char * name = frodata->named.name;
+
+ if (STR_PREFIX_P (name, old_prefix))
+ {
+ const char *rname = avr_replace_prefix (name,
+ old_prefix, new_prefix);
+
+ flags &= ~SECTION_CODE;
+ flags |= AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE;
+
+ return get_section (rname, flags, frodata->named.decl);
+ }
+ }
+ }
+
+ return progmem_swtable_section;
}
/* Implement `TARGET_ASM_NAMED_SECTION'. */
/* Track need of __do_clear_bss, __do_copy_data for named sections. */
-void
+static void
avr_asm_named_section (const char *name, unsigned int flags, tree decl)
{
+ if (flags & AVR_SECTION_PROGMEM)
+ {
+ addr_space_t as = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP;
+ int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+ const char *old_prefix = ".rodata";
+ const char *new_prefix = progmem_section_prefix[segment];
+ const char *sname = new_prefix;
+
+ if (STR_PREFIX_P (name, old_prefix))
+ {
+ sname = avr_replace_prefix (name, old_prefix, new_prefix);
+ }
+
+ default_elf_asm_named_section (sname, flags, decl);
+
+ return;
+ }
+
if (!avr_need_copy_data_p)
- avr_need_copy_data_p = (0 == strncmp (name, ".data", 5)
- || 0 == strncmp (name, ".rodata", 7)
- || 0 == strncmp (name, ".gnu.linkonce.d", 15));
+ avr_need_copy_data_p = (STR_PREFIX_P (name, ".data")
+ || STR_PREFIX_P (name, ".rodata")
+ || STR_PREFIX_P (name, ".gnu.linkonce.d"));
if (!avr_need_clear_bss_p)
- avr_need_clear_bss_p = (0 == strncmp (name, ".bss", 4));
+ avr_need_clear_bss_p = STR_PREFIX_P (name, ".bss");
default_elf_asm_named_section (name, flags, decl);
}
{
unsigned int flags = default_section_type_flags (decl, name, reloc);
- if (strncmp (name, ".noinit", 7) == 0)
+ if (STR_PREFIX_P (name, ".noinit"))
{
if (decl && TREE_CODE (decl) == VAR_DECL
&& DECL_INITIAL (decl) == NULL_TREE)
".noinit section");
}
- if (0 == strncmp (name, ".progmem.data", strlen (".progmem.data")))
- flags &= ~SECTION_WRITE;
+ if (decl && DECL_P (decl)
+ && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+ {
+ addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+
+ /* Attribute progmem puts data in generic address space.
+ Set section flags as if it was in __pgm to get the right
+ section prefix in the remainder. */
+
+ if (ADDR_SPACE_GENERIC_P (as))
+ as = ADDR_SPACE_PGM;
+
+ flags |= as * SECTION_MACH_DEP;
+ flags &= ~SECTION_WRITE;
+ flags &= ~SECTION_BSS;
+ }
return flags;
}
/* Implement `TARGET_ENCODE_SECTION_INFO'. */
static void
-avr_encode_section_info (tree decl, rtx rtl,
- int new_decl_p)
+avr_encode_section_info (tree decl, rtx rtl, int new_decl_p)
{
/* In avr_handle_progmem_attribute, DECL_INITIAL is not yet
readily available, see PR34734. So we postpone the warning
}
+/* Implement `TARGET_ASM_SELECT_SECTION' */
+
+static section *
+avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
+{
+ section * sect = default_elf_select_section (decl, reloc, align);
+
+ if (decl && DECL_P (decl)
+ && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+ {
+ addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+ int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+
+ if (sect->common.flags & SECTION_NAMED)
+ {
+ const char * name = sect->named.name;
+ const char * old_prefix = ".rodata";
+ const char * new_prefix = progmem_section_prefix[segment];
+
+ if (STR_PREFIX_P (name, old_prefix))
+ {
+ const char *sname = avr_replace_prefix (name,
+ old_prefix, new_prefix);
+
+ return get_section (sname, sect->common.flags, sect->named.decl);
+ }
+ }
+
+ return progmem_section[segment];
+ }
+
+ return sect;
+}
+
/* Implement `TARGET_ASM_FILE_START'. */
-/* Outputs some appropriate text to go at the start of an assembler
- file. */
+/* Outputs some text at the start of each assembler file. */
static void
avr_file_start (void)
{
+ int sfr_offset = avr_current_arch->sfr_offset;
+
if (avr_current_arch->asm_only)
error ("MCU %qs supported for assembler only", avr_current_device->name);
default_file_start ();
-/* fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/
- fputs ("__SREG__ = 0x3f\n"
- "__SP_H__ = 0x3e\n"
- "__SP_L__ = 0x3d\n", asm_out_file);
-
- fputs ("__tmp_reg__ = 0\n"
- "__zero_reg__ = 1\n", asm_out_file);
+ if (!AVR_HAVE_8BIT_SP)
+ fprintf (asm_out_file,
+ "__SP_H__ = 0x%02x\n",
+ -sfr_offset + SP_ADDR + 1);
+
+ fprintf (asm_out_file,
+ "__SP_L__ = 0x%02x\n"
+ "__SREG__ = 0x%02x\n"
+ "__RAMPZ__ = 0x%02x\n"
+ "__tmp_reg__ = %d\n"
+ "__zero_reg__ = %d\n",
+ -sfr_offset + SP_ADDR,
+ -sfr_offset + SREG_ADDR,
+ -sfr_offset + RAMPZ_ADDR,
+ TMP_REGNO,
+ ZERO_REGNO);
}
/* Implement `TARGET_MEMORY_MOVE_COST' */
static int
-avr_memory_move_cost (enum machine_mode mode, reg_class_t rclass ATTRIBUTE_UNUSED,
+avr_memory_move_cost (enum machine_mode mode,
+ reg_class_t rclass ATTRIBUTE_UNUSED,
bool in ATTRIBUTE_UNUSED)
{
return (mode == QImode ? 2
static int
avr_operand_rtx_cost (rtx x, enum machine_mode mode, enum rtx_code outer,
- bool speed)
+ int opno, bool speed)
{
enum rtx_code code = GET_CODE (x);
int total;
}
total = 0;
- avr_rtx_costs (x, code, outer, &total, speed);
+ avr_rtx_costs (x, code, outer, opno, &total, speed);
return total;
}
-/* The AVR backend's rtx_cost function. X is rtx expression whose cost
- is to be calculated. Return true if the complete cost has been
- computed, and false if subexpressions should be scanned. In either
- case, *TOTAL contains the cost result. */
+/* Worker function for AVR backend's rtx_cost function.
+ X is rtx expression whose cost is to be calculated.
+ Return true if the complete cost has been computed.
+ Return false if subexpressions should be scanned.
+ In either case, *TOTAL contains the cost result. */
static bool
-avr_rtx_costs (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED, int *total,
- bool speed)
+avr_rtx_costs_1 (rtx x, int codearg, int outer_code ATTRIBUTE_UNUSED,
+ int opno ATTRIBUTE_UNUSED, int *total, bool speed)
{
enum rtx_code code = (enum rtx_code) codearg;
enum machine_mode mode = GET_MODE (x);
{
case CONST_INT:
case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
/* Immediate constants are as cheap as registers. */
*total = 0;
return true;
case MEM:
- case CONST:
- case LABEL_REF:
- case SYMBOL_REF:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
return true;
*total = COSTS_N_INSNS (1);
break;
- case HImode:
- *total = COSTS_N_INSNS (3);
- break;
-
- case SImode:
- *total = COSTS_N_INSNS (7);
- break;
+ case HImode:
+ case PSImode:
+ case SImode:
+ *total = COSTS_N_INSNS (2 * GET_MODE_SIZE (mode) - 1);
+ break;
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case ABS:
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case NOT:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case ZERO_EXTEND:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode)
- GET_MODE_SIZE (GET_MODE (XEXP (x, 0))));
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case SIGN_EXTEND:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode) + 2
- GET_MODE_SIZE (GET_MODE (XEXP (x, 0))));
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case PLUS:
switch (mode)
{
case QImode:
+ if (AVR_HAVE_MUL
+ && MULT == GET_CODE (XEXP (x, 0))
+ && register_operand (XEXP (x, 1), QImode))
+ {
+ /* multiply-add */
+ *total = COSTS_N_INSNS (speed ? 4 : 3);
+ /* multiply-add with constant: will be split and load constant. */
+ if (CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ *total = COSTS_N_INSNS (1) + *total;
+ return true;
+ }
*total = COSTS_N_INSNS (1);
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
break;
case HImode:
+ if (AVR_HAVE_MUL
+ && (MULT == GET_CODE (XEXP (x, 0))
+ || ASHIFT == GET_CODE (XEXP (x, 0)))
+ && register_operand (XEXP (x, 1), HImode)
+ && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0))
+ || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 0), 0))))
+ {
+ /* multiply-add */
+ *total = COSTS_N_INSNS (speed ? 5 : 4);
+ /* multiply-add with constant: will be split and load constant. */
+ if (CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ *total = COSTS_N_INSNS (1) + *total;
+ return true;
+ }
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (2);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
*total = COSTS_N_INSNS (1);
*total = COSTS_N_INSNS (2);
break;
+ case PSImode:
+ if (!CONST_INT_P (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (3);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
+ }
+ else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = COSTS_N_INSNS (3);
+ break;
+
case SImode:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (4);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else if (INTVAL (XEXP (x, 1)) >= -63 && INTVAL (XEXP (x, 1)) <= 63)
*total = COSTS_N_INSNS (1);
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case MINUS:
+ if (AVR_HAVE_MUL
+ && QImode == mode
+ && register_operand (XEXP (x, 0), QImode)
+ && MULT == GET_CODE (XEXP (x, 1)))
+ {
+ /* multiply-sub */
+ *total = COSTS_N_INSNS (speed ? 4 : 3);
+ /* multiply-sub with constant: will be split and load constant. */
+ if (CONST_INT_P (XEXP (XEXP (x, 1), 1)))
+ *total = COSTS_N_INSNS (1) + *total;
+ return true;
+ }
+ if (AVR_HAVE_MUL
+ && HImode == mode
+ && register_operand (XEXP (x, 0), HImode)
+ && (MULT == GET_CODE (XEXP (x, 1))
+ || ASHIFT == GET_CODE (XEXP (x, 1)))
+ && (ZERO_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0))
+ || SIGN_EXTEND == GET_CODE (XEXP (XEXP (x, 1), 0))))
+ {
+ /* multiply-sub */
+ *total = COSTS_N_INSNS (speed ? 5 : 4);
+ /* multiply-sub with constant: will be split and load constant. */
+ if (CONST_INT_P (XEXP (XEXP (x, 1), 1)))
+ *total = COSTS_N_INSNS (1) + *total;
+ return true;
+ }
+ /* FALLTHRU */
case AND:
case IOR:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
return true;
case XOR:
*total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
return true;
case MULT:
case HImode:
if (AVR_HAVE_MUL)
- *total = COSTS_N_INSNS (!speed ? 7 : 10);
+ {
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+ enum rtx_code code0 = GET_CODE (op0);
+ enum rtx_code code1 = GET_CODE (op1);
+ bool ex0 = SIGN_EXTEND == code0 || ZERO_EXTEND == code0;
+ bool ex1 = SIGN_EXTEND == code1 || ZERO_EXTEND == code1;
+
+ if (ex0
+ && (u8_operand (op1, HImode)
+ || s8_operand (op1, HImode)))
+ {
+ *total = COSTS_N_INSNS (!speed ? 4 : 6);
+ return true;
+ }
+ if (ex0
+ && register_operand (op1, HImode))
+ {
+ *total = COSTS_N_INSNS (!speed ? 5 : 8);
+ return true;
+ }
+ else if (ex0 || ex1)
+ {
+ *total = COSTS_N_INSNS (!speed ? 3 : 5);
+ return true;
+ }
+ else if (register_operand (op0, HImode)
+ && (u8_operand (op1, HImode)
+ || s8_operand (op1, HImode)))
+ {
+ *total = COSTS_N_INSNS (!speed ? 6 : 9);
+ return true;
+ }
+ else
+ *total = COSTS_N_INSNS (!speed ? 7 : 10);
+ }
else if (!speed)
*total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
else
return false;
break;
+ case PSImode:
+ if (!speed)
+ *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
+ else
+ *total = 10;
+ break;
+
+ case SImode:
+ if (AVR_HAVE_MUL)
+ {
+ if (!speed)
+ {
+ /* Add some additional costs besides CALL like moves etc. */
+
+ *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4);
+ }
+ else
+ {
+ /* Just a rough estimate. Even with -O2 we don't want bulky
+ code expanded inline. */
+
+ *total = COSTS_N_INSNS (25);
+ }
+ }
+ else
+ {
+ if (speed)
+ *total = COSTS_N_INSNS (300);
+ else
+ /* Add some additional costs besides CALL like moves etc. */
+ *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 5 : 4);
+ }
+
+ return true;
+
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
return true;
case DIV:
case UDIV:
case UMOD:
if (!speed)
- *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
+ *total = COSTS_N_INSNS (AVR_HAVE_JMP_CALL ? 2 : 1);
else
- return false;
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total = COSTS_N_INSNS (15 * GET_MODE_SIZE (mode));
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
+ /* For div/mod with const-int divisor we have at least the cost of
+ loading the divisor. */
+ if (CONST_INT_P (XEXP (x, 1)))
+ *total += COSTS_N_INSNS (GET_MODE_SIZE (mode));
+ /* Add some overall penaly for clobbering and moving around registers */
+ *total += COSTS_N_INSNS (2);
return true;
case ROTATE:
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case ASHIFT:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 4 : 17);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
{
break;
case HImode:
+ if (AVR_HAVE_MUL)
+ {
+ if (const_2_to_7_operand (XEXP (x, 1), HImode)
+ && (SIGN_EXTEND == GET_CODE (XEXP (x, 0))
+ || ZERO_EXTEND == GET_CODE (XEXP (x, 0))))
+ {
+ *total = COSTS_N_INSNS (!speed ? 4 : 6);
+ return true;
+ }
+ }
+
+ if (const1_rtx == (XEXP (x, 1))
+ && SIGN_EXTEND == GET_CODE (XEXP (x, 0)))
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
+ case PSImode:
+ if (!CONST_INT_P (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (!speed ? 6 : 73);
+ }
+ else
+ switch (INTVAL (XEXP (x, 1)))
+ {
+ case 0:
+ *total = 0;
+ break;
+ case 1:
+ case 8:
+ case 16:
+ *total = COSTS_N_INSNS (3);
+ break;
+ case 23:
+ *total = COSTS_N_INSNS (5);
+ break;
+ default:
+ *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+ break;
+ }
+ break;
+
case SImode:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case ASHIFTRT:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 4 : 17);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
{
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
+ case PSImode:
+ if (!CONST_INT_P (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (!speed ? 6 : 73);
+ }
+ else
+ switch (INTVAL (XEXP (x, 1)))
+ {
+ case 0:
+ *total = 0;
+ break;
+ case 1:
+ *total = COSTS_N_INSNS (3);
+ break;
+ case 16:
+ case 8:
+ *total = COSTS_N_INSNS (5);
+ break;
+ case 23:
+ *total = COSTS_N_INSNS (4);
+ break;
+ default:
+ *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+ break;
+ }
+ break;
+
case SImode:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case LSHIFTRT:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 4 : 17);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
{
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 5 : 41);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
+ case PSImode:
+ if (!CONST_INT_P (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (!speed ? 6 : 73);
+ }
+ else
+ switch (INTVAL (XEXP (x, 1)))
+ {
+ case 0:
+ *total = 0;
+ break;
+ case 1:
+ case 8:
+ case 16:
+ *total = COSTS_N_INSNS (3);
+ break;
+ case 23:
+ *total = COSTS_N_INSNS (5);
+ break;
+ default:
+ *total = COSTS_N_INSNS (!speed ? 5 : 3 * INTVAL (XEXP (x, 1)));
+ break;
+ }
+ break;
+
case SImode:
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
{
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
else
switch (INTVAL (XEXP (x, 1)))
break;
default:
*total = COSTS_N_INSNS (!speed ? 7 : 113);
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1,
+ speed);
}
break;
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
case COMPARE:
case QImode:
*total = COSTS_N_INSNS (1);
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
break;
case HImode:
*total = COSTS_N_INSNS (2);
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
else if (INTVAL (XEXP (x, 1)) != 0)
*total += COSTS_N_INSNS (1);
break;
+ case PSImode:
+ *total = COSTS_N_INSNS (3);
+ if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) != 0)
+ *total += COSTS_N_INSNS (2);
+ break;
+
case SImode:
*total = COSTS_N_INSNS (4);
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 1), mode, code, 1, speed);
else if (INTVAL (XEXP (x, 1)) != 0)
*total += COSTS_N_INSNS (3);
break;
default:
return false;
}
- *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, speed);
+ *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed);
return true;
+ case TRUNCATE:
+ if (AVR_HAVE_MUL
+ && LSHIFTRT == GET_CODE (XEXP (x, 0))
+ && MULT == GET_CODE (XEXP (XEXP (x, 0), 0))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ {
+ if (QImode == mode || HImode == mode)
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+ }
+ break;
+
default:
break;
}
return false;
}
-/* Calculate the cost of a memory address. */
+
+/* Implement `TARGET_RTX_COSTS'. */
+
+static bool
+avr_rtx_costs (rtx x, int codearg, int outer_code,
+ int opno, int *total, bool speed)
+{
+ bool done = avr_rtx_costs_1 (x, codearg, outer_code,
+ opno, total, speed);
+
+ if (avr_log.rtx_costs)
+ {
+ avr_edump ("\n%?=%b (%s) total=%d, outer=%C:\n%r\n",
+ done, speed ? "speed" : "size", *total, outer_code, x);
+ }
+
+ return done;
+}
+
+
+/* Implement `TARGET_ADDRESS_COST'. */
static int
avr_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
{
+ int cost = 4;
+
if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x,1)) == CONST_INT
- && (REG_P (XEXP (x,0)) || GET_CODE (XEXP (x,0)) == SUBREG)
- && INTVAL (XEXP (x,1)) >= 61)
- return 18;
- if (CONSTANT_ADDRESS_P (x))
+ && CONST_INT_P (XEXP (x, 1))
+ && (REG_P (XEXP (x, 0))
+ || GET_CODE (XEXP (x, 0)) == SUBREG))
+ {
+ if (INTVAL (XEXP (x, 1)) >= 61)
+ cost = 18;
+ }
+ else if (CONSTANT_ADDRESS_P (x))
+ {
+ if (optimize > 0
+ && io_address_operand (x, QImode))
+ cost = 2;
+ }
+
+ if (avr_log.address_cost)
+ avr_edump ("\n%?: %d = %r\n", cost, x);
+
+ return cost;
+}
+
+/* Test for extra memory constraint 'Q'.
+ It's a memory address based on Y or Z pointer with valid displacement. */
+
+int
+extra_constraint_Q (rtx x)
+{
+ int ok = 0;
+
+ if (GET_CODE (XEXP (x,0)) == PLUS
+ && REG_P (XEXP (XEXP (x,0), 0))
+ && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT
+ && (INTVAL (XEXP (XEXP (x,0), 1))
+ <= MAX_LD_OFFSET (GET_MODE (x))))
+ {
+ rtx xx = XEXP (XEXP (x,0), 0);
+ int regno = REGNO (xx);
+
+ ok = (/* allocate pseudos */
+ regno >= FIRST_PSEUDO_REGISTER
+ /* strictly check */
+ || regno == REG_Z || regno == REG_Y
+ /* XXX frame & arg pointer checks */
+ || xx == frame_pointer_rtx
+ || xx == arg_pointer_rtx);
+
+ if (avr_log.constraints)
+ avr_edump ("\n%?=%d reload_completed=%d reload_in_progress=%d\n %r\n",
+ ok, reload_completed, reload_in_progress, x);
+ }
+
+ return ok;
+}
+
+/* Convert condition code CONDITION to the valid AVR condition code. */
+
+RTX_CODE
+avr_normalize_condition (RTX_CODE condition)
+{
+ switch (condition)
+ {
+ case GT:
+ return GE;
+ case GTU:
+ return GEU;
+ case LE:
+ return LT;
+ case LEU:
+ return LTU;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Helper function for `avr_reorg'. */
+
+static rtx
+avr_compare_pattern (rtx insn)
+{
+ rtx pattern = single_set (insn);
+
+ if (pattern
+ && NONJUMP_INSN_P (insn)
+ && SET_DEST (pattern) == cc0_rtx
+ && GET_CODE (SET_SRC (pattern)) == COMPARE
+ && DImode != GET_MODE (XEXP (SET_SRC (pattern), 0))
+ && DImode != GET_MODE (XEXP (SET_SRC (pattern), 1)))
+ {
+ return pattern;
+ }
+
+ return NULL_RTX;
+}
+
+/* Helper function for `avr_reorg'. */
+
+/* Expansion of switch/case decision trees leads to code like
+
+ cc0 = compare (Reg, Num)
+ if (cc0 == 0)
+ goto L1
+
+ cc0 = compare (Reg, Num)
+ if (cc0 > 0)
+ goto L2
+
+ The second comparison is superfluous and can be deleted.
+ The second jump condition can be transformed from a
+ "difficult" one to a "simple" one because "cc0 > 0" and
+ "cc0 >= 0" will have the same effect here.
+
+ This function relies on the way switch/case is being expaned
+ as binary decision tree. For example code see PR 49903.
+
+ Return TRUE if optimization performed.
+ Return FALSE if nothing changed.
+
+ INSN1 is a comparison, i.e. avr_compare_pattern != 0.
+
+ We don't want to do this in text peephole because it is
+ tedious to work out jump offsets there and the second comparison
+ might have been transormed by `avr_reorg'.
+
+ RTL peephole won't do because peephole2 does not scan across
+ basic blocks. */
+
+static bool
+avr_reorg_remove_redundant_compare (rtx insn1)
+{
+ rtx comp1, ifelse1, xcond1, branch1;
+ rtx comp2, ifelse2, xcond2, branch2, insn2;
+ enum rtx_code code;
+ rtx jump, target, cond;
+
+ /* Look out for: compare1 - branch1 - compare2 - branch2 */
+
+ branch1 = next_nonnote_nondebug_insn (insn1);
+ if (!branch1 || !JUMP_P (branch1))
+ return false;
+
+ insn2 = next_nonnote_nondebug_insn (branch1);
+ if (!insn2 || !avr_compare_pattern (insn2))
+ return false;
+
+ branch2 = next_nonnote_nondebug_insn (insn2);
+ if (!branch2 || !JUMP_P (branch2))
+ return false;
+
+ comp1 = avr_compare_pattern (insn1);
+ comp2 = avr_compare_pattern (insn2);
+ xcond1 = single_set (branch1);
+ xcond2 = single_set (branch2);
+
+ if (!comp1 || !comp2
+ || !rtx_equal_p (comp1, comp2)
+ || !xcond1 || SET_DEST (xcond1) != pc_rtx
+ || !xcond2 || SET_DEST (xcond2) != pc_rtx
+ || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1))
+ || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2)))
{
- if (optimize > 0 && io_address_operand (x, QImode))
- return 2;
- return 4;
+ return false;
}
- return 4;
-}
-
-/* Test for extra memory constraint 'Q'.
- It's a memory address based on Y or Z pointer with valid displacement. */
-int
-extra_constraint_Q (rtx x)
-{
- if (GET_CODE (XEXP (x,0)) == PLUS
- && REG_P (XEXP (XEXP (x,0), 0))
- && GET_CODE (XEXP (XEXP (x,0), 1)) == CONST_INT
- && (INTVAL (XEXP (XEXP (x,0), 1))
- <= MAX_LD_OFFSET (GET_MODE (x))))
+ comp1 = SET_SRC (comp1);
+ ifelse1 = SET_SRC (xcond1);
+ ifelse2 = SET_SRC (xcond2);
+
+ /* comp<n> is COMPARE now and ifelse<n> is IF_THEN_ELSE. */
+
+ if (EQ != GET_CODE (XEXP (ifelse1, 0))
+ || !REG_P (XEXP (comp1, 0))
+ || !CONST_INT_P (XEXP (comp1, 1))
+ || XEXP (ifelse1, 2) != pc_rtx
+ || XEXP (ifelse2, 2) != pc_rtx
+ || LABEL_REF != GET_CODE (XEXP (ifelse1, 1))
+ || LABEL_REF != GET_CODE (XEXP (ifelse2, 1))
+ || !COMPARISON_P (XEXP (ifelse2, 0))
+ || cc0_rtx != XEXP (XEXP (ifelse1, 0), 0)
+ || cc0_rtx != XEXP (XEXP (ifelse2, 0), 0)
+ || const0_rtx != XEXP (XEXP (ifelse1, 0), 1)
+ || const0_rtx != XEXP (XEXP (ifelse2, 0), 1))
{
- rtx xx = XEXP (XEXP (x,0), 0);
- int regno = REGNO (xx);
- if (TARGET_ALL_DEBUG)
- {
- fprintf (stderr, ("extra_constraint:\n"
- "reload_completed: %d\n"
- "reload_in_progress: %d\n"),
- reload_completed, reload_in_progress);
- debug_rtx (x);
- }
- if (regno >= FIRST_PSEUDO_REGISTER)
- return 1; /* allocate pseudos */
- else if (regno == REG_Z || regno == REG_Y)
- return 1; /* strictly check */
- else if (xx == frame_pointer_rtx
- || xx == arg_pointer_rtx)
- return 1; /* XXX frame & arg pointer checks */
+ return false;
}
- return 0;
-}
-/* Convert condition code CONDITION to the valid AVR condition code. */
+ /* We filtered the insn sequence to look like
+
+ (set (cc0)
+ (compare (reg:M N)
+ (const_int VAL)))
+ (set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (label_ref L1)
+ (pc)))
+
+ (set (cc0)
+ (compare (reg:M N)
+ (const_int VAL)))
+ (set (pc)
+ (if_then_else (CODE (cc0)
+ (const_int 0))
+ (label_ref L2)
+ (pc)))
+ */
+
+ code = GET_CODE (XEXP (ifelse2, 0));
+
+ /* Map GT/GTU to GE/GEU which is easier for AVR.
+ The first two instructions compare/branch on EQ
+ so we may replace the difficult
+
+ if (x == VAL) goto L1;
+ if (x > VAL) goto L2;
-RTX_CODE
-avr_normalize_condition (RTX_CODE condition)
-{
- switch (condition)
+ with easy
+
+ if (x == VAL) goto L1;
+ if (x >= VAL) goto L2;
+
+ Similarly, replace LE/LEU by LT/LTU. */
+
+ switch (code)
{
- case GT:
- return GE;
- case GTU:
- return GEU;
- case LE:
- return LT;
- case LEU:
- return LTU;
+ case EQ:
+ case LT: case LTU:
+ case GE: case GEU:
+ break;
+
+ case LE: case LEU:
+ case GT: case GTU:
+ code = avr_normalize_condition (code);
+ break;
+
default:
- gcc_unreachable ();
+ return false;
}
+
+ /* Wrap the branches into UNSPECs so they won't be changed or
+ optimized in the remainder. */
+
+ target = XEXP (XEXP (ifelse1, 1), 0);
+ cond = XEXP (ifelse1, 0);
+ jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1);
+
+ JUMP_LABEL (jump) = JUMP_LABEL (branch1);
+
+ target = XEXP (XEXP (ifelse2, 1), 0);
+ cond = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
+ jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2);
+
+ JUMP_LABEL (jump) = JUMP_LABEL (branch2);
+
+ /* The comparisons in insn1 and insn2 are exactly the same;
+ insn2 is superfluous so delete it. */
+
+ delete_insn (insn2);
+ delete_insn (branch1);
+ delete_insn (branch2);
+
+ return true;
}
-/* This function optimizes conditional jumps. */
+
+/* Implement `TARGET_MACHINE_DEPENDENT_REORG'. */
+/* Optimize conditional jumps. */
static void
avr_reorg (void)
{
- rtx insn, pattern;
+ rtx insn = get_insns();
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn))
{
- if (! (GET_CODE (insn) == INSN
- || GET_CODE (insn) == CALL_INSN
- || GET_CODE (insn) == JUMP_INSN)
- || !single_set (insn))
- continue;
+ rtx pattern = avr_compare_pattern (insn);
+
+ if (!pattern)
+ continue;
- pattern = PATTERN (insn);
+ if (optimize
+ && avr_reorg_remove_redundant_compare (insn))
+ {
+ continue;
+ }
- if (GET_CODE (pattern) == PARALLEL)
- pattern = XVECEXP (pattern, 0, 0);
- if (GET_CODE (pattern) == SET
- && SET_DEST (pattern) == cc0_rtx
- && compare_diff_p (insn))
+ if (compare_diff_p (insn))
{
- if (GET_CODE (SET_SRC (pattern)) == COMPARE)
- {
- /* Now we work under compare insn. */
-
- pattern = SET_SRC (pattern);
- if (true_regnum (XEXP (pattern,0)) >= 0
- && true_regnum (XEXP (pattern,1)) >= 0 )
- {
- rtx x = XEXP (pattern,0);
- rtx next = next_real_insn (insn);
- rtx pat = PATTERN (next);
- rtx src = SET_SRC (pat);
- rtx t = XEXP (src,0);
- PUT_CODE (t, swap_condition (GET_CODE (t)));
- XEXP (pattern,0) = XEXP (pattern,1);
- XEXP (pattern,1) = x;
- INSN_CODE (next) = -1;
- }
- else if (true_regnum (XEXP (pattern, 0)) >= 0
- && XEXP (pattern, 1) == const0_rtx)
- {
- /* This is a tst insn, we can reverse it. */
- rtx next = next_real_insn (insn);
- rtx pat = PATTERN (next);
- rtx src = SET_SRC (pat);
- rtx t = XEXP (src,0);
+ /* Now we work under compare insn with difficult branch. */
+
+ rtx next = next_real_insn (insn);
+ rtx pat = PATTERN (next);
+
+ pattern = SET_SRC (pattern);
+
+ if (true_regnum (XEXP (pattern, 0)) >= 0
+ && true_regnum (XEXP (pattern, 1)) >= 0)
+ {
+ rtx x = XEXP (pattern, 0);
+ rtx src = SET_SRC (pat);
+ rtx t = XEXP (src,0);
+ PUT_CODE (t, swap_condition (GET_CODE (t)));
+ XEXP (pattern, 0) = XEXP (pattern, 1);
+ XEXP (pattern, 1) = x;
+ INSN_CODE (next) = -1;
+ }
+ else if (true_regnum (XEXP (pattern, 0)) >= 0
+ && XEXP (pattern, 1) == const0_rtx)
+ {
+ /* This is a tst insn, we can reverse it. */
+ rtx src = SET_SRC (pat);
+ rtx t = XEXP (src,0);
- PUT_CODE (t, swap_condition (GET_CODE (t)));
- XEXP (pattern, 1) = XEXP (pattern, 0);
- XEXP (pattern, 0) = const0_rtx;
- INSN_CODE (next) = -1;
- INSN_CODE (insn) = -1;
- }
- else if (true_regnum (XEXP (pattern,0)) >= 0
- && GET_CODE (XEXP (pattern,1)) == CONST_INT)
- {
- rtx x = XEXP (pattern,1);
- rtx next = next_real_insn (insn);
- rtx pat = PATTERN (next);
- rtx src = SET_SRC (pat);
- rtx t = XEXP (src,0);
- enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
-
- if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
- {
- XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
- PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
- INSN_CODE (next) = -1;
- INSN_CODE (insn) = -1;
- }
- }
- }
- }
+ PUT_CODE (t, swap_condition (GET_CODE (t)));
+ XEXP (pattern, 1) = XEXP (pattern, 0);
+ XEXP (pattern, 0) = const0_rtx;
+ INSN_CODE (next) = -1;
+ INSN_CODE (insn) = -1;
+ }
+ else if (true_regnum (XEXP (pattern, 0)) >= 0
+ && CONST_INT_P (XEXP (pattern, 1)))
+ {
+ rtx x = XEXP (pattern, 1);
+ rtx src = SET_SRC (pat);
+ rtx t = XEXP (src,0);
+ enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
+
+ if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
+ {
+ XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
+ PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
+ INSN_CODE (next) = -1;
+ INSN_CODE (insn) = -1;
+ }
+ }
+ }
}
}
const_rtx func ATTRIBUTE_UNUSED)
{
int offs = GET_MODE_SIZE (mode);
- if (offs < 2)
- offs = 2;
+
+ if (offs <= 4)
+ offs = (offs + 1) & ~1;
+
return gen_rtx_REG (mode, avr_ret_register () + 2 - offs);
}
}
+/* Helper for jump_over_one_insn_p: Test if INSN is a 2-word instruction
+ and thus is suitable to be skipped by CPSE, SBRC, etc. */
+
+static bool
+avr_2word_insn_p (rtx insn)
+{
+ if (avr_current_device->errata_skip
+ || !insn
+ || 2 != get_attr_length (insn))
+ {
+ return false;
+ }
+
+ switch (INSN_CODE (insn))
+ {
+ default:
+ return false;
+
+ case CODE_FOR_movqi_insn:
+ {
+ rtx set = single_set (insn);
+ rtx src = SET_SRC (set);
+ rtx dest = SET_DEST (set);
+
+ /* Factor out LDS and STS from movqi_insn. */
+
+ if (MEM_P (dest)
+ && (REG_P (src) || src == const0_rtx))
+ {
+ return CONSTANT_ADDRESS_P (XEXP (dest, 0));
+ }
+ else if (REG_P (dest)
+ && MEM_P (src))
+ {
+ return CONSTANT_ADDRESS_P (XEXP (src, 0));
+ }
+
+ return false;
+ }
+
+ case CODE_FOR_call_insn:
+ case CODE_FOR_call_value_insn:
+ return true;
+ }
+}
+
+
int
jump_over_one_insn_p (rtx insn, rtx dest)
{
: dest);
int jump_addr = INSN_ADDRESSES (INSN_UID (insn));
int dest_addr = INSN_ADDRESSES (uid);
- return dest_addr - jump_addr == get_attr_length (insn) + 1;
+ int jump_offset = dest_addr - jump_addr - get_attr_length (insn);
+
+ return (jump_offset == 1
+ || (jump_offset == 2
+ && avr_2word_insn_p (next_active_insn (insn))));
}
/* Returns 1 if a value of mode MODE can be stored starting with hard
return !(regno & 1);
}
-const char *
-output_reload_inhi (rtx insn ATTRIBUTE_UNUSED, rtx *operands, int *len)
+
+/* Implement `MODE_CODE_BASE_REG_CLASS'. */
+
+reg_class_t
+avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
+ addr_space_t as, RTX_CODE outer_code,
+ RTX_CODE index_code ATTRIBUTE_UNUSED)
{
- int tmp;
- if (!len)
- len = &tmp;
+ if (!ADDR_SPACE_GENERIC_P (as))
+ {
+ return POINTER_Z_REGS;
+ }
+
+ if (!avr_strict_X)
+ return reload_completed ? BASE_POINTER_REGS : POINTER_REGS;
+
+ return PLUS == outer_code ? BASE_POINTER_REGS : POINTER_REGS;
+}
+
+
+/* Implement `REGNO_MODE_CODE_OK_FOR_BASE_P'. */
+
+bool
+avr_regno_mode_code_ok_for_base_p (int regno,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ addr_space_t as ATTRIBUTE_UNUSED,
+ RTX_CODE outer_code,
+ RTX_CODE index_code ATTRIBUTE_UNUSED)
+{
+ bool ok = false;
+
+ if (!ADDR_SPACE_GENERIC_P (as))
+ {
+ if (regno < FIRST_PSEUDO_REGISTER
+ && regno == REG_Z)
+ {
+ return true;
+ }
+
+ if (reg_renumber)
+ {
+ regno = reg_renumber[regno];
+
+ if (regno == REG_Z)
+ {
+ return true;
+ }
+ }
- if (GET_CODE (operands[1]) == CONST_INT)
+ return false;
+ }
+
+ if (regno < FIRST_PSEUDO_REGISTER
+ && (regno == REG_X
+ || regno == REG_Y
+ || regno == REG_Z
+ || regno == ARG_POINTER_REGNUM))
{
- int val = INTVAL (operands[1]);
- if ((val & 0xff) == 0)
- {
- *len = 3;
- return (AS2 (mov,%A0,__zero_reg__) CR_TAB
- AS2 (ldi,%2,hi8(%1)) CR_TAB
- AS2 (mov,%B0,%2));
- }
- else if ((val & 0xff00) == 0)
- {
- *len = 3;
- return (AS2 (ldi,%2,lo8(%1)) CR_TAB
- AS2 (mov,%A0,%2) CR_TAB
- AS2 (mov,%B0,__zero_reg__));
- }
- else if ((val & 0xff) == ((val & 0xff00) >> 8))
- {
- *len = 3;
- return (AS2 (ldi,%2,lo8(%1)) CR_TAB
- AS2 (mov,%A0,%2) CR_TAB
- AS2 (mov,%B0,%2));
- }
+ ok = true;
+ }
+ else if (reg_renumber)
+ {
+ regno = reg_renumber[regno];
+
+ if (regno == REG_X
+ || regno == REG_Y
+ || regno == REG_Z
+ || regno == ARG_POINTER_REGNUM)
+ {
+ ok = true;
+ }
+ }
+
+ if (avr_strict_X
+ && PLUS == outer_code
+ && regno == REG_X)
+ {
+ ok = false;
}
- *len = 4;
- return (AS2 (ldi,%2,lo8(%1)) CR_TAB
- AS2 (mov,%A0,%2) CR_TAB
- AS2 (ldi,%2,hi8(%1)) CR_TAB
- AS2 (mov,%B0,%2));
+
+ return ok;
}
-/* Reload a SI or SF compile time constant (OP[1]) into a GPR (OP[0]).
- CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
- into a NO_LD_REGS. If CLOBBER_REG is NULL_RTX we either don't need a
- clobber reg or have to cook one up.
+/* A helper for `output_reload_insisf' and `output_reload_inhi'. */
+/* Set 32-bit register OP[0] to compile-time constant OP[1].
+ CLOBBER_REG is a QI clobber register or NULL_RTX.
+ LEN == NULL: output instructions.
+ LEN != NULL: set *LEN to the length of the instruction sequence
+ (in words) printed with LEN = NULL.
+ If CLEAR_P is true, OP[0] had been cleard to Zero already.
+ If CLEAR_P is false, nothing is known about OP[0].
- LEN == NULL: Output instructions.
-
- LEN != NULL: Output nothing. Increment *LEN by number of words occupied
- by the insns printed.
+ The effect on cc0 is as follows:
- Return "". */
+ Load 0 to any register except ZERO_REG : NONE
+ Load ld register with any value : NONE
+ Anything else: : CLOBBER */
-const char *
-output_reload_insisf (rtx insn ATTRIBUTE_UNUSED,
- rtx *op, rtx clobber_reg, int *len)
+static void
+output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p)
{
rtx src = op[1];
rtx dest = op[0];
int clobber_val = 1234;
bool cooked_clobber_p = false;
bool set_p = false;
- unsigned int n;
enum machine_mode mode = GET_MODE (dest);
+ int n, n_bytes = GET_MODE_SIZE (mode);
- gcc_assert (REG_P (dest));
+ gcc_assert (REG_P (dest)
+ && CONSTANT_P (src));
if (len)
*len = 0;
/* (REG:SI 14) is special: It's neither in LD_REGS nor in NO_LD_REGS
but has some subregs that are in LD_REGS. Use the MSB (REG:QI 17). */
- if (14 == REGNO (dest))
+ if (REGNO (dest) < 16
+ && REGNO (dest) + GET_MODE_SIZE (mode) > 16)
{
- clobber_reg = gen_rtx_REG (QImode, 17);
+ clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1];
}
- /* We might need a clobber reg but don't have one. Look at the value
- to be loaded more closely. A clobber is only needed if it contains
- a byte that is neither 0, -1 or a power of 2. */
+ /* We might need a clobber reg but don't have one. Look at the value to
+ be loaded more closely. A clobber is only needed if it is a symbol
+ or contains a byte that is neither 0, -1 or a power of 2. */
if (NULL_RTX == clobber_reg
- && !test_hard_reg_class (LD_REGS, dest))
+ && !test_hard_reg_class (LD_REGS, dest)
+ && (! (CONST_INT_P (src) || CONST_DOUBLE_P (src))
+ || !avr_popcount_each_byte (src, n_bytes,
+ (1 << 0) | (1 << 1) | (1 << 8))))
{
- for (n = 0; n < GET_MODE_SIZE (mode); n++)
- {
- xval = simplify_gen_subreg (QImode, src, mode, n);
-
- if (!(const0_rtx == xval
- || constm1_rtx == xval
- || single_one_operand (xval, QImode)))
- {
- /* We have no clobber reg but need one. Cook one up.
- That's cheaper than loading from constant pool. */
-
- cooked_clobber_p = true;
- clobber_reg = gen_rtx_REG (QImode, REG_Z + 1);
- avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
- break;
- }
- }
+ /* We have no clobber register but need one. Cook one up.
+ That's cheaper than loading from constant pool. */
+
+ cooked_clobber_p = true;
+ clobber_reg = all_regs_rtx[REG_Z + 1];
+ avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1);
}
/* Now start filling DEST from LSB to MSB. */
- for (n = 0; n < GET_MODE_SIZE (mode); n++)
+ for (n = 0; n < n_bytes; n++)
{
+ int ldreg_p;
bool done_byte = false;
- unsigned int j;
+ int j;
rtx xop[3];
- /* Crop the n-th sub-byte. */
-
- xval = simplify_gen_subreg (QImode, src, mode, n);
+ /* Crop the n-th destination byte. */
+
xdest[n] = simplify_gen_subreg (QImode, dest, mode, n);
+ ldreg_p = test_hard_reg_class (LD_REGS, xdest[n]);
+
+ if (!CONST_INT_P (src)
+ && !CONST_DOUBLE_P (src))
+ {
+ static const char* const asm_code[][2] =
+ {
+ { "ldi %2,lo8(%1)" CR_TAB "mov %0,%2", "ldi %0,lo8(%1)" },
+ { "ldi %2,hi8(%1)" CR_TAB "mov %0,%2", "ldi %0,hi8(%1)" },
+ { "ldi %2,hlo8(%1)" CR_TAB "mov %0,%2", "ldi %0,hlo8(%1)" },
+ { "ldi %2,hhi8(%1)" CR_TAB "mov %0,%2", "ldi %0,hhi8(%1)" }
+ };
+
+ xop[0] = xdest[n];
+ xop[1] = src;
+ xop[2] = clobber_reg;
+
+ if (n >= 2 + (avr_current_arch->n_segments > 1))
+ avr_asm_len ("mov %0,__zero_reg__", xop, len, 1);
+ else
+ avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2);
+ continue;
+ }
+
+ /* Crop the n-th source byte. */
+
+ xval = simplify_gen_subreg (QImode, src, mode, n);
ival[n] = INTVAL (xval);
/* Look if we can reuse the low word by means of MOVW. */
if (n == 2
+ && n_bytes >= 4
&& AVR_HAVE_MOVW)
{
rtx lo16 = simplify_gen_subreg (HImode, src, mode, 0);
if (INTVAL (lo16) == INTVAL (hi16))
{
- avr_asm_len ("movw %C0,%A0", &op[0], len, 1);
+ if (0 != INTVAL (lo16)
+ || !clear_p)
+ {
+ avr_asm_len ("movw %C0,%A0", &op[0], len, 1);
+ }
+
break;
}
}
- /* Use CLR to zero a value so that cc0 is set as expected
- for zero. */
+ /* Don't use CLR so that cc0 is set as expected. */
if (ival[n] == 0)
{
- avr_asm_len ("clr %0", &xdest[n], len, 1);
+ if (!clear_p)
+ avr_asm_len (ldreg_p ? "ldi %0,0"
+ : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0"
+ : "mov %0,__zero_reg__",
+ &xdest[n], len, 1);
continue;
}
/* LD_REGS can use LDI to move a constant value */
- if (test_hard_reg_class (LD_REGS, xdest[n]))
+ if (ldreg_p)
{
xop[0] = xdest[n];
xop[1] = xval;
if (-1 == ival[n])
{
- avr_asm_len ("clr %0" CR_TAB
- "dec %0", &xdest[n], len, 2);
+ if (!clear_p)
+ avr_asm_len ("clr %0", &xdest[n], len, 1);
+
+ avr_asm_len ("dec %0", &xdest[n], len, 1);
+ continue;
+ }
+ else if (1 == ival[n])
+ {
+ if (!clear_p)
+ avr_asm_len ("clr %0", &xdest[n], len, 1);
+
+ avr_asm_len ("inc %0", &xdest[n], len, 1);
continue;
}
if (NULL_RTX == clobber_reg
&& single_one_operand (xval, QImode))
{
- if (1 == ival[n])
- {
- avr_asm_len ("clr %0" CR_TAB
- "inc %0", &xdest[n], len, 2);
- continue;
- }
-
xop[0] = xdest[n];
xop[1] = GEN_INT (exact_log2 (ival[n] & GET_MODE_MASK (QImode)));
avr_asm_len ("set", xop, len, 1);
}
- avr_asm_len ("clr %0" CR_TAB
- "bld %0,%1", xop, len, 2);
- continue;
+ if (!clear_p)
+ avr_asm_len ("clr %0", xop, len, 1);
+
+ avr_asm_len ("bld %0,%1", xop, len, 1);
+ continue;
+ }
+
+ /* We actually need the LD_REGS clobber reg. */
+
+ gcc_assert (NULL_RTX != clobber_reg);
+
+ xop[0] = xdest[n];
+ xop[1] = xval;
+ xop[2] = clobber_reg;
+ clobber_val = ival[n];
+
+ avr_asm_len ("ldi %2,lo8(%1)" CR_TAB
+ "mov %0,%2", xop, len, 2);
+ }
+
+ /* If we cooked up a clobber reg above, restore it. */
+
+ if (cooked_clobber_p)
+ {
+ avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1);
+ }
+}
+
+
+/* Reload the constant OP[1] into the HI register OP[0].
+ CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
+ into a NO_LD_REGS register. If CLOBBER_REG is NULL_RTX we either don't
+ need a clobber reg or have to cook one up.
+
+ PLEN == NULL: Output instructions.
+ PLEN != NULL: Output nothing. Set *PLEN to number of words occupied
+ by the insns printed.
+
+ Return "". */
+
+const char*
+output_reload_inhi (rtx *op, rtx clobber_reg, int *plen)
+{
+ output_reload_in_const (op, clobber_reg, plen, false);
+ return "";
+}
+
+
+/* Reload a SI or SF compile time constant OP[1] into the register OP[0].
+ CLOBBER_REG is a QI clobber reg needed to move vast majority of consts
+ into a NO_LD_REGS register. If CLOBBER_REG is NULL_RTX we either don't
+ need a clobber reg or have to cook one up.
+
+ LEN == NULL: Output instructions.
+
+ LEN != NULL: Output nothing. Set *LEN to number of words occupied
+ by the insns printed.
+
+ Return "". */
+
+const char *
+output_reload_insisf (rtx *op, rtx clobber_reg, int *len)
+{
+ if (AVR_HAVE_MOVW
+ && !test_hard_reg_class (LD_REGS, op[0])
+ && (CONST_INT_P (op[1])
+ || CONST_DOUBLE_P (op[1])))
+ {
+ int len_clr, len_noclr;
+
+ /* In some cases it is better to clear the destination beforehand, e.g.
+
+ CLR R2 CLR R3 MOVW R4,R2 INC R2
+
+ is shorther than
+
+ CLR R2 INC R2 CLR R3 CLR R4 CLR R5
+
+ We find it too tedious to work that out in the print function.
+ Instead, we call the print function twice to get the lengths of
+ both methods and use the shortest one. */
+
+ output_reload_in_const (op, clobber_reg, &len_clr, true);
+ output_reload_in_const (op, clobber_reg, &len_noclr, false);
+
+ if (len_noclr - len_clr == 4)
+ {
+ /* Default needs 4 CLR instructions: clear register beforehand. */
+
+ avr_asm_len ("mov %A0,__zero_reg__" CR_TAB
+ "mov %B0,__zero_reg__" CR_TAB
+ "movw %C0,%A0", &op[0], len, 3);
+
+ output_reload_in_const (op, clobber_reg, len, true);
+
+ if (len)
+ *len += 3;
+
+ return "";
}
+ }
- /* We actually need the LD_REGS clobber reg. */
+ /* Default: destination not pre-cleared. */
- gcc_assert (NULL_RTX != clobber_reg);
-
- xop[0] = xdest[n];
- xop[1] = xval;
- xop[2] = clobber_reg;
- clobber_val = ival[n];
-
- avr_asm_len ("ldi %2,lo8(%1)" CR_TAB
- "mov %0,%2", xop, len, 2);
- }
-
- /* If we cooked up a clobber reg above, restore it. */
-
- if (cooked_clobber_p)
- {
- avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1);
- }
-
+ output_reload_in_const (op, clobber_reg, len, false);
+ return "";
+}
+
+const char *
+avr_out_reload_inpsi (rtx *op, rtx clobber_reg, int *len)
+{
+ output_reload_in_const (op, clobber_reg, len, false);
return "";
}
void
avr_output_addr_vec_elt (FILE *stream, int value)
{
- switch_to_section (progmem_section);
if (AVR_HAVE_JMP_CALL)
fprintf (stream, "\t.word gs(.L%d)\n", value);
else
/* Returns true if SCRATCH are safe to be allocated as a scratch
registers (for a define_peephole2) in the current function. */
-bool
+static bool
avr_hard_regno_scratch_ok (unsigned int regno)
{
/* Interrupt functions can only use registers that have already been saved
avr_out_sbxx_branch (rtx insn, rtx operands[])
{
enum rtx_code comp = GET_CODE (operands[0]);
- int long_jump = (get_attr_length (insn) >= 4);
- int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
+ bool long_jump = get_attr_length (insn) >= 4;
+ bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
if (comp == GE)
comp = EQ;
if (reverse)
comp = reverse_condition (comp);
- if (GET_CODE (operands[1]) == CONST_INT)
+ switch (GET_CODE (operands[1]))
{
- if (INTVAL (operands[1]) < 0x40)
- {
- if (comp == EQ)
- output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands);
- else
- output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands);
- }
+ default:
+ gcc_unreachable();
+
+ case CONST_INT:
+
+ if (low_io_address_operand (operands[1], QImode))
+ {
+ if (comp == EQ)
+ output_asm_insn ("sbis %i1,%2", operands);
+ else
+ output_asm_insn ("sbic %i1,%2", operands);
+ }
else
- {
- output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands);
- if (comp == EQ)
- output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands);
- else
- output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands);
- }
+ {
+ output_asm_insn ("in __tmp_reg__,%i1", operands);
+ if (comp == EQ)
+ output_asm_insn ("sbrs __tmp_reg__,%2", operands);
+ else
+ output_asm_insn ("sbrc __tmp_reg__,%2", operands);
+ }
+
+ break; /* CONST_INT */
+
+ case REG:
+
+ if (GET_MODE (operands[1]) == QImode)
+ {
+ if (comp == EQ)
+ output_asm_insn ("sbrs %1,%2", operands);
+ else
+ output_asm_insn ("sbrc %1,%2", operands);
+ }
+ else /* HImode, PSImode or SImode */
+ {
+ static char buf[] = "sbrc %A1,0";
+ unsigned int bit_nr = UINTVAL (operands[2]);
+
+ buf[3] = (comp == EQ) ? 's' : 'c';
+ buf[6] = 'A' + (bit_nr / 8);
+ buf[9] = '0' + (bit_nr % 8);
+ output_asm_insn (buf, operands);
+ }
+
+ break; /* REG */
+ } /* switch */
+
+ if (long_jump)
+ return ("rjmp .+4" CR_TAB
+ "jmp %x3");
+
+ if (!reverse)
+ return "rjmp %x3";
+
+ return "";
+}
+
+/* Worker function for TARGET_ASM_CONSTRUCTOR. */
+
+static void
+avr_asm_out_ctor (rtx symbol, int priority)
+{
+ fputs ("\t.global __do_global_ctors\n", asm_out_file);
+ default_ctor_section_asm_out_constructor (symbol, priority);
+}
+
+/* Worker function for TARGET_ASM_DESTRUCTOR. */
+
+static void
+avr_asm_out_dtor (rtx symbol, int priority)
+{
+ fputs ("\t.global __do_global_dtors\n", asm_out_file);
+ default_dtor_section_asm_out_destructor (symbol, priority);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+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);
+ }
+ else
+ return false;
+}
+
+/* Worker function for CASE_VALUES_THRESHOLD. */
+
+static unsigned int
+avr_case_values_threshold (void)
+{
+ return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */
+
+static enum machine_mode
+avr_addr_space_address_mode (addr_space_t as)
+{
+ return avr_addrspace[as].pointer_size == 3 ? PSImode : HImode;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */
+
+static enum machine_mode
+avr_addr_space_pointer_mode (addr_space_t as)
+{
+ return avr_addr_space_address_mode (as);
+}
+
+
+/* Helper for following function. */
+
+static bool
+avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
+{
+ gcc_assert (REG_P (reg));
+
+ if (strict)
+ {
+ return REGNO (reg) == REG_Z;
+ }
+
+ /* Avoid combine to propagate hard regs. */
+
+ if (can_create_pseudo_p()
+ && REGNO (reg) < REG_Z)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */
+
+static bool
+avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
+ bool strict, addr_space_t as)
+{
+ bool ok = false;
+
+ switch (as)
+ {
+ default:
+ gcc_unreachable();
+
+ case ADDR_SPACE_GENERIC:
+ return avr_legitimate_address_p (mode, x, strict);
+
+ case ADDR_SPACE_PGM:
+ case ADDR_SPACE_PGM1:
+ case ADDR_SPACE_PGM2:
+ case ADDR_SPACE_PGM3:
+ case ADDR_SPACE_PGM4:
+ case ADDR_SPACE_PGM5:
+
+ switch (GET_CODE (x))
+ {
+ case REG:
+ ok = avr_reg_ok_for_pgm_addr (x, strict);
+ break;
+
+ case POST_INC:
+ ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
+ break;
+
+ default:
+ break;
+ }
+
+ break; /* PGM */
+
+ case ADDR_SPACE_PGMX:
+ if (REG_P (x))
+ ok = (!strict
+ && can_create_pseudo_p());
+
+ if (LO_SUM == GET_CODE (x))
+ {
+ rtx hi = XEXP (x, 0);
+ rtx lo = XEXP (x, 1);
+
+ ok = (REG_P (hi)
+ && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER)
+ && REG_P (lo)
+ && REGNO (lo) == REG_Z);
+ }
+
+ break; /* PGMX */
+ }
+
+ if (avr_log.legitimate_address_p)
+ {
+ avr_edump ("\n%?: ret=%b, mode=%m strict=%d "
+ "reload_completed=%d reload_in_progress=%d %s:",
+ ok, mode, strict, reload_completed, reload_in_progress,
+ reg_renumber ? "(reg_renumber)" : "");
+
+ if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1))
+ && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode))
+ && reg_renumber)
+ {
+ avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)),
+ true_regnum (XEXP (x, 0)));
+ }
+
+ avr_edump ("\n%r\n", x);
+ }
+
+ return ok;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'. */
+
+static rtx
+avr_addr_space_legitimize_address (rtx x, rtx old_x,
+ enum machine_mode mode, addr_space_t as)
+{
+ if (ADDR_SPACE_GENERIC_P (as))
+ return avr_legitimize_address (x, old_x, mode);
+
+ if (avr_log.legitimize_address)
+ {
+ avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x);
+ }
+
+ return old_x;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_CONVERT'. */
+
+static rtx
+avr_addr_space_convert (rtx src, tree type_from, tree type_to)
+{
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+
+ if (avr_log.progmem)
+ avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
+ src, type_from, type_to);
+
+ if (as_from != ADDR_SPACE_PGMX
+ && as_to == ADDR_SPACE_PGMX)
+ {
+ rtx new_src;
+ int n_segments = avr_current_arch->n_segments;
+ RTX_CODE code = GET_CODE (src);
+
+ if (CONST == code
+ && PLUS == GET_CODE (XEXP (src, 0))
+ && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0))
+ && CONST_INT_P (XEXP (XEXP (src, 0), 1)))
+ {
+ HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1));
+ const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0);
+
+ new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+ new_src = gen_rtx_CONST (PSImode,
+ plus_constant (new_src, offset));
+ return new_src;
+ }
+
+ if (SYMBOL_REF == code)
+ {
+ const char *name = XSTR (src, 0);
+
+ return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+ }
+
+ src = force_reg (Pmode, src);
+
+ if (ADDR_SPACE_GENERIC_P (as_from)
+ || as_from == ADDR_SPACE_PGM
+ || n_segments == 1)
+ {
+ return gen_rtx_ZERO_EXTEND (PSImode, src);
+ }
+ else
+ {
+ int segment = avr_addrspace[as_from].segment % n_segments;
+
+ new_src = gen_reg_rtx (PSImode);
+ emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src));
+
+ return new_src;
+ }
+ }
+
+ return src;
+}
+
+
+/* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */
+
+static bool
+avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+ if (subset == ADDR_SPACE_PGMX
+ && superset != ADDR_SPACE_PGMX)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Worker function for movmemhi insn.
+ XOP[0] Destination as MEM:BLK
+ XOP[1] Source " "
+ XOP[2] # Bytes to copy
+
+ Return TRUE if the expansion is accomplished.
+ Return FALSE if the operand compination is not supported. */
+
+bool
+avr_emit_movmemhi (rtx *xop)
+{
+ HOST_WIDE_INT count;
+ enum machine_mode loop_mode;
+ addr_space_t as = MEM_ADDR_SPACE (xop[1]);
+ rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x;
+ rtx a_hi8 = NULL_RTX;
+
+ if (avr_mem_pgm_p (xop[0]))
+ return false;
+
+ if (!CONST_INT_P (xop[2]))
+ return false;
+
+ count = INTVAL (xop[2]);
+ if (count <= 0)
+ return false;
+
+ a_src = XEXP (xop[1], 0);
+ a_dest = XEXP (xop[0], 0);
+
+ /* See if constant fits in 8 bits. */
+
+ loop_mode = (count <= 0x100) ? QImode : HImode;
+
+ if (PSImode == GET_MODE (a_src))
+ {
+ addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0);
+ a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2);
+ }
+ else
+ {
+ int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
+
+ addr1 = a_src;
+
+ if (segment)
+ a_hi8 = GEN_INT (segment);
+ }
+
+ if (a_hi8
+ && avr_current_arch->n_segments > 1)
+ {
+ emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8));
+ }
+ else if (!ADDR_SPACE_GENERIC_P (as))
+ {
+ as = ADDR_SPACE_PGM;
+ }
+
+ xas = GEN_INT (as);
+
+ /* Create loop counter register */
+
+ loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode));
+
+ /* Copy pointers into new pseudos - they will be changed */
+
+ addr0 = copy_to_mode_reg (HImode, a_dest);
+ addr1 = copy_to_mode_reg (HImode, addr1);
+
+ /* FIXME: Register allocator might come up with spill fails if it is left
+ on its own. Thus, we allocate the pointer registers by hand. */
+
+ emit_move_insn (lpm_addr_reg_rtx, addr1);
+ addr1 = lpm_addr_reg_rtx;
+
+ reg_x = gen_rtx_REG (HImode, REG_X);
+ emit_move_insn (reg_x, addr0);
+ addr0 = reg_x;
+
+ /* FIXME: Register allocator does a bad job and might spill address
+ register(s) inside the loop leading to additional move instruction
+ to/from stack which could clobber tmp_reg. Thus, do *not* emit
+ load and store as seperate insns. Instead, we perform the copy
+ by means of one monolithic insn. */
+
+ if (ADDR_SPACE_GENERIC_P (as))
+ {
+ rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+ = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+ insn = fun (addr0, addr1, xas, loop_reg,
+ addr0, addr1, tmp_reg_rtx, loop_reg);
}
- else /* GET_CODE (operands[1]) == REG */
+ else if (as == ADDR_SPACE_PGM)
{
- if (GET_MODE (operands[1]) == QImode)
- {
- if (comp == EQ)
- output_asm_insn (AS2 (sbrs,%1,%2), operands);
- else
- output_asm_insn (AS2 (sbrc,%1,%2), operands);
- }
- else /* HImode or SImode */
- {
- static char buf[] = "sbrc %A1,0";
- int bit_nr = INTVAL (operands[2]);
- buf[3] = (comp == EQ) ? 's' : 'c';
- buf[6] = 'A' + (bit_nr >> 3);
- buf[9] = '0' + (bit_nr & 7);
- output_asm_insn (buf, operands);
- }
+ rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+ = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi;
+
+ insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+ AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg);
+ }
+ else
+ {
+ rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+ = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm;
+
+ insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1,
+ AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg,
+ a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR));
}
- if (long_jump)
- return (AS1 (rjmp,.+4) CR_TAB
- AS1 (jmp,%x3));
- if (!reverse)
- return AS1 (rjmp,%x3);
- return "";
+ set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as);
+ emit_insn (insn);
+
+ return true;
}
-/* Worker function for TARGET_ASM_CONSTRUCTOR. */
-static void
-avr_asm_out_ctor (rtx symbol, int priority)
-{
- fputs ("\t.global __do_global_ctors\n", asm_out_file);
- default_ctor_section_asm_out_constructor (symbol, priority);
-}
+/* Print assembler for movmem_qi, movmem_hi insns...
+ $0, $4 : & dest
+ $1, $5 : & src
+ $2 : Address Space
+ $3, $7 : Loop register
+ $6 : Scratch register
-/* Worker function for TARGET_ASM_DESTRUCTOR. */
+ ...and movmem_qi_elpm, movmem_hi_elpm insns.
+
+ $8, $9 : hh8 (& src)
+ $10 : RAMPZ_ADDR
+*/
-static void
-avr_asm_out_dtor (rtx symbol, int priority)
+const char*
+avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
{
- fputs ("\t.global __do_global_dtors\n", asm_out_file);
- default_dtor_section_asm_out_destructor (symbol, priority);
-}
+ addr_space_t as = (addr_space_t) INTVAL (xop[2]);
+ enum machine_mode loop_mode = GET_MODE (xop[3]);
-/* Worker function for TARGET_RETURN_IN_MEMORY. */
+ bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]);
-static bool
-avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
-{
- if (TYPE_MODE (type) == BLKmode)
+ gcc_assert (REG_X == REGNO (xop[0])
+ && REG_Z == REGNO (xop[1]));
+
+ if (plen)
+ *plen = 0;
+
+ /* Loop label */
+
+ avr_asm_len ("0:", xop, plen, 0);
+
+ /* Load with post-increment */
+
+ switch (as)
{
- HOST_WIDE_INT size = int_size_in_bytes (type);
- return (size == -1 || size > 8);
+ default:
+ gcc_unreachable();
+
+ case ADDR_SPACE_GENERIC:
+
+ avr_asm_len ("ld %6,%a1+", xop, plen, 1);
+ break;
+
+ case ADDR_SPACE_PGM:
+
+ if (AVR_HAVE_LPMX)
+ avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
+ else
+ avr_asm_len ("lpm" CR_TAB
+ "adiw %1,1", xop, plen, 2);
+ break;
+
+ case ADDR_SPACE_PGM1:
+ case ADDR_SPACE_PGM2:
+ case ADDR_SPACE_PGM3:
+ case ADDR_SPACE_PGM4:
+ case ADDR_SPACE_PGM5:
+ case ADDR_SPACE_PGMX:
+
+ if (AVR_HAVE_ELPMX)
+ avr_asm_len ("elpm %6,%a1+", xop, plen, 1);
+ else
+ avr_asm_len ("elpm" CR_TAB
+ "adiw %1,1", xop, plen, 2);
+
+ if (as == ADDR_SPACE_PGMX
+ && !AVR_HAVE_ELPMX)
+ {
+ avr_asm_len ("adc %8,__zero_reg__" CR_TAB
+ "out __RAMPZ__,%8", xop, plen, 2);
+ }
+
+ break;
}
- else
- return false;
-}
-/* Worker function for CASE_VALUES_THRESHOLD. */
+ /* Store with post-increment */
-unsigned int avr_case_values_threshold (void)
-{
- return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17;
+ avr_asm_len ("st %a0+,%6", xop, plen, 1);
+
+ /* Decrement loop-counter and set Z-flag */
+
+ if (QImode == loop_mode)
+ {
+ avr_asm_len ("dec %3", xop, plen, 1);
+ }
+ else if (sbiw_p)
+ {
+ avr_asm_len ("sbiw %3,1", xop, plen, 1);
+ }
+ else
+ {
+ avr_asm_len ("subi %A3,1" CR_TAB
+ "sbci %B3,0", xop, plen, 2);
+ }
+
+ /* Loop until zero */
+
+ return avr_asm_len ("brne 0b", xop, plen, 1);
}
+
+\f
/* Helper for __builtin_avr_delay_cycles */
static void
}
}
+
+/* Return VAL * BASE + DIGIT. BASE = 0 is shortcut for BASE = 2^{32} */
+
+static double_int
+avr_double_int_push_digit (double_int val, int base,
+ unsigned HOST_WIDE_INT digit)
+{
+ val = 0 == base
+ ? double_int_lshift (val, 32, 64, false)
+ : double_int_mul (val, uhwi_to_double_int (base));
+
+ return double_int_add (val, uhwi_to_double_int (digit));
+}
+
+
+/* Compute the image of x under f, i.e. perform x --> f(x) */
+
+static int
+avr_map (double_int f, int x)
+{
+ return 0xf & double_int_to_uhwi (double_int_rshift (f, 4*x, 64, false));
+}
+
+
+/* Return the map R that reverses the bits of byte B.
+
+ R(0) = (0 7) o (1 6) o (2 5) o (3 4)
+ R(1) = (8 15) o (9 14) o (10 13) o (11 12)
+
+ Notice that R o R = id. */
+
+static double_int
+avr_revert_map (int b)
+{
+ int i;
+ double_int r = double_int_zero;
+
+ for (i = 16-1; i >= 0; i--)
+ r = avr_double_int_push_digit (r, 16, i >> 3 == b ? i ^ 7 : i);
+
+ return r;
+}
+
+
+/* Return the map R that swaps bit-chunks of size SIZE in byte B.
+
+ R(1,0) = (0 1) o (2 3) o (4 5) o (6 7)
+ R(1,1) = (8 9) o (10 11) o (12 13) o (14 15)
+
+ R(4,0) = (0 4) o (1 5) o (2 6) o (3 7)
+ R(4,1) = (8 12) o (9 13) o (10 14) o (11 15)
+
+ Notice that R o R = id. */
+
+static double_int
+avr_swap_map (int size, int b)
+{
+ int i;
+ double_int r = double_int_zero;
+
+ for (i = 16-1; i >= 0; i--)
+ r = avr_double_int_push_digit (r, 16, i ^ (i >> 3 == b ? size : 0));
+
+ return r;
+}
+
+
+/* Return Identity. */
+
+static double_int
+avr_id_map (void)
+{
+ int i;
+ double_int r = double_int_zero;
+
+ for (i = 16-1; i >= 0; i--)
+ r = avr_double_int_push_digit (r, 16, i);
+
+ return r;
+}
+
+
+enum
+ {
+ SIG_ID = 0,
+ /* for QI and HI */
+ SIG_ROL = 0xf,
+ SIG_REVERT_0 = 1 << 4,
+ SIG_SWAP1_0 = 1 << 5,
+ /* HI only */
+ SIG_REVERT_1 = 1 << 6,
+ SIG_SWAP1_1 = 1 << 7,
+ SIG_SWAP4_0 = 1 << 8,
+ SIG_SWAP4_1 = 1 << 9
+ };
+
+
+/* Return basic map with signature SIG. */
+
+static double_int
+avr_sig_map (int n ATTRIBUTE_UNUSED, int sig)
+{
+ if (sig == SIG_ID) return avr_id_map ();
+ else if (sig == SIG_REVERT_0) return avr_revert_map (0);
+ else if (sig == SIG_REVERT_1) return avr_revert_map (1);
+ else if (sig == SIG_SWAP1_0) return avr_swap_map (1, 0);
+ else if (sig == SIG_SWAP1_1) return avr_swap_map (1, 1);
+ else if (sig == SIG_SWAP4_0) return avr_swap_map (4, 0);
+ else if (sig == SIG_SWAP4_1) return avr_swap_map (4, 1);
+ else
+ gcc_unreachable();
+}
+
+
+/* Return the Hamming distance between the B-th byte of A and C. */
+
+static bool
+avr_map_hamming_byte (int n, int b, double_int a, double_int c, bool strict)
+{
+ int i, hamming = 0;
+
+ for (i = 8*b; i < n && i < 8*b + 8; i++)
+ {
+ int ai = avr_map (a, i);
+ int ci = avr_map (c, i);
+
+ hamming += ai != ci && (strict || (ai < n && ci < n));
+ }
+
+ return hamming;
+}
+
+
+/* Return the non-strict Hamming distance between A and B. */
+
+#define avr_map_hamming_nonstrict(N,A,B) \
+ (+ avr_map_hamming_byte (N, 0, A, B, false) \
+ + avr_map_hamming_byte (N, 1, A, B, false))
+
+
+/* Return TRUE iff A and B represent the same mapping. */
+
+#define avr_map_equal_p(N,A,B) (0 == avr_map_hamming_nonstrict (N, A, B))
+
+
+/* Return TRUE iff A is a map of signature S. Notice that there is no
+ 1:1 correspondance between maps and signatures and thus this is
+ only supported for basic signatures recognized by avr_sig_map(). */
+
+#define avr_map_sig_p(N,A,S) avr_map_equal_p (N, A, avr_sig_map (N, S))
+
+
+/* Swap odd/even bits of ld-reg %0: %0 = bit-swap (%0) */
+
+static const char*
+avr_out_swap_bits (rtx *xop, int *plen)
+{
+ xop[1] = tmp_reg_rtx;
+
+ return avr_asm_len ("mov %1,%0" CR_TAB
+ "andi %0,0xaa" CR_TAB
+ "eor %1,%0" CR_TAB
+ "lsr %0" CR_TAB
+ "lsl %1" CR_TAB
+ "or %0,%1", xop, plen, 6);
+}
+
+/* Revert bit order: %0 = Revert (%1) with %0 != %1 and clobber %1 */
+
+static const char*
+avr_out_revert_bits (rtx *xop, int *plen)
+{
+ return avr_asm_len ("inc __zero_reg__" "\n"
+ "0:\tror %1" CR_TAB
+ "rol %0" CR_TAB
+ "lsl __zero_reg__" CR_TAB
+ "brne 0b", xop, plen, 5);
+}
+
+
+/* If OUT_P = true: Output BST/BLD instruction according to MAP.
+ If OUT_P = false: Just dry-run and fix XOP[1] to resolve
+ early-clobber conflicts if XOP[0] = XOP[1]. */
+
+static void
+avr_move_bits (rtx *xop, double_int map, int n_bits, bool out_p, int *plen)
+{
+ int bit_dest, b, clobber = 0;
+
+ /* T-flag contains this bit of the source, i.e. of XOP[1] */
+ int t_bit_src = -1;
+
+ if (!optimize && !out_p)
+ {
+ avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+ xop[1] = tmp_reg_rtx;
+ return;
+ }
+
+ /* We order the operations according to the requested source bit b. */
+
+ for (b = 0; b < n_bits; b++)
+ for (bit_dest = 0; bit_dest < n_bits; bit_dest++)
+ {
+ int bit_src = avr_map (map, bit_dest);
+
+ if (b != bit_src
+ /* Same position: No need to copy as the caller did MOV. */
+ || bit_dest == bit_src
+ /* Accessing bits 8..f for 8-bit version is void. */
+ || bit_src >= n_bits)
+ continue;
+
+ if (t_bit_src != bit_src)
+ {
+ /* Source bit is not yet in T: Store it to T. */
+
+ t_bit_src = bit_src;
+
+ if (out_p)
+ {
+ xop[2] = GEN_INT (bit_src);
+ avr_asm_len ("bst %T1%T2", xop, plen, 1);
+ }
+ else if (clobber & (1 << bit_src))
+ {
+ /* Bit to be read was written already: Backup input
+ to resolve early-clobber conflict. */
+
+ avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+ xop[1] = tmp_reg_rtx;
+ return;
+ }
+ }
+
+ /* Load destination bit with T. */
+
+ if (out_p)
+ {
+ xop[2] = GEN_INT (bit_dest);
+ avr_asm_len ("bld %T0%T2", xop, plen, 1);
+ }
+
+ clobber |= 1 << bit_dest;
+ }
+}
+
+
+/* Print assembler code for `map_bitsqi' and `map_bitshi'. */
+
+const char*
+avr_out_map_bits (rtx insn, rtx *operands, int *plen)
+{
+ bool copy_0, copy_1;
+ int n_bits = GET_MODE_BITSIZE (GET_MODE (operands[0]));
+ double_int map = rtx_to_double_int (operands[1]);
+ rtx xop[3];
+
+ xop[0] = operands[0];
+ xop[1] = operands[2];
+
+ if (plen)
+ *plen = 0;
+ else if (flag_print_asm_name)
+ avr_fdump (asm_out_file, ASM_COMMENT_START "%X\n", map);
+
+ switch (n_bits)
+ {
+ default:
+ gcc_unreachable();
+
+ case 8:
+ if (avr_map_sig_p (n_bits, map, SIG_SWAP1_0))
+ {
+ return avr_out_swap_bits (xop, plen);
+ }
+ else if (avr_map_sig_p (n_bits, map, SIG_REVERT_0))
+ {
+ if (REGNO (xop[0]) == REGNO (xop[1])
+ || !reg_unused_after (insn, xop[1]))
+ {
+ avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1);
+ xop[1] = tmp_reg_rtx;
+ }
+
+ return avr_out_revert_bits (xop, plen);
+ }
+
+ break; /* 8 */
+
+ case 16:
+
+ break; /* 16 */
+ }
+
+ /* Copy whole byte is cheaper than moving bits that stay at the same
+ position. Some bits in a byte stay at the same position iff the
+ strict Hamming distance to Identity is not 8. */
+
+ copy_0 = 8 != avr_map_hamming_byte (n_bits, 0, map, avr_id_map(), true);
+ copy_1 = 8 != avr_map_hamming_byte (n_bits, 1, map, avr_id_map(), true);
+
+ /* Perform the move(s) just worked out. */
+
+ if (n_bits == 8)
+ {
+ if (REGNO (xop[0]) == REGNO (xop[1]))
+ {
+ /* Fix early-clobber clashes.
+ Notice XOP[0] hat no eary-clobber in its constraint. */
+
+ avr_move_bits (xop, map, n_bits, false, plen);
+ }
+ else if (copy_0)
+ {
+ avr_asm_len ("mov %0,%1", xop, plen, 1);
+ }
+ }
+ else if (AVR_HAVE_MOVW && copy_0 && copy_1)
+ {
+ avr_asm_len ("movw %A0,%A1", xop, plen, 1);
+ }
+ else
+ {
+ if (copy_0)
+ avr_asm_len ("mov %A0,%A1", xop, plen, 1);
+
+ if (copy_1)
+ avr_asm_len ("mov %B0,%B1", xop, plen, 1);
+ }
+
+ /* Move individual bits. */
+
+ avr_move_bits (xop, map, n_bits, true, plen);
+
+ return "";
+}
+
+
/* IDs for all the AVR builtins. */
enum avr_builtin_id
AVR_BUILTIN_WDR,
AVR_BUILTIN_SLEEP,
AVR_BUILTIN_SWAP,
+ AVR_BUILTIN_MAP8,
+ AVR_BUILTIN_MAP16,
AVR_BUILTIN_FMUL,
AVR_BUILTIN_FMULS,
AVR_BUILTIN_FMULSU,
AVR_BUILTIN_DELAY_CYCLES
};
+static void
+avr_init_builtin_int24 (void)
+{
+ tree int24_type = make_signed_type (GET_MODE_BITSIZE (PSImode));
+ tree uint24_type = make_unsigned_type (GET_MODE_BITSIZE (PSImode));
+
+ (*lang_hooks.types.register_builtin_type) (int24_type, "__int24");
+ (*lang_hooks.types.register_builtin_type) (uint24_type, "__uint24");
+}
+
#define DEF_BUILTIN(NAME, TYPE, CODE) \
do \
{ \
long_unsigned_type_node,
NULL_TREE);
+ tree uchar_ftype_ulong_uchar
+ = build_function_type_list (unsigned_char_type_node,
+ long_unsigned_type_node,
+ unsigned_char_type_node,
+ NULL_TREE);
+
+ tree uint_ftype_ullong_uint
+ = build_function_type_list (unsigned_type_node,
+ long_long_unsigned_type_node,
+ unsigned_type_node,
+ NULL_TREE);
+
DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP);
DEF_BUILTIN ("__builtin_avr_sei", void_ftype_void, AVR_BUILTIN_SEI);
DEF_BUILTIN ("__builtin_avr_cli", void_ftype_void, AVR_BUILTIN_CLI);
AVR_BUILTIN_FMULS);
DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar,
AVR_BUILTIN_FMULSU);
+
+ DEF_BUILTIN ("__builtin_avr_map8", uchar_ftype_ulong_uchar,
+ AVR_BUILTIN_MAP8);
+ DEF_BUILTIN ("__builtin_avr_map16", uint_ftype_ullong_uint,
+ AVR_BUILTIN_MAP16);
+
+ avr_init_builtin_int24 ();
}
#undef DEF_BUILTIN
{
{ CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL },
{ CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS },
- { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }
+ { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU },
+ { CODE_FOR_map_bitsqi, "__builtin_avr_map8", AVR_BUILTIN_MAP8 },
+ { CODE_FOR_map_bitshi, "__builtin_avr_map16", AVR_BUILTIN_MAP16 }
};
/* Subroutine of avr_expand_builtin to take care of unop insns. */
size_t i;
const struct avr_builtin_description *d;
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ const char* bname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
unsigned int id = DECL_FUNCTION_CODE (fndecl);
tree arg0;
rtx op0;
op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
if (! CONST_INT_P (op0))
- error ("__builtin_avr_delay_cycles expects a compile time integer constant.");
+ error ("%s expects a compile time integer constant", bname);
avr_expand_delay_cycles (op0);
return 0;
}
+
+ case AVR_BUILTIN_MAP8:
+ {
+ arg0 = CALL_EXPR_ARG (exp, 0);
+ op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+ if (!CONST_INT_P (op0))
+ {
+ error ("%s expects a compile time long integer constant"
+ " as first argument", bname);
+ return target;
+ }
+ }
+
+ case AVR_BUILTIN_MAP16:
+ {
+ arg0 = CALL_EXPR_ARG (exp, 0);
+ op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+
+ if (!const_double_operand (op0, VOIDmode))
+ {
+ error ("%s expects a compile time long long integer constant"
+ " as first argument", bname);
+ return target;
+ }
+ }
}
for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
gcc_unreachable ();
}
+struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-avr.h"