Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
R
riscv-gcc-1
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
riscv-gcc-1
Commits
6d3d9133
Commit
6d3d9133
authored
Dec 08, 2000
by
Nick Clifton
Committed by
Nick Clifton
Dec 08, 2000
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for interrupt function attribute
From-SVN: r38134
parent
2bff3fd5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
735 additions
and
447 deletions
+735
-447
gcc/ChangeLog
+55
-0
gcc/config/arm/arm-protos.h
+1
-0
gcc/config/arm/arm.c
+607
-424
gcc/config/arm/arm.h
+47
-6
gcc/extend.texi
+25
-17
No files found.
gcc/ChangeLog
View file @
6d3d9133
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.
...
...
gcc/config/arm/arm-protos.h
View file @
6d3d9133
...
@@ -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
));
...
...
gcc/config/arm/arm.c
View file @
6d3d9133
...
@@ -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
,
"
\t
mov
\t
%r, %r
\n
"
,
IP_REGNUM
,
PIC_OFFSET_TABLE_REGNUM
);
asm_fprintf
(
f
,
"
\t
mov
\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
,
"
\t
add
\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
,
"
\t
bx
\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
,
"
\t
add
\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
,
"
\t
mov
\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
,
"
\t
add
\t
%r, %r, #4
\n
"
,
SP_REGNUM
,
SP_REGNUM
);
else
asm_fprintf
(
f
,
"
\t
ldr
\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
,
"
\t
add
\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
,
"
\t
bx
\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
,
"
\t
add
\t
%r, %r, #4
\n
"
,
SP_REGNUM
,
SP_REGNUM
);
if
(
eh_ofs
)
else
asm_fprintf
(
f
,
"
\t
add
\t
%r, %r, #4
\n
"
,
SP_REGNUM
,
SP_REGNUM
);
print_multi_reg
(
f
,
"
\t
ldmfd
\t
%r!"
,
SP_REGNUM
,
live_regs_mask
|
(
1
<<
LR_REGNUM
),
FALSE
);
asm_fprintf
(
f
,
"
\t
add
\t
%r, %r, %r
\n
"
,
SP_REGNUM
,
SP_REGNUM
,
REGNO
(
eh_ofs
));
/* Jump to the target; even in 26-bit mode. */
asm_fprintf
(
f
,
"
\t
mov
\t
%r, %r
\n
"
,
PC_REGNUM
,
return_regnum
);
}
else
if
(
TARGET_APCS_32
&&
live_regs_mask
==
0
&&
!
really_return
)
asm_fprintf
(
f
,
"
\t
ldr
\t
%r, [%r], #4
\n
"
,
LR_REGNUM
,
SP_REGNUM
);
else
if
(
TARGET_APCS_32
&&
live_regs_mask
==
0
&&
really_return
)
asm_fprintf
(
f
,
"
\t
ldr
\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
,
"
\t
ldr
\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
,
"
\t
add
\t
%r, %r, %r
\n
"
,
SP_REGNUM
,
SP_REGNUM
,
asm_fprintf
(
f
,
"
\t
add
\t
%r, %r, #4
\n
"
,
SP_REGNUM
,
REGNO
(
eh_ofs
));
SP_REGNUM
);
else
asm_fprintf
(
f
,
"
\t
ldr
\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
,
"
\t
add
\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
,
"
\t
mov
\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
,
"
\t
subs
\t
%r, %r, #4
\n
"
,
PC_REGNUM
,
LR_REGNUM
);
if
(
TARGET_INTERWORK
)
break
;
asm_fprintf
(
f
,
"
\t
bx
\t
%r
\n
"
,
return_regnum
);
else
if
(
TARGET_APCS_32
||
eh_ofs
)
case
ARM_FT_EXCEPTION
:
asm_fprintf
(
f
,
"
\t
mov
\t
%r, %r
\n
"
,
PC_REGNUM
,
return_regnum
);
asm_fprintf
(
f
,
"
\t
movs
\t
%r, %r
\n
"
,
PC_REGNUM
,
LR_REGNUM
);
else
break
;
asm_fprintf
(
f
,
"
\t
movs
\t
%r, %r
\n
"
,
PC_REGNUM
,
return_regnum
);
}
case
ARM_FT_INTERWORKED
:
}
asm_fprintf
(
f
,
"
\t
bx
\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
,
"
\t
mov
\t
%r, %r
\n
"
,
PC_REGNUM
,
LR_REGNUM
);
else
asm_fprintf
(
f
,
"
\t
movs
\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
epi
logues. */
/* Naked functions don't have
pro
logues. */
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_re
gs
)
if
(
current_function_anonymous_ar
gs
)
{
{
int
num_pushes
;
int
num_pushes
;
...
...
gcc/config/arm/arm.h
View file @
6d3d9133
...
@@ -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) \
...
...
gcc/extend.texi
View file @
6d3d9133
...
@@ -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 A
VR
to indicate that the specified
Use this option on the A
RM or AVR ports
to indicate that the specified
function do
n'
t
have
a
prologue
/
epilogue
.
The
compiler
don
't generat
e
function do
not need prologue/epilogue sequences generated by th
e
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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment