Commit 6d3d9133 by Nick Clifton Committed by Nick Clifton

Add support for interrupt function attribute

From-SVN: r38134
parent 2bff3fd5
2000-12-08 Nick Clifton <nickc@redhat.com>
* extend.texi: Document ARM "interrupt" function attribute.
Mention that the ARM also support the "naked" function
attribute.
* config/arm/arm-protos.h (arm_current_func_type): Add
prototype.
* config/arm/arm.h (EXCEPTION_LR_REGNUM): Define.
(struct machine_function): Add 'func_type' field.
Define bit values for 'func_type' field.
(ARM_FUNC_TYPE): New macro.
(IS_INTERRUPT): New macro.
(IS_VOLATILE): New macro.
(IS_NAKED): New macro.
(IS_NESTED): New macro.
(ARM_INITIAL_ELIMINATION_OFFSET): Use IS_VOLATILE.
* config/arm/arm.c (isr_attribute_args): New Structure. A
list of "interrupt" function attribute modifiers.
(arm_isr_value): New Function: Returns the type of the current
interrupt function.
(arm_compute_func_type): New Function: Computes the type of
the current function.
(arm_current_func_type): New Function: Returns the type of the
current function.
(use_return_insn): Use arm_current_func_type.
(arm_valid_type_attribute_p): Accept "interrupt" function
attribute.
(arm_comp_type_attributes): Check "interrupt" attributes.
(arm_valid_machine_decl): Accept "interrupt" function
attribute.
(arm_function_ok_for_sibcall): Do not allow interrupt
functions to use sibcalls.
(arm_naked_function_p): Delete.
(print_multi_reg): Remove redundant parameter 'hat'.
(arm_compute_save_reg_mask): New Function: Compute a bit mask
of registers saved during the current function's prologue.
(output_arm_return_instruction): Use arm_current_func_type.
Generate return instruction when LR is not poppsed off the
stack.
(arm_volatile_func): Delete.
(output_arm_prologue): Use arm_current_func_type and
arm_compute_save_reg_mask.
Note presernce of interrupt functions.
(arm_output_epilogue): Use arm_current_func_type and
arm_compute_save_reg_mask.
(arm_expand_prologue): Use arm_current_func_type and
arm_compute_save_reg_mask.
(arm_init_machine_status): Initialise func_type field, if
necessary.
(thumb_expand_prologue): Use arm_current_func_type.
(output_thumb_prologue): Use arm_current_func_type.
2000-12-08 Brad Lucier <lucier@math.purdue.edu> 2000-12-08 Brad Lucier <lucier@math.purdue.edu>
* tradcpp.c (do_include): Make pointer differences 64-bit clean. * tradcpp.c (do_include): Make pointer differences 64-bit clean.
......
...@@ -34,6 +34,7 @@ extern void arm_expand_prologue PARAMS ((void)); ...@@ -34,6 +34,7 @@ extern void arm_expand_prologue PARAMS ((void));
/* Used in arm.md, but defined in output.c. */ /* Used in arm.md, but defined in output.c. */
extern void assemble_align PARAMS ((int)); extern void assemble_align PARAMS ((int));
extern const char * arm_strip_name_encoding PARAMS ((const char *)); extern const char * arm_strip_name_encoding PARAMS ((const char *));
extern unsigned long arm_current_func_type PARAMS ((void));
#ifdef TREE_CODE #ifdef TREE_CODE
extern int arm_return_in_memory PARAMS ((tree)); extern int arm_return_in_memory PARAMS ((tree));
......
...@@ -55,32 +55,32 @@ typedef struct minipool_fixup Mfix; ...@@ -55,32 +55,32 @@ typedef struct minipool_fixup Mfix;
#define Hint HOST_WIDE_INT #define Hint HOST_WIDE_INT
#define Mmode enum machine_mode #define Mmode enum machine_mode
#define Ulong unsigned long #define Ulong unsigned long
#define Ccstar const char *
/* Forward function declarations. */ /* Forward function declarations. */
static void arm_add_gc_roots PARAMS ((void)); static void arm_add_gc_roots PARAMS ((void));
static int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int)); static int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int));
static int arm_naked_function_p PARAMS ((tree));
static Ulong bit_count PARAMS ((signed int)); static Ulong bit_count PARAMS ((signed int));
static int const_ok_for_op PARAMS ((Hint, enum rtx_code)); static int const_ok_for_op PARAMS ((Hint, enum rtx_code));
static int eliminate_lr2ip PARAMS ((rtx *)); static int eliminate_lr2ip PARAMS ((rtx *));
static rtx emit_multi_reg_push PARAMS ((int)); static rtx emit_multi_reg_push PARAMS ((int));
static rtx emit_sfm PARAMS ((int, int)); static rtx emit_sfm PARAMS ((int, int));
static const char * fp_const_from_val PARAMS ((REAL_VALUE_TYPE *)); static Ccstar fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
static arm_cc get_arm_condition_code PARAMS ((rtx)); static arm_cc get_arm_condition_code PARAMS ((rtx));
static void init_fpa_table PARAMS ((void)); static void init_fpa_table PARAMS ((void));
static Hint int_log2 PARAMS ((Hint)); static Hint int_log2 PARAMS ((Hint));
static rtx is_jump_table PARAMS ((rtx)); static rtx is_jump_table PARAMS ((rtx));
static const char * output_multi_immediate PARAMS ((rtx *, const char *, const char *, int, Hint)); static Ccstar output_multi_immediate PARAMS ((rtx *, Ccstar, Ccstar, int, Hint));
static void print_multi_reg PARAMS ((FILE *, const char *, int, int, int)); static void print_multi_reg PARAMS ((FILE *, Ccstar, int, int));
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint)); static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
static const char * shift_op PARAMS ((rtx, Hint *)); static Ccstar shift_op PARAMS ((rtx, Hint *));
static void arm_init_machine_status PARAMS ((struct function *)); static void arm_init_machine_status PARAMS ((struct function *));
static void arm_mark_machine_status PARAMS ((struct function *)); static void arm_mark_machine_status PARAMS ((struct function *));
static int number_of_first_bit_set PARAMS ((int)); static int number_of_first_bit_set PARAMS ((int));
static void replace_symbols_in_block PARAMS ((tree, rtx, rtx)); static void replace_symbols_in_block PARAMS ((tree, rtx, rtx));
static void thumb_exit PARAMS ((FILE *, int, rtx)); static void thumb_exit PARAMS ((FILE *, int, rtx));
static void thumb_pushpop PARAMS ((FILE *, int, int)); static void thumb_pushpop PARAMS ((FILE *, int, int));
static const char * thumb_condition_code PARAMS ((rtx, int)); static Ccstar thumb_condition_code PARAMS ((rtx, int));
static rtx is_jump_table PARAMS ((rtx)); static rtx is_jump_table PARAMS ((rtx));
static Hint get_jump_table_size PARAMS ((rtx)); static Hint get_jump_table_size PARAMS ((rtx));
static Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint)); static Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint));
...@@ -96,10 +96,14 @@ static void push_minipool_barrier PARAMS ((rtx, Hint)); ...@@ -96,10 +96,14 @@ static void push_minipool_barrier PARAMS ((rtx, Hint));
static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx)); static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx));
static void note_invalid_constants PARAMS ((rtx, Hint)); static void note_invalid_constants PARAMS ((rtx, Hint));
static int current_file_function_operand PARAMS ((rtx)); static int current_file_function_operand PARAMS ((rtx));
static Ulong arm_compute_save_reg_mask PARAMS ((void));
static Ulong arm_isr_value PARAMS ((tree));
static Ulong arm_compute_func_type PARAMS ((void));
#undef Hint #undef Hint
#undef Mmode #undef Mmode
#undef Ulong #undef Ulong
#undef Ccstar
/* Obstack for minipool constant handling. */ /* Obstack for minipool constant handling. */
static struct obstack minipool_obstack; static struct obstack minipool_obstack;
...@@ -683,26 +687,150 @@ arm_add_gc_roots () ...@@ -683,26 +687,150 @@ arm_add_gc_roots ()
{ {
ggc_add_rtx_root (&arm_compare_op0, 1); ggc_add_rtx_root (&arm_compare_op0, 1);
ggc_add_rtx_root (&arm_compare_op1, 1); ggc_add_rtx_root (&arm_compare_op1, 1);
ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root */ ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root. */
gcc_obstack_init(&minipool_obstack); gcc_obstack_init(&minipool_obstack);
minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0); minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
} }
/* A table of known ARM exception types.
For use with the interrupt function attribute. */
typedef struct
{
const char * arg;
unsigned long return_value;
}
isr_attribute_arg;
static isr_attribute_arg isr_attribute_args [] =
{
{ "IRQ", ARM_FT_ISR },
{ "irq", ARM_FT_ISR },
{ "FIQ", ARM_FT_FIQ },
{ "fiq", ARM_FT_FIQ },
{ "ABORT", ARM_FT_ISR },
{ "abort", ARM_FT_ISR },
{ "ABORT", ARM_FT_ISR },
{ "abort", ARM_FT_ISR },
{ "UNDEF", ARM_FT_EXCEPTION },
{ "undef", ARM_FT_EXCEPTION },
{ "SWI", ARM_FT_EXCEPTION },
{ "swi", ARM_FT_EXCEPTION },
{ NULL, ARM_FT_NORMAL }
};
/* Returns the (interrupt) function type of the current
function, or ARM_FT_UNKNOWN if the type cannot be determined. */
static unsigned long
arm_isr_value (argument)
tree argument;
{
isr_attribute_arg * ptr;
const char * arg;
/* No argument - default to IRQ. */
if (argument == NULL_TREE)
return ARM_FT_ISR;
/* Get the value of the argument. */
if (TREE_VALUE (argument) == NULL_TREE
|| TREE_CODE (TREE_VALUE (argument)) != STRING_CST)
return ARM_FT_UNKNOWN;
arg = TREE_STRING_POINTER (TREE_VALUE (argument));
/* Check it against the list of known arguments. */
for (ptr = isr_attribute_args; ptr->arg != NULL; ptr ++)
if (strcmp (arg, ptr->arg) == 0)
return ptr->return_value;
/* An unrecognised interrupt type. */
return ARM_FT_UNKNOWN;
}
/* Computes the type of the current function. */
static unsigned long
arm_compute_func_type ()
{
unsigned long type = ARM_FT_UNKNOWN;
tree a;
tree attr;
if (TREE_CODE (current_function_decl) != FUNCTION_DECL)
abort ();
/* Decide if the current function is volatile. Such functions
never return, and many memory cycles can be saved by not storing
register values that will never be needed again. This optimization
was added to speed up context switching in a kernel application. */
if (optimize > 0
&& current_function_nothrow
&& TREE_THIS_VOLATILE (current_function_decl))
type |= ARM_FT_VOLATILE;
if (current_function_needs_context)
type |= ARM_FT_NESTED;
attr = DECL_MACHINE_ATTRIBUTES (current_function_decl);
a = lookup_attribute ("naked", attr);
if (a != NULL_TREE)
type |= ARM_FT_NAKED;
if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX)
type |= ARM_FT_EXCEPTION_HANDLER;
else
{
a = lookup_attribute ("isr", attr);
if (a == NULL_TREE)
a = lookup_attribute ("interrupt", attr);
if (a == NULL_TREE)
type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL;
else
type |= arm_isr_value (TREE_VALUE (a));
}
return type;
}
/* Returns the type of the current function. */
unsigned long
arm_current_func_type ()
{
if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN)
cfun->machine->func_type = arm_compute_func_type ();
return cfun->machine->func_type;
}
/* Return 1 if it is possible to return using a single instruction. */ /* Return 1 if it is possible to return using a single instruction. */
int int
use_return_insn (iscond) use_return_insn (iscond)
int iscond; int iscond;
{ {
int regno; int regno;
unsigned int func_type = arm_current_func_type ();
/* Never use a return instruction before reload has run. */ /* Never use a return instruction before reload has run. */
if (!reload_completed if (!reload_completed)
/* Or if the function is variadic. */ return 0;
|| current_function_pretend_args_size
/* Naked functions, volatile functiond and interrupt
functions all need special consideration. */
if (func_type & (ARM_FT_INTERRUPT | ARM_FT_VOLATILE | ARM_FT_NAKED))
return 0;
/* As do variadic functions. */
if (current_function_pretend_args_size
|| current_function_anonymous_args || current_function_anonymous_args
/* Of if the function calls __builtin_eh_return () */ /* Of if the function calls __builtin_eh_return () */
|| cfun->machine->eh_epilogue_sp_ofs != NULL || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
/* Or if there is no frame pointer and there is a stack adjustment. */ /* Or if there is no frame pointer and there is a stack adjustment. */
|| ((get_frame_size () + current_function_outgoing_args_size != 0) || ((get_frame_size () + current_function_outgoing_args_size != 0)
&& !frame_pointer_needed)) && !frame_pointer_needed))
...@@ -725,17 +853,13 @@ use_return_insn (iscond) ...@@ -725,17 +853,13 @@ use_return_insn (iscond)
return 0; return 0;
} }
/* Can't be done if any of the FPU regs are pushed, since this also /* Can't be done if any of the FPU regs are pushed,
requires an insn. */ since this also requires an insn. */
if (TARGET_HARD_FLOAT) if (TARGET_HARD_FLOAT)
for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++) for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
if (regs_ever_live[regno] && !call_used_regs[regno]) if (regs_ever_live[regno] && !call_used_regs[regno])
return 0; return 0;
/* If a function is naked, don't use the "return" insn. */
if (arm_naked_function_p (current_function_decl))
return 0;
return 1; return 1;
} }
...@@ -1685,6 +1809,11 @@ arm_valid_type_attribute_p (type, attributes, identifier, args) ...@@ -1685,6 +1809,11 @@ arm_valid_type_attribute_p (type, attributes, identifier, args)
if (is_attribute_p ("short_call", identifier)) if (is_attribute_p ("short_call", identifier))
return (args == NULL_TREE); return (args == NULL_TREE);
/* Interrupt Service Routines have special prologue and epilogue requirements. */
if (is_attribute_p ("isr", identifier)
|| is_attribute_p ("interrupt", identifier))
return arm_isr_value (args);
return 0; return 0;
} }
...@@ -1720,6 +1849,16 @@ arm_comp_type_attributes (type1, type2) ...@@ -1720,6 +1849,16 @@ arm_comp_type_attributes (type1, type2)
return 0; return 0;
} }
/* Check for mismatched ISR attribute. */
l1 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type1)) != NULL;
if (! l1)
l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type1)) != NULL;
l2 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type2)) != NULL;
if (! l2)
l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type2)) != NULL;
if (l1 != l2)
return 0;
return 1; return 1;
} }
...@@ -1745,7 +1884,7 @@ arm_encode_call_attribute (decl, flag) ...@@ -1745,7 +1884,7 @@ arm_encode_call_attribute (decl, flag)
newstr[0] = flag; newstr[0] = flag;
strcpy (newstr + 1, str); strcpy (newstr + 1, str);
newstr = ggc_alloc_string (newstr, len + 1); newstr = (char *) ggc_alloc_string (newstr, len + 1);
XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr; XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
} }
...@@ -1882,6 +2021,10 @@ arm_function_ok_for_sibcall (decl) ...@@ -1882,6 +2021,10 @@ arm_function_ok_for_sibcall (decl)
if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl)) if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl))
return 0; return 0;
/* Never tailcall from an ISR routine - it needs a special exit sequence. */
if (IS_INTERRUPT (arm_current_func_type ()))
return 0;
/* Everything else is ok. */ /* Everything else is ok. */
return 1; return 1;
} }
...@@ -3862,6 +4005,9 @@ multi_register_push (op, mode) ...@@ -3862,6 +4005,9 @@ multi_register_push (op, mode)
don't output any prologue or epilogue code, the user is assumed don't output any prologue or epilogue code, the user is assumed
to do the right thing. to do the right thing.
isr or interrupt:
Interrupt Service Routine.
interfacearm: interfacearm:
Always assume that this function will be entered in ARM mode, Always assume that this function will be entered in ARM mode,
not Thumb mode, and that the caller wishes to be returned to in not Thumb mode, and that the caller wishes to be returned to in
...@@ -3872,6 +4018,12 @@ arm_valid_machine_decl_attribute (decl, attr, args) ...@@ -3872,6 +4018,12 @@ arm_valid_machine_decl_attribute (decl, attr, args)
tree attr; tree attr;
tree args; tree args;
{ {
/* The interrupt attribute can take args, so check for it before
rejecting other attributes on the grounds that they did have args. */
if (is_attribute_p ("isr", attr)
|| is_attribute_p ("interrupt", attr))
return TREE_CODE (decl) == FUNCTION_DECL;
if (args != NULL_TREE) if (args != NULL_TREE)
return 0; return 0;
...@@ -3885,20 +4037,6 @@ arm_valid_machine_decl_attribute (decl, attr, args) ...@@ -3885,20 +4037,6 @@ arm_valid_machine_decl_attribute (decl, attr, args)
return 0; return 0;
} }
/* Return non-zero if FUNC is a naked function. */
static int
arm_naked_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
abort ();
a = lookup_attribute ("naked", DECL_MACHINE_ATTRIBUTES (func));
return a != NULL_TREE;
}
/* Routines for use in generating RTL. */ /* Routines for use in generating RTL. */
rtx rtx
...@@ -5895,16 +6033,15 @@ fp_const_from_val (r) ...@@ -5895,16 +6033,15 @@ fp_const_from_val (r)
/* Output the operands of a LDM/STM instruction to STREAM. /* Output the operands of a LDM/STM instruction to STREAM.
MASK is the ARM register set mask of which only bits 0-15 are important. MASK is the ARM register set mask of which only bits 0-15 are important.
INSTR is the possibly suffixed base register. HAT unequals zero if a hat REG is the base register, either the frame pointer or the stack pointer,
must follow the register list. */ INSTR is the possibly suffixed load or store instruction. */
static void static void
print_multi_reg (stream, instr, reg, mask, hat) print_multi_reg (stream, instr, reg, mask)
FILE * stream; FILE * stream;
const char * instr; const char * instr;
int reg; int reg;
int mask; int mask;
int hat;
{ {
int i; int i;
int not_first = FALSE; int not_first = FALSE;
...@@ -5923,7 +6060,7 @@ print_multi_reg (stream, instr, reg, mask, hat) ...@@ -5923,7 +6060,7 @@ print_multi_reg (stream, instr, reg, mask, hat)
not_first = TRUE; not_first = TRUE;
} }
fprintf (stream, "}%s\n", hat ? "^" : ""); fprintf (stream, "}%s\n", TARGET_APCS_32 ? "" : "^");
} }
/* Output a 'call' insn. */ /* Output a 'call' insn. */
...@@ -6694,6 +6831,86 @@ output_ascii_pseudo_op (stream, p, len) ...@@ -6694,6 +6831,86 @@ output_ascii_pseudo_op (stream, p, len)
fputs ("\"\n", stream); fputs ("\"\n", stream);
} }
/* Compute a bit mask of which registers need to be
saved on the stack for the current function. */
static unsigned long
arm_compute_save_reg_mask ()
{
unsigned int save_reg_mask = 0;
unsigned int reg;
unsigned long func_type = arm_current_func_type ();
if (IS_NAKED (func_type))
/* This should never really happen. */
return 0;
/* If we are creating a stack frame, then we must save the frame pointer,
IP (which will hold the old stack pointer), LR and the PC. */
if (frame_pointer_needed)
save_reg_mask |=
(1 << ARM_HARD_FRAME_POINTER_REGNUM)
| (1 << IP_REGNUM)
| (1 << LR_REGNUM)
| (1 << PC_REGNUM);
/* Volatile functions do not return, so there
is no need to save any other registers. */
if (IS_VOLATILE (func_type))
return save_reg_mask;
if (ARM_FUNC_TYPE (func_type) == ARM_FT_ISR)
{
/* FIQ handlers have registers r8 - r12 banked, so
we only need to check r0 - r7, they must save them. */
for (reg = 0; reg < 8; reg++)
if (regs_ever_live[reg])
save_reg_mask |= (1 << reg);
}
else
{
/* In the normal case we only need to save those registers
which are call saved and which are used by this function. */
for (reg = 0; reg <= 10; reg++)
if (regs_ever_live[reg] && ! call_used_regs [reg])
save_reg_mask |= (1 << reg);
/* Handle the frame pointer as a special case. */
if (! TARGET_APCS_FRAME
&& ! frame_pointer_needed
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
/* If we aren't loading the PIC register,
don't stack it even though it may be live. */
if (flag_pic
&& ! TARGET_SINGLE_PIC_BASE
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
}
/* Decide if we need to save the link register.
Interrupt routines have their own banked link register,
so they never need to save it.
Otheriwse if we do not use the link register we do not need to save
it. If we are pushing other registers onto the stack however, we
can save an instruction in the epilogue by pushing the link register
now and then popping it back into the PC. This incurs extra memory
accesses though, so we only do it when optimising for size, and only
if we know that we will not need a fancy return sequence. */
if (! IS_INTERRUPT (func_type)
&& (regs_ever_live [LR_REGNUM]
|| (save_reg_mask
&& optimize_size
&& ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)))
save_reg_mask |= 1 << LR_REGNUM;
return save_reg_mask;
}
/* Generate a function exit sequence. If REALLY_RETURN is true, then do
everything bar the final return instruction. */
const char * const char *
output_return_instruction (operand, really_return, reverse) output_return_instruction (operand, really_return, reverse)
...@@ -6701,17 +6918,18 @@ output_return_instruction (operand, really_return, reverse) ...@@ -6701,17 +6918,18 @@ output_return_instruction (operand, really_return, reverse)
int really_return; int really_return;
int reverse; int reverse;
{ {
char conditional[10];
char instr[100]; char instr[100];
int reg, live_regs = 0; int reg;
int volatile_func = arm_volatile_func (); unsigned long live_regs_mask;
unsigned long func_type;
func_type = arm_current_func_type ();
/* If a function is naked, don't use the "return" insn. */ if (IS_NAKED (func_type))
if (arm_naked_function_p (current_function_decl))
return ""; return "";
return_used_this_function = 1; if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
if (TARGET_ABORT_NORETURN && volatile_func)
{ {
/* If this function was declared non-returning, and we have found a tail /* If this function was declared non-returning, and we have found a tail
call, then we have to trust that the called function won't return. */ call, then we have to trust that the called function won't return. */
...@@ -6729,137 +6947,163 @@ output_return_instruction (operand, really_return, reverse) ...@@ -6729,137 +6947,163 @@ output_return_instruction (operand, really_return, reverse)
return ""; return "";
} }
if (current_function_calls_alloca && !really_return) if (current_function_calls_alloca && !really_return)
abort (); abort ();
for (reg = 0; reg <= 10; reg++)
if (regs_ever_live[reg] && !call_used_regs[reg])
live_regs++;
if (!TARGET_APCS_FRAME /* Construct the conditional part of the instruction(s) to be emitted. */
&& !frame_pointer_needed sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd');
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& !call_used_regs[HARD_FRAME_POINTER_REGNUM])
live_regs++;
if (flag_pic && !TARGET_SINGLE_PIC_BASE return_used_this_function = 1;
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
live_regs++;
if (live_regs || regs_ever_live[LR_REGNUM])
live_regs++;
if (frame_pointer_needed) live_regs_mask = arm_compute_save_reg_mask ();
live_regs += 4;
/* On some ARM architectures it is faster to use LDR rather than LDM to /* On some ARM architectures it is faster to use LDR rather than LDM to
load a single register. On other architectures, the cost is the same. */ load a single register. On other architectures, the cost is the same.
if (live_regs == 1 In 26 bit mode we have to use LDM in order to be able to restore the CPSR. */
&& regs_ever_live[LR_REGNUM] if ((live_regs_mask == (1 << LR_REGNUM))
&& !really_return) && (! really_return || TARGET_APCS_32))
output_asm_insn (reverse ? "ldr%?%D0\t%|lr, [%|sp], #4" {
: "ldr%?%d0\t%|lr, [%|sp], #4", &operand); if (! really_return)
else if (live_regs == 1 sprintf (instr, "ldr%s\t%%|lr, [%%|sp], #4", conditional);
&& regs_ever_live[LR_REGNUM] else
&& TARGET_APCS_32) sprintf (instr, "ldr%s\t%%|pc, [%%|sp], #4", conditional);
output_asm_insn (reverse ? "ldr%?%D0\t%|pc, [%|sp], #4" }
: "ldr%?%d0\t%|pc, [%|sp], #4", &operand); else if (live_regs_mask)
else if (live_regs) {
{ if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
if (!regs_ever_live[LR_REGNUM]) /* There are two possible reasons for the IP register being saved.
live_regs++; Either a stack frame was created, in which case IP contains the
old stack pointer, or an ISR routine corrupted it. If this in an
ISR routine then just restore IP, otherwise restore IP into SP. */
if (! IS_INTERRUPT (func_type))
{
live_regs_mask &= ~ (1 << IP_REGNUM);
live_regs_mask |= (1 << SP_REGNUM);
}
/* Generate the load multiple instruction to restore the registers. */
if (frame_pointer_needed) if (frame_pointer_needed)
strcpy (instr, sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
reverse ? "ldm%?%D0ea\t%|fp, {" : "ldm%?%d0ea\t%|fp, {");
else else
strcpy (instr, sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
reverse ? "ldm%?%D0fd\t%|sp!, {" : "ldm%?%d0fd\t%|sp!, {");
for (reg = 0; reg <= 10; reg++) for (reg = 0; reg <= SP_REGNUM; reg++)
if (regs_ever_live[reg] if (live_regs_mask & (1 << reg))
&& (!call_used_regs[reg] {
|| (flag_pic && !TARGET_SINGLE_PIC_BASE
&& reg == PIC_OFFSET_TABLE_REGNUM)))
{
strcat (instr, "%|"); strcat (instr, "%|");
strcat (instr, reg_names[reg]); strcat (instr, reg_names[reg]);
if (--live_regs) strcat (instr, ", ");
strcat (instr, ", "); }
}
if (frame_pointer_needed) if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
{ {
strcat (instr, "%|"); /* If we are not restoring the LR register then we will
strcat (instr, reg_names[11]); have added one too many commas to the list above.
strcat (instr, ", "); Replace it with a closing brace. */
strcat (instr, "%|"); instr [strlen (instr) - 2] = '}';
strcat (instr, reg_names[13]); }
strcat (instr, ", ");
strcat (instr, "%|");
strcat (instr, TARGET_INTERWORK || (!really_return)
? reg_names[LR_REGNUM] : reg_names[PC_REGNUM] );
}
else else
{ {
if (!TARGET_APCS_FRAME
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& !call_used_regs[HARD_FRAME_POINTER_REGNUM])
{
strcat (instr, "%|");
strcat (instr, reg_names[HARD_FRAME_POINTER_REGNUM]);
strcat (instr, ", ");
}
strcat (instr, "%|"); strcat (instr, "%|");
if (TARGET_INTERWORK && really_return) /* At this point there should only be one or two registers left in
strcat (instr, reg_names[IP_REGNUM]); live_regs_mask: always LR, and possibly PC if we created a stack
frame. LR contains the return address. If we do not have any
special requirements for function exit (eg interworking, or ISR)
then we can load this value directly into the PC and save an
instruction. */
if (! TARGET_INTERWORK
&& ! IS_INTERRUPT (func_type)
&& really_return)
strcat (instr, reg_names [PC_REGNUM]);
else else
strcat (instr, really_return ? reg_names[PC_REGNUM] : reg_names[LR_REGNUM]); strcat (instr, reg_names [LR_REGNUM]);
strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
} }
strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
output_asm_insn (instr, &operand);
if (TARGET_INTERWORK && really_return) if (really_return)
{ {
strcpy (instr, "bx%?"); /* See if we need to generate an extra instruction to
strcat (instr, reverse ? "%D0" : "%d0"); perform the actual function return. */
strcat (instr, "\t%|"); switch ((int) ARM_FUNC_TYPE (func_type))
strcat (instr, frame_pointer_needed ? "lr" : "ip"); {
case ARM_FT_ISR:
case ARM_FT_FIQ:
output_asm_insn (instr, & operand);
strcpy (instr, "sub");
strcat (instr, conditional);
strcat (instr, "s\t%|pc, %|lr, #4");
break;
case ARM_FT_EXCEPTION:
output_asm_insn (instr, & operand);
strcpy (instr, "mov");
strcat (instr, conditional);
strcat (instr, "s\t%|pc, %|lr");
break;
case ARM_FT_INTERWORKED:
output_asm_insn (instr, & operand);
strcpy (instr, "bx");
strcat (instr, conditional);
strcat (instr, "\t%|lr");
break;
output_asm_insn (instr, &operand); default:
/* The return has already been handled
by loading the LR into the PC. */
if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
{
output_asm_insn (instr, & operand);
strcpy (instr, "mov");
strcat (instr, conditional);
if (! TARGET_APCS_32)
strcat (instr, "s");
strcat (instr, "\t%|pc, %|lr");
}
break;
}
} }
} }
else if (really_return) else if (really_return)
{ {
if (TARGET_INTERWORK) switch ((int) ARM_FUNC_TYPE (func_type))
sprintf (instr, "bx%%?%%%s0\t%%|lr", reverse ? "D" : "d"); {
else case ARM_FT_ISR:
sprintf (instr, "mov%%?%%%s0%s\t%%|pc, %%|lr", case ARM_FT_FIQ:
reverse ? "D" : "d", TARGET_APCS_32 ? "" : "s"); sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional);
break;
output_asm_insn (instr, &operand);
case ARM_FT_INTERWORKED:
sprintf (instr, "bx%s\t%%|lr", conditional);
break;
case ARM_FT_EXCEPTION:
sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional);
break;
default:
sprintf (instr, "mov%s%s\t%%|pc, %%|lr",
conditional, TARGET_APCS_32 ? "" : "s");
break;
}
} }
else
/* Nothing to load off the stack, and
no return instruction to generate. */
return "";
output_asm_insn (instr, & operand);
return ""; return "";
} }
/* Return nonzero if optimizing and the current function is volatile.
Such functions never return, and many memory cycles can be saved
by not storing register values that will never be needed again.
This optimization was added to speed up context switching in a
kernel application. */
int
arm_volatile_func ()
{
return (optimize > 0
&& current_function_nothrow
&& TREE_THIS_VOLATILE (current_function_decl));
}
/* Write the function name into the code section, directly preceding /* Write the function name into the code section, directly preceding
the function prologue. the function prologue.
...@@ -6905,82 +7149,67 @@ arm_poke_function_name (stream, name) ...@@ -6905,82 +7149,67 @@ arm_poke_function_name (stream, name)
ASM_OUTPUT_INT (stream, x); ASM_OUTPUT_INT (stream, x);
} }
/* The amount of stack adjustment that happens here, in output_return and in /* Place some comments into the assembler stream
output_epilogue must be exactly the same as was calculated during reload, describing the current function. */
or things will point to the wrong place. The only time we can safely
ignore this constraint is when a function has no arguments on the stack,
no stack frame requirement and no live registers execpt for `lr'. If we
can guarantee that by making all function calls into tail calls and that
lr is not clobbered in any other way, then there is no need to push lr
onto the stack. */
void void
output_arm_prologue (f, frame_size) output_arm_prologue (f, frame_size)
FILE * f; FILE * f;
int frame_size; int frame_size;
{ {
int reg, live_regs_mask = 0; unsigned long func_type;
int volatile_func = arm_volatile_func ();
/* Sanity check. */
/* Nonzero if we must stuff some register arguments onto the stack as if
they were passed there. */
int store_arg_regs = 0;
if (arm_ccfsm_state || arm_target_insn) if (arm_ccfsm_state || arm_target_insn)
abort (); /* Sanity check. */ abort ();
if (arm_naked_function_p (current_function_decl))
return;
return_used_this_function = 0; func_type = arm_current_func_type ();
switch ((int) ARM_FUNC_TYPE (func_type))
{
default:
case ARM_FT_NORMAL:
break;
case ARM_FT_INTERWORKED:
asm_fprintf (f, "\t%@ Function supports interworking.\n");
break;
case ARM_FT_EXCEPTION_HANDLER:
asm_fprintf (f, "\t%@ C++ Exception Handler.\n");
break;
case ARM_FT_ISR:
asm_fprintf (f, "\t%@ Interrupt Service Routine.\n");
break;
case ARM_FT_FIQ:
asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n");
break;
case ARM_FT_EXCEPTION:
asm_fprintf (f, "\t%@ ARM Exception Handler.\n");
break;
}
if (IS_NAKED (func_type))
asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n");
if (IS_VOLATILE (func_type))
asm_fprintf (f, "\t%@ Volatile: function does not return.\n");
if (IS_NESTED (func_type))
asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n");
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n", asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
current_function_args_size, current_function_args_size,
current_function_pretend_args_size, frame_size); current_function_pretend_args_size, frame_size);
asm_fprintf (f, "\t%@ frame_needed = %d, current_function_anonymous_args = %d\n", asm_fprintf (f, "\t%@ frame_needed = %d, current_function_anonymous_args = %d\n",
frame_pointer_needed, frame_pointer_needed,
current_function_anonymous_args); current_function_anonymous_args);
if (volatile_func)
asm_fprintf (f, "\t%@ Volatile function.\n");
if (current_function_needs_context)
asm_fprintf (f, "\t%@ Nested function.\n");
if (current_function_anonymous_args && current_function_pretend_args_size)
store_arg_regs = 1;
for (reg = 0; reg <= 10; reg++)
if (regs_ever_live[reg] && !call_used_regs[reg])
live_regs_mask |= (1 << reg);
if (!TARGET_APCS_FRAME
&& !frame_pointer_needed
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& !call_used_regs[HARD_FRAME_POINTER_REGNUM])
live_regs_mask |= (1 << HARD_FRAME_POINTER_REGNUM);
if (flag_pic && !TARGET_SINGLE_PIC_BASE
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
live_regs_mask |= (1 << PIC_OFFSET_TABLE_REGNUM);
if (frame_pointer_needed)
live_regs_mask |= 0xD800;
else if (regs_ever_live[LR_REGNUM])
{
live_regs_mask |= 1 << LR_REGNUM;
}
if (live_regs_mask)
/* If a di mode load/store multiple is used, and the base register
is r3, then r4 can become an ever live register without lr
doing so, in this case we need to push lr as well, or we
will fail to get a proper return. */
live_regs_mask |= 1 << LR_REGNUM;
#ifdef AOF_ASSEMBLER #ifdef AOF_ASSEMBLER
if (flag_pic) if (flag_pic)
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM); asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM);
#endif #endif
return_used_this_function = 0;
} }
const char * const char *
...@@ -6988,68 +7217,51 @@ arm_output_epilogue (really_return) ...@@ -6988,68 +7217,51 @@ arm_output_epilogue (really_return)
int really_return; int really_return;
{ {
int reg; int reg;
int live_regs_mask = 0; unsigned long live_regs_mask;
unsigned long func_type;
/* If we need this, then it will always be at least this much. */ /* If we need this, then it will always be at least this much. */
int floats_offset = 12; int floats_offset = 12;
rtx operands[3]; rtx operands[3];
int frame_size = get_frame_size (); int frame_size = get_frame_size ();
rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
FILE * f = asm_out_file; FILE * f = asm_out_file;
int volatile_func = arm_volatile_func (); rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
int return_regnum;
/* If we have already generated the return instruction
then it is futile to generate anything else. */
if (use_return_insn (FALSE) && return_used_this_function) if (use_return_insn (FALSE) && return_used_this_function)
return ""; return "";
/* Naked functions don't have epilogues. */ func_type = arm_current_func_type ();
if (arm_naked_function_p (current_function_decl))
return "";
/* If we are throwing an exception, the address we want to jump to is in
R1; otherwise, it's in LR. */
return_regnum = eh_ofs ? 2 : LR_REGNUM;
/* If we are throwing an exception, then we really must be doing a return, if (IS_NAKED (func_type))
so we can't tail-call. */ /* Naked functions don't have epilogues. */
if (eh_ofs && !really_return) return "";
abort();
/* A volatile function should never return. Call abort. */ if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
if (TARGET_ABORT_NORETURN && volatile_func)
{ {
rtx op; rtx op;
/* A volatile function should never return. Call abort. */
op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort"); op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort");
assemble_external_libcall (op); assemble_external_libcall (op);
output_asm_insn ("bl\t%a0", &op); output_asm_insn ("bl\t%a0", &op);
return ""; return "";
} }
for (reg = 0; reg <= 10; reg++) if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
if (regs_ever_live[reg] && !call_used_regs[reg]) && ! really_return)
{ /* If we are throwing an exception, then we really must
live_regs_mask |= (1 << reg); be doing a return, so we can't tail-call. */
floats_offset += 4; abort ();
}
live_regs_mask = arm_compute_save_reg_mask ();
/* Handle the frame pointer as a special case. */
if (!TARGET_APCS_FRAME /* Compute how far away the floats will be. */
&& !frame_pointer_needed for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM] if (live_regs_mask & (1 << reg))
&& !call_used_regs[HARD_FRAME_POINTER_REGNUM])
{
live_regs_mask |= (1 << HARD_FRAME_POINTER_REGNUM);
floats_offset += 4;
}
/* If we aren't loading the PIC register, don't stack it even though it may
be live. */
if (flag_pic && !TARGET_SINGLE_PIC_BASE
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
{
live_regs_mask |= (1 << PIC_OFFSET_TABLE_REGNUM);
floats_offset += 4; floats_offset += 4;
}
if (frame_pointer_needed) if (frame_pointer_needed)
{ {
if (arm_fpu_arch == FP_SOFT2) if (arm_fpu_arch == FP_SOFT2)
...@@ -7096,37 +7308,28 @@ arm_output_epilogue (really_return) ...@@ -7096,37 +7308,28 @@ arm_output_epilogue (really_return)
reg + 1, start_reg - reg, reg + 1, start_reg - reg,
FP_REGNUM, floats_offset); FP_REGNUM, floats_offset);
} }
if (TARGET_INTERWORK) /* live_regs_mask should contain the IP, which at the time of stack
{ frame generation actually contains the old stack pointer. So a
live_regs_mask |= 0x6800; quick way to unwind the stack is just pop the IP register directly
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE); into the stack pointer. */
if (eh_ofs) if ((live_regs_mask & (1 << IP_REGNUM)) == 0)
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM, abort ();
REGNO (eh_ofs)); live_regs_mask &= ~ (1 << IP_REGNUM);
if (really_return) live_regs_mask |= (1 << SP_REGNUM);
asm_fprintf (f, "\tbx\t%r\n", return_regnum);
} /* There are two registers left in live_regs_mask - LR and PC. We
else if (eh_ofs || !really_return) only need to restore the LR register (the return address), but to
{ save time we can load it directly into the PC, unless we need a
live_regs_mask |= 0x6800; special function exit sequence, or we are not really returning. */
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE); if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
if (eh_ofs) /* Delete the LR from the register mask, so that the LR on
{ the stack is loaded into the PC in the register mask. */
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM, live_regs_mask &= ~ (1 << LR_REGNUM);
REGNO (eh_ofs));
/* Even in 26-bit mode we do a mov (rather than a movs)
because we don't have the PSR bits set in the
address. */
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
}
}
else else
{ live_regs_mask &= ~ (1 << PC_REGNUM);
live_regs_mask |= 0xA800;
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask);
TARGET_APCS_32 ? FALSE : TRUE);
}
} }
else else
{ {
...@@ -7178,102 +7381,86 @@ arm_output_epilogue (really_return) ...@@ -7178,102 +7381,86 @@ arm_output_epilogue (really_return)
start_reg, reg - start_reg, SP_REGNUM); start_reg, reg - start_reg, SP_REGNUM);
} }
if (current_function_pretend_args_size == 0 && regs_ever_live[LR_REGNUM]) /* If we can, restore the LR into the PC. */
if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
&& really_return
&& current_function_pretend_args_size == 0
&& regs_ever_live [LR_REGNUM])
{ {
if (TARGET_INTERWORK) live_regs_mask &= ~ (1 << LR_REGNUM);
{ live_regs_mask |= (1 << PC_REGNUM);
live_regs_mask |= 1 << LR_REGNUM; }
/* Handle LR on its own. */
if (live_regs_mask == (1 << LR_REGNUM))
{
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM,
SP_REGNUM);
else
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM,
SP_REGNUM);
}
else if (live_regs_mask != 0)
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask,
FALSE);
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
if (really_return) /* Load the registers off the stack. If we only have one register
asm_fprintf (f, "\tbx\t%r\n", return_regnum); to load use the LDR instruction - it is faster. */
} if (live_regs_mask == (1 << LR_REGNUM))
else if (eh_ofs) {
{ /* The excpetion handler ignores the LR, so we do
if (live_regs_mask == 0) not really need to load it off the stack. */
asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM); if (eh_ofs)
else asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
print_multi_reg (f, "\tldmfd\t%r!", SP_REGNUM,
live_regs_mask | (1 << LR_REGNUM), FALSE);
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
/* Jump to the target; even in 26-bit mode. */
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
}
else if (TARGET_APCS_32 && live_regs_mask == 0 && !really_return)
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
else if (TARGET_APCS_32 && live_regs_mask == 0 && really_return)
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", PC_REGNUM, SP_REGNUM);
else if (!really_return)
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
live_regs_mask | (1 << LR_REGNUM), FALSE);
else else
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
live_regs_mask | (1 << PC_REGNUM),
TARGET_APCS_32 ? FALSE : TRUE);
} }
else else if (live_regs_mask)
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask);
if (current_function_pretend_args_size)
{ {
if (live_regs_mask || regs_ever_live[LR_REGNUM]) /* Unwind the pre-pushed regs. */
{ operands[0] = operands[1] = stack_pointer_rtx;
/* Restore the integer regs, and the return address into lr. */ operands[2] = GEN_INT (current_function_pretend_args_size);
live_regs_mask |= 1 << LR_REGNUM; output_add_immediate (operands);
}
}
if (live_regs_mask == (1 << LR_REGNUM)) if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER)
{ /* Adjust the stack to remove the exception handler stuff. */
if (eh_ofs) asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, REGNO (eh_ofs));
SP_REGNUM);
else
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM,
SP_REGNUM);
}
else if (live_regs_mask != 0)
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask,
FALSE);
}
if (current_function_pretend_args_size) if (! really_return)
{ return "";
/* Unwind the pre-pushed regs. */
operands[0] = operands[1] = stack_pointer_rtx;
operands[2] = GEN_INT (current_function_pretend_args_size);
output_add_immediate (operands);
}
if (eh_ofs) /* Generate the return instruction. */
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM, switch ((int) ARM_FUNC_TYPE (func_type))
REGNO (eh_ofs)); {
case ARM_FT_EXCEPTION_HANDLER:
/* Even in 26-bit mode we do a mov (rather than a movs)
because we don't have the PSR bits set in the address. */
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM);
break;
if (really_return) case ARM_FT_ISR:
{ case ARM_FT_FIQ:
/* And finally, go home. */ asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM);
if (TARGET_INTERWORK) break;
asm_fprintf (f, "\tbx\t%r\n", return_regnum);
else if (TARGET_APCS_32 || eh_ofs) case ARM_FT_EXCEPTION:
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum); asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
else break;
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, return_regnum);
} case ARM_FT_INTERWORKED:
} asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
break;
default:
if (frame_pointer_needed)
/* If we used the frame pointer then the return adddress
will have been loaded off the stack directly into the
PC, so there is no need to issue a MOV instruction
here. */
;
else if (current_function_pretend_args_size == 0
&& regs_ever_live [LR_REGNUM])
/* Similarly we may have been able to load LR into the PC
even if we did not create a stack frame. */
;
else if (TARGET_APCS_32)
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM);
else
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
break;
} }
return ""; return "";
...@@ -7308,6 +7495,7 @@ output_func_epilogue (frame_size) ...@@ -7308,6 +7495,7 @@ output_func_epilogue (frame_size)
Unfortunately, since this insn does not reflect very well the actual Unfortunately, since this insn does not reflect very well the actual
semantics of the operation, we need to annotate the insn for the benefit semantics of the operation, we need to annotate the insn for the benefit
of DWARF2 frame unwind information. */ of DWARF2 frame unwind information. */
static rtx static rtx
emit_multi_reg_push (mask) emit_multi_reg_push (mask)
int mask; int mask;
...@@ -7476,53 +7664,33 @@ emit_sfm (base_reg, count) ...@@ -7476,53 +7664,33 @@ emit_sfm (base_reg, count)
return par; return par;
} }
/* Generate the prologue instructions for entry into an ARM function. */
void void
arm_expand_prologue () arm_expand_prologue ()
{ {
int reg; int reg;
rtx amount = GEN_INT (-(get_frame_size () rtx amount;
+ current_function_outgoing_args_size));
int live_regs_mask = 0;
int store_arg_regs = 0;
/* If this function doesn't return, then there is no need to push
the call-saved regs. */
int volatile_func = arm_volatile_func ();
rtx insn; rtx insn;
rtx ip_rtx; rtx ip_rtx;
unsigned long live_regs_mask;
unsigned long func_type;
int fp_offset = 0; int fp_offset = 0;
func_type = arm_current_func_type ();
/* Naked functions don't have prologues. */ /* Naked functions don't have prologues. */
if (arm_naked_function_p (current_function_decl)) if (IS_NAKED (func_type))
return; return;
if (current_function_anonymous_args && current_function_pretend_args_size) /* Compute which register we will have to save onto the stack. */
store_arg_regs = 1; live_regs_mask = arm_compute_save_reg_mask ();
if (!volatile_func)
{
for (reg = 0; reg <= 10; reg++)
if (regs_ever_live[reg] && !call_used_regs[reg])
live_regs_mask |= 1 << reg;
if (!TARGET_APCS_FRAME
&& !frame_pointer_needed
&& regs_ever_live[HARD_FRAME_POINTER_REGNUM]
&& !call_used_regs[HARD_FRAME_POINTER_REGNUM])
live_regs_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
live_regs_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
if (regs_ever_live[LR_REGNUM])
live_regs_mask |= 1 << LR_REGNUM;
}
ip_rtx = gen_rtx_REG (SImode, IP_REGNUM); ip_rtx = gen_rtx_REG (SImode, IP_REGNUM);
if (frame_pointer_needed) if (frame_pointer_needed)
{ {
if (current_function_needs_context) if (IS_NESTED (func_type))
{ {
/* The Static chain register is the same as the IP register /* The Static chain register is the same as the IP register
used as a scratch register during stack frame creation. used as a scratch register during stack frame creation.
...@@ -7530,17 +7698,24 @@ arm_expand_prologue () ...@@ -7530,17 +7698,24 @@ arm_expand_prologue ()
whilst the frame is being created. We try the following whilst the frame is being created. We try the following
places in order: places in order:
1. An unused argument register. 1. The last argument register.
2. A slot on the stack above the frame. (This only 2. A slot on the stack above the frame. (This only
works if the function is not a varargs function). works if the function is not a varargs function).
If neither of these places is available, we abort (for now). */ If neither of these places is available, we abort (for now).
Note - setting RTX_FRAME_RELATED_P on these insns breaks
the dwarf2 parsing code in various bits of gcc. This ought
to be fixed sometime, but until then the flag is suppressed.
[Use gcc/testsuite/gcc.c-torture/execute/921215-1.c with
"-O3 -g" to test this]. */
if (regs_ever_live[3] == 0) if (regs_ever_live[3] == 0)
{ {
insn = gen_rtx_REG (SImode, 3); insn = gen_rtx_REG (SImode, 3);
insn = gen_rtx_SET (SImode, insn, ip_rtx); insn = gen_rtx_SET (SImode, insn, ip_rtx);
insn = emit_insn (insn); insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1; /* RTX_FRAME_RELATED_P (insn) = 1; */
} }
else if (current_function_pretend_args_size == 0) else if (current_function_pretend_args_size == 0)
{ {
...@@ -7548,7 +7723,7 @@ arm_expand_prologue () ...@@ -7548,7 +7723,7 @@ arm_expand_prologue ()
insn = gen_rtx_MEM (SImode, insn); insn = gen_rtx_MEM (SImode, insn);
insn = gen_rtx_SET (VOIDmode, insn, ip_rtx); insn = gen_rtx_SET (VOIDmode, insn, ip_rtx);
insn = emit_insn (insn); insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1; /* RTX_FRAME_RELATED_P (insn) = 1; */
fp_offset = 4; fp_offset = 4;
} }
else else
...@@ -7560,8 +7735,6 @@ arm_expand_prologue () ...@@ -7560,8 +7735,6 @@ arm_expand_prologue ()
error ("Unable to find a temporary location for static chanin register"); error ("Unable to find a temporary location for static chanin register");
} }
live_regs_mask |= 0xD800;
if (fp_offset) if (fp_offset)
{ {
insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset)); insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset));
...@@ -7570,13 +7743,14 @@ arm_expand_prologue () ...@@ -7570,13 +7743,14 @@ arm_expand_prologue ()
else else
insn = gen_movsi (ip_rtx, stack_pointer_rtx); insn = gen_movsi (ip_rtx, stack_pointer_rtx);
insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (insn);
} }
if (current_function_pretend_args_size) if (current_function_pretend_args_size)
{ {
if (store_arg_regs) /* Push the argument registers, or reserve space for them. */
if (current_function_anonymous_args)
insn = emit_multi_reg_push insn = emit_multi_reg_push
((0xf0 >> (current_function_pretend_args_size / 4)) & 0xf); ((0xf0 >> (current_function_pretend_args_size / 4)) & 0xf);
else else
...@@ -7588,17 +7762,13 @@ arm_expand_prologue () ...@@ -7588,17 +7762,13 @@ arm_expand_prologue ()
if (live_regs_mask) if (live_regs_mask)
{ {
/* If we have to push any regs, then we must push lr as well, or
we won't get a proper return. */
live_regs_mask |= 1 << LR_REGNUM;
insn = emit_multi_reg_push (live_regs_mask); insn = emit_multi_reg_push (live_regs_mask);
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
} }
/* For now the integer regs are still pushed in output_arm_epilogue (). */
if (!volatile_func) if (! IS_VOLATILE (func_type))
{ {
/* Save any floating point call-saved registers used by this function. */
if (arm_fpu_arch == FP_SOFT2) if (arm_fpu_arch == FP_SOFT2)
{ {
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --) for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
...@@ -7647,11 +7817,12 @@ arm_expand_prologue () ...@@ -7647,11 +7817,12 @@ arm_expand_prologue ()
if (frame_pointer_needed) if (frame_pointer_needed)
{ {
/* Create the new frame pointer. */
insn = GEN_INT (-(4 + current_function_pretend_args_size + fp_offset)); insn = GEN_INT (-(4 + current_function_pretend_args_size + fp_offset));
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn)); insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
if (current_function_needs_context) if (IS_NESTED (func_type))
{ {
/* Recover the static chain register. */ /* Recover the static chain register. */
if (regs_ever_live [3] == 0) if (regs_ever_live [3] == 0)
...@@ -7659,7 +7830,7 @@ arm_expand_prologue () ...@@ -7659,7 +7830,7 @@ arm_expand_prologue ()
insn = gen_rtx_REG (SImode, 3); insn = gen_rtx_REG (SImode, 3);
insn = gen_rtx_SET (SImode, ip_rtx, insn); insn = gen_rtx_SET (SImode, ip_rtx, insn);
insn = emit_insn (insn); insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1; /* RTX_FRAME_RELATED_P (insn) = 1; */
} }
else /* if (current_function_pretend_args_size == 0) */ else /* if (current_function_pretend_args_size == 0) */
{ {
...@@ -7667,11 +7838,14 @@ arm_expand_prologue () ...@@ -7667,11 +7838,14 @@ arm_expand_prologue ()
insn = gen_rtx_MEM (SImode, insn); insn = gen_rtx_MEM (SImode, insn);
insn = gen_rtx_SET (SImode, ip_rtx, insn); insn = gen_rtx_SET (SImode, ip_rtx, insn);
insn = emit_insn (insn); insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1; /* RTX_FRAME_RELATED_P (insn) = 1; */
} }
} }
} }
amount = GEN_INT (-(get_frame_size ()
+ current_function_outgoing_args_size));
if (amount != const0_rtx) if (amount != const0_rtx)
{ {
insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
...@@ -7687,7 +7861,7 @@ arm_expand_prologue () ...@@ -7687,7 +7861,7 @@ arm_expand_prologue ()
gen_rtvec (2, stack_pointer_rtx, gen_rtvec (2, stack_pointer_rtx,
hard_frame_pointer_rtx), 4); hard_frame_pointer_rtx), 4);
emit_insn (gen_rtx_CLOBBER (VOIDmode, insn = emit_insn (gen_rtx_CLOBBER (VOIDmode,
gen_rtx_MEM (BLKmode, unspec))); gen_rtx_MEM (BLKmode, unspec)));
} }
} }
...@@ -9310,7 +9484,7 @@ static void ...@@ -9310,7 +9484,7 @@ static void
arm_mark_machine_status (p) arm_mark_machine_status (p)
struct function * p; struct function * p;
{ {
struct machine_function *machine = p->machine; machine_function *machine = p->machine;
ggc_mark_rtx (machine->ra_rtx); ggc_mark_rtx (machine->ra_rtx);
ggc_mark_rtx (machine->eh_epilogue_sp_ofs); ggc_mark_rtx (machine->eh_epilogue_sp_ofs);
...@@ -9321,7 +9495,11 @@ arm_init_machine_status (p) ...@@ -9321,7 +9495,11 @@ arm_init_machine_status (p)
struct function * p; struct function * p;
{ {
p->machine = p->machine =
(struct machine_function *) xcalloc (1, sizeof (struct machine_function)); (machine_function *) xcalloc (1, sizeof (machine_function));
#if ARM_FT_UNKNOWWN != 0
((machine_function *) p->machine)->func_type = ARM_FT_UNKNOWN;
#endif
} }
/* Return an RTX indicating where the return address to the /* Return an RTX indicating where the return address to the
...@@ -9379,11 +9557,20 @@ thumb_expand_prologue () ...@@ -9379,11 +9557,20 @@ thumb_expand_prologue ()
{ {
HOST_WIDE_INT amount = (get_frame_size () HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size); + current_function_outgoing_args_size);
unsigned long func_type;
func_type = arm_current_func_type ();
/* Naked functions don't have prologues. */ /* Naked functions don't have prologues. */
if (arm_naked_function_p (current_function_decl)) if (IS_NAKED (func_type))
return; return;
if (IS_INTERRUPT (func_type))
{
error ("Interrupt Service Routines cannot be coded in Thumb mode.");
return;
}
if (frame_pointer_needed) if (frame_pointer_needed)
emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
...@@ -9465,9 +9652,9 @@ thumb_expand_epilogue () ...@@ -9465,9 +9652,9 @@ thumb_expand_epilogue ()
{ {
HOST_WIDE_INT amount = (get_frame_size () HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size); + current_function_outgoing_args_size);
/* Naked functions don't have epilogues. */ /* Naked functions don't have prologues. */
if (arm_naked_function_p (current_function_decl)) if (IS_NAKED (arm_current_func_type ()))
return; return;
if (frame_pointer_needed) if (frame_pointer_needed)
...@@ -9503,10 +9690,9 @@ output_thumb_prologue (f) ...@@ -9503,10 +9690,9 @@ output_thumb_prologue (f)
{ {
int live_regs_mask = 0; int live_regs_mask = 0;
int high_regs_pushed = 0; int high_regs_pushed = 0;
int store_arg_regs = 0;
int regno; int regno;
if (arm_naked_function_p (current_function_decl)) if (IS_NAKED (arm_current_func_type ()))
return; return;
if (is_called_in_ARM_mode (current_function_decl)) if (is_called_in_ARM_mode (current_function_decl))
...@@ -9544,12 +9730,9 @@ output_thumb_prologue (f) ...@@ -9544,12 +9730,9 @@ output_thumb_prologue (f)
asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name); asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
} }
if (current_function_anonymous_args && current_function_pretend_args_size)
store_arg_regs = 1;
if (current_function_pretend_args_size) if (current_function_pretend_args_size)
{ {
if (store_arg_regs) if (current_function_anonymous_args)
{ {
int num_pushes; int num_pushes;
......
...@@ -903,7 +903,10 @@ extern const char * structure_size_string; ...@@ -903,7 +903,10 @@ extern const char * structure_size_string;
#define LAST_ARG_REGNUM ARG_REGISTER (NUM_ARG_REGS) #define LAST_ARG_REGNUM ARG_REGISTER (NUM_ARG_REGS)
/* The number of the last "lo" register (thumb). */ /* The number of the last "lo" register (thumb). */
#define LAST_LO_REGNUM 7 #define LAST_LO_REGNUM 7
/* The register that holds the return address in exception handlers. */
#define EXCEPTION_LR_REGNUM 2
/* The native (Norcroft) Pascal compiler for the ARM passes the static chain /* The native (Norcroft) Pascal compiler for the ARM passes the static chain
as an invisible last argument (possible since varargs don't exist in as an invisible last argument (possible since varargs don't exist in
...@@ -1394,9 +1397,44 @@ enum reg_class ...@@ -1394,9 +1397,44 @@ enum reg_class
#define CALL_LONG 0x00000001 /* Always call indirect. */ #define CALL_LONG 0x00000001 /* Always call indirect. */
#define CALL_SHORT 0x00000002 /* Never call indirect. */ #define CALL_SHORT 0x00000002 /* Never call indirect. */
/* A C structure for machine-specific, per-function data. This is added /* These bits describe the different types of function supported
to the cfun structure. */ by the ARM backend. They are exclusive. ie a function cannot be both a
struct machine_function normal function and an interworked function, for example. Knowing the
type of a function is important for determining its prologue and
epilogue sequences.
Note value 7 is currently unassigned. Also note that the interrupt
function types all have bit 2 set, so that they can be tested for easily.
Note that 0 is deliberately chosen for ARM_FT_UNKNOWN so that when the
machine_function structure is initialised (to zero) func_type will
default to unknown. This will force the first use of arm_current_func_type
to call arm_compute_func_type. */
#define ARM_FT_UNKNOWN 0 /* Type has not yet been determined. */
#define ARM_FT_NORMAL 1 /* Your normal, straightforward function. */
#define ARM_FT_INTERWORKED 2 /* A function that supports interworking. */
#define ARM_FT_EXCEPTION_HANDLER 3 /* A C++ exception handler. */
#define ARM_FT_ISR 4 /* An interrupt service routine. */
#define ARM_FT_FIQ 5 /* A fast interrupt service routine. */
#define ARM_FT_EXCEPTION 6 /* An ARM exception handler (subcase of ISR). */
#define ARM_FT_TYPE_MASK ((1 << 3) - 1)
/* In addition functions can have several type modifiers,
outlined by these bit masks: */
#define ARM_FT_INTERRUPT (1 << 2) /* Note overlap with FT_ISR and above. */
#define ARM_FT_NAKED (1 << 3) /* No prologue or epilogue. */
#define ARM_FT_VOLATILE (1 << 4) /* Does not return. */
#define ARM_FT_NESTED (1 << 5) /* Embedded inside another func. */
/* Some macros to test these flags. */
#define ARM_FUNC_TYPE(t) (t & ARM_FT_TYPE_MASK)
#define IS_INTERRUPT(t) (t & ARM_FT_INTERRUPT)
#define IS_VOLATILE(t) (t & ARM_FT_VOLATILE)
#define IS_NAKED(t) (t & ARM_FT_NAKED)
#define IS_NESTED(t) (t & ARM_FT_NESTED)
/* A C structure for machine-specific, per-function data.
This is added to the cfun structure. */
typedef struct machine_function
{ {
/* Records __builtin_return address. */ /* Records __builtin_return address. */
struct rtx_def *ra_rtx; struct rtx_def *ra_rtx;
...@@ -1406,7 +1444,10 @@ struct machine_function ...@@ -1406,7 +1444,10 @@ struct machine_function
int far_jump_used; int far_jump_used;
/* Records if ARG_POINTER was ever live. */ /* Records if ARG_POINTER was ever live. */
int arg_pointer_live; int arg_pointer_live;
}; /* Records the type of the current function. */
unsigned long func_type;
}
machine_function;
/* A C type for declaring a variable that is used as the first argument of /* A C type for declaring a variable that is used as the first argument of
`FUNCTION_ARG' and other related values. For some target machines, the `FUNCTION_ARG' and other related values. For some target machines, the
...@@ -1615,7 +1656,7 @@ typedef struct ...@@ -1615,7 +1656,7 @@ typedef struct
other its replacement, at the start of a routine. */ other its replacement, at the start of a routine. */
#define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ #define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \ { \
int volatile_func = arm_volatile_func (); \ int volatile_func = IS_VOLATILE (arm_current_func_type ()); \
if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\ if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
{ \ { \
if (! current_function_needs_context || ! frame_pointer_needed) \ if (! current_function_needs_context || ! frame_pointer_needed) \
......
...@@ -1760,6 +1760,27 @@ function is an interrupt handler. The compiler will generate function ...@@ -1760,6 +1760,27 @@ function is an interrupt handler. The compiler will generate function
entry and exit sequences suitable for use in an interrupt handler when this entry and exit sequences suitable for use in an interrupt handler when this
attribute is present. attribute is present.
@item interrupt
@cindex interrupt handler functions
Use this option on the ARM, AVR and M32R/D ports to indicate that the
specified function is an interrupt handler. The compiler will generate
function entry and exit sequences suitable for use in an interrupt
handler when this attribute is present.
Note, interrupt handlers for ther H8/300 and H8/300H processors can be
specified via the @code{interrupt_handler} attribute.
Note, on the AVR interrupts will be enabled inside the function.
Note, for the ARM you can specify the kind of interrupt to be handled by
adding an optional parameter to the interrupt attribute like this:
@smallexample
void f () __attribute__ ((interrupt ("IRQ")));
@end smallexample
Permissable values for this parameter are: IRQ, FIQ, SWI, ABORT and UNDEF.
@item eightbit_data @item eightbit_data
@cindex eight bit data on the H8/300 and H8/300H @cindex eight bit data on the H8/300 and H8/300H
Use this option on the H8/300 and H8/300H to indicate that the specified Use this option on the H8/300 and H8/300H to indicate that the specified
...@@ -1779,19 +1800,6 @@ The compiler will generate more efficient code for loads and stores ...@@ -1779,19 +1800,6 @@ The compiler will generate more efficient code for loads and stores
on data in the tiny data section. Note the tiny data area is limited to on data in the tiny data section. Note the tiny data area is limited to
slightly under 32kbytes of data. slightly under 32kbytes of data.
@item interrupt
@cindex interrupt handlers on the M32R/D
Use this option on the M32R/D to indicate that the specified
function is an interrupt handler. The compiler will generate function
entry and exit sequences suitable for use in an interrupt handler when this
attribute is present.
Interrupt handler functions on the AVR processors
Use this option on the AVR to indicate that the specified
function is an interrupt handler. The compiler will generate function
entry and exit sequences suitable for use in an interrupt handler when this
attribute is present. Interrupts will be enabled inside function.
@item signal @item signal
@cindex signal handler functions on the AVR processors @cindex signal handler functions on the AVR processors
Use this option on the AVR to indicate that the specified Use this option on the AVR to indicate that the specified
...@@ -1800,10 +1808,10 @@ entry and exit sequences suitable for use in an signal handler when this ...@@ -1800,10 +1808,10 @@ entry and exit sequences suitable for use in an signal handler when this
attribute is present. Interrupts will be disabled inside function. attribute is present. Interrupts will be disabled inside function.
@item naked @item naked
@cindex function without a prologue/epilogue code on the AVR processors @cindex function without a prologue/epilogue code
Use this option on the AVR to indicate that the specified Use this option on the ARM or AVR ports to indicate that the specified
function don't have a prologue/epilogue. The compiler don't generate function do not need prologue/epilogue sequences generated by the
function entry and exit sequences. compiler. It is up to the programmer to provide these sequences.
@item model (@var{model-name}) @item model (@var{model-name})
@cindex function addressability on the M32R/D @cindex function addressability on the M32R/D
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment