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
ff9940b0
Commit
ff9940b0
authored
Oct 03, 1993
by
Richard Earnshaw
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Major rewrite -- See ChangeLog for details
From-SVN: r5564
parent
b980bec0
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2005 additions
and
320 deletions
+2005
-320
gcc/config/arm/arm.c
+1265
-104
gcc/config/arm/arm.h
+715
-216
gcc/config/arm/arm.md
+0
-0
gcc/config/arm/xm-arm.h
+25
-0
No files found.
gcc/config/arm/arm.c
View file @
ff9940b0
/* Output routines for GCC for ARM/RISCiX.
/* Output routines for GCC for ARM/RISCiX.
Copyright (C) 1991 Free Software Foundation, Inc.
Copyright (C) 1991
, 1993
Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC.
This file is part of GNU CC.
...
@@ -18,7 +19,7 @@ GNU General Public License for more details.
...
@@ -18,7 +19,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <stdio.h>
#include "assert.h"
#include "assert.h"
#include "config.h"
#include "config.h"
...
@@ -43,6 +44,16 @@ extern char *output_multi_immediate ();
...
@@ -43,6 +44,16 @@ extern char *output_multi_immediate ();
extern
char
*
arm_output_asm_insn
();
extern
char
*
arm_output_asm_insn
();
extern
void
arm_increase_location
();
extern
void
arm_increase_location
();
/* Define the information needed to generate branch insns. This is
stored from the compare operation. */
rtx
arm_compare_op0
,
arm_compare_op1
;
int
arm_compare_fp
;
/* What type of cpu are we compiling for? */
enum
processor_type
arm_cpu
;
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
must report the mode of the memory reference from PRINT_OPERAND to
must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */
PRINT_OPERAND_ADDRESS. */
...
@@ -54,6 +65,10 @@ int current_function_anonymous_args;
...
@@ -54,6 +65,10 @@ int current_function_anonymous_args;
/* Location counter of .text segment. */
/* Location counter of .text segment. */
int
arm_text_location
=
0
;
int
arm_text_location
=
0
;
/* Set to one if we think that lr is only saved because of subroutine calls,
but all of these can be `put after' return insns */
int
lr_save_eliminated
;
/* A hash table is used to store text segment labels and their associated
/* A hash table is used to store text segment labels and their associated
offset from the start of the text segment. */
offset from the start of the text segment. */
struct
label_offset
struct
label_offset
...
@@ -67,13 +82,38 @@ struct label_offset
...
@@ -67,13 +82,38 @@ struct label_offset
static
struct
label_offset
*
offset_table
[
LABEL_HASH_SIZE
];
static
struct
label_offset
*
offset_table
[
LABEL_HASH_SIZE
];
/* Set to 1 when a return insn is output, this means that the epilogue
is not needed. */
static
int
return_used_this_function
;
/* For an explanation of these variables, see final_prescan_insn below. */
/* For an explanation of these variables, see final_prescan_insn below. */
int
arm_ccfsm_state
;
int
arm_ccfsm_state
;
int
arm_current_cc
;
int
arm_current_cc
;
rtx
arm_target_insn
;
rtx
arm_target_insn
;
int
arm_target_label
;
int
arm_target_label
;
char
*
arm_condition_codes
[];
/* Return 1 if it is possible to return using a single instruction */
int
use_return_insn
()
{
int
regno
;
if
(
!
reload_completed
||
current_function_pretend_args_size
||
current_function_anonymous_args
||
(
get_frame_size
()
&&
!
(
TARGET_APCS
||
frame_pointer_needed
)))
return
0
;
/* Can't be done if any of the FPU regs are pushed, since this also
requires an insn */
for
(
regno
=
20
;
regno
<
24
;
regno
++
)
if
(
regs_ever_live
[
regno
])
return
0
;
return
1
;
}
/* Return the number of mov instructions needed to get the constant VALUE into
/* Return the number of mov instructions needed to get the constant VALUE into
a register. */
a register. */
...
@@ -96,38 +136,134 @@ arm_const_nmoves (value)
...
@@ -96,38 +136,134 @@ arm_const_nmoves (value)
int
int
const_ok_for_arm
(
i
)
const_ok_for_arm
(
i
)
int
i
;
HOST_WIDE_INT
i
;
{
{
unsigned
int
mask
=
~
0xFF
;
unsigned
HOST_WIDE_INT
mask
=
~
0xFF
;
do
do
{
{
if
((
i
&
mask
)
==
0
)
if
((
i
&
mask
&
0xffffffffu
)
==
0
)
return
(
TRUE
);
return
(
TRUE
);
mask
=
(
mask
<<
2
)
|
(
mask
>>
(
32
-
2
))
;
mask
=
(
mask
<<
2
)
|
(
(
mask
&
0xffffffffu
)
>>
(
32
-
2
))
|
~
0xffffffffu
;
}
while
(
mask
!=
~
0xFF
);
}
while
(
mask
!=
~
0xFF
);
return
(
FALSE
);
return
(
FALSE
);
}
/* const_ok_for_arm */
}
/* const_ok_for_arm */
/* This code has been fixed for cross compilation. */
static
int
fpa_consts_inited
=
0
;
char
*
strings_fpa
[
8
]
=
{
"0.0"
,
"1.0"
,
"2.0"
,
"3.0"
,
"4.0"
,
"5.0"
,
"0.5"
,
"10.0"
};
static
REAL_VALUE_TYPE
values_fpa
[
8
];
static
void
init_fpa_table
()
{
int
i
;
REAL_VALUE_TYPE
r
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
r
=
REAL_VALUE_ATOF
(
strings_fpa
[
i
],
DFmode
);
values_fpa
[
i
]
=
r
;
}
fpa_consts_inited
=
1
;
}
/* Return TRUE if rtx X is a valid immediate FPU constant. */
/* Return TRUE if rtx X is a valid immediate FPU constant. */
int
int
const_double_rtx_ok_for_fpu
(
x
)
const_double_rtx_ok_for_fpu
(
x
)
rtx
x
;
rtx
x
;
{
{
double
d
;
REAL_VALUE_TYPE
r
;
union
real_extract
u
;
int
i
;
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
if
(
!
fpa_consts_inited
)
d
=
u
.
d
;
init_fpa_table
();
return
(
d
==
0
.
0
||
d
==
1
.
0
||
d
==
2
.
0
||
d
==
3
.
0
REAL_VALUE_FROM_CONST_DOUBLE
(
r
,
x
);
||
d
==
4
.
0
||
d
==
5
.
0
||
d
==
0
.
5
||
d
==
10
.
0
);
if
(
REAL_VALUE_MINUS_ZERO
(
r
))
return
0
;
for
(
i
=
0
;
i
<
8
;
i
++
)
if
(
REAL_VALUES_EQUAL
(
r
,
values_fpa
[
i
]))
return
1
;
return
0
;
}
/* const_double_rtx_ok_for_fpu */
}
/* const_double_rtx_ok_for_fpu */
/* Return TRUE if rtx X is a valid immediate FPU constant. */
int
neg_const_double_rtx_ok_for_fpu
(
x
)
rtx
x
;
{
REAL_VALUE_TYPE
r
;
int
i
;
if
(
!
fpa_consts_inited
)
init_fpa_table
();
REAL_VALUE_FROM_CONST_DOUBLE
(
r
,
x
);
r
=
REAL_VALUE_NEGATE
(
r
);
if
(
REAL_VALUE_MINUS_ZERO
(
r
))
return
0
;
for
(
i
=
0
;
i
<
8
;
i
++
)
if
(
REAL_VALUES_EQUAL
(
r
,
values_fpa
[
i
]))
return
1
;
return
0
;
}
/* neg_const_double_rtx_ok_for_fpu */
/* Predicates for `match_operand' and `match_operator'. */
/* Predicates for `match_operand' and `match_operator'. */
/* s_register_operand is the same as register_operand, but it doesn't accept
(SUBREG (MEM)...). */
int
s_register_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
GET_MODE
(
op
)
!=
mode
&&
mode
!=
VOIDmode
)
return
0
;
if
(
GET_CODE
(
op
)
==
SUBREG
)
{
op
=
SUBREG_REG
(
op
);
}
/* We don't consider registers whose class is NO_REGS
to be a register operand. */
return
(
GET_CODE
(
op
)
==
REG
&&
(
REGNO
(
op
)
>=
FIRST_PSEUDO_REGISTER
||
REGNO_REG_CLASS
(
REGNO
(
op
))
!=
NO_REGS
));
}
/* Return 1 if OP is an item in memory, given that we are in reload. */
int
reload_memory_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
regno
=
true_regnum
(
op
);
return
(
!
CONSTANT_P
(
op
)
&&
(
regno
==
-
1
||
(
GET_CODE
(
op
)
==
REG
&&
REGNO
(
op
)
>=
FIRST_PSEUDO_REGISTER
)));
}
/* Return TRUE for valid operands for the rhs of an ARM instruction. */
/* Return TRUE for valid operands for the rhs of an ARM instruction. */
int
int
...
@@ -135,10 +271,48 @@ arm_rhs_operand (op, mode)
...
@@ -135,10 +271,48 @@ arm_rhs_operand (op, mode)
rtx
op
;
rtx
op
;
enum
machine_mode
mode
;
enum
machine_mode
mode
;
{
{
return
(
register_operand
(
op
,
mode
)
return
(
s_
register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
const_ok_for_arm
(
INTVAL
(
op
))));
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
const_ok_for_arm
(
INTVAL
(
op
))));
}
/* arm_rhs_operand */
}
/* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an ARM instruction, or a load.
*/
int
arm_rhsm_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
s_register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
const_ok_for_arm
(
INTVAL
(
op
)))
||
memory_operand
(
op
,
mode
));
}
/* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an ARM instruction, or if a
constant that is valid when negated. */
int
arm_add_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
s_register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
(
const_ok_for_arm
(
INTVAL
(
op
))
||
const_ok_for_arm
(
-
INTVAL
(
op
)))));
}
/* arm_rhs_operand */
int
arm_not_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
s_register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
(
const_ok_for_arm
(
INTVAL
(
op
))
||
const_ok_for_arm
(
~
INTVAL
(
op
)))));
}
/* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an FPU instruction. */
/* Return TRUE for valid operands for the rhs of an FPU instruction. */
int
int
...
@@ -146,13 +320,26 @@ fpu_rhs_operand (op, mode)
...
@@ -146,13 +320,26 @@ fpu_rhs_operand (op, mode)
rtx
op
;
rtx
op
;
enum
machine_mode
mode
;
enum
machine_mode
mode
;
{
{
if
(
register_operand
(
op
,
mode
))
if
(
s_
register_operand
(
op
,
mode
))
return
(
TRUE
);
return
(
TRUE
);
else
if
(
GET_CODE
(
op
)
==
CONST_DOUBLE
)
else
if
(
GET_CODE
(
op
)
==
CONST_DOUBLE
)
return
(
const_double_rtx_ok_for_fpu
(
op
));
return
(
const_double_rtx_ok_for_fpu
(
op
));
else
return
(
FALSE
);
else
return
(
FALSE
);
}
/* fpu_rhs_operand */
}
/* fpu_rhs_operand */
int
fpu_add_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
if
(
s_register_operand
(
op
,
mode
))
return
(
TRUE
);
else
if
(
GET_CODE
(
op
)
==
CONST_DOUBLE
)
return
const_double_rtx_ok_for_fpu
(
op
)
||
neg_const_double_rtx_ok_for_fpu
(
op
);
return
(
FALSE
);
}
/* Return nonzero if OP is a constant power of two. */
/* Return nonzero if OP is a constant power of two. */
int
int
...
@@ -169,15 +356,16 @@ power_of_two_operand (op, mode)
...
@@ -169,15 +356,16 @@ power_of_two_operand (op, mode)
}
/* power_of_two_operand */
}
/* power_of_two_operand */
/* Return TRUE for a valid operand of a DImode operation.
/* Return TRUE for a valid operand of a DImode operation.
Either: REG, CONST_DOUBLE or MEM(offsettable).
Either: REG, CONST_DOUBLE or MEM(DImode_address).
Note that this disallows MEM(REG+REG). */
Note that this disallows MEM(REG+REG), but allows
MEM(PRE/POST_INC/DEC(REG)). */
int
int
di_operand
(
op
,
mode
)
di_operand
(
op
,
mode
)
rtx
op
;
rtx
op
;
enum
machine_mode
mode
;
enum
machine_mode
mode
;
{
{
if
(
register_operand
(
op
,
mode
))
if
(
s_
register_operand
(
op
,
mode
))
return
(
TRUE
);
return
(
TRUE
);
switch
(
GET_CODE
(
op
))
switch
(
GET_CODE
(
op
))
...
@@ -186,8 +374,7 @@ di_operand (op, mode)
...
@@ -186,8 +374,7 @@ di_operand (op, mode)
case
CONST_INT
:
case
CONST_INT
:
return
(
TRUE
);
return
(
TRUE
);
case
MEM
:
case
MEM
:
return
(
memory_address_p
(
DImode
,
XEXP
(
op
,
0
))
return
(
memory_address_p
(
DImode
,
XEXP
(
op
,
0
)));
&&
offsettable_address_p
(
FALSE
,
DImode
,
XEXP
(
op
,
0
)));
default
:
default
:
return
(
FALSE
);
return
(
FALSE
);
}
}
...
@@ -200,10 +387,25 @@ index_operand (op, mode)
...
@@ -200,10 +387,25 @@ index_operand (op, mode)
rtx
op
;
rtx
op
;
enum
machine_mode
mode
;
enum
machine_mode
mode
;
{
{
return
(
register_operand
(
op
,
mode
)
return
(
s_register_operand
(
op
,
mode
)
||
(
immediate_operand
(
op
,
mode
)
&&
abs
(
INTVAL
(
op
))
<
4096
));
||
(
immediate_operand
(
op
,
mode
)
&&
INTVAL
(
op
)
<
4096
&&
INTVAL
(
op
)
>
-
4096
));
}
/* index_operand */
}
/* index_operand */
/* Return TRUE for valid shifts by a constant. This also accepts any
power of two on the (somewhat overly relaxed) assumption that the
shift operator in this case was a mult. */
int
const_shift_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
power_of_two_operand
(
op
,
mode
)
||
(
immediate_operand
(
op
,
mode
)
&&
(
INTVAL
(
op
)
<
32
&&
INTVAL
(
op
)
>
0
)));
}
/* const_shift_operand */
/* Return TRUE for arithmetic operators which can be combined with a multiply
/* Return TRUE for arithmetic operators which can be combined with a multiply
(shift). */
(shift). */
...
@@ -236,13 +438,384 @@ shift_operator (x, mode)
...
@@ -236,13 +438,384 @@ shift_operator (x, mode)
{
{
enum
rtx_code
code
=
GET_CODE
(
x
);
enum
rtx_code
code
=
GET_CODE
(
x
);
if
(
code
==
MULT
)
return
power_of_two_operand
(
XEXP
(
x
,
1
));
return
(
code
==
ASHIFT
||
code
==
LSHIFT
return
(
code
==
ASHIFT
||
code
==
LSHIFT
||
code
==
ASHIFTRT
||
code
==
LSHIFTRT
);
||
code
==
ASHIFTRT
||
code
==
LSHIFTRT
);
}
}
}
/* shift_operator */
}
/* shift_operator */
int
equality_operator
(
x
,
mode
)
rtx
x
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
x
)
==
EQ
||
GET_CODE
(
x
)
==
NE
);
}
/* Return TRUE for SMIN SMAX UMIN UMAX operators. */
int
minmax_operator
(
x
,
mode
)
rtx
x
;
enum
machine_mode
mode
;
{
enum
rtx_code
code
=
GET_CODE
(
x
);
if
(
GET_MODE
(
x
)
!=
mode
)
return
FALSE
;
return
code
==
SMIN
||
code
==
SMAX
||
code
==
UMIN
||
code
==
UMAX
;
}
/* minmax_operator */
/* return TRUE if x is EQ or NE */
/* Return TRUE if this is the condition code register, if we aren't given
a mode, accept any class CCmode register */
int
cc_register
(
x
,
mode
)
rtx
x
;
enum
machine_mode
mode
;
{
if
(
mode
==
VOIDmode
)
{
mode
=
GET_MODE
(
x
);
if
(
GET_MODE_CLASS
(
mode
)
!=
MODE_CC
)
return
FALSE
;
}
if
(
mode
==
GET_MODE
(
x
)
&&
GET_CODE
(
x
)
==
REG
&&
REGNO
(
x
)
==
24
)
return
TRUE
;
return
FALSE
;
}
enum
rtx_code
minmax_code
(
x
)
rtx
x
;
{
enum
rtx_code
code
=
GET_CODE
(
x
);
if
(
code
==
SMAX
)
return
GE
;
if
(
code
==
SMIN
)
return
LE
;
if
(
code
==
UMIN
)
return
LEU
;
if
(
code
==
UMAX
)
return
GEU
;
abort
();
}
/* Return 1 if memory locations are adjacent */
adjacent_mem_locations
(
a
,
b
)
rtx
a
,
b
;
{
int
val0
=
0
,
val1
=
0
;
int
reg0
,
reg1
;
if
((
GET_CODE
(
XEXP
(
a
,
0
))
==
REG
||
(
GET_CODE
(
XEXP
(
a
,
0
))
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
a
,
0
),
1
))
==
CONST_INT
))
&&
(
GET_CODE
(
XEXP
(
b
,
0
))
==
REG
||
(
GET_CODE
(
XEXP
(
b
,
0
))
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
b
,
0
),
1
))
==
CONST_INT
)))
{
if
(
GET_CODE
(
XEXP
(
a
,
0
))
==
PLUS
)
{
reg0
=
REGNO
(
XEXP
(
XEXP
(
a
,
0
),
0
));
val0
=
INTVAL
(
XEXP
(
XEXP
(
a
,
0
),
1
));
}
else
reg0
=
REGNO
(
XEXP
(
a
,
0
));
if
(
GET_CODE
(
XEXP
(
b
,
0
))
==
PLUS
)
{
reg1
=
REGNO
(
XEXP
(
XEXP
(
b
,
0
),
0
));
val1
=
INTVAL
(
XEXP
(
XEXP
(
b
,
0
),
1
));
}
else
reg1
=
REGNO
(
XEXP
(
b
,
0
));
return
(
reg0
==
reg1
)
&&
((
val1
-
val0
)
==
4
||
(
val0
-
val1
)
==
4
);
}
return
0
;
}
/* Return 1 if OP is a load multiple operation. It is known to be
parallel and the first section will be tested. */
load_multiple_operation
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
count
=
XVECLEN
(
op
,
0
);
int
dest_regno
;
rtx
src_addr
;
int
i
=
1
,
base
=
0
;
rtx
elt
;
if
(
count
<=
1
||
GET_CODE
(
XVECEXP
(
op
,
0
,
0
))
!=
SET
)
return
0
;
/* Check to see if this might be a write-back */
if
(
GET_CODE
(
SET_SRC
(
elt
=
XVECEXP
(
op
,
0
,
0
)))
==
PLUS
)
{
i
++
;
base
=
1
;
/* Now check it more carefully */
if
(
GET_CODE
(
SET_DEST
(
elt
))
!=
REG
||
GET_CODE
(
XEXP
(
SET_SRC
(
elt
),
0
))
!=
REG
||
REGNO
(
XEXP
(
SET_SRC
(
elt
),
0
))
!=
REGNO
(
SET_DEST
(
elt
))
||
GET_CODE
(
XEXP
(
SET_SRC
(
elt
),
1
))
!=
CONST_INT
||
INTVAL
(
XEXP
(
SET_SRC
(
elt
),
1
))
!=
(
count
-
2
)
*
4
||
GET_CODE
(
XVECEXP
(
op
,
0
,
count
-
1
))
!=
CLOBBER
||
GET_CODE
(
XEXP
(
XVECEXP
(
op
,
0
,
count
-
1
),
0
))
!=
REG
||
REGNO
(
XEXP
(
XVECEXP
(
op
,
0
,
count
-
1
),
0
))
!=
REGNO
(
SET_DEST
(
elt
)))
return
0
;
count
--
;
}
/* Perform a quick check so we don't blow up below. */
if
(
count
<=
i
||
GET_CODE
(
XVECEXP
(
op
,
0
,
i
-
1
))
!=
SET
||
GET_CODE
(
SET_DEST
(
XVECEXP
(
op
,
0
,
i
-
1
)))
!=
REG
||
GET_CODE
(
SET_SRC
(
XVECEXP
(
op
,
0
,
i
-
1
)))
!=
MEM
)
return
0
;
dest_regno
=
REGNO
(
SET_DEST
(
XVECEXP
(
op
,
0
,
i
-
1
)));
src_addr
=
XEXP
(
SET_SRC
(
XVECEXP
(
op
,
0
,
i
-
1
)),
0
);
for
(;
i
<
count
;
i
++
)
{
rtx
elt
=
XVECEXP
(
op
,
0
,
i
);
if
(
GET_CODE
(
elt
)
!=
SET
||
GET_CODE
(
SET_DEST
(
elt
))
!=
REG
||
GET_MODE
(
SET_DEST
(
elt
))
!=
SImode
||
REGNO
(
SET_DEST
(
elt
))
!=
dest_regno
+
i
-
base
||
GET_CODE
(
SET_SRC
(
elt
))
!=
MEM
||
GET_MODE
(
SET_SRC
(
elt
))
!=
SImode
||
GET_CODE
(
XEXP
(
SET_SRC
(
elt
),
0
))
!=
PLUS
||
!
rtx_equal_p
(
XEXP
(
XEXP
(
SET_SRC
(
elt
),
0
),
0
),
src_addr
)
||
GET_CODE
(
XEXP
(
XEXP
(
SET_SRC
(
elt
),
0
),
1
))
!=
CONST_INT
||
INTVAL
(
XEXP
(
XEXP
(
SET_SRC
(
elt
),
0
),
1
))
!=
(
i
-
base
)
*
4
)
return
0
;
}
return
1
;
}
/* Return 1 if OP is a store multiple operation. It is known to be
parallel and the first section will be tested. */
store_multiple_operation
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
count
=
XVECLEN
(
op
,
0
);
int
src_regno
;
rtx
dest_addr
;
int
i
=
1
,
base
=
0
;
rtx
elt
;
if
(
count
<=
1
||
GET_CODE
(
XVECEXP
(
op
,
0
,
0
))
!=
SET
)
return
0
;
/* Check to see if this might be a write-back */
if
(
GET_CODE
(
SET_SRC
(
elt
=
XVECEXP
(
op
,
0
,
0
)))
==
PLUS
)
{
i
++
;
base
=
1
;
/* Now check it more carefully */
if
(
GET_CODE
(
SET_DEST
(
elt
))
!=
REG
||
GET_CODE
(
XEXP
(
SET_SRC
(
elt
),
0
))
!=
REG
||
REGNO
(
XEXP
(
SET_SRC
(
elt
),
0
))
!=
REGNO
(
SET_DEST
(
elt
))
||
GET_CODE
(
XEXP
(
SET_SRC
(
elt
),
1
))
!=
CONST_INT
||
INTVAL
(
XEXP
(
SET_SRC
(
elt
),
1
))
!=
(
count
-
2
)
*
4
||
GET_CODE
(
XVECEXP
(
op
,
0
,
count
-
1
))
!=
CLOBBER
||
GET_CODE
(
XEXP
(
XVECEXP
(
op
,
0
,
count
-
1
),
0
))
!=
REG
||
REGNO
(
XEXP
(
XVECEXP
(
op
,
0
,
count
-
1
),
0
))
!=
REGNO
(
SET_DEST
(
elt
)))
return
0
;
count
--
;
}
/* Perform a quick check so we don't blow up below. */
if
(
count
<=
i
||
GET_CODE
(
XVECEXP
(
op
,
0
,
i
-
1
))
!=
SET
||
GET_CODE
(
SET_DEST
(
XVECEXP
(
op
,
0
,
i
-
1
)))
!=
MEM
||
GET_CODE
(
SET_SRC
(
XVECEXP
(
op
,
0
,
i
-
1
)))
!=
REG
)
return
0
;
src_regno
=
REGNO
(
SET_SRC
(
XVECEXP
(
op
,
0
,
i
-
1
)));
dest_addr
=
XEXP
(
SET_DEST
(
XVECEXP
(
op
,
0
,
i
-
1
)),
0
);
for
(;
i
<
count
;
i
++
)
{
elt
=
XVECEXP
(
op
,
0
,
i
);
if
(
GET_CODE
(
elt
)
!=
SET
||
GET_CODE
(
SET_SRC
(
elt
))
!=
REG
||
GET_MODE
(
SET_SRC
(
elt
))
!=
SImode
||
REGNO
(
SET_SRC
(
elt
))
!=
src_regno
+
i
-
base
||
GET_CODE
(
SET_DEST
(
elt
))
!=
MEM
||
GET_MODE
(
SET_DEST
(
elt
))
!=
SImode
||
GET_CODE
(
XEXP
(
SET_DEST
(
elt
),
0
))
!=
PLUS
||
!
rtx_equal_p
(
XEXP
(
XEXP
(
SET_DEST
(
elt
),
0
),
0
),
dest_addr
)
||
GET_CODE
(
XEXP
(
XEXP
(
SET_DEST
(
elt
),
0
),
1
))
!=
CONST_INT
||
INTVAL
(
XEXP
(
XEXP
(
SET_DEST
(
elt
),
0
),
1
))
!=
(
i
-
base
)
*
4
)
return
0
;
}
return
1
;
}
/* Routines for use in generating RTL */
rtx
arm_gen_load_multiple
(
base_regno
,
count
,
from
,
up
,
write_back
)
int
base_regno
;
int
count
;
rtx
from
;
int
up
;
int
write_back
;
{
int
i
=
0
,
j
;
rtx
result
;
int
sign
=
up
?
1
:
-
1
;
result
=
gen_rtx
(
PARALLEL
,
VOIDmode
,
rtvec_alloc
(
count
+
(
write_back
?
2
:
0
)));
if
(
write_back
)
{
XVECEXP
(
result
,
0
,
0
)
=
gen_rtx
(
SET
,
GET_MODE
(
from
),
from
,
plus_constant
(
from
,
count
*
4
*
sign
));
i
=
1
;
count
++
;
}
for
(
j
=
0
;
i
<
count
;
i
++
,
j
++
)
{
XVECEXP
(
result
,
0
,
i
)
=
gen_rtx
(
SET
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
base_regno
+
j
),
gen_rtx
(
MEM
,
SImode
,
plus_constant
(
from
,
j
*
4
*
sign
)));
}
if
(
write_back
)
XVECEXP
(
result
,
0
,
i
)
=
gen_rtx
(
CLOBBER
,
SImode
,
from
);
return
result
;
}
rtx
arm_gen_store_multiple
(
base_regno
,
count
,
to
,
up
,
write_back
)
int
base_regno
;
int
count
;
rtx
to
;
int
up
;
int
write_back
;
{
int
i
=
0
,
j
;
rtx
result
;
int
sign
=
up
?
1
:
-
1
;
result
=
gen_rtx
(
PARALLEL
,
VOIDmode
,
rtvec_alloc
(
count
+
(
write_back
?
2
:
0
)));
if
(
write_back
)
{
XVECEXP
(
result
,
0
,
0
)
=
gen_rtx
(
SET
,
GET_MODE
(
to
),
to
,
plus_constant
(
to
,
count
*
4
*
sign
));
i
=
1
;
count
++
;
}
for
(
j
=
0
;
i
<
count
;
i
++
,
j
++
)
{
XVECEXP
(
result
,
0
,
i
)
=
gen_rtx
(
SET
,
VOIDmode
,
gen_rtx
(
MEM
,
SImode
,
plus_constant
(
to
,
j
*
4
*
sign
)),
gen_rtx
(
REG
,
SImode
,
base_regno
+
j
));
}
if
(
write_back
)
XVECEXP
(
result
,
0
,
i
)
=
gen_rtx
(
CLOBBER
,
SImode
,
to
);
return
result
;
}
/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for register 0 in the proper mode. FP means this is a
floating point compare: I don't think that it is needed on the arm. */
rtx
gen_compare_reg
(
code
,
x
,
y
,
fp
)
enum
rtx_code
code
;
rtx
x
,
y
;
{
enum
machine_mode
mode
=
SELECT_CC_MODE
(
code
,
x
,
y
);
rtx
cc_reg
=
gen_rtx
(
REG
,
mode
,
24
);
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
cc_reg
,
gen_rtx
(
COMPARE
,
mode
,
x
,
y
)));
return
cc_reg
;
}
/* Check to see if a branch is forwards or backwards. Return TRUE if it
is backwards. */
int
arm_backwards_branch
(
from
,
to
)
int
from
,
to
;
{
return
(
insn_addresses
[
to
]
<
insn_addresses
[
from
]);
}
/* Check to see if a branch is within the distance that can be done using
an arithmetic expression. */
int
short_branch
(
from
,
to
)
int
from
,
to
;
{
int
delta
=
insn_addresses
[
from
]
+
2
-
insn_addresses
[
to
];
return
abs
(
delta
)
<
245
;
/* A small margin for safety */
}
/* Check to see that the insn isn't the target of the conditionalizing
code */
int
arm_insn_not_targeted
(
insn
)
rtx
insn
;
{
return
insn
!=
arm_target_insn
;
}
/* Routines to output assembly language. */
/* Routines to output assembly language. */
/* fp_immediate_constant
if the rtx is the correct value then return the string of the number.
In this way we can ensure that valid double constants are generated even
when cross compiling. */
char
*
fp_immediate_constant
(
x
)
rtx
(
x
);
{
REAL_VALUE_TYPE
r
;
int
i
;
if
(
!
fpa_consts_inited
)
init_fpa_table
();
REAL_VALUE_FROM_CONST_DOUBLE
(
r
,
x
);
for
(
i
=
0
;
i
<
8
;
i
++
)
if
(
REAL_VALUES_EQUAL
(
r
,
values_fpa
[
i
]))
return
strings_fpa
[
i
];
abort
();
}
/* 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
INSTR is the possibly suffixed base register. HAT unequals zero if a hat
...
@@ -289,6 +862,134 @@ output_call (operands)
...
@@ -289,6 +862,134 @@ output_call (operands)
return
(
""
);
return
(
""
);
}
/* output_call */
}
/* output_call */
static
int
eliminate_lr2ip
(
x
)
rtx
*
x
;
{
int
something_changed
=
0
;
rtx
x0
=
*
x
;
int
code
=
GET_CODE
(
x0
);
register
int
i
,
j
;
register
char
*
fmt
;
switch
(
code
)
{
case
REG
:
if
(
REGNO
(
x0
)
==
14
)
{
*
x
=
gen_rtx
(
REG
,
SImode
,
12
);
return
1
;
}
return
0
;
default
:
/* Scan through the sub-elements and change any references there */
fmt
=
GET_RTX_FORMAT
(
code
);
for
(
i
=
GET_RTX_LENGTH
(
code
)
-
1
;
i
>=
0
;
i
--
)
if
(
fmt
[
i
]
==
'e'
)
something_changed
|=
eliminate_lr2ip
(
&
XEXP
(
x0
,
i
));
else
if
(
fmt
[
i
]
==
'E'
)
for
(
j
=
0
;
j
<
XVECLEN
(
x0
,
i
);
j
++
)
something_changed
|=
eliminate_lr2ip
(
&
XVECEXP
(
x0
,
i
,
j
));
return
something_changed
;
}
}
/* Output a 'call' insn that is a reference in memory. */
char
*
output_call_mem
(
operands
)
rtx
operands
[];
{
operands
[
0
]
=
copy_rtx
(
operands
[
0
]);
/* Be ultra careful */
/* Handle calls using lr by using ip (which may be clobbered in subr anyway).
*/
if
(
eliminate_lr2ip
(
&
operands
[
0
]))
arm_output_asm_insn
(
"mov
\t
ip, lr"
,
operands
);
arm_output_asm_insn
(
"mov
\t
lr, pc"
,
operands
);
arm_output_asm_insn
(
"ldr
\t
pc, %0"
,
operands
);
return
(
""
);
}
/* output_call */
/* Output a move from arm registers to an fpu registers.
OPERANDS[0] is an fpu register.
OPERANDS[1] is the first registers of an arm register pair. */
char
*
output_mov_long_double_fpu_from_arm
(
operands
)
rtx
operands
[];
{
int
arm_reg0
=
REGNO
(
operands
[
1
]);
rtx
ops
[
3
];
if
(
arm_reg0
==
12
)
abort
();
ops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
arm_reg0
);
ops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
1
+
arm_reg0
);
ops
[
2
]
=
gen_rtx
(
REG
,
SImode
,
2
+
arm_reg0
);
arm_output_asm_insn
(
"stmfd
\t
sp!, {%0, %1, %2}"
,
ops
);
arm_output_asm_insn
(
"ldfe
\t
%0, [sp], #12"
,
operands
);
return
(
""
);
}
/* output_mov_long_double_fpu_from_arm */
/* Output a move from an fpu register to arm registers.
OPERANDS[0] is the first registers of an arm register pair.
OPERANDS[1] is an fpu register. */
char
*
output_mov_long_double_arm_from_fpu
(
operands
)
rtx
operands
[];
{
int
arm_reg0
=
REGNO
(
operands
[
0
]);
rtx
ops
[
3
];
if
(
arm_reg0
==
12
)
abort
();
ops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
arm_reg0
);
ops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
1
+
arm_reg0
);
ops
[
2
]
=
gen_rtx
(
REG
,
SImode
,
2
+
arm_reg0
);
arm_output_asm_insn
(
"stfe
\t
%1, [sp, #-12]!"
,
operands
);
arm_output_asm_insn
(
"ldmfd
\t
sp!, {%0, %1, %2}"
,
ops
);
return
(
""
);
}
/* output_mov_long_double_arm_from_fpu */
/* Output a move from arm registers to arm registers of a long double
OPERANDS[0] is the destination.
OPERANDS[1] is the source. */
char
*
output_mov_long_double_arm_from_arm
(
operands
)
rtx
operands
[];
{
/* We have to be careful here because the two might overlap */
int
dest_start
=
REGNO
(
operands
[
0
]);
int
src_start
=
REGNO
(
operands
[
1
]);
rtx
ops
[
2
];
int
i
;
if
(
dest_start
<
src_start
)
{
for
(
i
=
0
;
i
<
3
;
i
++
)
{
ops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
dest_start
+
i
);
ops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
src_start
+
i
);
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
ops
);
}
}
else
{
for
(
i
=
2
;
i
>=
0
;
i
--
)
{
ops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
dest_start
+
i
);
ops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
src_start
+
i
);
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
ops
);
}
}
return
""
;
}
/* Output a move from arm registers to an fpu registers.
/* Output a move from arm registers to an fpu registers.
OPERANDS[0] is an fpu register.
OPERANDS[0] is an fpu register.
OPERANDS[1] is the first registers of an arm register pair. */
OPERANDS[1] is the first registers of an arm register pair. */
...
@@ -371,26 +1072,51 @@ output_move_double (operands)
...
@@ -371,26 +1072,51 @@ output_move_double (operands)
CONST_DOUBLE_HIGH
(
operands
[
1
]));
CONST_DOUBLE_HIGH
(
operands
[
1
]));
operands
[
1
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
operands
[
1
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
CONST_DOUBLE_LOW
(
operands
[
1
]));
CONST_DOUBLE_LOW
(
operands
[
1
]));
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
operands
);
output_mov_immediate
(
operands
,
FALSE
,
""
);
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
otherops
);
output_mov_immediate
(
otherops
,
FALSE
,
""
);
}
}
else
if
(
code1
==
CONST_INT
)
else
if
(
code1
==
CONST_INT
)
{
{
otherops
[
1
]
=
const0_rtx
;
otherops
[
1
]
=
const0_rtx
;
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
operands
);
/* sign extend the intval into the high-order word */
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
otherops
);
/* Note: output_mov_immediate may clobber operands[1], so we
put this out first */
if
(
INTVAL
(
operands
[
1
])
<
0
)
arm_output_asm_insn
(
"mvn
\t
%0, %1"
,
otherops
);
else
arm_output_asm_insn
(
"mov
\t
%0, %1"
,
otherops
);
output_mov_immediate
(
operands
,
FALSE
,
""
);
}
}
else
if
(
code1
==
MEM
)
else
if
(
code1
==
MEM
)
{
{
if
(
GET_CODE
(
XEXP
(
operands
[
1
],
0
))
==
REG
)
switch
(
GET_CODE
(
XEXP
(
operands
[
1
],
0
))
)
{
{
case
REG
:
/* Handle the simple case where address is [r, #0] more
/* Handle the simple case where address is [r, #0] more
efficient. */
efficient. */
operands
[
1
]
=
XEXP
(
operands
[
1
],
0
);
operands
[
1
]
=
XEXP
(
operands
[
1
],
0
);
arm_output_asm_insn
(
"ldmia
\t
%1, %M0"
,
operands
);
arm_output_asm_insn
(
"ldmia
\t
%1, %M0"
,
operands
);
}
break
;
else
case
PRE_INC
:
{
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
arm_output_asm_insn
(
"add
\t
%1, %1, #8"
,
operands
);
arm_output_asm_insn
(
"ldmia
\t
%1, %M0"
,
operands
);
break
;
case
PRE_DEC
:
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
arm_output_asm_insn
(
"sub
\t
%1, %1, #8"
,
operands
);
arm_output_asm_insn
(
"ldmia
\t
%1, %M0"
,
operands
);
break
;
case
POST_INC
:
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
arm_output_asm_insn
(
"ldmia
\t
%1!, %M0"
,
operands
);
break
;
case
POST_DEC
:
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
arm_output_asm_insn
(
"ldmia
\t
%1, %M0"
,
operands
);
arm_output_asm_insn
(
"sub
\t
%1, %1, #8"
,
operands
);
break
;
default
:
otherops
[
1
]
=
adj_offsettable_operand
(
operands
[
1
],
4
);
otherops
[
1
]
=
adj_offsettable_operand
(
operands
[
1
],
4
);
/* Take care of overlapping base/data reg. */
/* Take care of overlapping base/data reg. */
if
(
reg_mentioned_p
(
operands
[
0
],
operands
[
1
]))
if
(
reg_mentioned_p
(
operands
[
0
],
operands
[
1
]))
...
@@ -411,14 +1137,32 @@ output_move_double (operands)
...
@@ -411,14 +1137,32 @@ output_move_double (operands)
{
{
if
(
REGNO
(
operands
[
1
])
==
12
)
if
(
REGNO
(
operands
[
1
])
==
12
)
abort
();
abort
();
switch
(
GET_CODE
(
XEXP
(
operands
[
0
],
0
)))
if
(
GET_CODE
(
XEXP
(
operands
[
0
],
0
))
==
REG
)
{
{
case
REG
:
operands
[
0
]
=
XEXP
(
operands
[
0
],
0
);
operands
[
0
]
=
XEXP
(
operands
[
0
],
0
);
arm_output_asm_insn
(
"stmia
\t
%0, %M1"
,
operands
);
arm_output_asm_insn
(
"stmia
\t
%0, %M1"
,
operands
);
}
break
;
else
case
PRE_INC
:
{
operands
[
0
]
=
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
);
arm_output_asm_insn
(
"add
\t
%0, %0, #8"
,
operands
);
arm_output_asm_insn
(
"stmia
\t
%0, %M1"
,
operands
);
break
;
case
PRE_DEC
:
operands
[
0
]
=
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
);
arm_output_asm_insn
(
"sub
\t
%0, %0, #8"
,
operands
);
arm_output_asm_insn
(
"stmia
\t
%0, %M1"
,
operands
);
break
;
case
POST_INC
:
operands
[
0
]
=
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
);
arm_output_asm_insn
(
"stmia
\t
%0!, %M1"
,
operands
);
break
;
case
POST_DEC
:
operands
[
0
]
=
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
);
arm_output_asm_insn
(
"stmia
\t
%0, %M1"
,
operands
);
arm_output_asm_insn
(
"sub
\t
%0, %0, #8"
,
operands
);
break
;
default
:
otherops
[
0
]
=
adj_offsettable_operand
(
operands
[
0
],
4
);
otherops
[
0
]
=
adj_offsettable_operand
(
operands
[
0
],
4
);
otherops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
1
+
REGNO
(
operands
[
1
]));
otherops
[
1
]
=
gen_rtx
(
REG
,
SImode
,
1
+
REGNO
(
operands
[
1
]));
arm_output_asm_insn
(
"str
\t
%1, %0"
,
operands
);
arm_output_asm_insn
(
"str
\t
%1, %0"
,
operands
);
...
@@ -593,6 +1337,10 @@ shift_instr (op, shift_ptr)
...
@@ -593,6 +1337,10 @@ shift_instr (op, shift_ptr)
mnem
=
"lsr"
;
mnem
=
"lsr"
;
max_shift
=
32
;
max_shift
=
32
;
break
;
break
;
case
MULT
:
*
shift_ptr
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
int_log2
(
INTVAL
(
*
shift_ptr
)));
return
(
"asl"
);
default
:
default
:
abort
();
abort
();
}
}
...
@@ -719,6 +1467,21 @@ output_shifted_move (op, operands)
...
@@ -719,6 +1467,21 @@ output_shifted_move (op, operands)
return
(
arm_output_asm_insn
(
mnemonic
,
operands
));
return
(
arm_output_asm_insn
(
mnemonic
,
operands
));
}
/* output_shifted_move */
}
/* output_shifted_move */
char
*
output_shift_compare
(
operands
,
neg
)
rtx
*
operands
;
int
neg
;
{
char
buf
[
80
];
if
(
neg
)
sprintf
(
buf
,
"cmn
\t
%%1, %%3, %s %%4"
,
shift_instr
(
GET_CODE
(
operands
[
2
]),
&
operands
[
4
]));
else
sprintf
(
buf
,
"cmp
\t
%%1, %%3, %s %%4"
,
shift_instr
(
GET_CODE
(
operands
[
2
]),
&
operands
[
4
]));
return
arm_output_asm_insn
(
buf
,
operands
);
}
/* output_shift_compare */
/* Output a .ascii pseudo-op, keeping track of lengths. This is because
/* Output a .ascii pseudo-op, keeping track of lengths. This is because
/bin/as is horribly restrictive. */
/bin/as is horribly restrictive. */
...
@@ -768,31 +1531,211 @@ output_ascii_pseudo_op (stream, p, len)
...
@@ -768,31 +1531,211 @@ output_ascii_pseudo_op (stream, p, len)
arm_increase_location
(
chars_so_far
);
arm_increase_location
(
chars_so_far
);
}
/* output_ascii_pseudo_op */
}
/* output_ascii_pseudo_op */
/* Try to determine whether a pattern really clobbers the link register.
This information is useful when peepholing, so that lr need not be pushed
if we combine a call followed by a return */
static
int
pattern_really_clobbers_lr
(
x
)
rtx
x
;
{
int
i
;
switch
(
GET_CODE
(
x
))
{
case
SET
:
switch
(
GET_CODE
(
SET_DEST
(
x
)))
{
case
REG
:
return
REGNO
(
SET_DEST
(
x
))
==
14
;
case
SUBREG
:
if
(
GET_CODE
(
XEXP
(
SET_DEST
(
x
),
0
))
==
REG
)
return
REGNO
(
XEXP
(
SET_DEST
(
x
),
0
))
==
14
;
abort
();
default
:
return
0
;
}
case
PARALLEL
:
for
(
i
=
0
;
i
<
XVECLEN
(
x
,
0
);
i
++
)
if
(
pattern_really_clobbers_lr
(
XVECEXP
(
x
,
0
,
i
)))
return
1
;
return
0
;
case
CLOBBER
:
switch
(
GET_CODE
(
XEXP
(
x
,
0
)))
{
case
REG
:
return
REGNO
(
XEXP
(
x
,
0
))
==
14
;
case
SUBREG
:
if
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
REG
)
return
REGNO
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
14
;
abort
();
default
:
return
0
;
}
case
UNSPEC
:
return
1
;
default
:
return
0
;
}
}
static
int
function_really_clobbers_lr
(
first
)
rtx
first
;
{
rtx
insn
,
next
;
for
(
insn
=
first
;
insn
;
insn
=
next_nonnote_insn
(
insn
))
{
switch
(
GET_CODE
(
insn
))
{
case
BARRIER
:
case
NOTE
:
case
CODE_LABEL
:
case
JUMP_INSN
:
/* Jump insns only change the PC (and conds) */
case
INLINE_HEADER
:
break
;
case
INSN
:
if
(
pattern_really_clobbers_lr
(
PATTERN
(
insn
)))
return
1
;
break
;
case
CALL_INSN
:
/* Don't yet know how to handle those calls that are not to a
SYMBOL_REF */
if
(
GET_CODE
(
PATTERN
(
insn
))
!=
PARALLEL
)
abort
();
switch
(
GET_CODE
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
)))
{
case
CALL
:
if
(
GET_CODE
(
XEXP
(
XEXP
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
),
0
),
0
))
!=
SYMBOL_REF
)
return
1
;
break
;
case
SET
:
if
(
GET_CODE
(
XEXP
(
XEXP
(
SET_SRC
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
)),
0
),
0
))
!=
SYMBOL_REF
)
return
1
;
break
;
default
:
/* Don't recognize it, be safe */
return
1
;
}
/* A call can be made (by peepholing) not to clobber lr iff it is
followed by a return. There may, however, be a use insn iff
we are returning the result of the call.
If we run off the end of the insn chain, then that means the
call was at the end of the function. Unfortunately we don't
have a return insn for the peephole to recognize, so we
must reject this. (Can this be fixed by adding our own insn?) */
if
((
next
=
next_nonnote_insn
(
insn
))
==
NULL
)
return
1
;
if
(
GET_CODE
(
next
)
==
INSN
&&
GET_CODE
(
PATTERN
(
next
))
==
USE
&&
(
GET_CODE
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
))
==
SET
)
&&
(
REGNO
(
SET_DEST
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
)))
==
REGNO
(
XEXP
(
PATTERN
(
next
),
0
))))
if
((
next
=
next_nonnote_insn
(
next
))
==
NULL
)
return
1
;
if
(
GET_CODE
(
next
)
==
JUMP_INSN
&&
GET_CODE
(
PATTERN
(
next
))
==
RETURN
)
break
;
return
1
;
default
:
abort
();
}
}
/* We have reached the end of the chain so lr was _not_ clobbered */
return
0
;
}
char
*
output_return_instruction
(
operand
,
really_return
)
rtx
operand
;
int
really_return
;
{
char
instr
[
100
];
int
reg
,
live_regs
=
0
;
if
(
current_function_calls_alloca
&&
!
really_return
)
abort
();
for
(
reg
=
4
;
reg
<
10
;
reg
++
)
if
(
regs_ever_live
[
reg
])
live_regs
++
;
if
(
live_regs
||
(
regs_ever_live
[
14
]
&&
!
lr_save_eliminated
))
live_regs
++
;
if
(
frame_pointer_needed
)
live_regs
+=
4
;
if
(
live_regs
)
{
if
(
lr_save_eliminated
||
!
regs_ever_live
[
14
])
live_regs
++
;
if
(
frame_pointer_needed
)
strcpy
(
instr
,
"ldm%d0ea
\t
fp, {"
);
else
strcpy
(
instr
,
"ldm%d0fd
\t
sp!, {"
);
for
(
reg
=
4
;
reg
<
10
;
reg
++
)
if
(
regs_ever_live
[
reg
])
{
strcat
(
instr
,
reg_names
[
reg
]);
if
(
--
live_regs
)
strcat
(
instr
,
", "
);
}
if
(
frame_pointer_needed
)
{
strcat
(
instr
,
reg_names
[
11
]);
strcat
(
instr
,
", "
);
strcat
(
instr
,
reg_names
[
13
]);
strcat
(
instr
,
", "
);
strcat
(
instr
,
really_return
?
reg_names
[
15
]
:
reg_names
[
14
]);
}
else
strcat
(
instr
,
really_return
?
reg_names
[
15
]
:
reg_names
[
14
]);
strcat
(
instr
,
(
TARGET_6
||
!
really_return
)
?
"}"
:
"}^"
);
arm_output_asm_insn
(
instr
,
&
operand
);
}
else
if
(
really_return
)
{
strcpy
(
instr
,
TARGET_6
?
"mov%d0
\t
pc, lr"
:
"mov%d0s
\t
pc, lr"
);
arm_output_asm_insn
(
instr
,
&
operand
);
}
return_used_this_function
=
1
;
return
""
;
}
/* The amount of stack adjustment that happens here, in output_return and in
output_epilogue must be exactly the same as was calculated during reload,
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_prologue
(
f
,
frame_size
)
output_prologue
(
f
,
frame_size
)
FILE
*
f
;
FILE
*
f
;
int
frame_size
;
int
frame_size
;
{
{
int
reg
,
live_regs_mask
=
0
,
code_size
=
0
;
int
reg
,
live_regs_mask
=
0
,
code_size
=
0
;
rtx
operands
[
3
];
rtx
operands
[
3
];
/* Nonzero if the `fp' (argument pointer) register is needed. */
int
fp_needed
=
0
;
/* Nonzero if we must stuff some register arguments onto the stack as if
/* Nonzero if we must stuff some register arguments onto the stack as if
they were passed there. */
they were passed there. */
int
store_arg_regs
=
0
;
int
store_arg_regs
=
0
;
return_used_this_function
=
0
;
lr_save_eliminated
=
0
;
fprintf
(
f
,
"
\t
@ args = %d, pretend = %d, frame = %d
\n
"
,
fprintf
(
f
,
"
\t
@ args = %d, pretend = %d, frame = %d
\n
"
,
current_function_args_size
,
current_function_pretend_args_size
,
frame_size
);
current_function_args_size
,
current_function_pretend_args_size
,
fprintf
(
f
,
"
\t
@ frame_pointer_needed = %d, current_function_anonymous_args = %d
\n
"
,
frame_size
);
fprintf
(
f
,
"
\t
@ frame_needed = %d, current_function_anonymous_args = %d
\n
"
,
frame_pointer_needed
,
current_function_anonymous_args
);
frame_pointer_needed
,
current_function_anonymous_args
);
if
(
current_function_pretend_args_size
||
current_function_args_size
||
frame_pointer_needed
||
current_function_anonymous_args
||
TARGET_APCS
)
fp_needed
=
1
;
if
(
current_function_anonymous_args
&&
current_function_pretend_args_size
)
if
(
current_function_anonymous_args
&&
current_function_pretend_args_size
)
store_arg_regs
=
1
;
store_arg_regs
=
1
;
...
@@ -800,18 +1743,23 @@ output_prologue (f, frame_size)
...
@@ -800,18 +1743,23 @@ output_prologue (f, frame_size)
if
(
regs_ever_live
[
reg
])
if
(
regs_ever_live
[
reg
])
live_regs_mask
|=
(
1
<<
reg
);
live_regs_mask
|=
(
1
<<
reg
);
if
(
f
p
_needed
)
if
(
f
rame_pointer
_needed
)
{
{
live_regs_mask
|=
0xD800
;
live_regs_mask
|=
0xD800
;
/* The following statement is probably redundant now
because the frame pointer is recorded in regs_ever_live. */
if
(
frame_pointer_needed
)
live_regs_mask
|=
(
1
<<
FRAME_POINTER_REGNUM
);
fputs
(
"
\t
mov
\t
ip, sp
\n
"
,
f
);
fputs
(
"
\t
mov
\t
ip, sp
\n
"
,
f
);
code_size
+=
4
;
code_size
+=
4
;
}
}
else
if
(
regs_ever_live
[
14
])
else
if
(
regs_ever_live
[
14
])
live_regs_mask
|=
0x4000
;
{
if
(
!
current_function_args_size
&&
!
function_really_clobbers_lr
(
get_insns
()))
{
fprintf
(
f
,
"
\t
@ I don't think this function clobbers lr
\n
"
);
lr_save_eliminated
=
1
;
}
else
live_regs_mask
|=
0x4000
;
}
/* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
/* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
room. If also STORE_ARG_REGS store the argument registers involved in
room. If also STORE_ARG_REGS store the argument registers involved in
...
@@ -827,6 +1775,7 @@ output_prologue (f, frame_size)
...
@@ -827,6 +1775,7 @@ output_prologue (f, frame_size)
arg_size
>
0
;
reg
--
,
arg_size
-=
4
)
arg_size
>
0
;
reg
--
,
arg_size
-=
4
)
mask
|=
(
1
<<
reg
);
mask
|=
(
1
<<
reg
);
print_multi_reg
(
f
,
"stmfd
\t
sp!"
,
mask
,
FALSE
);
print_multi_reg
(
f
,
"stmfd
\t
sp!"
,
mask
,
FALSE
);
code_size
+=
4
;
}
}
else
else
{
{
...
@@ -839,6 +1788,13 @@ output_prologue (f, frame_size)
...
@@ -839,6 +1788,13 @@ output_prologue (f, frame_size)
if
(
live_regs_mask
)
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
|=
0x4000
;
lr_save_eliminated
=
0
;
print_multi_reg
(
f
,
"stmfd
\t
sp!"
,
live_regs_mask
,
FALSE
);
print_multi_reg
(
f
,
"stmfd
\t
sp!"
,
live_regs_mask
,
FALSE
);
code_size
+=
4
;
code_size
+=
4
;
}
}
...
@@ -850,23 +1806,17 @@ output_prologue (f, frame_size)
...
@@ -850,23 +1806,17 @@ output_prologue (f, frame_size)
code_size
+=
4
;
code_size
+=
4
;
}
}
if
(
f
p
_needed
)
if
(
f
rame_pointer
_needed
)
{
{
/* Make `fp' point to saved value of `pc'. */
/* Make `fp' point to saved value of `pc'. */
operands
[
0
]
=
arg_pointer_rtx
;
operands
[
0
]
=
gen_rtx
(
REG
,
SImode
,
HARD_FRAME_POINTER_REGNUM
)
;
operands
[
1
]
=
gen_rtx
(
REG
,
SImode
,
12
);
operands
[
1
]
=
gen_rtx
(
REG
,
SImode
,
12
);
operands
[
2
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
operands
[
2
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
-
(
4
+
current_function_pretend_args_size
));
-
(
4
+
current_function_pretend_args_size
));
output_add_immediate
(
operands
);
output_add_immediate
(
operands
);
}
}
if
(
frame_pointer_needed
)
{
fprintf
(
f
,
"
\t
mov
\t
rfp, sp
\n
"
);
code_size
+=
4
;
}
if
(
frame_size
)
if
(
frame_size
)
{
{
operands
[
0
]
=
operands
[
1
]
=
stack_pointer_rtx
;
operands
[
0
]
=
operands
[
1
]
=
stack_pointer_rtx
;
...
@@ -884,36 +1834,41 @@ output_epilogue (f, frame_size)
...
@@ -884,36 +1834,41 @@ output_epilogue (f, frame_size)
int
frame_size
;
int
frame_size
;
{
{
int
reg
,
live_regs_mask
=
0
,
code_size
=
0
,
fp_needed
=
0
;
int
reg
,
live_regs_mask
=
0
,
code_size
=
0
,
fp_needed
=
0
;
/* If we need this then it will always be at lesat this much */
int
floats_offset
=
24
;
rtx
operands
[
3
];
rtx
operands
[
3
];
if
(
current_function_pretend_args_size
||
current_function_args_size
if
(
use_return_insn
()
&&
return_used_this_function
)
||
frame_pointer_needed
||
current_function_anonymous_args
||
TARGET_APCS
)
fp_needed
=
1
;
for
(
reg
=
4
;
reg
<
10
;
reg
++
)
if
(
regs_ever_live
[
reg
])
live_regs_mask
|=
(
1
<<
reg
);
if
(
fp_needed
)
{
{
live_regs_mask
|=
0xA800
;
if
(
frame_size
&&
!
(
frame_pointer_needed
||
TARGET_APCS
))
if
(
frame_pointer_needed
)
{
live_regs_mask
|=
(
1
<<
FRAME_POINTER_REGNUM
);
abort
();
}
return
;
}
}
else
if
(
regs_ever_live
[
14
])
live_regs_mask
|=
0x4000
;
for
(
reg
=
20
;
reg
<
24
;
reg
++
)
for
(
reg
=
4
;
reg
<=
10
;
reg
++
)
if
(
regs_ever_live
[
reg
])
if
(
regs_ever_live
[
reg
])
{
{
fprintf
(
f
,
"
\t
ldfe
\t
%s, [%s], #12
\n
"
,
reg_names
[
reg
],
live_regs_mask
|=
(
1
<<
reg
);
frame_pointer_needed
?
"rfp"
:
"sp"
);
floats_offset
+=
4
;
code_size
+=
4
;
}
}
if
(
fp_needed
)
if
(
frame_pointer_needed
)
{
{
print_multi_reg
(
f
,
"ldmea
\t
fp"
,
live_regs_mask
,
TRUE
);
for
(
reg
=
23
;
reg
>=
20
;
reg
--
)
if
(
regs_ever_live
[
reg
])
{
fprintf
(
f
,
"
\t
ldfe
\t
%s, [fp, #-%d]
\n
"
,
reg_names
[
reg
],
floats_offset
);
floats_offset
+=
12
;
code_size
+=
4
;
}
live_regs_mask
|=
0xA800
;
print_multi_reg
(
f
,
"ldmea
\t
fp"
,
live_regs_mask
,
TARGET_6
?
FALSE
:
TRUE
);
code_size
+=
4
;
code_size
+=
4
;
}
}
else
else
...
@@ -926,16 +1881,23 @@ output_epilogue (f, frame_size)
...
@@ -926,16 +1881,23 @@ output_epilogue (f, frame_size)
output_add_immediate
(
operands
);
output_add_immediate
(
operands
);
}
}
for
(
reg
=
20
;
reg
<
24
;
reg
++
)
if
(
regs_ever_live
[
reg
])
{
fprintf
(
f
,
"
\t
ldfe
\t
%s, [sp], #12
\n
"
,
reg_names
[
reg
]);
code_size
+=
4
;
}
if
(
current_function_pretend_args_size
==
0
&&
regs_ever_live
[
14
])
if
(
current_function_pretend_args_size
==
0
&&
regs_ever_live
[
14
])
{
{
print_multi_reg
(
f
,
"ldmfd
\t
sp!"
,
print_multi_reg
(
f
,
"ldmfd
\t
sp!"
,
live_regs_mask
|
0x8000
,
(
live_regs_mask
&
~
0x4000
)
|
0x8000
,
TRUE
);
TARGET_6
?
FALSE
:
TRUE
);
code_size
+=
4
;
code_size
+=
4
;
}
}
else
else
{
{
if
(
live_regs_mask
)
if
(
live_regs_mask
||
regs_ever_live
[
14
]
)
{
{
live_regs_mask
|=
0x4000
;
print_multi_reg
(
f
,
"ldmfd
\t
sp!"
,
live_regs_mask
,
FALSE
);
print_multi_reg
(
f
,
"ldmfd
\t
sp!"
,
live_regs_mask
,
FALSE
);
code_size
+=
4
;
code_size
+=
4
;
}
}
...
@@ -946,7 +1908,7 @@ output_epilogue (f, frame_size)
...
@@ -946,7 +1908,7 @@ output_epilogue (f, frame_size)
current_function_pretend_args_size
);
current_function_pretend_args_size
);
output_add_immediate
(
operands
);
output_add_immediate
(
operands
);
}
}
fputs
(
"
\t
movs
\t
pc, lr
\n
"
,
f
);
fputs
(
TARGET_6
?
"
\t
mov
\t
pc, lr
\n
"
:
"
\t
movs
\t
pc, lr
\n
"
,
f
);
code_size
+=
4
;
code_size
+=
4
;
}
}
}
}
...
@@ -1086,6 +2048,72 @@ arm_output_llc (operands)
...
@@ -1086,6 +2048,72 @@ arm_output_llc (operands)
return
(
""
);
return
(
""
);
}
/* arm_output_llc */
}
/* arm_output_llc */
/* output_load_symbol ()
load a symbol that is known to be in the text segment into a register */
char
*
output_load_symbol
(
operands
)
rtx
*
operands
;
{
char
*
s
,
*
name
=
XSTR
(
operands
[
1
],
0
);
struct
label_offset
*
he
;
int
hash
=
0
;
int
offset
;
if
(
*
name
!=
'*'
)
abort
();
for
(
s
=
&
name
[
1
];
*
s
;
s
++
)
hash
+=
*
s
;
hash
=
hash
%
LABEL_HASH_SIZE
;
he
=
offset_table
[
hash
];
while
(
he
&&
strcmp
(
he
->
name
,
&
name
[
1
]))
he
=
he
->
cdr
;
if
(
!
he
)
abort
();
offset
=
(
arm_text_location
+
8
-
he
->
offset
);
if
(
offset
<
0
)
abort
();
/* If the symbol is word aligned then we might be able to reduce the
number of loads */
if
((
offset
&
3
)
==
0
)
{
arm_output_asm_insn
(
"sub
\t
%0, pc, #(8 + . -%a1) & 1023"
,
operands
);
if
(
offset
>
0x3ff
)
{
arm_output_asm_insn
(
"sub
\t
%0, %0, #(4 + . -%a1) & 261120"
,
operands
);
if
(
offset
>
0x3ffff
)
{
arm_output_asm_insn
(
"sub
\t
%0, %0, #(. -%a1) & 66846720"
,
operands
);
if
(
offset
>
0x3ffffff
)
arm_output_asm_insn
(
"sub
\t
%0, %0, #(. - 4 -%a1) & -67108864"
,
operands
);
}
}
}
else
{
arm_output_asm_insn
(
"sub
\t
%0, pc, #(8 + . -%a1) & 255"
,
operands
);
if
(
offset
>
0x0ff
)
{
arm_output_asm_insn
(
"sub
\t
%0, %0, #(4 + . -%a1) & 65280"
,
operands
);
if
(
offset
>
0x0ffff
)
{
arm_output_asm_insn
(
"sub
\t
%0, %0, #(. -%a1) & 16711680"
,
operands
);
if
(
offset
>
0x0ffffff
)
arm_output_asm_insn
(
"sub
\t
%0, %0, #(. - 4 -%a1) & -16777216"
,
operands
);
}
}
}
return
""
;
}
/* Output code resembling an .lcomm directive. /bin/as doesn't have this
/* Output code resembling an .lcomm directive. /bin/as doesn't have this
directive hence this hack, which works by reserving some `.space' in the
directive hence this hack, which works by reserving some `.space' in the
...
@@ -1131,6 +2159,10 @@ output_lcomm_directive (stream, name, size, rounded)
...
@@ -1131,6 +2159,10 @@ output_lcomm_directive (stream, name, size, rounded)
4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
(the target insn is arm_target_insn).
(the target insn is arm_target_insn).
If the jump clobbers the conditions then we use states 2 and 4.
A similar thing can be done with conditional return insns.
XXX In case the `target' is an unconditional branch, this conditionalising
XXX In case the `target' is an unconditional branch, this conditionalising
of the instructions always reduces code size, but not always execution
of the instructions always reduces code size, but not always execution
time. But then, I want to reduce the code size to somewhere near what
time. But then, I want to reduce the code size to somewhere near what
...
@@ -1185,6 +2217,15 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1185,6 +2217,15 @@ final_prescan_insn (insn, opvec, noperands)
reversed if it appears to fail. */
reversed if it appears to fail. */
int
reverse
=
0
;
int
reverse
=
0
;
/* JUMP_CLOBBERS will be one implies that the conditions if a branch is
taken are clobbered, even if the rtl suggests otherwise. It also
means that we have to grub around within the jump expression to find
out what the conditions are when the jump isn't taken. */
int
jump_clobbers
=
0
;
/* If we start with a return insn, we only succeed if we find another one. */
int
seeking_return
=
0
;
/* START_INSN will hold the insn from where we start looking. This is the
/* START_INSN will hold the insn from where we start looking. This is the
first insn after the following code_label if REVERSE is true. */
first insn after the following code_label if REVERSE is true. */
rtx
start_insn
=
insn
;
rtx
start_insn
=
insn
;
...
@@ -1219,6 +2260,21 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1219,6 +2260,21 @@ final_prescan_insn (insn, opvec, noperands)
else
else
return
;
return
;
}
}
else
if
(
GET_CODE
(
body
)
==
RETURN
)
{
start_insn
=
next_nonnote_insn
(
start_insn
);
if
(
GET_CODE
(
start_insn
)
==
BARRIER
)
start_insn
=
next_nonnote_insn
(
start_insn
);
if
(
GET_CODE
(
start_insn
)
==
CODE_LABEL
&&
CODE_LABEL_NUMBER
(
start_insn
)
==
arm_target_label
&&
LABEL_NUSES
(
start_insn
)
==
1
)
{
reverse
=
TRUE
;
seeking_return
=
1
;
}
else
return
;
}
else
else
return
;
return
;
}
}
...
@@ -1228,6 +2284,20 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1228,6 +2284,20 @@ final_prescan_insn (insn, opvec, noperands)
if
(
GET_CODE
(
insn
)
!=
JUMP_INSN
)
if
(
GET_CODE
(
insn
)
!=
JUMP_INSN
)
return
;
return
;
/* This jump might be paralled with a clobber of the condition codes
the jump should always come first */
if
(
GET_CODE
(
body
)
==
PARALLEL
&&
XVECLEN
(
body
,
0
)
>
0
)
body
=
XVECEXP
(
body
,
0
,
0
);
#if 0
/* If this is a conditional return then we don't want to know */
if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
&& (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN
|| GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN))
return;
#endif
if
(
reverse
if
(
reverse
||
(
GET_CODE
(
body
)
==
SET
&&
GET_CODE
(
SET_DEST
(
body
))
==
PC
||
(
GET_CODE
(
body
)
==
SET
&&
GET_CODE
(
SET_DEST
(
body
))
==
PC
&&
GET_CODE
(
SET_SRC
(
body
))
==
IF_THEN_ELSE
))
&&
GET_CODE
(
SET_SRC
(
body
))
==
IF_THEN_ELSE
))
...
@@ -1235,11 +2305,17 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1235,11 +2305,17 @@ final_prescan_insn (insn, opvec, noperands)
int
insns_skipped
=
0
,
fail
=
FALSE
,
succeed
=
FALSE
;
int
insns_skipped
=
0
,
fail
=
FALSE
,
succeed
=
FALSE
;
/* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
/* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
int
then_not_else
=
TRUE
;
int
then_not_else
=
TRUE
;
rtx
this_insn
=
start_insn
,
label
;
rtx
this_insn
=
start_insn
,
label
=
0
;
if
(
get_attr_conds
(
insn
)
==
CONDS_JUMP_CLOB
)
jump_clobbers
=
1
;
/* Register the insn jumped to. */
/* Register the insn jumped to. */
if
(
reverse
)
if
(
reverse
)
label
=
XEXP
(
SET_SRC
(
body
),
0
);
{
if
(
!
seeking_return
)
label
=
XEXP
(
SET_SRC
(
body
),
0
);
}
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
1
))
==
LABEL_REF
)
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
1
))
==
LABEL_REF
)
label
=
XEXP
(
XEXP
(
SET_SRC
(
body
),
1
),
0
);
label
=
XEXP
(
XEXP
(
SET_SRC
(
body
),
1
),
0
);
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
2
))
==
LABEL_REF
)
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
2
))
==
LABEL_REF
)
...
@@ -1247,6 +2323,13 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1247,6 +2323,13 @@ final_prescan_insn (insn, opvec, noperands)
label
=
XEXP
(
XEXP
(
SET_SRC
(
body
),
2
),
0
);
label
=
XEXP
(
XEXP
(
SET_SRC
(
body
),
2
),
0
);
then_not_else
=
FALSE
;
then_not_else
=
FALSE
;
}
}
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
1
))
==
RETURN
)
seeking_return
=
1
;
else
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
2
))
==
RETURN
)
{
seeking_return
=
1
;
then_not_else
=
FALSE
;
}
else
else
abort
();
abort
();
...
@@ -1272,33 +2355,54 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1272,33 +2355,54 @@ final_prescan_insn (insn, opvec, noperands)
control falls in from somewhere else. */
control falls in from somewhere else. */
if
(
this_insn
==
label
)
if
(
this_insn
==
label
)
{
{
arm_ccfsm_state
=
1
;
if
(
jump_clobbers
)
{
arm_ccfsm_state
=
2
;
this_insn
=
next_nonnote_insn
(
this_insn
);
}
else
arm_ccfsm_state
=
1
;
succeed
=
TRUE
;
succeed
=
TRUE
;
}
}
else
else
fail
=
TRUE
;
fail
=
TRUE
;
break
;
break
;
case
BARRIER
:
/* XXX Is this case necessary? */
case
BARRIER
:
/* Succeed if the following insn is the target label.
/* Succeed if the following insn is the target label.
Otherwise fail. */
Otherwise fail.
If return insns are used then the last insn in a function
will be a barrier. */
this_insn
=
next_nonnote_insn
(
this_insn
);
this_insn
=
next_nonnote_insn
(
this_insn
);
if
(
this_insn
==
label
)
if
(
this_insn
&&
this_insn
==
label
)
{
{
arm_ccfsm_state
=
1
;
if
(
jump_clobbers
)
{
arm_ccfsm_state
=
2
;
this_insn
=
next_nonnote_insn
(
this_insn
);
}
else
arm_ccfsm_state
=
1
;
succeed
=
TRUE
;
succeed
=
TRUE
;
}
}
else
else
fail
=
TRUE
;
fail
=
TRUE
;
break
;
break
;
case
CALL_INSN
:
/* The arm 6xx uses full 32 bit addresses so the cc is not
preserved over calls */
if
(
TARGET_6
)
fail
=
TRUE
;
break
;
case
JUMP_INSN
:
case
JUMP_INSN
:
/* If this is an unconditional branch to the same label, succeed.
/* If this is an unconditional branch to the same label, succeed.
If it is to another label, do nothing. If it is conditional,
If it is to another label, do nothing. If it is conditional,
fail. */
fail. */
/* XXX Probably, the test for the SET and the PC are unnecessary. */
/* XXX Probably, the test for the SET and the PC are unnecessary. */
if
(
GET_CODE
(
scanbody
)
==
SET
&&
GET_CODE
(
SET_DEST
(
scanbody
))
==
PC
)
if
(
GET_CODE
(
scanbody
)
==
SET
&&
GET_CODE
(
SET_DEST
(
scanbody
))
==
PC
)
{
{
if
(
GET_CODE
(
SET_SRC
(
scanbody
))
==
LABEL_REF
if
(
GET_CODE
(
SET_SRC
(
scanbody
))
==
LABEL_REF
&&
XEXP
(
SET_SRC
(
scanbody
),
0
)
==
label
&&
!
reverse
)
&&
XEXP
(
SET_SRC
(
scanbody
),
0
)
==
label
&&
!
reverse
)
...
@@ -1309,11 +2413,31 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1309,11 +2413,31 @@ final_prescan_insn (insn, opvec, noperands)
else
if
(
GET_CODE
(
SET_SRC
(
scanbody
))
==
IF_THEN_ELSE
)
else
if
(
GET_CODE
(
SET_SRC
(
scanbody
))
==
IF_THEN_ELSE
)
fail
=
TRUE
;
fail
=
TRUE
;
}
}
else
if
(
GET_CODE
(
scanbody
)
==
RETURN
&&
seeking_return
)
{
arm_ccfsm_state
=
2
;
succeed
=
TRUE
;
}
else
if
(
GET_CODE
(
scanbody
)
==
PARALLEL
)
{
switch
(
get_attr_conds
(
this_insn
))
{
case
CONDS_NOCOND
:
break
;
default
:
fail
=
TRUE
;
break
;
}
}
break
;
break
;
case
INSN
:
case
INSN
:
/* Instructions affecting the condition codes make it fail. */
/* Instructions using or affecting the condition codes make it
if
(
sets_cc0_p
(
scanbody
))
fail. */
if
((
GET_CODE
(
scanbody
)
==
SET
||
GET_CODE
(
scanbody
)
==
PARALLEL
)
&&
get_attr_conds
(
this_insn
)
!=
CONDS_NOCOND
)
fail
=
TRUE
;
fail
=
TRUE
;
break
;
break
;
...
@@ -1323,20 +2447,57 @@ final_prescan_insn (insn, opvec, noperands)
...
@@ -1323,20 +2447,57 @@ final_prescan_insn (insn, opvec, noperands)
}
}
if
(
succeed
)
if
(
succeed
)
{
{
if
(
arm_ccfsm_state
==
1
||
reverse
)
if
(
(
!
seeking_return
)
&&
(
arm_ccfsm_state
==
1
||
reverse
)
)
arm_target_label
=
CODE_LABEL_NUMBER
(
label
);
arm_target_label
=
CODE_LABEL_NUMBER
(
label
);
else
if
(
arm_ccfsm_state
==
2
)
else
if
(
seeking_return
||
arm_ccfsm_state
==
2
)
arm_target_insn
=
this_insn
;
{
while
(
this_insn
&&
GET_CODE
(
PATTERN
(
this_insn
))
==
USE
)
{
this_insn
=
next_nonnote_insn
(
this_insn
);
if
(
this_insn
&&
(
GET_CODE
(
this_insn
)
==
BARRIER
||
GET_CODE
(
this_insn
)
==
CODE_LABEL
))
abort
();
}
if
(
!
this_insn
)
{
/* Oh, dear! we ran off the end.. give up */
recog
(
PATTERN
(
insn
),
insn
,
NULL_PTR
);
arm_ccfsm_state
=
0
;
return
;
}
arm_target_insn
=
this_insn
;
}
else
else
abort
();
abort
();
if
(
jump_clobbers
)
{
if
(
reverse
)
abort
();
arm_current_cc
=
get_arm_condition_code
(
XEXP
(
XEXP
(
XEXP
(
SET_SRC
(
body
),
0
),
0
),
1
));
if
(
GET_CODE
(
XEXP
(
XEXP
(
SET_SRC
(
body
),
0
),
0
))
==
AND
)
arm_current_cc
=
ARM_INVERSE_CONDITION_CODE
(
arm_current_cc
);
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
body
),
0
))
==
NE
)
arm_current_cc
=
ARM_INVERSE_CONDITION_CODE
(
arm_current_cc
);
}
else
{
/* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
what it was. */
if
(
!
reverse
)
arm_current_cc
=
get_arm_condition_code
(
XEXP
(
SET_SRC
(
body
),
0
));
}
/* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what
it was. */
if
(
!
reverse
)
arm_current_cc
=
get_arm_condition_code
(
XEXP
(
SET_SRC
(
body
),
0
));
if
(
reverse
||
then_not_else
)
if
(
reverse
||
then_not_else
)
arm_current_cc
=
ARM_INVERSE_CONDITION_CODE
(
arm_current_cc
);
arm_current_cc
=
ARM_INVERSE_CONDITION_CODE
(
arm_current_cc
);
}
}
/* restore recog_operand (getting the attributes of other insns can
destroy this array, but final.c assumes that it remains intact
accross this call; since the insn has been recognized already we
call recog direct). */
recog
(
PATTERN
(
insn
),
insn
,
NULL_PTR
);
}
}
}
/* final_prescan_insn */
}
/* final_prescan_insn */
...
...
gcc/config/arm/arm.h
View file @
ff9940b0
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
GNU CC is free software; you can redistribute it and/or modify
...
@@ -27,30 +28,45 @@ extern void output_prologue ();
...
@@ -27,30 +28,45 @@ extern void output_prologue ();
extern
void
output_epilogue
();
extern
void
output_epilogue
();
extern
char
*
arm_output_asm_insn
();
extern
char
*
arm_output_asm_insn
();
extern
char
*
arm_output_llc
();
extern
char
*
arm_output_llc
();
extern
char
*
arithmetic_instr
();
extern
char
*
output_add_immediate
();
extern
char
*
output_add_immediate
();
extern
char
*
output_call
();
extern
char
*
output_call
();
extern
char
*
output_call_mem
();
extern
char
*
output_move_double
();
extern
char
*
output_move_double
();
extern
char
*
output_mov_double_fpu_from_arm
();
extern
char
*
output_mov_double_fpu_from_arm
();
extern
char
*
output_mov_double_arm_from_fpu
();
extern
char
*
output_mov_double_arm_from_fpu
();
extern
char
*
output_mov_immediate
();
extern
char
*
output_mov_immediate
();
extern
char
*
output_multi_immediate
();
extern
char
*
output_multi_immediate
();
extern
char
*
output_shifted_move
();
extern
char
*
output_shifted_move
();
extern
char
*
output_shift_compare
();
extern
char
*
output_arithmetic_with_immediate_multiply
();
extern
char
*
output_arithmetic_with_immediate_multiply
();
extern
char
*
output_arithmetic_with_shift
();
extern
char
*
output_return_instruction
();
extern
char
*
output_load_symbol
();
extern
char
*
fp_immediate_constant
();
extern
struct
rtx_def
*
gen_compare_reg
();
extern
struct
rtx_def
*
arm_gen_store_multiple
();
extern
struct
rtx_def
*
arm_gen_load_multiple
();
extern
char
*
arm_condition_codes
[];
/* This is needed by the tail-calling peepholes */
extern
int
frame_pointer_needed
;
/* Translation to find startup files. On RISCiX boxes, gcrt0.o is in
#ifndef CPP_PREDEFINES
/usr/lib. */
#define CPP_PREDEFINES "-Darm -Acpu(arm) -Amachine(arm)"
#define STARTFILE_SPEC \
#endif
"%{pg:/usr/lib/gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}"
#ifdef riscos
#ifndef CPP_SPEC
#define CPP_PREDEFINES "-Darm -Driscos -Acpu(arm) -Amachine(arm)"
#define CPP_SPEC "%{m6:-D__arm6__}"
#else
#define CPP_PREDEFINES "-Darm -Driscix -Dunix -Asystem(unix) -Acpu(arm) -Amachine(arm)"
#endif
#endif
/* Run-time Target Specification. */
/* Run-time Target Specification. */
#ifndef TARGET_VERSION
#define TARGET_VERSION \
#define TARGET_VERSION \
fputs (" (ARM/RISCiX)", stderr);
fputs (" (ARM/generic)", stderr);
#endif
/* Run-time compilation parameters selecting different hardware subsets.
/* Run-time compilation parameters selecting different hardware subsets.
On the ARM, misuse it in a different way. */
On the ARM, misuse it in a different way. */
...
@@ -70,14 +86,50 @@ extern int target_flags;
...
@@ -70,14 +86,50 @@ extern int target_flags;
case instruction scheduling becomes very uninteresting. */
case instruction scheduling becomes very uninteresting. */
#define TARGET_FPE (target_flags & 4)
#define TARGET_FPE (target_flags & 4)
/* Nonzero if destined for an ARM6xx. Takes out bits that assume restoration
of condition flags when returning from a branch & link (ie. a function) */
#define TARGET_6 (target_flags & 8)
/* ARM_EXTRA_TARGET_SWITCHES is used in riscix.h to define some options which
are passed to the preprocessor and the assembler post-processor. They
aren't needed in the main pass of the compiler, but if we don't define
them in target switches cc1 complains about them. For the sake of
argument lets allocate bit 31 of target flags for such options. */
#ifndef ARM_EXTRA_TARGET_SWITCHES
#define ARM_EXTRA_TARGET_SWITCHES
#endif
#define TARGET_SWITCHES \
#define TARGET_SWITCHES \
{ \
{ \
{"apcs", 1}, \
{"apcs", 1}, \
{"poke-function-name", 2}, \
{"poke-function-name", 2}, \
{"fpe", 4}, \
{"fpe", 4}, \
{"6", 8}, \
{"2", -8}, \
{"3", -8}, \
ARM_EXTRA_TARGET_SWITCHES \
{"", TARGET_DEFAULT } \
{"", TARGET_DEFAULT } \
}
}
/* Which processor we are running on. Currently this is only used to
get the condition code clobbering attribute right when we are running on
an arm 6 */
enum
processor_type
{
PROCESSOR_ARM2
,
PROCESSOR_ARM3
,
PROCESSOR_ARM6
};
/* Recast the cpu class to be the cpu attribute. */
/* Recast the cpu class to be the cpu attribute. */
#define arm_cpu_attr ((enum attr_cpu)arm_cpu)
extern
enum
processor_type
arm_cpu
;
#define TARGET_DEFAULT 0
#define TARGET_DEFAULT 0
#define TARGET_MEM_FUNCTIONS 1
#define TARGET_MEM_FUNCTIONS 1
...
@@ -88,33 +140,77 @@ extern int target_flags;
...
@@ -88,33 +140,77 @@ extern int target_flags;
- if floating point is done by emulation, forget about instruction
- if floating point is done by emulation, forget about instruction
scheduling. Note that this only saves compilation time; it doesn't
scheduling. Note that this only saves compilation time; it doesn't
matter for the final code. */
matter for the final code. */
#ifdef riscos
#ifndef TARGET_WHEN_DEBUGGING
#define TARGET_WHEN_DEBUGGING 3
#else
#define TARGET_WHEN_DEBUGGING 1
#define TARGET_WHEN_DEBUGGING 1
#endif
#endif
#define OVERRIDE_OPTIONS \
#define OVERRIDE_OPTIONS \
{ \
{ \
if (write_symbols != NO_DEBUG
)
\
if (write_symbols != NO_DEBUG
&& flag_omit_frame_pointer)
\
target_flags |= TARGET_WHEN_DEBUGGING;
\
warning ("-g without a frame pointer may not give sensible debugging");
\
else
if (TARGET_POKE_FUNCTION_NAME) \
if (TARGET_POKE_FUNCTION_NAME) \
target_flags |= 1; \
target_flags |= 1; \
if (TARGET_FPE) \
if (TARGET_FPE) \
flag_schedule_insns = flag_schedule_insns_after_reload = 0; \
flag_schedule_insns = flag_schedule_insns_after_reload = 0; \
arm_cpu = TARGET_6 ? PROCESSOR_ARM6: PROCESSOR_ARM2; \
}
}
/* Omitting the frame pointer is a very good idea on the ARM, especially if
/* Omitting the frame pointer is a very good idea on the ARM, especially if
not TARGET_APCS, in which case all that pushing on function entry isn't
not TARGET_APCS, in which case all that pushing on function entry isn't
mandatory anymore. */
mandatory anymore.
Forcing loads to be explicit also allows cse to work better */
#define OPTIMIZATION_OPTIONS(OPTIMIZE) \
#define OPTIMIZATION_OPTIONS(OPTIMIZE) \
{ \
{ \
if (OPTIMIZE) \
if (OPTIMIZE) \
flag_omit_frame_pointer = 1; \
{ \
flag_force_mem = 1; \
flag_omit_frame_pointer = 1; \
} \
}
}
/* Target machine storage Layout. */
/* Target machine storage Layout. */
/* Define this macro if it is advisable to hold scalars in registers
in a wider mode than that declared by the program. In such cases,
the value is constrained to be within the bounds of the declared
type, but kept valid in the wider mode. The signedness of the
extension may differ from that of the type. */
/* It is far faster to zero extend chars than to sign extend them */
#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \
if (GET_MODE_CLASS (MODE) == MODE_INT \
&& GET_MODE_SIZE (MODE) < 4) \
{ \
if (MODE == QImode) \
UNSIGNEDP = 1; \
(MODE) = SImode; \
}
/* Define for XFmode extended real floating point support.
This will automatically cause REAL_ARITHMETIC to be defined. */
/* For the ARM:
I think I have added all the code to make this work. Unfortunately,
early releases of the floating point emulation code on RISCiX used a
different format for extended precision numbers. On my RISCiX box there
is a bug somewhere which causes the machine to lock up when running enquire
with long doubles. There is the additional aspect that Norcroft C
treats long doubles as doubles and we ought to remain compatible.
Perhaps someone with an FPA coprocessor and not running RISCiX would like
to try this someday. */
/* #define LONG_DOUBLE_TYPE_SIZE 96 */
/* Disable XFmode patterns in md file */
#define ENABLE_XF_PATTERNS 0
/* Define if you don't want extended real, but do want to use the
software floating point emulator for REAL_ARITHMETIC and
decimal <-> binary conversion. */
/* See comment above */
#define REAL_ARITHMETIC
/* Define this if most significant bit is lowest numbered
/* Define this if most significant bit is lowest numbered
in instructions that operate on numbered bit-fields. */
in instructions that operate on numbered bit-fields. */
#define BITS_BIG_ENDIAN 0
#define BITS_BIG_ENDIAN 0
...
@@ -145,11 +241,20 @@ extern int target_flags;
...
@@ -145,11 +241,20 @@ extern int target_flags;
#define BIGGEST_ALIGNMENT 32
#define BIGGEST_ALIGNMENT 32
/* Make strings word-aligned so strcpy from constants will be faster. */
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
(TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
/* Every structures size must be a multiple of 32 bits. */
/* Every structures size must be a multiple of 32 bits. */
#define STRUCTURE_SIZE_BOUNDARY 32
#define STRUCTURE_SIZE_BOUNDARY 32
/* Non-zero if move instructions will actually fail to work
when given unaligned data. */
#define STRICT_ALIGNMENT 1
#define STRICT_ALIGNMENT 1
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
/* Define number of bits in most basic integer type.
/* Define number of bits in most basic integer type.
(If undefined, default is BITS_PER_WORD). */
(If undefined, default is BITS_PER_WORD). */
/* #define INT_TYPE_SIZE */
/* #define INT_TYPE_SIZE */
...
@@ -177,10 +282,43 @@ extern int target_flags;
...
@@ -177,10 +282,43 @@ extern int target_flags;
f4-f7 S floating point variable
f4-f7 S floating point variable
cc This is NOT a real register, but is used internally
to represent things that use or set the condition
codes.
sfp This isn't either. It is used during rtl generation
since the offset between the frame pointer and the
auto's isn't known until after register allocation.
afp Nor this, we only need this because of non-local
goto. Without it fp appears to be used and the
elimination code won't get rid of sfp. It tracks
fp exactly at all times.
*: See CONDITIONAL_REGISTER_USAGE */
*: See CONDITIONAL_REGISTER_USAGE */
/* The number of hard registers is 16 ARM + 8 FPU. */
/* The stack backtrace structure is as follows:
#define FIRST_PSEUDO_REGISTER 24
fp points to here: | save code pointer | [fp]
| return link value | [fp, #-4]
| return sp value | [fp, #-8]
| return fp value | [fp, #-12]
[| saved r10 value |]
[| saved r9 value |]
[| saved r8 value |]
[| saved r7 value |]
[| saved r6 value |]
[| saved r5 value |]
[| saved r4 value |]
[| saved r3 value |]
[| saved r2 value |]
[| saved r1 value |]
[| saved r0 value |]
[| saved f7 value |] three words
[| saved f6 value |] three words
[| saved f5 value |] three words
[| saved f4 value |] three words
r0-r3 are not normally saved in a C function. */
/* The number of hard registers is 16 ARM + 8 FPU + 1 CC + 1 SFP. */
#define FIRST_PSEUDO_REGISTER 27
/* 1 for registers that have pervasive standard uses
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */
and are not available for the register allocator. */
...
@@ -188,7 +326,8 @@ extern int target_flags;
...
@@ -188,7 +326,8 @@ extern int target_flags;
{ \
{ \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,1,1,0,1,0,1, \
0,0,1,1,0,1,0,1, \
0,0,0,0,0,0,0,0 \
0,0,0,0,0,0,0,0, \
1,1,1 \
}
}
/* 1 for registers not available across function calls.
/* 1 for registers not available across function calls.
...
@@ -196,12 +335,15 @@ extern int target_flags;
...
@@ -196,12 +335,15 @@ extern int target_flags;
registers that can be used without being saved.
registers that can be used without being saved.
The latter must include the registers where values are returned
The latter must include the registers where values are returned
and the register where structure-value addresses are passed.
and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you like. */
Aside from that, you can include as many other registers as you like.
The CC is not preserved over function calls on the ARM 6, so it is
easier to assume this for all. SFP is preserved, since FP is. */
#define CALL_USED_REGISTERS \
#define CALL_USED_REGISTERS \
{ \
{ \
1,1,1,1,0,0,0,0, \
1,1,1,1,0,0,0,0, \
0,0,1,1,1,1,1,1, \
0,0,1,1,1,1,1,1, \
1,1,1,1,0,0,0,0 \
1,1,1,1,0,0,0,0, \
1,1,1 \
}
}
/* If doing stupid life analysis, avoid a bug causing a return value r0 to be
/* If doing stupid life analysis, avoid a bug causing a return value r0 to be
...
@@ -221,15 +363,19 @@ extern int target_flags;
...
@@ -221,15 +363,19 @@ extern int target_flags;
On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP
On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP
mode. */
mode. */
#define HARD_REGNO_NREGS(REGNO, MODE) \
#define HARD_REGNO_NREGS(REGNO, MODE) \
((REGNO) >= 16 ? 1 \
(((REGNO) >= 16 && REGNO != FRAME_POINTER_REGNUM \
&& (REGNO) != ARG_POINTER_REGNUM) ? 1 \
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
This is TRUE for ARM regs since they can hold anything, and TRUE for FPU
This is TRUE for ARM regs since they can hold anything, and TRUE for FPU
regs holding FP. */
regs holding FP. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
((REGNO) < 16 || GET_MODE_CLASS (MODE) == MODE_FLOAT)
((GET_MODE_CLASS (MODE) == MODE_CC) ? (REGNO == CC_REGNUM) : \
((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM \
|| GET_MODE_CLASS (MODE) == MODE_FLOAT))
/* Value is 1 if it is a good idea to tie two pseudo registers
/* Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2.
when one has mode MODE1 and one has mode MODE2.
...
@@ -249,15 +395,24 @@ extern int target_flags;
...
@@ -249,15 +395,24 @@ extern int target_flags;
#define STACK_POINTER_REGNUM 13
#define STACK_POINTER_REGNUM 13
/* Base register for access to local variables of the function. */
/* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM 9
#define FRAME_POINTER_REGNUM 25
/* Define this to be where the real frame pointer is if it is not possible to
work out the offset between the frame pointer and the automatic variables
until after register allocation has taken place. FRAME_POINTER_REGNUM
should point to a special register that we will make sure is eliminated. */
#define HARD_FRAME_POINTER_REGNUM 11
/* Value should be nonzero if functions must have frame pointers.
/* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms may be accessed
Zero means the frame pointer need not be set up (and parms may be accessed
via the stack pointer) in functions that seem suitable. */
via the stack pointer) in functions that seem suitable.
#define FRAME_POINTER_REQUIRED 0
If we have to have a frame pointer we might as well make use of it.
APCS says that the frame pointer does not need to be pushed in leaf
functions. */
#define FRAME_POINTER_REQUIRED (TARGET_APCS && !leaf_function_p ())
/* Base register for access to arguments of the function. */
/* Base register for access to arguments of the function. */
#define ARG_POINTER_REGNUM
11
#define ARG_POINTER_REGNUM
26
/* 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
...
@@ -268,14 +423,22 @@ extern int target_flags;
...
@@ -268,14 +423,22 @@ extern int target_flags;
is passed to a function. */
is passed to a function. */
#define STRUCT_VALUE_REGNUM 0
#define STRUCT_VALUE_REGNUM 0
/* Internal, so that we don't need to refer to a raw number */
#define CC_REGNUM 24
/* The order in which register should be allocated. It is good to use ip
/* The order in which register should be allocated. It is good to use ip
since no saving is required (though calls clobber it). It is quite good to
since no saving is required (though calls clobber it) and it never contains
use lr since other calls may clobber it anyway. */
function parameters. It is quite good to use lr since other calls may
clobber it anyway. Allocate r0 through r3 in reverse order since r3 is
least likely to contain a function parameter; in addition results are
returned in r0.
*/
#define REG_ALLOC_ORDER \
#define REG_ALLOC_ORDER \
{ \
{ \
0, 1, 2, 3
, 12, 14, 4, 5, \
3, 2, 1, 0
, 12, 14, 4, 5, \
6, 7, 8, 10, 9, 11, 13, 15, \
6, 7, 8, 10, 9, 11, 13, 15, \
16, 17, 18, 19, 20, 21, 22, 23 \
16, 17, 18, 19, 20, 21, 22, 23, \
24, 25 \
}
}
/* Register and constant classes. */
/* Register and constant classes. */
...
@@ -306,18 +469,21 @@ enum reg_class
...
@@ -306,18 +469,21 @@ enum reg_class
of length N_REG_CLASSES. */
of length N_REG_CLASSES. */
#define REG_CLASS_CONTENTS \
#define REG_CLASS_CONTENTS \
{ \
{ \
0x000000
,
/* NO_REGS */
\
0x000000
0,
/* NO_REGS */
\
0x
FF0000,
/* FPU_REGS */
\
0x
0FF0000,
/* FPU_REGS */
\
0x
00FFFF,
/* GENERAL_REGS */
\
0x
200FFFF,
/* GENERAL_REGS */
\
0x
FFFFFF
/* ALL_REGS */
\
0x
2FFFFFF
/* ALL_REGS */
\
}
}
/* The same information, inverted:
/* The same information, inverted:
Return the class number of the smallest class containing
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
reg number REGNO. This could be a conditional expression
or could index an array. */
or could index an array. */
#define REGNO_REG_CLASS(REGNO) \
#define REGNO_REG_CLASS(REGNO) \
((REGNO) < 16 ? GENERAL_REGS : FPU_REGS)
(((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM) \
? GENERAL_REGS : (REGNO) == CC_REGNUM \
? NO_REGS : FPU_REGS)
/* The class value for index registers, and the one for base regs. */
/* The class value for index registers, and the one for base regs. */
#define INDEX_REG_CLASS GENERAL_REGS
#define INDEX_REG_CLASS GENERAL_REGS
...
@@ -334,14 +500,30 @@ enum reg_class
...
@@ -334,14 +500,30 @@ enum reg_class
C is the letter, and VALUE is a constant value.
C is the letter, and VALUE is a constant value.
Return 1 if VALUE is in the range specified by C.
Return 1 if VALUE is in the range specified by C.
I: immediate arithmetic operand (i.e. 8 bits shifted as required).
I: immediate arithmetic operand (i.e. 8 bits shifted as required).
J: valid indexing constants. */
J: valid indexing constants.
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
K: as I but also (not (value)) ok.
((C) == 'I' ? const_ok_for_arm (VALUE) : \
L: as I but also (neg (value)) ok.*/
(C) == 'J' ? (abs (VALUE) < 4096) : 0)
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? const_ok_for_arm (VALUE) : \
/* Constant letter 'G' for the FPU immediate constants. */
(C) == 'J' ? ((VALUE) < 4096 && (VALUE) > -4096) : \
#define CONST_DOUBLE_OK_FOR_LETTER_P(X,C) \
(C) == 'K' ? (const_ok_for_arm (VALUE) || const_ok_for_arm (~(VALUE))) : \
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) : 0)
(C) == 'L' ? (const_ok_for_arm (VALUE) || const_ok_for_arm (-(VALUE))) : 0)
/* For the ARM, `Q' means that this is a memory operand that is just
an offset from a register.
`S' means any symbol that has the SYMBOL_REF_FLAG set or a CONSTANT_POOL
address. This means that the symbol is in the text segment and can be
accessed without using a load. */
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'Q' ? GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG \
: (C) == 'S' ? CONSTANT_ADDRESS_P (OP) : 0)
/* Constant letter 'G' for the FPU immediate constants.
'H' means the same constant negated. */
#define CONST_DOUBLE_OK_FOR_LETTER_P(X,C) \
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) \
: (C) == 'H' ? neg_const_double_rtx_ok_for_fpu (X) : 0)
/* Given an rtx X being reloaded into a reg required to be
/* Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use.
in class CLASS, return the class of reg to actually use.
...
@@ -349,6 +531,14 @@ enum reg_class
...
@@ -349,6 +531,14 @@ enum reg_class
in some cases it is preferable to use a more restrictive class. */
in some cases it is preferable to use a more restrictive class. */
#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
/* Return the register class of a scratch register needed to copy IN into
or out of a register in CLASS in MODE. If it can be done directly,
NO_REGS is returned. */
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS,MODE,X) \
(((MODE) == DFmode && (CLASS) == GENERAL_REGS \
&& true_regnum (X) == -1) ? GENERAL_REGS \
: NO_REGS)
/* Return the maximum number of consecutive registers
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS.
needed to represent mode MODE in a register of class CLASS.
ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */
ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */
...
@@ -356,11 +546,11 @@ enum reg_class
...
@@ -356,11 +546,11 @@ enum reg_class
((CLASS) == FPU_REGS ? 1 \
((CLASS) == FPU_REGS ? 1 \
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
/* Moves between FPU_REGS and GENERAL_REGS are two insns. */
/* Moves between FPU_REGS and GENERAL_REGS are two
memory
insns. */
#define REGISTER_MOVE_COST(CLASS1, CLASS2) \
#define REGISTER_MOVE_COST(CLASS1, CLASS2) \
((((CLASS1) == FPU_REGS && (CLASS2) != FPU_REGS) \
((((CLASS1) == FPU_REGS && (CLASS2) != FPU_REGS) \
|| ((CLASS2) == FPU_REGS && (CLASS1) != FPU_REGS)) \
|| ((CLASS2) == FPU_REGS && (CLASS1) != FPU_REGS)) \
?
4
: 2)
?
20
: 2)
/* Stack layout; function entry, exit and calling. */
/* Stack layout; function entry, exit and calling. */
...
@@ -462,7 +652,7 @@ enum reg_class
...
@@ -462,7 +652,7 @@ enum reg_class
For a library call, FNTYPE is 0.
For a library call, FNTYPE is 0.
On the ARM, the offset starts at 0. */
On the ARM, the offset starts at 0. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME) \
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME) \
((CUM) = (((FNTYPE) && aggregate_value_p (TREE_TYPE ((FNTYPE))) ? 4 : 0))
((CUM) = (((FNTYPE) && aggregate_value_p (TREE_TYPE ((FNTYPE)))
)
? 4 : 0))
/* Update the data in CUM to advance over an argument
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
of mode MODE and data type TYPE.
...
@@ -529,15 +719,91 @@ enum reg_class
...
@@ -529,15 +719,91 @@ enum reg_class
/* Determine if the epilogue should be output as RTL.
/* Determine if the epilogue should be output as RTL.
You should override this if you define FUNCTION_EXTRA_EPILOGUE. */
You should override this if you define FUNCTION_EXTRA_EPILOGUE. */
/* #define USE_RETURN_INSN use_return_insn () */
#define USE_RETURN_INSN use_return_insn ()
/* Definitions for register eliminations.
This is an array of structures. Each structure initializes one pair
of eliminable registers. The "from" register number is given first,
followed by "to". Eliminations of the same "from" register are listed
in order of preference.
We have two registers that can be eliminated on the ARM. First, the
arg pointer register can often be eliminated in favor of the stack
pointer register. Secondly, the pseudo frame pointer register can always
be eliminated; it is replaced with either the stack or the real frame
pointer. */
#define ELIMINABLE_REGS \
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
/* Given FROM and TO register numbers, say whether this elimination is allowed.
Frame pointer elimination is automatically handled.
All eliminations are permissible. Note that ARG_POINTER_REGNUM and
HARD_FRAME_POINTER_REGNUM are infact the same thing. If we need a frame
pointer, we must eliminate FRAME_POINTER_REGNUM into
HARD_FRAME_POINTER_REGNUM and not into STACK_POINTER_REGNUM. */
#define CAN_ELIMINATE(FROM, TO) \
(((TO) == STACK_POINTER_REGNUM && frame_pointer_needed) ? 0 : 1)
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \
if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
(OFFSET) = 0; \
else if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM)\
(OFFSET) = (get_frame_size () + 3 & ~3); \
else \
{ \
int regno; \
int offset = 12; \
\
for (regno = 4; regno <= 10; regno++) \
if (regs_ever_live[regno]) \
offset += 4; \
for (regno = 20; regno <=23; regno++) \
if (regs_ever_live[regno]) \
offset += 12; \
if ((FROM) == FRAME_POINTER_REGNUM) \
(OFFSET) = -offset; \
else \
{ \
if (! regs_ever_live[HARD_FRAME_POINTER_REGNUM]) \
offset -= 16; \
if (regs_ever_live[14]) \
offset += 4; \
(OFFSET) = (get_frame_size () + 3 & ~3) + offset; \
} \
} \
}
#if 0
/* Store in the variable DEPTH the initial difference between the frame
/* Store in the variable DEPTH the initial difference between the frame
pointer reg contents and the stack pointer reg contents, as of the start of
pointer reg contents and the stack pointer reg contents, as of the start of
the function body. This depends on the layout of the fixed parts of the
the function body. This depends on the layout of the fixed parts of the
stack frame and on how registers are saved. */
stack frame and on how registers are saved. */
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
{ \
int regno; \
int offset = 12; \
\
for (regno = 0; regno < FRAME_POINTER_REGNUM; regno++) \
if (regs_ever_live[regno]) \
offset += 4; \
for (regno = 20; regno < 24; regno++) \
if (regs_ever_live[regno]) \
offset += 12; \
(DEPTH) = offset + (get_frame_size () + 3 & ~3); \
}
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
(DEPTH) = (get_frame_size () + 3) & ~3;
(DEPTH) = (get_frame_size () + 3) & ~3;
#endif
/* Output assembler code for a block containing the constant parts
/* Output assembler code for a block containing the constant parts
of a trampoline, leaving space for the variable parts.
of a trampoline, leaving space for the variable parts.
...
@@ -600,15 +866,19 @@ enum reg_class
...
@@ -600,15 +866,19 @@ enum reg_class
has been allocated, which happens in local-alloc.c.
has been allocated, which happens in local-alloc.c.
On the ARM, don't allow the pc to be used. */
On the ARM, don't allow the pc to be used. */
#define REGNO_OK_FOR_BASE_P(REGNO) \
#define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < 15 || (unsigned) reg_renumber[(REGNO)] < 15)
((REGNO) < 15 || (REGNO) == FRAME_POINTER_REGNUM \
#define REGNO_OK_FOR_INDEX_P(REGNO) \
|| (REGNO) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[(REGNO)] < 15 \
|| (unsigned) reg_renumber[(REGNO)] == FRAME_POINTER_REGNUM \
|| (unsigned) reg_renumber[(REGNO)] == ARG_POINTER_REGNUM)
#define REGNO_OK_FOR_INDEX_P(REGNO) \
REGNO_OK_FOR_BASE_P(REGNO)
REGNO_OK_FOR_BASE_P(REGNO)
/* Maximum number of registers that can appear in a valid memory address.
/* Maximum number of registers that can appear in a valid memory address.
The addressing mode [ra,rb, <shift> rc] uses the greatest number of
Shifts in addresses can't be by a register. */
registers. */
#define MAX_REGS_PER_ADDRESS
3
#define MAX_REGS_PER_ADDRESS
2
/* Recognize any constant value that is a valid address. */
/* Recognize any constant value that is a valid address. */
/* XXX We can address any constant, eventually... */
/* XXX We can address any constant, eventually... */
...
@@ -620,8 +890,9 @@ enum reg_class
...
@@ -620,8 +890,9 @@ enum reg_class
|| GET_CODE(X) == CONST )
|| GET_CODE(X) == CONST )
#endif
#endif
#define CONSTANT_ADDRESS_P(X) \
#define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X))
(GET_CODE (X) == SYMBOL_REF \
&& (CONSTANT_POOL_ADDRESS_P (X) || SYMBOL_REF_FLAG (X)))
/* Nonzero if the constant value X is a legitimate general operand.
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
...
@@ -632,10 +903,20 @@ enum reg_class
...
@@ -632,10 +903,20 @@ enum reg_class
#define LEGITIMATE_CONSTANT_P(X) \
#define LEGITIMATE_CONSTANT_P(X) \
(GET_CODE (X) == CONST_INT \
(GET_CODE (X) == CONST_INT \
|| (GET_CODE (X) == CONST_DOUBLE \
|| (GET_CODE (X) == CONST_DOUBLE \
&& const_double_rtx_ok_for_fpu (X)))
&& (const_double_rtx_ok_for_fpu (X) \
#if 0
|| neg_const_double_rtx_ok_for_fpu (X))) \
|| GET_CODE(X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P(X))
|| CONSTANT_ADDRESS_P (X))
#endif
/* Symbols in the text segment can be accessed without indirecting via the
constant pool; it may take an extra binary operation, but this is still
faster than indirecting via memory. */
#define ENCODE_SECTION_INFO(decl) \
{ \
if (TREE_CONSTANT (decl) \
&& (!flag_writable_strings || TREE_CODE (decl) != STRING_CST)) \
SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (decl), 0)) = 1; \
}
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class.
and check its validity for a certain class.
...
@@ -644,23 +925,36 @@ enum reg_class
...
@@ -644,23 +925,36 @@ enum reg_class
them unless they have been allocated suitable hard regs.
them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used. */
The symbol REG_OK_STRICT causes the latter definition to be used. */
#ifndef REG_OK_STRICT
#ifndef REG_OK_STRICT
/* Nonzero if X is a hard reg that can be used as a base reg
/* Nonzero if X is a hard reg that can be used as a base reg
or if it is a pseudo reg. */
or if it is a pseudo reg. */
#define REG_OK_FOR_BASE_P(X) \
#define REG_OK_FOR_BASE_P(X) \
(REGNO (X) < 16 || REGNO (X) >= 24)
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
/* Nonzero if X is a hard reg that can be used as an index
/* Nonzero if X is a hard reg that can be used as an index
or if it is a pseudo reg. */
or if it is a pseudo reg. */
#define REG_OK_FOR_INDEX_P(X) \
#define REG_OK_FOR_INDEX_P(X) \
REG_OK_FOR_BASE_P(X)
REG_OK_FOR_BASE_P(X)
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER)
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
#else
#else
/* Nonzero if X is a hard reg that can be used as a base reg. */
/* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
/* Nonzero if X is a hard reg that can be used as an index. */
/* Nonzero if X is a hard reg that can be used as an index. */
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || (unsigned) reg_renumber[REGNO (X)] < 16)
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || (unsigned) reg_renumber[REGNO (X)] < 16 \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == FRAME_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == ARG_POINTER_REGNUM)
#endif
#endif
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
...
@@ -678,18 +972,24 @@ enum reg_class
...
@@ -678,18 +972,24 @@ enum reg_class
/* A C statement (sans semicolon) to jump to LABEL for legitimate index RTXs
/* A C statement (sans semicolon) to jump to LABEL for legitimate index RTXs
used by the macro GO_IF_LEGITIMATE_ADDRESS. Floating point indices can
used by the macro GO_IF_LEGITIMATE_ADDRESS. Floating point indices can
only be small constants. */
only be small constants. */
#define GO_IF_LEGITIMATE_INDEX(MODE, BASE_REGNO, INDEX, LABEL)
\
#define GO_IF_LEGITIMATE_INDEX(MODE, BASE_REGNO, INDEX, LABEL)
\
do \
do \
{ \
{ \
int range; \
int range; \
int code = GET_CODE (INDEX); \
\
\
if (GET_MODE_CLASS (MODE) == MODE_FLOAT) \
if (GET_MODE_CLASS (MODE) == MODE_FLOAT) \
range = 1024; \
{ \
if (code == CONST_INT && INTVAL (INDEX) < 1024 \
&& INTVAL (INDEX) > -1024 \
&& (INTVAL (INDEX) & 3) == 0) \
goto LABEL; \
} \
else \
else \
{ \
{ \
if (INDEX_REGISTER_RTX_P (INDEX)
)
\
if (INDEX_REGISTER_RTX_P (INDEX)
&& GET_MODE_SIZE (MODE) <= 4)
\
goto LABEL; \
goto LABEL; \
if (GET_MODE_SIZE (MODE) <= 4 &&
GET_CODE (INDEX) == MULT)
\
if (GET_MODE_SIZE (MODE) <= 4 &&
code == MULT)
\
{ \
{ \
rtx xiop0 = XEXP (INDEX, 0); \
rtx xiop0 = XEXP (INDEX, 0); \
rtx xiop1 = XEXP (INDEX, 1); \
rtx xiop1 = XEXP (INDEX, 1); \
...
@@ -700,20 +1000,29 @@ do \
...
@@ -700,20 +1000,29 @@ do \
&& power_of_two_operand (xiop0, SImode)) \
&& power_of_two_operand (xiop0, SImode)) \
goto LABEL; \
goto LABEL; \
} \
} \
range = 4096; \
if (GET_MODE_SIZE (MODE) <= 4 \
&& (code == LSHIFTRT || code == ASHIFTRT || code == LSHIFT \
|| code == ASHIFT || code == ROTATERT)) \
{ \
rtx op = XEXP (INDEX, 1); \
if (INDEX_REGISTER_RTX_P (XEXP (INDEX, 0)) \
&& GET_CODE (op) == CONST_INT && INTVAL (op) > 0 \
&& INTVAL (op) <= 31) \
goto LABEL; \
} \
range = (MODE) == HImode ? 4095 : 4096; \
if (code == CONST_INT && INTVAL (INDEX) < range \
&& INTVAL (INDEX) > -range) \
goto LABEL; \
} \
} \
\
if (GET_CODE (INDEX) == CONST_INT && INTVAL (INDEX) < range \
&& INTVAL (INDEX) > -range) \
goto LABEL; \
} while (0)
} while (0)
/* Jump to LABEL if X is a valid address RTX. This must also take
/* Jump to LABEL if X is a valid address RTX. This must also take
REG_OK_STRICT into account when deciding about valid registers, but it uses
REG_OK_STRICT into account when deciding about valid registers, but it uses
the above macros so we are in luck. Allow REG, REG+REG, REG+INDEX,
the above macros so we are in luck. Allow REG, REG+REG, REG+INDEX,
INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool.
INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool.
Allow REG-only and AUTINC-REG if handling TImode
. Other symbol refs must
Allow REG-only and AUTINC-REG if handling TImode
or HImode. Other symbol
be forced though a static cell to ensure addressability. */
refs must
be forced though a static cell to ensure addressability. */
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \
{ \
{ \
if (BASE_REGISTER_RTX_P (X)) \
if (BASE_REGISTER_RTX_P (X)) \
...
@@ -834,10 +1143,9 @@ do \
...
@@ -834,10 +1143,9 @@ do \
/* This is the kind of divide that is easiest to do in the general case. */
/* This is the kind of divide that is easiest to do in the general case. */
#define EASY_DIV_EXPR TRUNC_DIV_EXPR
#define EASY_DIV_EXPR TRUNC_DIV_EXPR
/* 'char' is signed by default on RISCiX, unsigned on RISCOS. */
/* signed 'char' is most compatible, but RISC OS wants it unsigned.
#ifdef riscos
unsigned is probably best, but may break some code. */
#define DEFAULT_SIGNED_CHAR 0
#ifndef DEFAULT_SIGNED_CHAR
#else
#define DEFAULT_SIGNED_CHAR 1
#define DEFAULT_SIGNED_CHAR 1
#endif
#endif
...
@@ -848,6 +1156,17 @@ do \
...
@@ -848,6 +1156,17 @@ do \
in one reasonably fast instruction. */
in one reasonably fast instruction. */
#define MOVE_MAX 4
#define MOVE_MAX 4
/* Define if operations between registers always perform the operation
on the full register even if a narrower mode is specified. */
#define WORD_REGISTER_OPERATIONS
/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD
will either zero-extend or sign-extend. The value of this macro should
be the code that says which one of the two operations is implicitly
done, NIL if none. */
#define LOAD_EXTEND_OP(MODE) \
((MODE) == QImode ? ZERO_EXTEND : NIL)
/* Define this if zero-extension is slow (more than one real instruction).
/* Define this if zero-extension is slow (more than one real instruction).
On the ARM, it is more than one instruction only if not fetching from
On the ARM, it is more than one instruction only if not fetching from
memory. */
memory. */
...
@@ -861,10 +1180,11 @@ do \
...
@@ -861,10 +1180,11 @@ do \
that the native compiler puts too large (> 32) immediate shift counts
that the native compiler puts too large (> 32) immediate shift counts
into a register and shifts by the register, letting the ARM decide what
into a register and shifts by the register, letting the ARM decide what
to do instead of doing that itself. */
to do instead of doing that itself. */
#define SHIFT_COUNT_TRUNCATED 1
/* This is all wrong. Defining SHIFT_COUNT_TRUNCATED tells combine that
code like (X << (Y % 32)) for register X, Y is equivalent to (X << Y).
/* We have the vprintf function. */
On the arm, Y in a register is used modulo 256 for the shift. Only for
#define HAVE_VPRINTF 1
rotates is modulo 32 used. */
/* #define SHIFT_COUNT_TRUNCATED 1 */
/* XX This is not true, is it? */
/* XX This is not true, is it? */
/* All integers have the same format so truncation is easy. */
/* All integers have the same format so truncation is easy. */
...
@@ -886,77 +1206,194 @@ do \
...
@@ -886,77 +1206,194 @@ do \
/* The relative costs of various types of constants. Note that cse.c defines
/* The relative costs of various types of constants. Note that cse.c defines
REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */
REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */
#define CONST_COSTS(RTX, CODE, OUTER_CODE) \
#define CONST_COSTS(RTX, CODE, OUTER_CODE) \
case CONST_INT: \
case CONST_INT: \
if (const_ok_for_arm (INTVAL (RTX))) \
if (const_ok_for_arm (INTVAL (RTX))) \
return (2); \
return (OUTER_CODE) == SET ? 2 : -1; \
else \
else if (OUTER_CODE == AND \
return (5); \
&& const_ok_for_arm (~INTVAL (RTX))) \
\
return -1; \
case CONST: \
else if ((OUTER_CODE == COMPARE \
case LABEL_REF: \
|| OUTER_CODE == PLUS || OUTER_CODE == MINUS) \
case SYMBOL_REF: \
&& const_ok_for_arm (-INTVAL (RTX))) \
return (6); \
return -1; \
\
else \
case CONST_DOUBLE: \
return 5; \
if (const_double_rtx_ok_for_fpu (RTX)) \
case CONST: \
return(2); \
case LABEL_REF: \
else \
case SYMBOL_REF: \
return(7);
return 6; \
case CONST_DOUBLE: \
if (const_double_rtx_ok_for_fpu (RTX)) \
return (OUTER_CODE) == SET ? 2 : -1; \
else if (((OUTER_CODE) == COMPARE || (OUTER_CODE) == PLUS) \
&& neg_const_double_rtx_ok_for_fpu (RTX)) \
return -1; \
return(7);
#define RTX_COSTS(X,CODE,OUTER_CODE) \
case MEM: \
{ \
int num_words = (GET_MODE_SIZE (GET_MODE (X)) > UNITS_PER_WORD) ? 2 : 1;\
return (COSTS_N_INSNS (10*num_words)); \
} \
case MULT: \
if (GET_CODE (XEXP (X, 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (X, 1))) >= 0) \
return rtx_cost (XEXP (X, 0), GET_CODE (X))+1; \
return COSTS_N_INSNS (9); \
case LSHIFT: \
case ASHIFT: \
case LSHIFTRT: \
case ASHIFTRT: \
if (GET_CODE (XEXP (X, 1)) == CONST_INT) \
return rtx_cost (XEXP (X, 0), GET_CODE (X))+1; \
break; \
case MINUS: \
{ \
enum rtx_code code = GET_CODE (XEXP (X, 1)); \
if (code == MULT) \
{ \
if (GET_CODE (XEXP (XEXP (X, 1), 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (XEXP (X, 0), 1))) >= 0) \
return COSTS_N_INSNS (1); \
break; \
} \
else if (code == ASHIFT || code == LSHIFT || code == ASHIFTRT \
|| code == LSHIFTRT) \
return COSTS_N_INSNS (1); \
}
/* fall through */
\
case PLUS: \
case IOR: \
case XOR: \
case AND: \
{ \
enum rtx_code code = GET_CODE (XEXP (X, 0)); \
if (code == MULT) \
{ \
if (GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (XEXP (X, 0), 1))) >= 0) \
return COSTS_N_INSNS (1); \
if (GET_CODE (X) == PLUS) \
return COSTS_N_INSNS (12); \
break; \
} \
else if (code == ASHIFT || code == LSHIFT || code == ASHIFTRT \
|| code == LSHIFTRT) \
return COSTS_N_INSNS (1); \
break; \
} \
case NOT: \
return rtx_cost (XEXP (X, 0), GET_CODE (XEXP (X, 0))); \
case IF_THEN_ELSE: \
{ \
if (GET_CODE (XEXP(X,1)) == PC || GET_CODE (XEXP(X,2)) == PC) \
return COSTS_N_INSNS (4); \
return COSTS_N_INSNS (1); \
} \
case SIGN_EXTEND: \
return COSTS_N_INSNS (2); \
case ZERO_EXTEND: \
if (GET_MODE (XEXP (X, 0)) == QImode) \
{ \
if (GET_CODE (XEXP (X, 0)) == MEM) \
return COSTS_N_INSNS (10); \
return COSTS_N_INSNS (1); \
} \
break; \
case COMPARE: \
if (GET_CODE (XEXP (X, 1)) == REG) \
return 4; \
case SMIN: \
case SMAX: \
case UMIN: \
case UMAX: \
return COSTS_N_INSNS (3); \
case ABS: \
if (GET_MODE (X) == SImode) \
return COSTS_N_INSNS (2); \
return COSTS_N_INSNS (1);
/* Moves to and from memory are quite expensive */
#define MEMORY_MOVE_COST(MODE) 10
/* All address computations that can be done are free */
#define ADDRESS_COST(x) 2
/* Try to generate sequences that don't involve branches, we can then use
conditional instructions */
#define BRANCH_COST 4
/* Condition code information. */
/* Condition code information. */
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
/* Store in cc_status the expressions
return the mode to be used for the comparison.
that the condition codes will describe
CCFPEmode should be used with floating inequalites,
after execution of an instruction whose pattern is EXP.
CCFPmode should be used with floating equalities.
Do not alter them if the instruction would not alter the cc's. */
CC_NOOVmode should be used with SImode integer equalites
CCmode should be used otherwise. */
/* On the ARM nothing sets the condition code implicitly---apart from DImode
operations excluding moves---but we have to watch for registers in the
#define EXTRA_CC_MODES CC_NOOVmode, CCFPmode, CCFPEmode
condition code value being clobbered. This clobbering includes (alas)
function calls. XXX They could just be considered to clobber regs 0-3 and
#define EXTRA_CC_NAMES "CC_NOOV", "CCFP", "CCFPE"
10-15 with extra work. */
#define NOTICE_UPDATE_CC(EXP, INSN) \
#define SELECT_CC_MODE(OP,X,Y) \
{ \
(GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \
if (GET_MODE (EXP) == DImode \
? ((OP == EQ || OP == NE) ? CCFPmode : CCFPEmode) \
&& GET_CODE (EXP) == SET \
: ((GET_MODE (X) == SImode) \
&& GET_CODE (SET_SRC (EXP)) != REG \
&& ((OP) == EQ || (OP) == NE) \
&& GET_CODE (SET_SRC (EXP)) != MEM \
&& (GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \
&& GET_CODE (SET_SRC (EXP)) != CONST_INT) \
|| GET_CODE (X) == AND || GET_CODE (X) == IOR \
CC_STATUS_INIT; \
|| GET_CODE (X) == XOR || GET_CODE (X) == MULT \
else if (GET_CODE (EXP) == SET) \
|| GET_CODE (X) == NOT || GET_CODE (X) == NEG \
{ \
|| GET_CODE (X) == LSHIFT || GET_CODE (X) == LSHIFTRT \
rtx dest = SET_DEST (EXP); \
|| GET_CODE (X) == ASHIFT || GET_CODE (X) == ASHIFTRT \
if (dest == cc0_rtx) \
|| GET_CODE (X) == ROTATERT || GET_CODE (X) == ZERO_EXTRACT) \
{ \
? CC_NOOVmode \
cc_status.flags = 0; \
: GET_MODE (X) == QImode ? CC_NOOVmode : CCmode))
cc_status.value1 = SET_DEST (EXP); \
cc_status.value2 = SET_SRC (EXP); \
#define STORE_FLAG_VALUE 1
} \
if (BASE_REGISTER_RTX_P (dest)) \
/* Define the information needed to generate branch insns. This is
{ \
stored from the compare operation. Note that we can't use "rtx" here
if (cc_status.value1 \
since it hasn't been defined! */
&& reg_overlap_mentioned_p (dest, cc_status.value1)) \
cc_status.value1 = 0; \
extern
struct
rtx_def
*
arm_compare_op0
,
*
arm_compare_op1
;
if (cc_status.value2 \
extern
int
arm_compare_fp
;
&& reg_overlap_mentioned_p (dest, cc_status.value2)) \
cc_status.value2 = 0; \
/* Define the codes that are matched by predicates in arm.c */
} \
#define PREDICATE_CODES \
} \
{"s_register_operand", {SUBREG, REG}}, \
else if (GET_CODE (INSN) != JUMP_INSN && GET_CODE (EXP) == PARALLEL) \
{"arm_add_operand", {SUBREG, REG, CONST_INT}}, \
{ \
{"fpu_add_operand", {SUBREG, REG, CONST_DOUBLE}}, \
CC_STATUS_INIT; \
{"arm_rhs_operand", {SUBREG, REG, CONST_INT}}, \
} \
{"fpu_rhs_operand", {SUBREG, REG, CONST_DOUBLE}}, \
}
{"arm_not_operand", {SUBREG, REG, CONST_INT}}, \
{"shiftable_operator", {PLUS, MINUS, AND, IOR, XOR}}, \
{"minmax_operator", {SMIN, SMAX, UMIN, UMAX}}, \
{"shift_operator", {ASHIFT, LSHIFT, ASHIFTRT, LSHIFTRT, MULT}}, \
{"di_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE, MEM}}, \
{"load_multiple_operation", {PARALLEL}}, \
{"store_multiple_operation", {PARALLEL}}, \
{"equality_operator", {EQ, NE}}, \
{"arm_rhsm_operand", {SUBREG, REG, CONST_INT, MEM}}, \
{"const_shift_operand", {CONST_INT}}, \
{"index_operand", {SUBREG, REG, CONST_INT}}, \
{"cc_register", {REG}},
/* Assembler output control */
/* Assembler output control */
#ifndef ARM_OS_NAME
#define ARM_OS_NAME "(generic)"
#endif
/* The text to go at the start of the assembler file */
/* The text to go at the start of the assembler file */
#define ASM_FILE_START(STREAM) \
#define ASM_FILE_START(STREAM) \
{ \
{ \
extern char *version_string; \
extern char *version_string; \
\
\
fprintf (STREAM,"@ Generated by gcc %s for ARM/RISCiX\n", version_string); \
fprintf (STREAM,"@ Generated by gcc %s for ARM/%s\n", version_string, \
ARM_OS_NAME); \
fprintf (STREAM,"rfp\t.req\tr9\n"); \
fprintf (STREAM,"rfp\t.req\tr9\n"); \
fprintf (STREAM,"fp\t.req\tr11\n"); \
fprintf (STREAM,"fp\t.req\tr11\n"); \
fprintf (STREAM,"ip\t.req\tr12\n"); \
fprintf (STREAM,"ip\t.req\tr12\n"); \
...
@@ -980,18 +1417,35 @@ do \
...
@@ -980,18 +1417,35 @@ do \
{ \
{ \
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
"r8","rfp", "sl", "fp", "ip", "sp", "lr", "pc", \
"r8","rfp", "sl", "fp", "ip", "sp", "lr", "pc", \
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" \
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \
"cc", "sfp", "afp" \
}
}
/* Arm Assembler barfs on dollars */
#define DOLLARS_IN_IDENTIFIERS 0
#define NO_DOLLAR_IN_LABEL
/* DBX register number for a given compiler register number */
/* DBX register number for a given compiler register number */
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
/* Generate DBX debugging information. */
/* Generate DBX debugging information. riscix.h will undefine this because
the native assembler does not support stabs. */
#define DBX_DEBUGGING_INFO 1
#define DBX_DEBUGGING_INFO 1
/* Acorn dbx moans about continuation chars, so don't use any. */
/* Acorn dbx moans about continuation chars, so don't use any. */
#define DBX_CONTIN_LENGTH 0
#define DBX_CONTIN_LENGTH 0
/* Output a source filename for the debugger. RISCiX dbx insists that the
``desc'' field is set to compiler version number >= 315 (sic). */
#define DBX_OUTPUT_MAIN_SOURCE_FILENAME(STREAM,NAME) \
do { \
fprintf (STREAM, ".stabs \"%s\",%d,0,315,%s\n", (NAME), N_SO, \
<ext_label_name[1]); \
text_section (); \
ASM_OUTPUT_INTERNAL_LABEL (STREAM, "Ltext", 0); \
} while (0)
/* Output a label definition. */
/* Output a label definition. */
#define ASM_OUTPUT_LABEL(STREAM,NAME) \
#define ASM_OUTPUT_LABEL(STREAM,NAME) \
arm_asm_output_label ((STREAM), (NAME))
arm_asm_output_label ((STREAM), (NAME))
...
@@ -1021,11 +1475,14 @@ do \
...
@@ -1021,11 +1475,14 @@ do \
char *s = (char *) alloca (11 + strlen (PREFIX)); \
char *s = (char *) alloca (11 + strlen (PREFIX)); \
extern int arm_target_label, arm_ccfsm_state; \
extern int arm_target_label, arm_ccfsm_state; \
\
\
if (arm_ccfsm_state == 3 && arm_target_label == (NUM)) \
if (arm_ccfsm_state == 3 && arm_target_label == (NUM) \
arm_ccfsm_state = 0; \
&& !strcmp (PREFIX, "L")) \
strcpy (s, "*"); \
{ \
sprintf (&s[strlen (s)], "%s%d", (PREFIX), (NUM)); \
arm_ccfsm_state = 0; \
arm_asm_output_label (STREAM, s); \
} \
strcpy (s, "*"); \
sprintf (&s[strlen (s)], "%s%d", (PREFIX), (NUM)); \
arm_asm_output_label (STREAM, s); \
} while (0)
} while (0)
/* Nothing special is done about jump tables */
/* Nothing special is done about jump tables */
...
@@ -1057,13 +1514,30 @@ do \
...
@@ -1057,13 +1514,30 @@ do \
, fprintf (STREAM, "\t.word\tL%d\n", VALUE))
, fprintf (STREAM, "\t.word\tL%d\n", VALUE))
/* Output various types of constants. */
/* Output various types of constants. */
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \
#define ASM_OUTPUT_LONG_DOUBLE(STREAM,VALUE) \
(arm_increase_location (sizeof (double)) \
do { long l[3]; \
, fprintf (STREAM, "\t.double\t%20.20f\n", VALUE))
arm_increase_location (12); \
REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \
if (sizeof (int) == sizeof (long)) \
(arm_increase_location (sizeof (float)) \
fprintf (STREAM, "\t.long 0x%x,0x%x,0x%x\n", l[2], l[1], l[0]); \
, fprintf (STREAM, "\t.float\t%20.20f\n", VALUE))
else \
fprintf (STREAM, "\t.long 0x%lx,0x%lx,0x%lx\n", l[2], l[1], l[0]); \
} while (0)
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \
do { char dstr[30]; \
arm_increase_location (8); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.double %s\n", dstr); \
} while (0)
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \
do { char dstr[30]; \
arm_increase_location (4); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.float %s\n", dstr); \
} while (0);
#define ASM_OUTPUT_INT(STREAM, EXP) \
#define ASM_OUTPUT_INT(STREAM, EXP) \
(fprintf (STREAM, "\t.word\t"), \
(fprintf (STREAM, "\t.word\t"), \
...
@@ -1154,7 +1628,9 @@ do \
...
@@ -1154,7 +1628,9 @@ do \
small-distance conditional branches and have ASM_OUTPUT_OPCODE make the
small-distance conditional branches and have ASM_OUTPUT_OPCODE make the
instructions conditional. Suffixes like s (affect flags) and b (bytewise
instructions conditional. Suffixes like s (affect flags) and b (bytewise
load/store) need to stay suffixes, so the possible condition code comes
load/store) need to stay suffixes, so the possible condition code comes
before these suffixes. */
before these suffixes. %d<n> or %D<n> may appear in the opcode if
it can take a condition; a null rtx will cause no condition to be added,
this is what we expect to happen if arm_ccfsm_state is non-zero. */
#define ASM_OUTPUT_OPCODE(STREAM, PTR) \
#define ASM_OUTPUT_OPCODE(STREAM, PTR) \
{ \
{ \
extern int arm_ccfsm_state, arm_current_cc; \
extern int arm_ccfsm_state, arm_current_cc; \
...
@@ -1162,17 +1638,13 @@ do \
...
@@ -1162,17 +1638,13 @@ do \
int i; \
int i; \
\
\
fflush (STREAM);
/* XXX for debugging only. */
\
fflush (STREAM);
/* XXX for debugging only. */
\
if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2) \
if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) \
{ \
fprintf (STREAM, "@ \t"); \
arm_ccfsm_state += 2; \
} \
else if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) \
{ \
{ \
for (i = 0; *(PTR) != ' ' && *(PTR) != '\t' && i < 3; i++, (PTR)++) \
for (i = 0; *(PTR) != ' ' && *(PTR) != '\t' && *(PTR) != '%' && i < 3;\
i++, (PTR)++) \
putc (*(PTR), STREAM); \
putc (*(PTR), STREAM); \
fprintf (STREAM, "%s", arm_condition_codes[arm_current_cc]); \
fprintf (STREAM, "%s", arm_condition_codes[arm_current_cc]); \
for (; *(PTR) != ' ' && *(PTR) != '\t'
; (PTR)++)
\
for (; *(PTR) != ' ' && *(PTR) != '\t'
&& *(PTR) != '%'; (PTR)++)
\
putc (*(PTR), STREAM); \
putc (*(PTR), STREAM); \
} \
} \
}
}
...
@@ -1186,44 +1658,56 @@ do \
...
@@ -1186,44 +1658,56 @@ do \
/* Output an operand of an instruction. If X is a REG and CODE is `M', output
/* Output an operand of an instruction. If X is a REG and CODE is `M', output
a ldm/stm style multi-reg. */
a ldm/stm style multi-reg. */
#define PRINT_OPERAND(STREAM, X, CODE) \
#define PRINT_OPERAND(STREAM, X, CODE) \
{ \
{ \
if ((CODE) == 'R') \
if ((CODE) == 'd') \
fputs (reg_names[REGNO (X) + 1], (STREAM)); \
{ \
else if (GET_CODE (X) == REG) \
if (X) \
{ \
fputs (arm_condition_codes[get_arm_condition_code (X)], \
if ((CODE) != 'M') \
(STREAM)); \
fputs (reg_names[REGNO (X)], (STREAM)); \
} \
else \
else if ((CODE) == 'D') \
fprintf ((STREAM), "{%s-%s}", \
{ \
reg_names[REGNO (X)], \
if (X) \
reg_names[REGNO (X) - 1 \
fputs (arm_condition_codes[get_arm_condition_code (X) ^ 1], \
+ ((GET_MODE_SIZE (GET_MODE (X)) \
(STREAM)); \
+ GET_MODE_SIZE (SImode) - 1) \
} \
/ GET_MODE_SIZE (SImode))]); \
else if ((CODE) == 'R') \
} \
fputs (reg_names[REGNO (X) + 1], (STREAM)); \
else if (GET_CODE (X) == MEM) \
else if (GET_CODE (X) == REG) \
{ \
{ \
extern int output_memory_reference_mode; \
if ((CODE) != 'M') \
output_memory_reference_mode = GET_MODE (X); \
fputs (reg_names[REGNO (X)], (STREAM)); \
output_address (XEXP (X, 0)); \
else \
} \
fprintf ((STREAM), "{%s-%s}", \
else if (GET_CODE(X) == CONST_DOUBLE) \
reg_names[REGNO (X)], \
{ \
reg_names[REGNO (X) - 1 \
union real_extract u; \
+ ((GET_MODE_SIZE (GET_MODE (X)) \
u.i[0] = CONST_DOUBLE_LOW (X); \
+ GET_MODE_SIZE (SImode) - 1) \
u.i[1] = CONST_DOUBLE_HIGH (X); \
/ GET_MODE_SIZE (SImode))]); \
fprintf(STREAM,"#%20.20f",u.d); \
} \
} \
else if (GET_CODE (X) == MEM) \
else if (GET_CODE (X) == NEG) \
{ \
{ \
extern int output_memory_reference_mode; \
fputc ('-', (STREAM)); \
output_memory_reference_mode = GET_MODE (X); \
output_operand ((X), 0); \
output_address (XEXP (X, 0)); \
} \
} \
else \
else if (GET_CODE(X) == CONST_DOUBLE) \
{ \
{ \
fputc('#', STREAM); \
union real_extract u; \
output_addr_const(STREAM, X); \
u.i[0] = CONST_DOUBLE_LOW (X); \
} \
u.i[1] = CONST_DOUBLE_HIGH (X); \
fprintf(STREAM,"#%s", fp_immediate_constant(X)); \
} \
else if (GET_CODE (X) == NEG) \
{ \
fputc ('-', (STREAM)); \
output_operand ((X), 0); \
} \
else \
{ \
fputc('#', STREAM); \
output_addr_const(STREAM, X); \
} \
}
}
/* Output the address of an operand. */
/* Output the address of an operand. */
...
@@ -1275,10 +1759,25 @@ do \
...
@@ -1275,10 +1759,25 @@ do \
} \
} \
else \
else \
abort(); \
abort(); \
fprintf (STREAM, "[%s, %s%s, asl#%d]", base_reg_name, \
fprintf (STREAM, "[%s, %s%s, asl
#%d]", base_reg_name, \
is_minus ? "-" : "", reg_names[REGNO (index)], \
is_minus ? "-" : "", reg_names[REGNO (index)], \
shift); \
shift); \
break; \
break; \
case ASHIFTRT: \
case LSHIFTRT: \
case ASHIFT: \
case LSHIFT: \
case ROTATERT: \
{ \
char *shift_type = shift_instr (GET_CODE (index), \
&XEXP (index, 1)); \
shift = INTVAL (XEXP (index, 1)); \
index = XEXP (index, 0); \
fprintf (STREAM, "[%s, %s%s, %s #%d]", base_reg_name, \
is_minus ? "-" : "", reg_names[REGNO (index)], \
shift_type, shift); \
break; \
} \
\
\
default: \
default: \
abort(); \
abort(); \
...
...
gcc/config/arm/arm.md
View file @
ff9940b0
This source diff could not be displayed because it is too large. You can
view the blob
instead.
gcc/config/arm/xm-arm.h
View file @
ff9940b0
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC.
This file is part of GNU CC.
...
@@ -29,6 +30,28 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
...
@@ -29,6 +30,28 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define HOST_BITS_PER_INT 32
#define HOST_BITS_PER_INT 32
#define HOST_BITS_PER_LONG 32
#define HOST_BITS_PER_LONG 32
/* A code distinguishing the floating point format of the host
machine. There are three defined values: IEEE_FLOAT_FORMAT,
VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */
#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT
/* If not compiled with GNU C, use C alloca. */
#ifndef __GNUC__
#define USE_C_ALLOCA
#endif
/* Define this if the library function putenv is available on your machine */
#define HAVE_PUTENV 1
/* Define this if the library function vprintf is available on your machine */
#define HAVE_VPRINTF 1
/* Define this to be 1 if you know the host compiler supports prototypes, even
if it doesn't define __STDC__, or define it to be 0 if you do not want any
prototypes when compiling GNU CC. */
#define USE_PROTOTYPES 1
/* target machine dependencies.
/* target machine dependencies.
tm.h is a symbolic link to the actual target specific file. */
tm.h is a symbolic link to the actual target specific file. */
#include "tm.h"
#include "tm.h"
...
@@ -38,3 +61,5 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
...
@@ -38,3 +61,5 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define FATAL_EXIT_CODE 33
#define FATAL_EXIT_CODE 33
/* EOF xm-arm.h */
/* EOF xm-arm.h */
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