summaryrefslogtreecommitdiff
path: root/gcc/config/avr/avr.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/avr/avr.c')
-rw-r--r--gcc/config/avr/avr.c539
1 files changed, 454 insertions, 85 deletions
diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c
index 4f385d5682f..1f333ccc1b2 100644
--- a/gcc/config/avr/avr.c
+++ b/gcc/config/avr/avr.c
@@ -553,9 +553,9 @@ avr_optimize_casesi (rtx_insn *insns[6], rtx *xop)
HOST_WIDE_INT hig_idx = low_idx + num_idx;
// Maximum ranges of (un)signed QImode resp. HImode.
- int imin = QImode == mode ? INT8_MIN : INT16_MIN;
- int imax = QImode == mode ? INT8_MAX : INT16_MAX;
- unsigned umax = QImode == mode ? UINT8_MAX : UINT16_MAX;
+ unsigned umax = QImode == mode ? 0xff : 0xffff;
+ int imax = QImode == mode ? 0x7f : 0x7fff;
+ int imin = -imax - 1;
// Testing the case range and whether it fits into the range of the
// (un)signed mode. This test should actually always pass because it
@@ -774,6 +774,10 @@ avr_option_override (void)
if (flag_pie == 2)
warning (OPT_fPIE, "-fPIE is not supported");
+#if !defined (HAVE_AS_AVR_MGCCISR_OPTION)
+ TARGET_GASISR_PROLOGUES = 0;
+#endif
+
if (!avr_set_core_architecture())
return;
@@ -1007,6 +1011,15 @@ avr_OS_main_function_p (tree func)
}
+/* Return nonzero if FUNC is a no_gccisr function as specified
+ by the "no_gccisr" attribute. */
+
+static int
+avr_no_gccisr_function_p (tree func)
+{
+ return avr_lookup_function_attribute1 (func, "no_gccisr");
+}
+
/* Implement `TARGET_SET_CURRENT_FUNCTION'. */
/* Sanity cheching for above function attributes. */
@@ -1030,6 +1043,7 @@ avr_set_current_function (tree decl)
cfun->machine->is_interrupt = avr_interrupt_function_p (decl);
cfun->machine->is_OS_task = avr_OS_task_function_p (decl);
cfun->machine->is_OS_main = avr_OS_main_function_p (decl);
+ cfun->machine->is_no_gccisr = avr_no_gccisr_function_p (decl);
isr = cfun->machine->is_interrupt ? "interrupt" : "signal";
@@ -1062,12 +1076,6 @@ avr_set_current_function (tree decl)
name = default_strip_name_encoding (name);
- /* Silently ignore 'signal' if 'interrupt' is present. AVR-LibC startet
- using this when it switched from SIGNAL and INTERRUPT to ISR. */
-
- if (cfun->machine->is_interrupt)
- cfun->machine->is_signal = 0;
-
/* Interrupt handlers must be void __vector (void) functions. */
if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE)
@@ -1076,14 +1084,36 @@ avr_set_current_function (tree decl)
if (TREE_CODE (ret) != VOID_TYPE)
error_at (loc, "%qs function cannot return a value", isr);
+#if defined WITH_AVRLIBC
+ /* Silently ignore 'signal' if 'interrupt' is present. AVR-LibC startet
+ using this when it switched from SIGNAL and INTERRUPT to ISR. */
+
+ if (cfun->machine->is_interrupt)
+ cfun->machine->is_signal = 0;
+
/* If the function has the 'signal' or 'interrupt' attribute, ensure
that the name of the function is "__vector_NN" so as to catch
when the user misspells the vector name. */
if (!STR_PREFIX_P (name, "__vector"))
warning_at (loc, OPT_Wmisspelled_isr, "%qs appears to be a misspelled "
- "%s handler, missing __vector prefix", name, isr);
+ "%qs handler, missing %<__vector%> prefix", name, isr);
+#endif // AVR-LibC naming conventions
+ }
+
+#if defined WITH_AVRLIBC
+ // Common problem is using "ISR" without first including avr/interrupt.h.
+ const char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ name = default_strip_name_encoding (name);
+ if (0 == strcmp ("ISR", name)
+ || 0 == strcmp ("INTERRUPT", name)
+ || 0 == strcmp ("SIGNAL", name))
+ {
+ warning_at (loc, OPT_Wmisspelled_isr, "%qs is a reserved indentifier"
+ " in AVR-LibC. Consider %<#include <avr/interrupt.h>%>"
+ " before using the %qs macro", name, name);
}
+#endif // AVR-LibC naming conventions
/* Don't print the above diagnostics more than once. */
@@ -1220,6 +1250,9 @@ avr_initial_elimination_offset (int from, int to)
int offset = frame_pointer_needed ? 2 : 0;
int avr_pc_size = AVR_HAVE_EIJMP_EICALL ? 3 : 2;
+ // If FROM is ARG_POINTER_REGNUM, we are not in an ISR as ISRs
+ // might not have arguments. Hence the following is not affected
+ // by gasisr prologues.
offset += avr_regs_to_save (NULL);
return (get_frame_size () + avr_outgoing_args_size()
+ avr_pc_size + 1 + offset);
@@ -1314,6 +1347,8 @@ avr_return_addr_rtx (int count, rtx tem)
else
r = gen_rtx_SYMBOL_REF (Pmode, ".L__stack_usage+1");
+ cfun->machine->use_L__stack_usage = 1;
+
r = gen_rtx_PLUS (Pmode, tem, r);
r = gen_frame_mem (Pmode, memory_address (Pmode, r));
r = gen_rtx_ROTATE (HImode, r, GEN_INT (8));
@@ -1394,6 +1429,97 @@ sequent_regs_live (void)
return (cur_seq == live_seq) ? live_seq : 0;
}
+namespace {
+static const pass_data avr_pass_data_pre_proep =
+{
+ RTL_PASS, // type
+ "", // name (will be patched)
+ OPTGROUP_NONE, // optinfo_flags
+ TV_DF_SCAN, // tv_id
+ 0, // properties_required
+ 0, // properties_provided
+ 0, // properties_destroyed
+ 0, // todo_flags_start
+ 0 // todo_flags_finish
+};
+
+
+class avr_pass_pre_proep : public rtl_opt_pass
+{
+public:
+ avr_pass_pre_proep (gcc::context *ctxt, const char *name)
+ : rtl_opt_pass (avr_pass_data_pre_proep, ctxt)
+ {
+ this->name = name;
+ }
+
+ void compute_maybe_gasisr (function*);
+
+ virtual unsigned int execute (function *fun)
+ {
+ if (TARGET_GASISR_PROLOGUES
+ // Whether this function is an ISR worth scanning at all.
+ && !fun->machine->is_no_gccisr
+ && (fun->machine->is_interrupt
+ || fun->machine->is_signal)
+ && !cfun->machine->is_naked
+ // Paranoia: Non-local gotos and labels that might escape.
+ && !cfun->calls_setjmp
+ && !cfun->has_nonlocal_label
+ && !cfun->has_forced_label_in_static)
+ {
+ compute_maybe_gasisr (fun);
+ }
+
+ return 0;
+ }
+
+}; // avr_pass_pre_proep
+
+} // anon namespace
+
+rtl_opt_pass*
+make_avr_pass_pre_proep (gcc::context *ctxt)
+{
+ return new avr_pass_pre_proep (ctxt, "avr-pre-proep");
+}
+
+
+/* Set fun->machine->gasisr.maybe provided we don't find anything that
+ prohibits GAS generating parts of ISR prologues / epilogues for us. */
+
+void
+avr_pass_pre_proep::compute_maybe_gasisr (function *fun)
+{
+ // Don't use BB iterators so that we see JUMP_TABLE_DATA.
+
+ for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ // Transparent calls always use [R]CALL and are filtered out by GAS.
+ // ISRs don't use -mcall-prologues, hence what remains to be filtered
+ // out are open coded (tail) calls.
+
+ if (CALL_P (insn))
+ return;
+
+ // __tablejump2__ clobbers something and is targeted by JMP so
+ // that GAS won't see its usage.
+
+ if (AVR_HAVE_JMP_CALL
+ && JUMP_TABLE_DATA_P (insn))
+ return;
+
+ // Non-local gotos not seen in *FUN.
+
+ if (JUMP_P (insn)
+ && find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX))
+ return;
+ }
+
+ fun->machine->gasisr.maybe = 1;
+}
+
+
/* Obtain the length sequence of insns. */
int
@@ -1418,6 +1544,46 @@ avr_incoming_return_addr_rtx (void)
return gen_frame_mem (HImode, plus_constant (Pmode, stack_pointer_rtx, 1));
}
+
+/* Unset a bit in *SET. If successful, return the respective bit number.
+ Otherwise, return -1 and *SET is unaltered. */
+
+static int
+avr_hregs_split_reg (HARD_REG_SET *set)
+{
+ for (int regno = 0; regno < 32; regno++)
+ if (TEST_HARD_REG_BIT (*set, regno))
+ {
+ // Don't remove a register from *SET which might indicate that
+ // some RAMP* register might need ISR prologue / epilogue treatment.
+
+ if (AVR_HAVE_RAMPX
+ && (REG_X == regno || REG_X + 1 == regno)
+ && TEST_HARD_REG_BIT (*set, REG_X)
+ && TEST_HARD_REG_BIT (*set, REG_X + 1))
+ continue;
+
+ if (AVR_HAVE_RAMPY
+ && !frame_pointer_needed
+ && (REG_Y == regno || REG_Y + 1 == regno)
+ && TEST_HARD_REG_BIT (*set, REG_Y)
+ && TEST_HARD_REG_BIT (*set, REG_Y + 1))
+ continue;
+
+ if (AVR_HAVE_RAMPZ
+ && (REG_Z == regno || REG_Z + 1 == regno)
+ && TEST_HARD_REG_BIT (*set, REG_Z)
+ && TEST_HARD_REG_BIT (*set, REG_Z + 1))
+ continue;
+
+ CLEAR_HARD_REG_BIT (*set, regno);
+ return regno;
+ }
+
+ return -1;
+}
+
+
/* Helper for expand_prologue. Emit a push of a byte register. */
static void
@@ -1438,24 +1604,24 @@ emit_push_byte (unsigned regno, bool frame_related_p)
}
-/* Helper for expand_prologue. Emit a push of a SFR via tmp_reg.
+/* Helper for expand_prologue. Emit a push of a SFR via register TREG.
SFR is a MEM representing the memory location of the SFR.
If CLR_P then clear the SFR after the push using zero_reg. */
static void
-emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p)
+emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p, int treg)
{
rtx_insn *insn;
gcc_assert (MEM_P (sfr));
- /* IN __tmp_reg__, IO(SFR) */
- insn = emit_move_insn (tmp_reg_rtx, sfr);
+ /* IN treg, IO(SFR) */
+ insn = emit_move_insn (all_regs_rtx[treg], sfr);
if (frame_related_p)
RTX_FRAME_RELATED_P (insn) = 1;
- /* PUSH __tmp_reg__ */
- emit_push_byte (AVR_TMP_REGNO, frame_related_p);
+ /* PUSH treg */
+ emit_push_byte (treg, frame_related_p);
if (clr_p)
{
@@ -1759,37 +1925,66 @@ avr_expand_prologue (void)
if (cfun->machine->is_interrupt || cfun->machine->is_signal)
{
+ int treg = AVR_TMP_REGNO;
/* Enable interrupts. */
if (cfun->machine->is_interrupt)
emit_insn (gen_enable_interrupt ());
- /* Push zero reg. */
- emit_push_byte (AVR_ZERO_REGNO, true);
+ if (cfun->machine->gasisr.maybe)
+ {
+ /* Let GAS PR21472 emit prologue preamble for us which handles SREG,
+ ZERO_REG and TMP_REG and one additional, optional register for
+ us in an optimal way. This even scans through inline asm. */
+
+ cfun->machine->gasisr.yes = 1;
+
+ // The optional reg or TMP_REG if we don't need one. If we need one,
+ // remove that reg from SET so that it's not puhed / popped twice.
+ // We also use it below instead of TMP_REG in some places.
+
+ treg = avr_hregs_split_reg (&set);
+ if (treg < 0)
+ treg = AVR_TMP_REGNO;
+ cfun->machine->gasisr.regno = treg;
+
+ // The worst case of pushes. The exact number can be inferred
+ // at assembly time by magic expression __gcc_isr.n_pushed.
+ cfun->machine->stack_usage += 3 + (treg != AVR_TMP_REGNO);
+
+ // Emit a Prologue chunk. Epilogue chunk(s) might follow.
+ // The final Done chunk is emit by final postscan.
+ emit_insn (gen_gasisr (GEN_INT (GASISR_Prologue), GEN_INT (treg)));
+ }
+ else // !TARGET_GASISR_PROLOGUES: Classic, dumb prologue preamble.
+ {
+ /* Push zero reg. */
+ emit_push_byte (AVR_ZERO_REGNO, true);
- /* Push tmp reg. */
- emit_push_byte (AVR_TMP_REGNO, true);
+ /* Push tmp reg. */
+ emit_push_byte (AVR_TMP_REGNO, true);
- /* Push SREG. */
- /* ??? There's no dwarf2 column reserved for SREG. */
- emit_push_sfr (sreg_rtx, false, false /* clr */);
+ /* Push SREG. */
+ /* ??? There's no dwarf2 column reserved for SREG. */
+ emit_push_sfr (sreg_rtx, false, false /* clr */, AVR_TMP_REGNO);
- /* Clear zero reg. */
- emit_move_insn (zero_reg_rtx, const0_rtx);
+ /* 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);
+ /* Prevent any attempt to delete the setting of ZERO_REG! */
+ emit_use (zero_reg_rtx);
+ }
/* Push and clear RAMPD/X/Y/Z if present and low-part register is used.
??? There are no dwarf2 columns reserved for RAMPD/X/Y/Z. */
if (AVR_HAVE_RAMPD)
- emit_push_sfr (rampd_rtx, false /* frame-related */, true /* clr */);
+ emit_push_sfr (rampd_rtx, false /* frame */, true /* clr */, treg);
if (AVR_HAVE_RAMPX
&& TEST_HARD_REG_BIT (set, REG_X)
&& TEST_HARD_REG_BIT (set, REG_X + 1))
{
- emit_push_sfr (rampx_rtx, false /* frame-related */, true /* clr */);
+ emit_push_sfr (rampx_rtx, false /* frame */, true /* clr */, treg);
}
if (AVR_HAVE_RAMPY
@@ -1797,14 +1992,14 @@ avr_expand_prologue (void)
|| (TEST_HARD_REG_BIT (set, REG_Y)
&& TEST_HARD_REG_BIT (set, REG_Y + 1))))
{
- emit_push_sfr (rampy_rtx, false /* frame-related */, true /* clr */);
+ emit_push_sfr (rampy_rtx, false /* frame */, true /* clr */, treg);
}
if (AVR_HAVE_RAMPZ
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
- emit_push_sfr (rampz_rtx, false /* frame-related */, AVR_HAVE_RAMPD);
+ emit_push_sfr (rampz_rtx, false /* frame */, AVR_HAVE_RAMPD, treg);
}
} /* is_interrupt is_signal */
@@ -1846,11 +2041,23 @@ avr_asm_function_end_prologue (FILE *file)
fprintf (file, "/* frame size = " HOST_WIDE_INT_PRINT_DEC " */\n",
get_frame_size());
- fprintf (file, "/* stack size = %d */\n",
- cfun->machine->stack_usage);
- /* Create symbol stack offset here so all functions have it. Add 1 to stack
- usage for offset so that SP + .L__stack_offset = return address. */
- fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage);
+
+ if (!cfun->machine->gasisr.yes)
+ {
+ fprintf (file, "/* stack size = %d */\n", cfun->machine->stack_usage);
+ // Create symbol stack offset so all functions have it. Add 1 to stack
+ // usage for offset so that SP + .L__stack_offset = return address.
+ fprintf (file, ".L__stack_usage = %d\n", cfun->machine->stack_usage);
+ }
+ else
+ {
+ int used_by_gasisr = 3 + (cfun->machine->gasisr.regno != AVR_TMP_REGNO);
+ int to = cfun->machine->stack_usage;
+ int from = to - used_by_gasisr;
+ // Number of pushed regs is only known at assembly-time.
+ fprintf (file, "/* stack size = %d...%d */\n", from , to);
+ fprintf (file, ".L__stack_usage = %d + __gcc_isr.n_pushed\n", from);
+ }
}
@@ -2026,6 +2233,15 @@ avr_expand_epilogue (bool sibcall_p)
/* Restore used registers. */
+ int treg = AVR_TMP_REGNO;
+
+ if (isr_p
+ && cfun->machine->gasisr.yes)
+ {
+ treg = cfun->machine->gasisr.regno;
+ CLEAR_HARD_REG_BIT (set, treg);
+ }
+
for (int reg = 31; reg >= 0; --reg)
if (TEST_HARD_REG_BIT (set, reg))
emit_pop_byte (reg);
@@ -2039,8 +2255,8 @@ avr_expand_epilogue (bool sibcall_p)
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
- emit_pop_byte (TMP_REGNO);
- emit_move_insn (rampz_rtx, tmp_reg_rtx);
+ emit_pop_byte (treg);
+ emit_move_insn (rampz_rtx, all_regs_rtx[treg]);
}
if (AVR_HAVE_RAMPY
@@ -2048,34 +2264,43 @@ avr_expand_epilogue (bool sibcall_p)
|| (TEST_HARD_REG_BIT (set, REG_Y)
&& TEST_HARD_REG_BIT (set, REG_Y + 1))))
{
- emit_pop_byte (TMP_REGNO);
- emit_move_insn (rampy_rtx, tmp_reg_rtx);
+ emit_pop_byte (treg);
+ emit_move_insn (rampy_rtx, all_regs_rtx[treg]);
}
if (AVR_HAVE_RAMPX
&& TEST_HARD_REG_BIT (set, REG_X)
&& TEST_HARD_REG_BIT (set, REG_X + 1))
{
- emit_pop_byte (TMP_REGNO);
- emit_move_insn (rampx_rtx, tmp_reg_rtx);
+ emit_pop_byte (treg);
+ emit_move_insn (rampx_rtx, all_regs_rtx[treg]);
}
if (AVR_HAVE_RAMPD)
{
- emit_pop_byte (TMP_REGNO);
- emit_move_insn (rampd_rtx, tmp_reg_rtx);
+ emit_pop_byte (treg);
+ emit_move_insn (rampd_rtx, all_regs_rtx[treg]);
}
- /* Restore SREG using tmp_reg as scratch. */
+ if (cfun->machine->gasisr.yes)
+ {
+ // Emit an Epilogue chunk.
+ emit_insn (gen_gasisr (GEN_INT (GASISR_Epilogue),
+ GEN_INT (cfun->machine->gasisr.regno)));
+ }
+ else // !TARGET_GASISR_PROLOGUES
+ {
+ /* Restore SREG using tmp_reg as scratch. */
- emit_pop_byte (AVR_TMP_REGNO);
- emit_move_insn (sreg_rtx, tmp_reg_rtx);
+ emit_pop_byte (AVR_TMP_REGNO);
+ emit_move_insn (sreg_rtx, tmp_reg_rtx);
- /* Restore tmp REG. */
- emit_pop_byte (AVR_TMP_REGNO);
+ /* Restore tmp REG. */
+ emit_pop_byte (AVR_TMP_REGNO);
- /* Restore zero REG. */
- emit_pop_byte (AVR_ZERO_REGNO);
+ /* Restore zero REG. */
+ emit_pop_byte (AVR_ZERO_REGNO);
+ }
}
if (!sibcall_p)
@@ -2088,6 +2313,7 @@ avr_expand_epilogue (bool sibcall_p)
static void
avr_asm_function_begin_epilogue (FILE *file)
{
+ app_disable();
fprintf (file, "/* epilogue start */\n");
}
@@ -3088,8 +3314,31 @@ avr_final_prescan_insn (rtx_insn *insn, rtx *operand ATTRIBUTE_UNUSED,
rtx_cost (PATTERN (insn), VOIDmode, INSN, 0,
optimize_insn_for_speed_p()));
}
+
+ if (avr_log.insn_addresses)
+ fprintf (asm_out_file, ";; ADDR = %d\n",
+ (int) INSN_ADDRESSES (INSN_UID (insn)));
}
+
+/* Implement `TARGET_ASM_FINAL_POSTSCAN_INSN'. */
+/* When GAS generates (parts of) ISR prologue / epilogue for us, we must
+ hint GAS about the end of the code to scan. There migh be code located
+ after the last epilogue. */
+
+static void
+avr_asm_final_postscan_insn (FILE *stream, rtx_insn *insn, rtx*, int)
+{
+ if (cfun->machine->gasisr.yes
+ && !next_real_insn (insn))
+ {
+ app_disable();
+ fprintf (stream, "\t__gcc_isr %d,r%d\n", GASISR_Done,
+ cfun->machine->gasisr.regno);
+ }
+}
+
+
/* Return 0 if undefined, 1 if always true or always false. */
int
@@ -3820,7 +4069,7 @@ out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen)
if (CONSTANT_ADDRESS_P (x))
{
int n_words = AVR_TINY ? 1 : 2;
- return optimize > 0 && io_address_operand (x, QImode)
+ return io_address_operand (x, QImode)
? avr_asm_len ("in %0,%i1", op, plen, -1)
: avr_asm_len ("lds %0,%m1", op, plen, -n_words);
}
@@ -4088,7 +4337,7 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen)
else if (CONSTANT_ADDRESS_P (base))
{
int n_words = AVR_TINY ? 2 : 4;
- return optimize > 0 && io_address_operand (base, HImode)
+ return io_address_operand (base, HImode)
? avr_asm_len ("in %A0,%i1" CR_TAB
"in %B0,%i1+1", op, plen, -2)
@@ -5215,7 +5464,7 @@ out_movqi_mr_r (rtx_insn *insn, rtx op[], int *plen)
if (CONSTANT_ADDRESS_P (x))
{
int n_words = AVR_TINY ? 1 : 2;
- return optimize > 0 && io_address_operand (x, QImode)
+ return io_address_operand (x, QImode)
? avr_asm_len ("out %i0,%1", op, plen, -1)
: avr_asm_len ("sts %m0,%1", op, plen, -n_words);
}
@@ -5291,13 +5540,12 @@ avr_out_movhi_mr_r_xmega (rtx_insn *insn, rtx op[], int *plen)
if (CONSTANT_ADDRESS_P (base))
{
- int n_words = AVR_TINY ? 2 : 4;
- return optimize > 0 && io_address_operand (base, HImode)
+ return io_address_operand (base, HImode)
? avr_asm_len ("out %i0,%A1" CR_TAB
"out %i0+1,%B1", op, plen, -2)
: avr_asm_len ("sts %m0,%A1" CR_TAB
- "sts %m0+1,%B1", op, plen, -n_words);
+ "sts %m0+1,%B1", op, plen, -4);
}
if (reg_base > 0)
@@ -5477,7 +5725,7 @@ out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen)
if (CONSTANT_ADDRESS_P (base))
{
int n_words = AVR_TINY ? 2 : 4;
- return optimize > 0 && io_address_operand (base, HImode)
+ return io_address_operand (base, HImode)
? avr_asm_len ("out %i0+1,%B1" CR_TAB
"out %i0,%A1", op, plen, -2)
@@ -8297,7 +8545,7 @@ avr_out_addto_sp (rtx *op, int *plen)
}
while (addend++ < 0)
- avr_asm_len ("push __zero_reg__", op, plen, 1);
+ avr_asm_len ("push __tmp_reg__", op, plen, 1);
}
else if (addend > 0)
{
@@ -9138,6 +9386,12 @@ avr_adjust_insn_length (rtx_insn *insn, int len)
rtx *op = recog_data.operand;
enum attr_adjust_len adjust_len;
+ /* As we pretend jump tables in .text, fix branch offsets crossing jump
+ tables now. */
+
+ if (JUMP_TABLE_DATA_P (insn))
+ return 0;
+
/* 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
@@ -9622,6 +9876,8 @@ avr_attribute_table[] =
false },
{ "interrupt", 0, 0, true, false, false, avr_handle_fndecl_attribute,
false },
+ { "no_gccisr", 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,
@@ -10001,7 +10257,9 @@ avr_asm_init_sections (void)
resp. `avr_need_copy_data_p'. If flash is not mapped to RAM then
we have also to track .rodata because it is located in RAM then. */
+#if defined HAVE_LD_AVR_AVRXMEGA3_RODATA_IN_FLASH
if (0 == avr_arch->flash_pm_offset)
+#endif
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;
@@ -10037,7 +10295,10 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl)
|| STR_PREFIX_P (name, ".gnu.linkonce.d"));
if (!avr_need_copy_data_p
- && 0 == avr_arch->flash_pm_offset)
+#if defined HAVE_LD_AVR_AVRXMEGA3_RODATA_IN_FLASH
+ && 0 == avr_arch->flash_pm_offset
+#endif
+ )
avr_need_copy_data_p = (STR_PREFIX_P (name, ".rodata")
|| STR_PREFIX_P (name, ".gnu.linkonce.r"));
@@ -10131,18 +10392,26 @@ avr_encode_section_info (tree decl, rtx rtl, int new_decl_p)
if (new_decl_p
&& decl && DECL_P (decl)
- && NULL_TREE == DECL_INITIAL (decl)
&& !DECL_EXTERNAL (decl)
&& avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
{
- // Don't warn for (implicit) aliases like in PR80462.
- tree asmname = DECL_ASSEMBLER_NAME (decl);
- varpool_node *node = varpool_node::get_for_asmname (asmname);
- bool alias_p = node && node->alias;
+ if (!TREE_READONLY (decl))
+ {
+ // This might happen with C++ if stuff needs constructing.
+ error ("variable %q+D with dynamic initialization put "
+ "into program memory area", decl);
+ }
+ else if (NULL_TREE == DECL_INITIAL (decl))
+ {
+ // Don't warn for (implicit) aliases like in PR80462.
+ tree asmname = DECL_ASSEMBLER_NAME (decl);
+ varpool_node *node = varpool_node::get_for_asmname (asmname);
+ bool alias_p = node && node->alias;
- if (!alias_p)
- warning (OPT_Wuninitialized, "uninitialized variable %q+D put into "
- "program memory area", decl);
+ if (!alias_p)
+ warning (OPT_Wuninitialized, "uninitialized variable %q+D put "
+ "into program memory area", decl);
+ }
}
default_encode_section_info (decl, rtl, new_decl_p);
@@ -10440,6 +10709,33 @@ avr_memory_move_cost (machine_mode mode,
}
+/* Cost for mul highpart. X is a LSHIFTRT, i.e. the outer TRUNCATE is
+ already stripped off. */
+
+static int
+avr_mul_highpart_cost (rtx x, int)
+{
+ if (AVR_HAVE_MUL
+ && LSHIFTRT == GET_CODE (x)
+ && MULT == GET_CODE (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
+ {
+ // This is the wider mode.
+ machine_mode mode = GET_MODE (x);
+
+ // The middle-end might still have PR81444, i.e. it is calling the cost
+ // functions with strange modes. Fix this now by also considering
+ // PSImode (should actually be SImode instead).
+ if (HImode == mode || PSImode == mode || SImode == mode)
+ {
+ return COSTS_N_INSNS (2);
+ }
+ }
+
+ return 10000;
+}
+
+
/* Mutually recursive subroutine of avr_rtx_cost for calculating the
cost of an RTX operand given its context. X is the rtx of the
operand, MODE is its mode, and OUTER is the rtx_code of this
@@ -10479,7 +10775,7 @@ avr_operand_rtx_cost (rtx x, machine_mode mode, enum rtx_code outer,
In either case, *TOTAL contains the cost result. */
static bool
-avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code ATTRIBUTE_UNUSED,
+avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code,
int opno ATTRIBUTE_UNUSED, int *total, bool speed)
{
enum rtx_code code = GET_CODE (x);
@@ -11133,6 +11429,12 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code ATTRIBUTE_UNUSED,
return true;
case LSHIFTRT:
+ if (outer_code == TRUNCATE)
+ {
+ *total = avr_mul_highpart_cost (x, speed);
+ return true;
+ }
+
switch (mode)
{
case QImode:
@@ -11310,16 +11612,10 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code ATTRIBUTE_UNUSED,
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 (LSHIFTRT == GET_CODE (XEXP (x, 0)))
{
- if (QImode == mode || HImode == mode)
- {
- *total = COSTS_N_INSNS (2);
- return true;
- }
+ *total = avr_mul_highpart_cost (XEXP (x, 0), speed);
+ return true;
}
break;
@@ -11367,8 +11663,7 @@ avr_address_cost (rtx x, machine_mode mode ATTRIBUTE_UNUSED,
}
else if (CONSTANT_ADDRESS_P (x))
{
- if (optimize > 0
- && io_address_operand (x, QImode))
+ if (io_address_operand (x, QImode))
cost = 2;
if (AVR_TINY
@@ -12309,17 +12604,88 @@ avr_out_reload_inpsi (rtx *op, rtx clobber_reg, int *len)
}
-/* Worker function for `ASM_OUTPUT_ADDR_VEC_ELT'. */
+/* Worker function for `ASM_OUTPUT_ADDR_VEC'. */
+/* Emit jump tables out-of-line so that branches crossing the table
+ get shorter offsets. If we have JUMP + CALL, then put the tables
+ in a dedicated non-.text section so that CALLs get better chance to
+ be relaxed to RCALLs.
+
+ We emit the tables by hand because `function_rodata_section' does not
+ work as expected, cf. PR71151, and we do *NOT* want the table to be
+ in .rodata, hence setting JUMP_TABLES_IN_TEXT_SECTION = 0 is of limited
+ use; and setting it to 1 attributes table lengths to branch offsets...
+ Moreover, fincal.c keeps switching section before each table entry
+ which we find too fragile as to rely on section caching. */
void
-avr_output_addr_vec_elt (FILE *stream, int value)
+avr_output_addr_vec (rtx_insn *labl, rtx table)
{
- if (AVR_HAVE_JMP_CALL)
- fprintf (stream, "\t.word gs(.L%d)\n", value);
+ FILE *stream = asm_out_file;
+
+ app_disable();
+
+ // Switch to appropriate (sub)section.
+
+ if (DECL_SECTION_NAME (current_function_decl)
+ && symtab_node::get (current_function_decl)
+ && ! symtab_node::get (current_function_decl)->implicit_section)
+ {
+ // .subsection will emit the code after the function and in the
+ // section as chosen by the user.
+
+ switch_to_section (current_function_section ());
+ fprintf (stream, "\t.subsection\t1\n");
+ }
else
- fprintf (stream, "\trjmp .L%d\n", value);
+ {
+ // Since PR63223 there is no restriction where to put the table; it
+ // may even reside above 128 KiB. We put it in a section as high as
+ // possible and avoid progmem in order not to waste flash <= 64 KiB.
+
+ const char *sec_name = ".jumptables.gcc";
+
+ // The table belongs to its host function, therefore use fine
+ // grained sections so that, if that function is removed by
+ // --gc-sections, the child table(s) may also be removed. */
+
+ tree asm_name = DECL_ASSEMBLER_NAME (current_function_decl);
+ const char *fname = IDENTIFIER_POINTER (asm_name);
+ fname = targetm.strip_name_encoding (fname);
+ sec_name = ACONCAT ((sec_name, ".", fname, NULL));
+
+ fprintf (stream, "\t.section\t%s,\"%s\",@progbits\n", sec_name,
+ AVR_HAVE_JMP_CALL ? "a" : "ax");
+ }
+
+ // Output the label that preceeds the table.
+
+ ASM_OUTPUT_ALIGN (stream, 1);
+ targetm.asm_out.internal_label (stream, "L", CODE_LABEL_NUMBER (labl));
+
+ // Output the table's content.
+
+ int vlen = XVECLEN (table, 0);
+
+ for (int idx = 0; idx < vlen; idx++)
+ {
+ int value = CODE_LABEL_NUMBER (XEXP (XVECEXP (table, 0, idx), 0));
+
+ if (AVR_HAVE_JMP_CALL)
+ fprintf (stream, "\t.word gs(.L%d)\n", value);
+ else
+ fprintf (stream, "\trjmp .L%d\n", value);
+ }
+
+ // Switch back to original section. As we clobbered the section above,
+ // forget the current section before switching back.
+
+ in_section = NULL;
+ switch_to_section (current_function_section ());
}
+
+/* Implement `TARGET_CONDITIONAL_REGISTER_USAGE'. */
+
static void
avr_conditional_register_usage (void)
{
@@ -14286,6 +14652,9 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg,
#undef TARGET_ASM_SELECT_SECTION
#define TARGET_ASM_SELECT_SECTION avr_asm_select_section
+#undef TARGET_ASM_FINAL_POSTSCAN_INSN
+#define TARGET_ASM_FINAL_POSTSCAN_INSN avr_asm_final_postscan_insn
+
#undef TARGET_REGISTER_MOVE_COST
#define TARGET_REGISTER_MOVE_COST avr_register_move_cost
#undef TARGET_MEMORY_MOVE_COST