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
ea5e64f9
Commit
ea5e64f9
authored
Jan 14, 1992
by
Richard Kenner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial revision
From-SVN: r184
parent
4c9c8227
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1286 additions
and
0 deletions
+1286
-0
gcc/config/a29k/a29k.c
+1286
-0
No files found.
gcc/config/a29k/a29k.c
0 → 100644
View file @
ea5e64f9
/* Subroutines used for code generation on AMD Am29000.
Copyright (C) 1987, 1988, 1990, 1991 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@nyu.edu)
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
#include "obstack.h"
#include "tree.h"
#define min(A,B) ((A) < (B) ? (A) : (B))
/* This gives the size in words of the register stack for the current
procedure. */
static
int
a29k_regstack_size
;
/* This points to the last insn of the insn prologue. It is set when
an insn without a filled delay slot is found near the start of the
function. */
static
char
*
a29k_last_prologue_insn
;
/* This points to the first insn that will be in the epilogue. It is null if
no epilogue is required. */
static
char
*
a29k_first_epilogue_insn
;
/* This is nonzero if a a29k_first_epilogue_insn was put in a delay slot. It
indicates that an intermediate label needs to be written. */
static
int
a29k_first_epilogue_insn_used
;
/* Location to hold the name of the current function. We need this prolog to
contain the tag words prior to the declaration. So the name must be stored
away. */
char
*
a29k_function_name
;
/* Mapping of registers to debug register numbers. The only change is
for the frame pointer and the register numbers used for the incoming
arguments. */
int
a29k_debug_reg_map
[
FIRST_PSEUDO_REGISTER
];
/* Save information from a "cmpxx" operation until the branch or scc is
emitted. */
rtx
a29k_compare_op0
,
a29k_compare_op1
;
int
a29k_compare_fp_p
;
/* Gives names for registers. */
extern
char
*
reg_names
[];
/* Returns 1 if OP is a 8-bit constant. */
int
cint_8_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
return
GET_CODE
(
op
)
==
CONST_INT
&&
(
INTVAL
(
op
)
&
0xffffff00
)
==
0
;
}
/* Returns 1 if OP is a 16-bit constant. */
int
cint_16_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
GET_CODE
(
op
)
==
CONST_INT
&&
(
INTVAL
(
op
)
&
0xffff0000
)
==
0
;
}
/* Returns 1 if OP cannot be moved in a single insn. */
int
long_const_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
!
CONSTANT_P
(
op
))
return
0
;
if
(
TARGET_29050
&&
GET_CODE
(
op
)
==
CONST_INT
&&
(
INTVAL
(
op
)
&
0xffff
)
==
0
)
return
0
;
return
(
GET_CODE
(
op
)
!=
CONST_INT
||
((
INTVAL
(
op
)
&
0xffff0000
)
!=
0
&&
(
INTVAL
(
op
)
&
0xffff0000
)
!=
0xffff0000
&&
INTVAL
(
op
)
!=
0x80000000
));
}
/* The following four functions detect constants of 0, 8, 16, and 24 used as
a position in ZERO_EXTRACT operations. They can either be the appropriate
constant integer or a shift (which will be produced by combine). */
static
int
shift_constant_operand
(
op
,
mode
,
val
)
rtx
op
;
enum
machine_mode
mode
;
int
val
;
{
return
((
GET_CODE
(
op
)
==
CONST_INT
&&
INTVAL
(
op
)
==
val
)
||
(
GET_CODE
(
op
)
==
ASHIFT
&&
GET_CODE
(
XEXP
(
op
,
0
))
==
CONST_INT
&&
INTVAL
(
XEXP
(
op
,
0
))
==
val
/
8
&&
GET_CODE
(
XEXP
(
op
,
1
))
==
CONST_INT
&&
INTVAL
(
XEXP
(
op
,
1
))
==
3
));
}
int
const_0_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
shift_constant_operand
(
op
,
mode
,
0
);
}
int
const_8_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
shift_constant_operand
(
op
,
mode
,
8
);
}
int
const_16_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
;
{
return
shift_constant_operand
(
op
,
mode
,
16
);
}
int
const_24_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
;
{
return
shift_constant_operand
(
op
,
mode
,
24
);
}
/* Returns 1 if OP is a floating-point constant of the proper mode. */
int
float_const_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
GET_CODE
(
op
)
==
CONST_DOUBLE
&&
GET_MODE
(
op
)
==
mode
;
}
/* Returns 1 if OP is a floating-point constant of the proper mode or a
general-purpose register. */
int
gen_reg_or_float_constant_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
float_const_operand
(
op
,
mode
)
||
gen_reg_operand
(
op
,
mode
);
}
/* Returns 1 if OP is an integer constant of the proper mode or a
general-purpose register. */
int
gen_reg_or_integer_constant_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
((
GET_MODE
(
op
)
==
VOIDmode
&&
(
GET_CODE
(
op
)
==
CONST_INT
||
GET_CODE
(
op
)
==
CONST_DOUBLE
))
||
gen_reg_operand
(
op
,
mode
));
}
/* Returns 1 if OP is a special machine register. */
int
spec_reg_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
GET_MODE
(
op
)
==
SImode
&&
GET_CODE
(
op
)
==
REG
&&
REGNO
(
op
)
>=
R_BP
&&
REGNO
(
op
)
<=
R_EXO
;
}
/* Returns 1 if OP is an accumulator register. */
int
accum_reg_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
REG
&&
REGNO
(
op
)
>=
R_ACC
(
0
)
&&
REGNO
(
op
)
<=
R_ACC
(
3
));
}
/* Returns 1 if OP is a normal data register. */
int
gen_reg_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
regno
;
if
(
GET_MODE
(
op
)
!=
mode
&&
mode
!=
VOIDmode
)
return
0
;
if
(
GET_CODE
(
op
)
==
REG
)
regno
=
REGNO
(
op
);
else
if
(
GET_CODE
(
op
)
==
SUBREG
&&
GET_CODE
(
SUBREG_REG
(
op
))
==
REG
)
{
regno
=
REGNO
(
SUBREG_REG
(
op
));
if
(
regno
<
FIRST_PSEUDO_REGISTER
)
regno
+=
SUBREG_WORD
(
op
);
}
else
return
0
;
return
regno
>=
FIRST_PSEUDO_REGISTER
||
regno
<
R_BP
;
}
/* Returns 1 if OP is either an 8-bit constant integer or a general register.
If a register, it must be in the proper mode unless MODE is VOIDmode. */
int
srcb_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
GET_CODE
(
op
)
==
CONST_INT
&&
(
mode
==
QImode
||
(
INTVAL
(
op
)
&
0xffffff00
)
==
0
))
return
1
;
if
(
GET_MODE
(
op
)
!=
mode
&&
mode
!=
VOIDmode
)
return
0
;
return
gen_reg_operand
(
op
,
mode
);
}
/* Return 1 if OP is either an immediate or a general register. This is used
for the input operand of mtsr/mtrsim. */
int
gen_reg_or_immediate_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
;
{
return
gen_reg_operand
(
op
,
mode
)
||
immediate_operand
(
op
,
mode
);
}
/* Return 1 if OP can be used as the second operand of and AND insn. This
includes srcb_operand and a constant whose complement fits in 8 bits. */
int
and_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
;
{
return
(
srcb_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
((
unsigned
)
((
~
INTVAL
(
op
))
&
GET_MODE_MASK
(
mode
))
<
256
)));
}
/* Return 1 if OP can be used as the second operand of an ADD insn.
This is the same as above, except we use negative, rather than
complement. */
int
add_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
;
{
return
(
srcb_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
((
unsigned
)
((
-
INTVAL
(
op
))
&
GET_MODE_MASK
(
mode
))
<
256
)));
}
/* Return 1 if OP can be used as the input operand for a move insn. */
int
in_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
rtx
orig_op
=
op
;
if
(
!
general_operand
(
op
,
mode
))
return
0
;
while
(
GET_CODE
(
op
)
==
SUBREG
)
op
=
SUBREG_REG
(
op
);
switch
(
GET_CODE
(
op
))
{
case
REG
:
return
1
;
case
MEM
:
return
(
GET_MODE_SIZE
(
mode
)
>=
UNITS_PER_WORD
||
TARGET_DW_ENABLE
);
case
CONST_INT
:
if
(
GET_MODE_CLASS
(
mode
)
!=
MODE_INT
)
return
0
;
return
1
;
case
CONST
:
case
SYMBOL_REF
:
case
LABEL_REF
:
return
(
GET_MODE
(
op
)
==
mode
||
mode
==
SImode
||
mode
==
HImode
||
mode
==
QImode
);
case
CONST_DOUBLE
:
return
((
GET_MODE_CLASS
(
mode
)
==
MODE_FLOAT
&&
mode
==
GET_MODE
(
op
))
||
(
GET_MODE
(
op
)
==
VOIDmode
&&
GET_MODE_CLASS
(
mode
)
==
MODE_INT
));
default
:
return
0
;
}
}
/* Return 1 if OP can be used as the output operand for a move insn. */
int
out_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
rtx
orig_op
=
op
;
if
(
!
general_operand
(
op
,
mode
))
return
0
;
while
(
GET_CODE
(
op
)
==
SUBREG
)
op
=
SUBREG_REG
(
op
);
if
(
GET_CODE
(
op
)
==
REG
)
return
(
mode
==
SImode
||
gen_reg_operand
(
orig_op
,
mode
)
||
(
GET_MODE_CLASS
(
mode
)
==
MODE_FLOAT
&&
accum_reg_operand
(
orig_op
,
mode
)));
else
if
(
GET_CODE
(
op
)
==
MEM
)
return
mode
==
SImode
||
mode
==
SFmode
||
TARGET_DW_ENABLE
;
else
return
0
;
}
/* Return 1 if OP is some extension operator. */
int
extend_operator
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
((
mode
==
VOIDmode
||
GET_MODE
(
op
)
==
mode
)
&&
(
GET_CODE
(
op
)
==
ZERO_EXTEND
||
GET_CODE
(
op
)
==
SIGN_EXTEND
));
}
/* Return 1 if OP is a comparison operator that we have in floating-point. */
int
fp_comparison_operator
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
((
mode
==
VOIDmode
||
mode
==
GET_MODE
(
op
))
&&
(
GET_CODE
(
op
)
==
EQ
||
GET_CODE
(
op
)
==
GT
||
GET_CODE
(
op
)
==
GE
));
}
/* Return 1 if OP is a valid branch comparison. */
int
branch_operator
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
((
mode
==
VOIDmode
||
mode
==
GET_MODE
(
op
))
&&
(
GET_CODE
(
op
)
==
GE
||
GET_CODE
(
op
)
==
LT
));
}
/* Return 1 if OP is a load multiple operation. It is known to be a
PARALLEL and the first three sections will be tested. */
int
load_multiple_operation
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
count
=
XVECLEN
(
op
,
0
)
-
2
;
int
dest_regno
;
rtx
src_addr
;
int
i
;
/* Perform a quick check so we don't blow up below. */
if
(
count
<=
1
||
GET_CODE
(
XVECEXP
(
op
,
0
,
0
))
!=
SET
||
GET_CODE
(
SET_DEST
(
XVECEXP
(
op
,
0
,
0
)))
!=
REG
||
GET_CODE
(
SET_SRC
(
XVECEXP
(
op
,
0
,
0
)))
!=
MEM
)
return
0
;
dest_regno
=
REGNO
(
SET_DEST
(
XVECEXP
(
op
,
0
,
0
)));
src_addr
=
XEXP
(
SET_SRC
(
XVECEXP
(
op
,
0
,
0
)),
0
);
for
(
i
=
1
;
i
<
count
;
i
++
)
{
rtx
elt
=
XVECEXP
(
op
,
0
,
i
+
2
);
if
(
GET_CODE
(
elt
)
!=
SET
||
GET_CODE
(
SET_DEST
(
elt
))
!=
REG
||
GET_MODE
(
SET_DEST
(
elt
))
!=
SImode
||
REGNO
(
SET_DEST
(
elt
))
!=
dest_regno
+
i
||
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
*
4
)
return
0
;
}
return
1
;
}
/* Similar, but tests for store multiple. */
int
store_multiple_operation
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
int
num_special
=
TARGET_NO_STOREM_BUG
?
2
:
1
;
int
count
=
XVECLEN
(
op
,
0
)
-
num_special
;
int
src_regno
;
rtx
dest_addr
;
int
i
;
/* Perform a quick check so we don't blow up below. */
if
(
count
<=
1
||
GET_CODE
(
XVECEXP
(
op
,
0
,
0
))
!=
SET
||
GET_CODE
(
SET_DEST
(
XVECEXP
(
op
,
0
,
0
)))
!=
MEM
||
GET_CODE
(
SET_SRC
(
XVECEXP
(
op
,
0
,
0
)))
!=
REG
)
return
0
;
src_regno
=
REGNO
(
SET_SRC
(
XVECEXP
(
op
,
0
,
0
)));
dest_addr
=
XEXP
(
SET_DEST
(
XVECEXP
(
op
,
0
,
0
)),
0
);
for
(
i
=
1
;
i
<
count
;
i
++
)
{
rtx
elt
=
XVECEXP
(
op
,
0
,
i
+
num_special
);
if
(
GET_CODE
(
elt
)
!=
SET
||
GET_CODE
(
SET_SRC
(
elt
))
!=
REG
||
GET_MODE
(
SET_SRC
(
elt
))
!=
SImode
||
REGNO
(
SET_SRC
(
elt
))
!=
src_regno
+
i
||
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
*
4
)
return
0
;
}
return
1
;
}
/* Given a special register REG and MASK, a value being masked against a
quantity to which the special register is set, return 1 if the masking
operation is built-in to the setting of that special register. */
int
masks_bits_for_special
(
reg
,
mask
)
rtx
reg
;
rtx
mask
;
{
int
needed_mask_value
;
if
(
GET_CODE
(
reg
)
!=
REG
||
GET_CODE
(
mask
)
!=
CONST_INT
)
abort
();
switch
(
REGNO
(
reg
))
{
case
R_BP
:
case
R_INT
:
needed_mask_value
=
3
;
break
;
case
R_FC
:
needed_mask_value
=
31
;
break
;
case
R_CR
:
case
R_LRU
:
needed_mask_value
=
255
;
break
;
case
R_FPE
:
needed_mask_value
=
511
;
break
;
case
R_MMU
:
needed_mask_value
=
0x3ff
;
break
;
case
R_OPS
:
case
R_CPS
:
case
R_RBP
:
case
R_FPS
:
needed_mask_value
=
0xffff
;
break
;
case
R_VAB
:
needed_mask_value
=
0xffff0000
;
break
;
case
R_Q
:
case
R_CFG
:
case
R_CHA
:
case
R_CHD
:
case
R_CHC
:
case
R_TMC
:
case
R_TMR
:
case
R_PC0
:
case
R_PC1
:
case
R_PC2
:
return
0
;
default
:
abort
();
}
return
(
INTVAL
(
mask
)
&
~
needed_mask_value
)
==
0
;
}
/* Return nonzero if this label is that of the return point, but there is
a non-null epilogue. */
int
epilogue_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
next_active_insn
(
op
)
==
0
&&
a29k_first_epilogue_insn
!=
0
;
}
/* 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. */
enum
reg_class
secondary_reload_class
(
class
,
mode
,
in
)
enum
reg_class
class
;
enum
machine_mode
mode
;
rtx
in
;
{
int
regno
=
-
1
;
if
(
GET_CODE
(
in
)
==
REG
||
GET_CODE
(
in
)
==
SUBREG
)
regno
=
true_regnum
(
in
);
/* We can place anything into GENERAL_REGS and can put GENERAL_REGS
into anything. */
if
(
class
==
GENERAL_REGS
||
(
regno
!=
-
1
&&
regno
<
R_BP
))
return
NO_REGS
;
/* We can place 16-bit constants into a special register. */
if
(
GET_CODE
(
in
)
==
CONST_INT
&&
(
GET_MODE_BITSIZE
(
mode
)
<=
16
||
(
unsigned
)
INTVAL
(
in
)
<=
65535
)
&&
(
class
==
BP_REGS
||
class
==
Q_REGS
||
class
==
SPECIAL_REGS
))
return
NO_REGS
;
/* Otherwise, we need GENERAL_REGS. */
return
GENERAL_REGS
;
}
/* START is the zero-based incoming argument register index used (0 is 160,
i.e., the first incoming argument register) and COUNT is the number used.
Mark the corresponding incoming registers as neither fixed nor call used.
For each register used for incoming arguments, we have one less local
register that can be used. So also mark some high-numbered registers as
fixed.
Return the first register number to use for the argument. */
int
incoming_reg
(
start
,
count
)
int
start
;
int
count
;
{
int
i
;
if
(
!
TARGET_NO_REUSE_ARGS
)
/* Mark all the used registers as not fixed and saved over calls. */
for
(
i
=
R_AR
(
start
);
i
<
R_AR
(
16
)
&&
i
<
R_AR
(
start
+
count
);
i
++
)
{
fixed_regs
[
i
]
=
call_used_regs
[
i
]
=
call_fixed_regs
[
i
]
=
0
;
CLEAR_HARD_REG_BIT
(
fixed_reg_set
,
i
);
CLEAR_HARD_REG_BIT
(
call_used_reg_set
,
i
);
CLEAR_HARD_REG_BIT
(
call_fixed_reg_set
,
i
);
}
/* Shorten the maximum size of the frame. */
for
(
i
=
R_AR
(
0
)
-
start
-
count
;
i
<
R_AR
(
0
)
-
start
;
i
++
)
{
fixed_regs
[
i
]
=
call_used_regs
[
i
]
=
call_fixed_regs
[
i
]
=
1
;
SET_HARD_REG_BIT
(
fixed_reg_set
,
i
);
SET_HARD_REG_BIT
(
call_used_reg_set
,
i
);
SET_HARD_REG_BIT
(
call_fixed_reg_set
,
i
);
}
return
R_AR
(
start
);
}
/* These routines are used in finding insns to fill delay slots in the
epilogue. */
/* Return 1 if the current function will adjust the register stack. */
int
needs_regstack_p
()
{
int
i
;
rtx
insn
;
if
(
frame_pointer_needed
)
return
1
;
/* If any local register is used, we need to adjust the regstack. */
for
(
i
=
R_LR
(
127
);
i
>=
R_LR
(
0
);
i
--
)
if
(
regs_ever_live
[
i
])
return
1
;
/* We need a register stack if we make any calls. */
for
(
insn
=
get_insns
();
insn
;
insn
=
next_insn
(
insn
))
if
(
GET_CODE
(
insn
)
==
CALL_INSN
||
(
GET_CODE
(
insn
)
==
INSN
&&
GET_CODE
(
PATTERN
(
insn
))
==
SEQUENCE
&&
GET_CODE
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
))
==
CALL_INSN
))
return
1
;
/* Otherwise, we don't. */
return
0
;
}
/* Return 1 if X uses a local register. */
int
uses_local_reg_p
(
x
)
rtx
x
;
{
char
*
fmt
;
int
i
,
j
;
switch
(
GET_CODE
(
x
))
{
case
REG
:
return
REGNO
(
x
)
>=
R_LR
(
0
)
&&
REGNO
(
x
)
<=
R_FP
;
case
CONST_INT
:
case
CONST
:
case
PC
:
case
CC0
:
case
LABEL_REF
:
case
SYMBOL_REF
:
return
0
;
}
fmt
=
GET_RTX_FORMAT
(
GET_CODE
(
x
));
for
(
i
=
GET_RTX_LENGTH
(
GET_CODE
(
x
))
-
1
;
i
>=
0
;
i
--
)
{
if
(
fmt
[
i
]
==
'e'
)
{
if
(
uses_local_reg_p
(
XEXP
(
x
,
i
)))
return
1
;
}
else
if
(
fmt
[
i
]
==
'E'
)
{
for
(
j
=
XVECLEN
(
x
,
i
)
-
1
;
j
>=
0
;
j
--
)
if
(
uses_local_reg_p
(
XVECEXP
(
x
,
i
,
j
)))
return
1
;
}
}
return
0
;
}
/* Returns 1 if this function is known to have a null epilogue. */
int
null_epilogue
()
{
return
(
reload_completed
&&
!
needs_regstack_p
()
&&
get_frame_size
()
==
0
&&
current_function_pretend_args_size
==
0
);
}
/* Write out the assembler form of an operand. Recognize the following
special options:
%N means write the low-order 8 bits of the negative of the constant
%Q means write a QImode operand (truncate constants to 8 bits)
%M means write the low-order 16 bits of the constant
%C means write the low-order 8 bits of the complement of the constant
%X means write the cntl values for LOAD with operand an extension op
%b means write `f' is this is a reversed condition, `t' otherwise
%B means write `t' is this is a reversed condition, `f' otherwise
%J means write the 29k opcode part for a comparison operation
%e means write the label with an extra `X' is this is the epilogue
otherwise the normal label name
%E means write nothing if this insn has a delay slot,
a nop unless this is the epilogue label, in which case
write the first epilogue insn
%F means write just the normal operand if the insn has a delay slot;
otherwise, this is a recursive call so output the
symbol + 4 and write the first prologue insn in the
delay slot.
%L means write the register number plus one ("low order" register)
or the low-order part of a multi-word constant
%O means write the register number plus two
%P means write the register number plus three ("low order" of TImode)
%S means write the number of words in the mode of the operand,
minus one (for CR)
%V means write the number of elements in a PARALLEL minus 1
%# means write nothing if we have a delay slot, "\n\tnop" otherwise
%* means write the register name for TPC. */
void
print_operand
(
file
,
x
,
code
)
FILE
*
file
;
rtx
x
;
char
code
;
{
char
buf
[
100
];
/* These macros test for integers and extract the low-order bits. */
#define INT_P(X) \
((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \
&& GET_MODE (X) == VOIDmode)
#define INT_LOWPART(X) \
(GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X))
switch
(
code
)
{
case
'Q'
:
if
(
GET_CODE
(
x
)
==
REG
)
break
;
else
if
(
!
INT_P
(
x
))
output_operand_lossage
(
"invalid %%Q value"
);
fprintf
(
file
,
"%d"
,
INT_LOWPART
(
x
)
&
0xff
);
return
;
case
'C'
:
if
(
!
INT_P
(
x
))
output_operand_lossage
(
"invalid %%C value"
);
fprintf
(
file
,
"%d"
,
(
~
INT_LOWPART
(
x
))
&
0xff
);
return
;
case
'N'
:
if
(
!
INT_P
(
x
))
output_operand_lossage
(
"invalid %%N value"
);
fprintf
(
file
,
"%d"
,
(
-
INT_LOWPART
(
x
))
&
0xff
);
return
;
case
'M'
:
if
(
!
INT_P
(
x
))
output_operand_lossage
(
"invalid %%M value"
);
fprintf
(
file
,
"%d"
,
INT_LOWPART
(
x
)
&
0xffff
);
return
;
case
'X'
:
fprintf
(
file
,
"%d"
,
((
GET_MODE
(
XEXP
(
x
,
0
))
==
QImode
?
1
:
2
)
+
(
GET_CODE
(
x
)
==
SIGN_EXTEND
?
16
:
0
)));
return
;
case
'b'
:
if
(
GET_CODE
(
x
)
==
GE
)
fprintf
(
file
,
"f"
);
else
fprintf
(
file
,
"t"
);
return
;
case
'B'
:
if
(
GET_CODE
(
x
)
==
GE
)
fprintf
(
file
,
"t"
);
else
fprintf
(
file
,
"f"
);
return
;
case
'J'
:
/* It so happens that the RTX names for the conditions are the same as
the 29k's insns except for "ne", which requires "neq". */
fprintf
(
file
,
GET_RTX_NAME
(
GET_CODE
(
x
)));
if
(
GET_CODE
(
x
)
==
NE
)
fprintf
(
file
,
"q"
);
return
;
case
'e'
:
if
(
optimize
&&
flag_delayed_branch
&&
a29k_last_prologue_insn
==
0
&&
epilogue_operand
(
x
,
VOIDmode
)
&&
dbr_sequence_length
()
==
0
)
{
/* We need to output the label number of the last label in the
function, which is not necessarily X since there might be
a USE insn in between. First go forward to the last insn, then
back up to a label. */
while
(
NEXT_INSN
(
x
)
!=
0
)
x
=
NEXT_INSN
(
x
);
while
(
GET_CODE
(
x
)
!=
CODE_LABEL
)
x
=
PREV_INSN
(
x
);
ASM_GENERATE_INTERNAL_LABEL
(
buf
,
"LX"
,
CODE_LABEL_NUMBER
(
x
));
assemble_name
(
file
,
buf
);
}
else
output_asm_label
(
x
);
return
;
case
'E'
:
if
(
dbr_sequence_length
())
;
else
if
(
a29k_last_prologue_insn
)
{
fprintf
(
file
,
"
\n\t
%s"
,
a29k_last_prologue_insn
);
a29k_last_prologue_insn
=
0
;
}
else
if
(
optimize
&&
flag_delayed_branch
&&
epilogue_operand
(
x
,
VOIDmode
))
{
fprintf
(
file
,
"
\n\t
%s"
,
a29k_first_epilogue_insn
);
a29k_first_epilogue_insn_used
=
1
;
}
else
fprintf
(
file
,
"
\n\t
nop"
);
return
;
case
'F'
:
output_addr_const
(
file
,
x
);
if
(
!
strcmp
(
XSTR
(
x
,
0
),
current_function_name
)
&&
dbr_sequence_length
()
==
0
)
fprintf
(
file
,
"+4
\n\t
%s,%d"
,
a29k_regstack_size
>=
64
?
"const gr121"
:
"sub gr1,gr1"
,
a29k_regstack_size
*
4
);
return
;
case
'L'
:
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE
(
x
)
==
DFmode
)
{
union
real_extract
u
;
bcopy
(
&
CONST_DOUBLE_LOW
(
x
),
&
u
,
sizeof
u
);
fprintf
(
file
,
"$double1(%.20e)"
,
u
.
d
);
}
else
if
(
GET_CODE
(
x
)
==
REG
)
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
x
)
+
1
]);
else
output_operand_lossage
(
"invalid %%L value"
);
return
;
case
'O'
:
if
(
GET_CODE
(
x
)
!=
REG
)
output_operand_lossage
(
"invalid %%O value"
);
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
x
)
+
2
]);
return
;
case
'P'
:
if
(
GET_CODE
(
x
)
!=
REG
)
output_operand_lossage
(
"invalid %%P value"
);
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
x
)
+
3
]);
return
;
case
'S'
:
fprintf
(
file
,
"%d"
,
(
GET_MODE_SIZE
(
GET_MODE
(
x
))
/
UNITS_PER_WORD
)
-
1
);
return
;
case
'V'
:
if
(
GET_CODE
(
x
)
!=
PARALLEL
)
output_operand_lossage
(
"invalid %%V value"
);
fprintf
(
file
,
"%d"
,
XVECLEN
(
x
,
0
)
-
2
);
return
;
case
'#'
:
if
(
dbr_sequence_length
()
==
0
)
{
if
(
a29k_last_prologue_insn
)
{
fprintf
(
file
,
"
\n\t
%s"
,
a29k_last_prologue_insn
);
a29k_last_prologue_insn
=
0
;
}
else
fprintf
(
file
,
"
\n\t
nop"
);
}
return
;
case
'*'
:
fprintf
(
file
,
"%s"
,
reg_names
[
R_TPC
]);
return
;
}
if
(
GET_CODE
(
x
)
==
REG
)
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
x
)]);
else
if
(
GET_CODE
(
x
)
==
MEM
)
output_address
(
XEXP
(
x
,
0
));
else
if
(
GET_CODE
(
x
)
==
CONST
&&
GET_CODE
(
XEXP
(
x
,
0
))
==
SUBREG
&&
GET_CODE
(
SUBREG_REG
(
XEXP
(
x
,
0
)))
==
CONST_DOUBLE
)
{
union
real_extract
u
;
if
(
GET_MODE
(
SUBREG_REG
(
XEXP
(
x
,
0
)))
==
SFmode
)
fprintf
(
file
,
"$float"
);
else
fprintf
(
file
,
"$double%d"
,
SUBREG_WORD
(
XEXP
(
x
,
0
)));
bcopy
(
&
CONST_DOUBLE_LOW
(
SUBREG_REG
(
XEXP
(
x
,
0
))),
&
u
,
sizeof
u
);
fprintf
(
file
,
"(%.20e)"
,
u
.
d
);
}
else
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE_CLASS
(
GET_MODE
(
x
))
==
MODE_FLOAT
)
{
union
real_extract
u
;
bcopy
(
&
CONST_DOUBLE_LOW
(
x
),
&
u
,
sizeof
u
);
fprintf
(
file
,
"$%s(%.20e)"
,
GET_MODE
(
x
)
==
SFmode
?
"float"
:
"double0"
,
u
.
d
);
}
else
output_addr_const
(
file
,
x
);
}
/* This page contains routines to output function prolog and epilog code. */
/* Output function prolog code to file FILE. Memory stack size is SIZE.
Also sets register names for incoming arguments and frame pointer. */
void
output_prolog
(
file
,
size
)
FILE
*
file
;
int
size
;
{
int
makes_calls
=
0
;
int
arg_count
=
0
;
rtx
insn
;
int
i
;
unsigned
int
tag_word
;
/* See if we make any calls. We need to set lr1 if so. */
for
(
insn
=
get_insns
();
insn
;
insn
=
next_insn
(
insn
))
if
(
GET_CODE
(
insn
)
==
CALL_INSN
||
(
GET_CODE
(
insn
)
==
INSN
&&
GET_CODE
(
PATTERN
(
insn
))
==
SEQUENCE
&&
GET_CODE
(
XVECEXP
(
PATTERN
(
insn
),
0
,
0
))
==
CALL_INSN
))
{
makes_calls
=
1
;
break
;
}
/* Find the highest local register used. */
for
(
i
=
R_LR
(
127
);
i
>=
R_LR
(
0
);
i
--
)
if
(
regs_ever_live
[
i
])
break
;
a29k_regstack_size
=
i
-
(
R_LR
(
0
)
-
1
);
/* If calling routines, ensure we count lr0 & lr1. */
if
(
makes_calls
&&
a29k_regstack_size
<
2
)
a29k_regstack_size
=
2
;
/* Count frame pointer and align to 8 byte boundary (even number of
registers). */
a29k_regstack_size
+=
frame_pointer_needed
;
if
(
a29k_regstack_size
&
1
)
a29k_regstack_size
++
;
/* See how many incoming arguments we have in registers. */
for
(
i
=
R_AR
(
0
);
i
<
R_AR
(
16
);
i
++
)
if
(
!
fixed_regs
[
i
])
arg_count
++
;
/* The argument count includes the caller's lr0 and lr1. */
arg_count
+=
2
;
/* Set the names and numbers of the frame pointer and incoming argument
registers. */
for
(
i
=
0
;
i
<
FIRST_PSEUDO_REGISTER
;
i
++
)
a29k_debug_reg_map
[
i
]
=
i
;
reg_names
[
FRAME_POINTER_REGNUM
]
=
reg_names
[
R_LR
(
a29k_regstack_size
-
1
)];
a29k_debug_reg_map
[
FRAME_POINTER_REGNUM
]
=
R_LR
(
a29k_regstack_size
-
1
);
for
(
i
=
0
;
i
<
16
;
i
++
)
{
reg_names
[
R_AR
(
i
)]
=
reg_names
[
R_LR
(
a29k_regstack_size
+
i
+
2
)];
a29k_debug_reg_map
[
R_AR
(
i
)]
=
R_LR
(
a29k_regstack_size
+
i
+
2
);
}
/* Compute memory stack size. Add in number of bytes that the we should
push and pretend the caller did and the size of outgoing arguments.
Then round to a doubleword boundary. */
size
+=
(
current_function_pretend_args_size
+
current_function_outgoing_args_size
);
size
=
(
size
+
7
)
&
~
7
;
/* Write header words. See if one or two word form. */
tag_word
=
(
frame_pointer_needed
?
0x400000
:
0
)
+
(
arg_count
<<
16
);
if
(
size
/
8
>
0xff
)
fprintf
(
file
,
"
\t
.word %d, 0x%0x
\n
"
,
(
size
/
8
)
<<
2
,
0x800000
+
tag_word
);
else
fprintf
(
file
,
"
\t
.word 0x%0x
\n
"
,
tag_word
+
((
size
/
8
)
<<
3
));
/* Define the function name. */
assemble_name
(
file
,
a29k_function_name
);
fprintf
(
file
,
":
\n
"
);
/* Push the register stack by the proper amount. There are two possible
ways to do this. */
if
(
a29k_regstack_size
>=
256
/
4
)
fprintf
(
file
,
"
\t
const %s,%d
\n\t
sub gr1,gr1,%s
\n
"
,
reg_names
[
R_TAV
],
a29k_regstack_size
*
4
,
reg_names
[
R_TAV
]);
else
if
(
a29k_regstack_size
)
fprintf
(
file
,
"
\t
sub gr1,gr1,%d
\n
"
,
a29k_regstack_size
*
4
);
/* Test that the registers are available. */
if
(
a29k_regstack_size
)
fprintf
(
file
,
"
\t
asgeu V_%sSPILL,gr1,%s
\n
"
,
TARGET_KERNEL_REGISTERS
?
"K"
:
""
,
reg_names
[
R_RAB
]);
/* Set up frame pointer, if one is needed. */
if
(
frame_pointer_needed
)
fprintf
(
file
,
"
\t
sll %s,%s,0
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
reg_names
[
R_MSP
]);
/* Make room for any frame space. There are three ways to do this. */
if
(
size
>=
256
)
{
fprintf
(
file
,
"
\t
const %s,%d
\n
"
,
reg_names
[
R_TAV
],
size
);
if
(
size
>=
65536
)
fprintf
(
file
,
"
\t
consth %s,%d
\n
"
,
reg_names
[
R_TAV
],
size
);
if
(
TARGET_STACK_CHECK
)
fprintf
(
file
,
"
\t
call %s,__msp_check
\n
"
,
reg_names
[
R_TPC
]);
fprintf
(
file
,
"
\t
sub %s,%s,%s
\n
"
,
reg_names
[
R_MSP
],
reg_names
[
R_MSP
],
reg_names
[
R_TAV
]);
}
else
if
(
size
)
{
if
(
TARGET_STACK_CHECK
)
fprintf
(
file
,
"
\t
call %s,__msp_check
\n
"
,
reg_names
[
R_TPC
]);
fprintf
(
file
,
"
\t
sub %s,%s,%d
\n
"
,
reg_names
[
R_MSP
],
reg_names
[
R_MSP
],
size
);
}
/* If this routine will make calls, set lr1. If we see an insn that
can use a delay slot before a call or jump, save this insn for that
slot (this condition is equivalent to seeing if we have an insn that
needs delay slots before an insn that has a filled delay slot). */
a29k_last_prologue_insn
=
0
;
if
(
makes_calls
)
{
i
=
(
a29k_regstack_size
+
arg_count
)
*
4
;
if
(
i
>=
256
)
fprintf
(
file
,
"
\t
const %s,%d
\n\t
sub lr1,gr1,%s
\n
"
,
reg_names
[
R_TAV
],
i
,
reg_names
[
R_TAV
]);
else
{
if
(
optimize
&&
flag_delayed_branch
)
for
(
insn
=
get_insns
();
insn
;
insn
=
NEXT_INSN
(
insn
))
{
if
(
GET_CODE
(
insn
)
==
CODE_LABEL
||
(
GET_CODE
(
insn
)
==
INSN
&&
GET_CODE
(
PATTERN
(
insn
))
==
SEQUENCE
))
break
;
if
(
GET_CODE
(
insn
)
==
NOTE
||
(
GET_CODE
(
insn
)
==
INSN
&&
(
GET_CODE
(
PATTERN
(
insn
))
==
USE
||
GET_CODE
(
PATTERN
(
insn
))
==
CLOBBER
)))
continue
;
if
(
num_delay_slots
(
insn
)
>
0
)
{
a29k_last_prologue_insn
=
(
char
*
)
oballoc
(
100
);
sprintf
(
a29k_last_prologue_insn
,
"add lr1,gr1,%d"
,
i
);
break
;
}
}
if
(
a29k_last_prologue_insn
==
0
)
fprintf
(
file
,
"
\t
add lr1,gr1,%d
\n
"
,
i
);
}
}
/* Compute the first insn of the epilogue. */
a29k_first_epilogue_insn_used
=
0
;
if
(
size
==
0
&&
a29k_regstack_size
==
0
&&
!
frame_pointer_needed
)
a29k_first_epilogue_insn
=
0
;
else
a29k_first_epilogue_insn
=
(
char
*
)
oballoc
(
100
);
if
(
frame_pointer_needed
)
sprintf
(
a29k_first_epilogue_insn
,
"sll %s,%s,0"
,
reg_names
[
R_MSP
],
reg_names
[
FRAME_POINTER_REGNUM
]);
else
if
(
a29k_regstack_size
)
{
if
(
a29k_regstack_size
>=
256
/
4
)
sprintf
(
a29k_first_epilogue_insn
,
"const %s,%d"
,
reg_names
[
R_TAV
],
a29k_regstack_size
*
4
);
else
sprintf
(
a29k_first_epilogue_insn
,
"add gr1,gr1,%d"
,
a29k_regstack_size
*
4
);
}
else
if
(
size
)
{
if
(
size
>=
256
)
sprintf
(
a29k_first_epilogue_insn
,
"const %s,%d"
,
reg_names
[
R_TAV
],
size
);
else
sprintf
(
a29k_first_epilogue_insn
,
"add %s,%s,%d"
,
reg_names
[
R_MSP
],
reg_names
[
R_MSP
],
size
);
}
}
/* Call this after writing what might be the first instruction of the
epilogue. If that first insn was used in a delay slot, an intermediate
label is written. */
static
void
check_epilogue_internal_label
(
file
)
FILE
*
file
;
{
rtx
insn
;
if
(
!
a29k_first_epilogue_insn_used
)
return
;
for
(
insn
=
get_last_insn
();
GET_CODE
(
insn
)
!=
CODE_LABEL
;
insn
=
PREV_INSN
(
insn
))
;
ASM_OUTPUT_INTERNAL_LABEL
(
file
,
"LX"
,
CODE_LABEL_NUMBER
(
insn
));
a29k_first_epilogue_insn_used
=
0
;
}
/* Output the epilog of the last procedure to file FILE. SIZE is the memory
stack size. The register stack size is in the variable
A29K_REGSTACK_SIZE. */
void
output_epilog
(
file
,
size
)
FILE
*
file
;
int
size
;
{
rtx
insn
;
int
locals_unavailable
=
0
;
/* True until after first insn
after gr1 update. */
/* If we hit a BARRIER before a real insn or CODE_LABEL, we don't
need to do anything because we are never jumped to. */
insn
=
get_last_insn
();
if
(
GET_CODE
(
insn
)
==
NOTE
)
insn
=
prev_nonnote_insn
(
insn
);
if
(
insn
&&
GET_CODE
(
insn
)
==
BARRIER
)
return
;
/* If a frame pointer was needed we must restore the memory stack pointer
before adjusting the register stack. */
if
(
frame_pointer_needed
)
{
fprintf
(
file
,
"
\t
sll %s,%s,0
\n
"
,
reg_names
[
R_MSP
],
reg_names
[
FRAME_POINTER_REGNUM
]);
check_epilogue_internal_label
(
file
);
}
/* Restore the register stack. There are two ways to do this. */
if
(
a29k_regstack_size
)
{
if
(
a29k_regstack_size
>=
256
/
4
)
{
fprintf
(
file
,
"
\t
const %s,%d
\n
"
,
reg_names
[
R_TAV
],
a29k_regstack_size
*
4
);
check_epilogue_internal_label
(
file
);
fprintf
(
file
,
"
\t
add gr1,gr1,%s
\n
"
,
reg_names
[
R_TAV
]);
}
else
{
fprintf
(
file
,
"
\t
add gr1,gr1,%d
\n
"
,
a29k_regstack_size
*
4
);
check_epilogue_internal_label
(
file
);
}
locals_unavailable
=
1
;
}
/* Restore the memory stack pointer if there is no frame pointer.
Adjust the size to include any pretend arguments and pushed
arguments and round to doubleword boundary. */
size
+=
(
current_function_pretend_args_size
+
current_function_outgoing_args_size
);
size
=
(
size
+
7
)
&
~
7
;
if
(
size
&&
!
frame_pointer_needed
)
{
if
(
size
>=
256
)
{
fprintf
(
file
,
"
\t
const %s,%d
\n
"
,
reg_names
[
R_TAV
],
size
);
check_epilogue_internal_label
(
file
);
locals_unavailable
=
0
;
if
(
size
>=
65536
)
fprintf
(
file
,
"
\t
consth %s,%d
\n
"
,
reg_names
[
R_TAV
],
size
);
fprintf
(
file
,
"
\t
add %s,%s,%s
\n
"
,
reg_names
[
R_MSP
],
reg_names
[
R_MSP
],
reg_names
[
R_TAV
]);
}
else
{
fprintf
(
file
,
"
\t
add %s,%s,%d
\n
"
,
reg_names
[
R_MSP
],
reg_names
[
R_MSP
],
size
);
check_epilogue_internal_label
(
file
);
locals_unavailable
=
0
;
}
}
if
(
locals_unavailable
)
{
/* If we have an insn for this delay slot, write it. */
if
(
current_function_epilogue_delay_list
)
final_scan_insn
(
XEXP
(
current_function_epilogue_delay_list
,
0
),
file
,
1
,
-
2
,
1
);
else
fprintf
(
file
,
"
\t
nop
\n
"
);
}
fprintf
(
file
,
"
\t
jmpi lr0
\n
"
);
if
(
a29k_regstack_size
)
fprintf
(
file
,
"
\t
asleu V_%sFILL,lr1,%s
\n
"
,
TARGET_KERNEL_REGISTERS
?
"K"
:
""
,
reg_names
[
R_RFB
]);
else
if
(
current_function_epilogue_delay_list
)
final_scan_insn
(
XEXP
(
current_function_epilogue_delay_list
,
0
),
file
,
1
,
-
2
,
1
);
else
fprintf
(
file
,
"
\t
nop
\n
"
);
}
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