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
2a2ab3f9
Commit
2a2ab3f9
authored
Feb 01, 1992
by
James Van Artsdalen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial revision
From-SVN: r264
parent
8b62f241
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
2541 additions
and
0 deletions
+2541
-0
gcc/config/i386/i386.c
+1874
-0
gcc/config/svr4.h
+667
-0
No files found.
gcc/config/i386/i386.c
0 → 100644
View file @
2a2ab3f9
/* Subroutines for insn-output.c for Intel 80386.
Copyright (C) 1988 Free Software Foundation, Inc.
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 "tree.h"
#include "flags.h"
#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))
extern
FILE
*
asm_out_file
;
extern
char
*
strcat
();
char
*
singlemove_string
();
char
*
output_move_const_single
();
static
char
*
hi_reg_name
[]
=
HI_REGISTER_NAMES
;
static
char
*
qi_reg_name
[]
=
QI_REGISTER_NAMES
;
static
char
*
qi_high_reg_name
[]
=
QI_HIGH_REGISTER_NAMES
;
/* Output an insn whose source is a 386 integer register. SRC is the
rtx for the register, and TEMPLATE is the op-code template. SRC may
be either SImode or DImode.
The template will be output with operands[0] as SRC, and operands[1]
as a pointer to the top of the 386 stack. So a call from floatsidf2
would look like this:
output_op_from_reg (operands[1], AS1 (fild%z0,%1));
where %z0 corresponds to the caller's operands[1], and is used to
emit the proper size suffix.
??? Extend this to handle HImode - a 387 can load and store HImode
values directly. */
void
output_op_from_reg
(
src
,
template
)
rtx
src
;
char
*
template
;
{
rtx
xops
[
4
];
xops
[
0
]
=
src
;
xops
[
1
]
=
AT_SP
(
Pmode
);
xops
[
2
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
GET_MODE_SIZE
(
GET_MODE
(
src
)));
xops
[
3
]
=
stack_pointer_rtx
;
if
(
GET_MODE_SIZE
(
GET_MODE
(
src
))
>
UNITS_PER_WORD
)
{
rtx
high
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
src
)
+
1
);
output_asm_insn
(
AS1
(
push
%
L0
,
%
0
),
&
high
);
}
output_asm_insn
(
AS1
(
push
%
L0
,
%
0
),
&
src
);
output_asm_insn
(
template
,
xops
);
output_asm_insn
(
AS2
(
add
%
L3
,
%
2
,
%
3
),
xops
);
}
/* Output an insn to pop an value from the 387 top-of-stack to 386
register DEST. The 387 register stack is popped if DIES is true. If
the mode of DEST is an integer mode, a `fist' integer store is done,
otherwise a `fst' float store is done. */
void
output_to_reg
(
dest
,
dies
)
rtx
dest
;
int
dies
;
{
rtx
xops
[
4
];
xops
[
0
]
=
AT_SP
(
Pmode
);
xops
[
1
]
=
stack_pointer_rtx
;
xops
[
2
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
GET_MODE_SIZE
(
GET_MODE
(
dest
)));
xops
[
3
]
=
dest
;
output_asm_insn
(
AS2
(
sub
%
L1
,
%
2
,
%
1
),
xops
);
if
(
GET_MODE_CLASS
(
GET_MODE
(
dest
))
==
MODE_INT
)
{
if
(
dies
)
output_asm_insn
(
AS1
(
fistp
%
z3
,
%
y0
),
xops
);
else
output_asm_insn
(
AS1
(
fist
%
z3
,
%
y0
),
xops
);
}
else
if
(
GET_MODE_CLASS
(
GET_MODE
(
dest
))
==
MODE_FLOAT
)
{
if
(
dies
)
output_asm_insn
(
AS1
(
fstp
%
z3
,
%
y0
),
xops
);
else
output_asm_insn
(
AS1
(
fst
%
z3
,
%
y0
),
xops
);
}
else
abort
();
output_asm_insn
(
AS1
(
pop
%
L0
,
%
0
),
&
dest
);
if
(
GET_MODE_SIZE
(
GET_MODE
(
dest
))
>
UNITS_PER_WORD
)
{
dest
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
dest
)
+
1
);
output_asm_insn
(
AS1
(
pop
%
L0
,
%
0
),
&
dest
);
}
}
char
*
singlemove_string
(
operands
)
rtx
*
operands
;
{
rtx
x
;
if
(
GET_CODE
(
operands
[
0
])
==
MEM
&&
GET_CODE
(
x
=
XEXP
(
operands
[
0
],
0
))
==
PRE_DEC
)
{
if
(
XEXP
(
x
,
0
)
!=
stack_pointer_rtx
)
abort
();
return
"push%L1 %1"
;
}
else
if
(
GET_CODE
(
operands
[
1
])
==
CONST_DOUBLE
)
{
return
output_move_const_single
(
operands
);
}
else
if
(
GET_CODE
(
operands
[
0
])
==
REG
||
GET_CODE
(
operands
[
1
])
==
REG
)
return
AS2
(
mov
%
L0
,
%
1
,
%
0
);
else
if
(
CONSTANT_P
(
operands
[
1
]))
return
AS2
(
mov
%
L0
,
%
1
,
%
0
);
else
{
output_asm_insn
(
"push%L1 %1"
,
operands
);
return
"pop%L0 %0"
;
}
}
/* Return a REG that occurs in ADDR with coefficient 1.
ADDR can be effectively incremented by incrementing REG. */
static
rtx
find_addr_reg
(
addr
)
rtx
addr
;
{
while
(
GET_CODE
(
addr
)
==
PLUS
)
{
if
(
GET_CODE
(
XEXP
(
addr
,
0
))
==
REG
)
addr
=
XEXP
(
addr
,
0
);
else
if
(
GET_CODE
(
XEXP
(
addr
,
1
))
==
REG
)
addr
=
XEXP
(
addr
,
1
);
else
if
(
CONSTANT_P
(
XEXP
(
addr
,
0
)))
addr
=
XEXP
(
addr
,
1
);
else
if
(
CONSTANT_P
(
XEXP
(
addr
,
1
)))
addr
=
XEXP
(
addr
,
0
);
else
abort
();
}
if
(
GET_CODE
(
addr
)
==
REG
)
return
addr
;
abort
();
}
/* Output an insn to add the constant N to the register X. */
static
void
asm_add
(
n
,
x
)
int
n
;
rtx
x
;
{
rtx
xops
[
2
];
xops
[
1
]
=
x
;
if
(
n
<
0
)
{
xops
[
0
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
-
n
);
output_asm_insn
(
AS2
(
sub
%
L0
,
%
0
,
%
1
),
xops
);
}
else
if
(
n
>
0
)
{
xops
[
0
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
n
);
output_asm_insn
(
AS2
(
add
%
L0
,
%
0
,
%
1
),
xops
);
}
}
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
char
*
output_move_double
(
operands
)
rtx
*
operands
;
{
enum
{
REGOP
,
OFFSOP
,
MEMOP
,
PUSHOP
,
POPOP
,
CNSTOP
,
RNDOP
}
optype0
,
optype1
;
rtx
latehalf
[
2
];
rtx
addreg0
=
0
,
addreg1
=
0
;
/* First classify both operands. */
if
(
REG_P
(
operands
[
0
]))
optype0
=
REGOP
;
else
if
(
offsettable_memref_p
(
operands
[
0
]))
optype0
=
OFFSOP
;
else
if
(
GET_CODE
(
XEXP
(
operands
[
0
],
0
))
==
POST_INC
)
optype0
=
POPOP
;
else
if
(
GET_CODE
(
XEXP
(
operands
[
0
],
0
))
==
PRE_DEC
)
optype0
=
PUSHOP
;
else
if
(
GET_CODE
(
operands
[
0
])
==
MEM
)
optype0
=
MEMOP
;
else
optype0
=
RNDOP
;
if
(
REG_P
(
operands
[
1
]))
optype1
=
REGOP
;
else
if
(
CONSTANT_P
(
operands
[
1
]))
optype1
=
CNSTOP
;
else
if
(
offsettable_memref_p
(
operands
[
1
]))
optype1
=
OFFSOP
;
else
if
(
GET_CODE
(
XEXP
(
operands
[
1
],
0
))
==
POST_INC
)
optype1
=
POPOP
;
else
if
(
GET_CODE
(
XEXP
(
operands
[
1
],
0
))
==
PRE_DEC
)
optype1
=
PUSHOP
;
else
if
(
GET_CODE
(
operands
[
1
])
==
MEM
)
optype1
=
MEMOP
;
else
optype1
=
RNDOP
;
/* Check for the cases that the operand constraints are not
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
if
(
optype0
==
RNDOP
||
optype1
==
RNDOP
)
abort
();
/* If one operand is decrementing and one is incrementing
decrement the former register explicitly
and change that operand into ordinary indexing. */
if
(
optype0
==
PUSHOP
&&
optype1
==
POPOP
)
{
operands
[
0
]
=
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
);
asm_add
(
-
8
,
operands
[
0
]);
operands
[
0
]
=
gen_rtx
(
MEM
,
DImode
,
operands
[
0
]);
optype0
=
OFFSOP
;
}
if
(
optype0
==
POPOP
&&
optype1
==
PUSHOP
)
{
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
asm_add
(
-
8
,
operands
[
1
]);
operands
[
1
]
=
gen_rtx
(
MEM
,
DImode
,
operands
[
1
]);
optype1
=
OFFSOP
;
}
/* If an operand is an unoffsettable memory ref, find a register
we can increment temporarily to make it refer to the second word. */
if
(
optype0
==
MEMOP
)
addreg0
=
find_addr_reg
(
XEXP
(
operands
[
0
],
0
));
if
(
optype1
==
MEMOP
)
addreg1
=
find_addr_reg
(
XEXP
(
operands
[
1
],
0
));
/* Ok, we can do one word at a time.
Normally we do the low-numbered word first,
but if either operand is autodecrementing then we
do the high-numbered word first.
In either case, set up in LATEHALF the operands to use
for the high-numbered word and in some cases alter the
operands in OPERANDS to be suitable for the low-numbered word. */
if
(
optype0
==
REGOP
)
latehalf
[
0
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
0
])
+
1
);
else
if
(
optype0
==
OFFSOP
)
latehalf
[
0
]
=
adj_offsettable_operand
(
operands
[
0
],
4
);
else
latehalf
[
0
]
=
operands
[
0
];
if
(
optype1
==
REGOP
)
latehalf
[
1
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
1
])
+
1
);
else
if
(
optype1
==
OFFSOP
)
latehalf
[
1
]
=
adj_offsettable_operand
(
operands
[
1
],
4
);
else
if
(
optype1
==
CNSTOP
)
{
if
(
GET_CODE
(
operands
[
1
])
==
CONST_DOUBLE
)
split_double
(
operands
[
1
],
&
operands
[
1
],
&
latehalf
[
1
]);
else
if
(
CONSTANT_P
(
operands
[
1
]))
latehalf
[
1
]
=
const0_rtx
;
}
else
latehalf
[
1
]
=
operands
[
1
];
/* If insn is effectively movd N (sp),-(sp) then we will do the
high word first. We should use the adjusted operand 1 (which is N+4 (sp))
for the low word as well, to compensate for the first decrement of sp. */
if
(
optype0
==
PUSHOP
&&
REGNO
(
XEXP
(
XEXP
(
operands
[
0
],
0
),
0
))
==
STACK_POINTER_REGNUM
&&
reg_overlap_mentioned_p
(
stack_pointer_rtx
,
operands
[
1
]))
operands
[
1
]
=
latehalf
[
1
];
/* If one or both operands autodecrementing,
do the two words, high-numbered first. */
/* Likewise, the first move would clobber the source of the second one,
do them in the other order. This happens only for registers;
such overlap can't happen in memory unless the user explicitly
sets it up, and that is an undefined circumstance. */
if
(
optype0
==
PUSHOP
||
optype1
==
PUSHOP
||
(
optype0
==
REGOP
&&
optype1
==
REGOP
&&
REGNO
(
operands
[
0
])
==
REGNO
(
latehalf
[
1
])))
{
/* Make any unoffsettable addresses point at high-numbered word. */
if
(
addreg0
)
asm_add
(
4
,
addreg0
);
if
(
addreg1
)
asm_add
(
4
,
addreg1
);
/* Do that word. */
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
/* Undo the adds we just did. */
if
(
addreg0
)
asm_add
(
-
4
,
addreg0
);
if
(
addreg1
)
asm_add
(
-
4
,
addreg1
);
/* Do low-numbered word. */
return
singlemove_string
(
operands
);
}
/* Normal case: do the two words, low-numbered first. */
output_asm_insn
(
singlemove_string
(
operands
),
operands
);
/* Make any unoffsettable addresses point at high-numbered word. */
if
(
addreg0
)
asm_add
(
4
,
addreg0
);
if
(
addreg1
)
asm_add
(
4
,
addreg1
);
/* Do that word. */
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
/* Undo the adds we just did. */
if
(
addreg0
)
asm_add
(
-
4
,
addreg0
);
if
(
addreg1
)
asm_add
(
-
4
,
addreg1
);
return
""
;
}
int
standard_80387_constant_p
(
x
)
rtx
x
;
{
union
real_extract
u
;
register
double
d
;
bcopy
(
&
CONST_DOUBLE_LOW
(
x
),
&
u
,
sizeof
u
);
d
=
u
.
d
;
if
(
d
==
0
)
return
1
;
if
(
d
==
1
)
return
2
;
/* Note that on the 80387, other constants, such as pi,
are much slower to load as standard constants
than to load from doubles in memory! */
return
0
;
}
char
*
output_move_const_single
(
operands
)
rtx
*
operands
;
{
if
(
FP_REG_P
(
operands
[
0
]))
{
int
conval
=
standard_80387_constant_p
(
operands
[
1
]);
if
(
conval
==
1
)
return
"fldz"
;
if
(
conval
==
2
)
return
"fld1"
;
}
if
(
GET_CODE
(
operands
[
1
])
==
CONST_DOUBLE
)
{
union
{
int
i
[
2
];
double
d
;}
u1
;
union
{
int
i
;
float
f
;}
u2
;
u1
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
operands
[
1
]);
u1
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
operands
[
1
]);
u2
.
f
=
u1
.
d
;
operands
[
1
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
u2
.
i
);
}
return
singlemove_string
(
operands
);
}
/* Returns 1 if OP is either a symbol reference or a sum of a symbol
reference and a constant. */
int
symbolic_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
switch
(
GET_CODE
(
op
))
{
case
SYMBOL_REF
:
case
LABEL_REF
:
return
1
;
case
CONST
:
op
=
XEXP
(
op
,
0
);
return
((
GET_CODE
(
XEXP
(
op
,
0
))
==
SYMBOL_REF
||
GET_CODE
(
XEXP
(
op
,
0
))
==
LABEL_REF
)
&&
GET_CODE
(
XEXP
(
op
,
1
))
==
CONST_INT
);
default
:
return
0
;
}
}
/* Returns 1 if OP contains a symbol reference */
int
symbolic_reference_mentioned_p
(
op
)
rtx
op
;
{
register
char
*
fmt
;
register
int
i
;
if
(
GET_CODE
(
op
)
==
SYMBOL_REF
||
GET_CODE
(
op
)
==
LABEL_REF
)
return
1
;
fmt
=
GET_RTX_FORMAT
(
GET_CODE
(
op
));
for
(
i
=
GET_RTX_LENGTH
(
GET_CODE
(
op
))
-
1
;
i
>=
0
;
i
--
)
{
if
(
fmt
[
i
]
==
'E'
)
{
register
int
j
;
for
(
j
=
XVECLEN
(
op
,
i
)
-
1
;
j
>=
0
;
j
--
)
if
(
symbolic_reference_mentioned_p
(
XVECEXP
(
op
,
i
,
j
)))
return
1
;
}
else
if
(
fmt
[
i
]
==
'e'
&&
symbolic_reference_mentioned_p
(
XEXP
(
op
,
i
)))
return
1
;
}
return
0
;
}
/* Return a legitimate reference for ORIG (an address) using the
register REG. If REG is 0, a new pseudo is generated.
There are three types of references that must be handled:
1. Global data references must load the address from the GOT, via
the PIC reg. An insn is emitted to do this load, and the reg is
returned.
2. Static data references must compute the address as an offset
from the GOT, whose base is in the PIC reg. An insn is emitted to
compute the address into a reg, and the reg is returned. Static
data objects have SYMBOL_REF_FLAG set to differentiate them from
global data objects.
3. Constant pool addresses must be handled special. They are
considered legitimate addresses, but only if not used with regs.
When printed, the output routines know to print the reference with the
PIC reg, even though the PIC reg doesn't appear in the RTL.
GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
reg also appears in the address (except for constant pool references,
noted above).
"switch" statements also require special handling when generating
PIC code. See comments by the `casesi' insn in i386.md for details. */
rtx
legitimize_pic_address
(
orig
,
reg
)
rtx
orig
;
rtx
reg
;
{
rtx
addr
=
orig
;
rtx
new
=
orig
;
if
(
GET_CODE
(
addr
)
==
SYMBOL_REF
||
GET_CODE
(
addr
)
==
LABEL_REF
)
{
if
(
GET_CODE
(
addr
)
==
SYMBOL_REF
&&
CONSTANT_POOL_ADDRESS_P
(
addr
))
reg
=
new
=
orig
;
else
{
if
(
reg
==
0
)
reg
=
gen_reg_rtx
(
Pmode
);
if
(
GET_CODE
(
addr
)
==
SYMBOL_REF
&&
SYMBOL_REF_FLAG
(
addr
))
new
=
gen_rtx
(
PLUS
,
Pmode
,
pic_offset_table_rtx
,
orig
);
else
new
=
gen_rtx
(
MEM
,
Pmode
,
gen_rtx
(
PLUS
,
Pmode
,
pic_offset_table_rtx
,
orig
));
emit_move_insn
(
reg
,
new
);
}
current_function_uses_pic_offset_table
=
1
;
return
reg
;
}
else
if
(
GET_CODE
(
addr
)
==
CONST
||
GET_CODE
(
addr
)
==
PLUS
)
{
rtx
base
;
if
(
GET_CODE
(
addr
)
==
CONST
)
{
addr
=
XEXP
(
addr
,
0
);
if
(
GET_CODE
(
addr
)
!=
PLUS
)
abort
();
}
if
(
XEXP
(
addr
,
0
)
==
pic_offset_table_rtx
)
return
orig
;
if
(
reg
==
0
)
reg
=
gen_reg_rtx
(
Pmode
);
base
=
legitimize_pic_address
(
XEXP
(
addr
,
0
),
reg
);
addr
=
legitimize_pic_address
(
XEXP
(
addr
,
1
),
base
==
reg
?
0
:
reg
);
if
(
GET_CODE
(
addr
)
==
CONST_INT
)
return
plus_constant
(
base
,
INTVAL
(
addr
));
if
(
GET_CODE
(
addr
)
==
PLUS
&&
CONSTANT_P
(
XEXP
(
addr
,
1
)))
{
base
=
gen_rtx
(
PLUS
,
Pmode
,
base
,
XEXP
(
addr
,
0
));
addr
=
XEXP
(
addr
,
1
);
}
return
gen_rtx
(
PLUS
,
Pmode
,
base
,
addr
);
}
return
new
;
}
/* Emit insns to move operands[1] into operands[0]. */
void
emit_pic_move
(
operands
,
mode
)
rtx
*
operands
;
enum
machine_mode
mode
;
{
rtx
temp
=
reload_in_progress
?
operands
[
0
]
:
gen_reg_rtx
(
Pmode
);
if
(
GET_CODE
(
operands
[
0
])
==
MEM
&&
SYMBOLIC_CONST
(
operands
[
1
]))
operands
[
1
]
=
(
rtx
)
force_reg
(
SImode
,
operands
[
1
]);
else
operands
[
1
]
=
legitimize_pic_address
(
operands
[
1
],
temp
);
}
/* This function generates the assembly code for function entry.
FILE is an stdio stream to output the code to.
SIZE is an int: how many units of temporary storage to allocate. */
void
function_prologue
(
file
,
size
)
FILE
*
file
;
int
size
;
{
register
int
regno
;
int
limit
;
rtx
xops
[
4
];
xops
[
0
]
=
stack_pointer_rtx
;
xops
[
1
]
=
frame_pointer_rtx
;
xops
[
2
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
size
);
if
(
frame_pointer_needed
)
{
output_asm_insn
(
"push%L1 %1"
,
xops
);
output_asm_insn
(
AS2
(
mov
%
L0
,
%
0
,
%
1
),
xops
);
}
if
(
size
)
output_asm_insn
(
AS2
(
sub
%
L0
,
%
2
,
%
0
),
xops
);
/* Note If use enter it is NOT reversed args.
This one is not reversed from intel!!
I think enter is slower. Also sdb doesn't like it.
But if you want it the code is:
{
xops[3] = const0_rtx;
output_asm_insn ("enter %2,%3", xops);
}
*/
limit
=
(
frame_pointer_needed
?
FRAME_POINTER_REGNUM
:
STACK_POINTER_REGNUM
);
for
(
regno
=
limit
-
1
;
regno
>=
0
;
regno
--
)
if
((
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
||
(
current_function_uses_pic_offset_table
&&
regno
==
PIC_OFFSET_TABLE_REGNUM
))
{
xops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
regno
);
output_asm_insn
(
"push%L0 %0"
,
xops
);
}
if
(
current_function_uses_pic_offset_table
)
{
xops
[
0
]
=
pic_offset_table_rtx
;
xops
[
1
]
=
(
rtx
)
gen_label_rtx
();
output_asm_insn
(
AS1
(
call
,
%
P1
),
xops
);
ASM_OUTPUT_INTERNAL_LABEL
(
file
,
"L"
,
CODE_LABEL_NUMBER
(
xops
[
1
]));
output_asm_insn
(
AS1
(
pop
%
L0
,
%
0
),
xops
);
output_asm_insn
(
"addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0"
,
xops
);
}
}
/* Return 1 if it is appropriate to emit `ret' instructions in the
body of a function. Do this only if the epilogue is simple, needing a
couple of insns. Prior to reloading, we can't tell how many registers
must be saved, so return 0 then.
If NON_SAVING_SETJMP is defined and true, then it is not possible
for the epilogue to be simple, so return 0. This is a special case
since NON_SAVING_SETJMP will not cause regs_ever_live to change until
final, but jump_optimize may need to know sooner if a `return' is OK. */
int
simple_386_epilogue
()
{
int
regno
;
int
nregs
=
0
;
int
reglimit
=
(
frame_pointer_needed
?
FRAME_POINTER_REGNUM
:
STACK_POINTER_REGNUM
);
#ifdef NON_SAVING_SETJMP
if
(
NON_SAVING_SETJMP
&&
current_function_calls_setjmp
)
return
0
;
#endif
if
(
!
reload_completed
)
return
0
;
for
(
regno
=
reglimit
-
1
;
regno
>=
0
;
regno
--
)
if
((
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
||
(
current_function_uses_pic_offset_table
&&
regno
==
PIC_OFFSET_TABLE_REGNUM
))
nregs
++
;
return
nregs
==
0
||
!
frame_pointer_needed
;
}
/* This function generates the assembly code for function exit.
FILE is an stdio stream to output the code to.
SIZE is an int: how many units of temporary storage to deallocate. */
void
function_epilogue
(
file
,
size
)
FILE
*
file
;
int
size
;
{
register
int
regno
;
register
int
nregs
,
limit
;
int
offset
;
rtx
xops
[
3
];
/* Compute the number of registers to pop */
limit
=
(
frame_pointer_needed
?
FRAME_POINTER_REGNUM
:
STACK_POINTER_REGNUM
);
nregs
=
0
;
for
(
regno
=
limit
-
1
;
regno
>=
0
;
regno
--
)
if
((
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
||
(
current_function_uses_pic_offset_table
&&
regno
==
PIC_OFFSET_TABLE_REGNUM
))
nregs
++
;
/* sp is often unreliable so we must go off the frame pointer,
*/
/* In reality, we may not care if sp is unreliable, because we can
restore the register relative to the frame pointer. In theory,
since each move is the same speed as a pop, and we don't need the
leal, this is faster. For now restore multiple registers the old
way. */
offset
=
-
size
-
(
nregs
*
UNITS_PER_WORD
);
xops
[
2
]
=
stack_pointer_rtx
;
if
(
nregs
>
1
||
!
frame_pointer_needed
)
{
if
(
frame_pointer_needed
)
{
xops
[
0
]
=
adj_offsettable_operand
(
AT_BP
(
Pmode
),
offset
);
output_asm_insn
(
AS2
(
lea
%
L2
,
%
0
,
%
2
),
xops
);
}
for
(
regno
=
0
;
regno
<
limit
;
regno
++
)
if
((
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
||
(
current_function_uses_pic_offset_table
&&
regno
==
PIC_OFFSET_TABLE_REGNUM
))
{
xops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
regno
);
output_asm_insn
(
"pop%L0 %0"
,
xops
);
}
}
else
for
(
regno
=
0
;
regno
<
limit
;
regno
++
)
if
((
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
||
(
current_function_uses_pic_offset_table
&&
regno
==
PIC_OFFSET_TABLE_REGNUM
))
{
xops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
regno
);
xops
[
1
]
=
adj_offsettable_operand
(
AT_BP
(
Pmode
),
offset
);
output_asm_insn
(
AS2
(
mov
%
L0
,
%
1
,
%
0
),
xops
);
offset
+=
4
;
}
if
(
frame_pointer_needed
)
{
/* On i486, mov & pop is faster than "leave". */
if
(
TARGET_486
)
{
xops
[
0
]
=
frame_pointer_rtx
;
output_asm_insn
(
AS2
(
mov
%
L2
,
%
0
,
%
2
),
xops
);
output_asm_insn
(
"pop%L0 %0"
,
xops
);
}
else
output_asm_insn
(
"leave"
,
xops
);
}
else
if
(
size
)
{
/* If there is no frame pointer, we must still release the frame. */
xops
[
0
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
size
);
output_asm_insn
(
AS2
(
add
%
L2
,
%
0
,
%
2
),
xops
);
}
if
(
current_function_pops_args
&&
current_function_args_size
)
{
xops
[
1
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
current_function_pops_args
);
/* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If
asked to pop more, pop return address, do explicit add, and jump
indirectly to the caller. */
if
(
current_function_pops_args
>=
32768
)
{
/* ??? Which register to use here? */
xops
[
0
]
=
gen_rtx
(
REG
,
SImode
,
2
);
output_asm_insn
(
"pop%L0 %0"
,
xops
);
output_asm_insn
(
AS2
(
add
%
L2
,
%
1
,
%
2
),
xops
);
output_asm_insn
(
"jmp %*%0"
,
xops
);
}
else
output_asm_insn
(
"ret %1"
,
xops
);
}
else
if
(
current_function_returns_struct
)
{
xops
[
0
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
4
);
output_asm_insn
(
"ret %0"
,
xops
);
}
else
output_asm_insn
(
"ret"
,
xops
);
}
/* Print an integer constant expression in assembler syntax. Addition
and subtraction are the only arithmetic that may appear in these
expressions. FILE is the stdio stream to write to, X is the rtx, and
CODE is the operand print code from the output string. */
static
void
output_pic_addr_const
(
file
,
x
,
code
)
FILE
*
file
;
rtx
x
;
int
code
;
{
char
buf
[
256
];
switch
(
GET_CODE
(
x
))
{
case
PC
:
if
(
flag_pic
)
putc
(
'.'
,
file
);
else
abort
();
break
;
case
SYMBOL_REF
:
case
LABEL_REF
:
if
(
GET_CODE
(
x
)
==
SYMBOL_REF
)
assemble_name
(
file
,
XSTR
(
x
,
0
));
else
{
ASM_GENERATE_INTERNAL_LABEL
(
buf
,
"L"
,
CODE_LABEL_NUMBER
(
XEXP
(
x
,
0
)));
assemble_name
(
asm_out_file
,
buf
);
}
if
(
GET_CODE
(
x
)
==
SYMBOL_REF
&&
CONSTANT_POOL_ADDRESS_P
(
x
))
fprintf
(
file
,
"@GOTOFF(%%ebx)"
);
else
if
(
code
==
'P'
)
fprintf
(
file
,
"@PLT"
);
else
if
(
GET_CODE
(
x
)
==
LABEL_REF
||
!
SYMBOL_REF_FLAG
(
x
))
fprintf
(
file
,
"@GOT"
);
else
fprintf
(
file
,
"@GOTOFF"
);
break
;
case
CODE_LABEL
:
ASM_GENERATE_INTERNAL_LABEL
(
buf
,
"L"
,
CODE_LABEL_NUMBER
(
x
));
assemble_name
(
asm_out_file
,
buf
);
break
;
case
CONST_INT
:
fprintf
(
file
,
"%d"
,
INTVAL
(
x
));
break
;
case
CONST
:
/* This used to output parentheses around the expression,
but that does not work on the 386 (either ATT or BSD assembler). */
output_pic_addr_const
(
file
,
XEXP
(
x
,
0
),
code
);
break
;
case
CONST_DOUBLE
:
if
(
GET_MODE
(
x
)
==
VOIDmode
)
{
/* We can use %d if the number is <32 bits and positive. */
if
(
CONST_DOUBLE_HIGH
(
x
)
||
CONST_DOUBLE_LOW
(
x
)
<
0
)
fprintf
(
file
,
"0x%x%08x"
,
CONST_DOUBLE_HIGH
(
x
),
CONST_DOUBLE_LOW
(
x
));
else
fprintf
(
file
,
"%d"
,
CONST_DOUBLE_LOW
(
x
));
}
else
/* We can't handle floating point constants;
PRINT_OPERAND must handle them. */
output_operand_lossage
(
"floating constant misused"
);
break
;
case
PLUS
:
/* Some assemblers need integer constants to appear last (eg masm). */
if
(
GET_CODE
(
XEXP
(
x
,
0
))
==
CONST_INT
)
{
output_pic_addr_const
(
file
,
XEXP
(
x
,
1
),
code
);
if
(
INTVAL
(
XEXP
(
x
,
0
))
>=
0
)
fprintf
(
file
,
"+"
);
output_pic_addr_const
(
file
,
XEXP
(
x
,
0
),
code
);
}
else
{
output_pic_addr_const
(
file
,
XEXP
(
x
,
0
),
code
);
if
(
INTVAL
(
XEXP
(
x
,
1
))
>=
0
)
fprintf
(
file
,
"+"
);
output_pic_addr_const
(
file
,
XEXP
(
x
,
1
),
code
);
}
break
;
case
MINUS
:
output_pic_addr_const
(
file
,
XEXP
(
x
,
0
),
code
);
fprintf
(
file
,
"-"
);
output_pic_addr_const
(
file
,
XEXP
(
x
,
1
),
code
);
break
;
default
:
output_operand_lossage
(
"invalid expression as operand"
);
}
}
/* Print the name of a register based on its machine mode and number.
If CODE is 'w', pretend the mode is HImode.
If CODE is 'b', pretend the mode is QImode.
If CODE is 'k', pretend the mode is SImode.
If CODE is 'h', pretend the reg is the `high' byte register.
If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op. */
#define PRINT_REG(X, CODE, FILE) \
do { if (REGNO (X) == ARG_POINTER_REGNUM) \
abort (); \
fprintf (FILE, "%s", RP); \
switch ((CODE == 'w' ? 2 \
: CODE == 'b' ? 1 \
: CODE == 'k' ? 4 \
: CODE == 'y' ? 3 \
: CODE == 'h' ? 0 \
: GET_MODE_SIZE (GET_MODE (X)))) \
{ \
case 3: \
if (STACK_TOP_P (X)) \
{ \
fputs ("st(0)", FILE); \
break; \
} \
case 4: \
case 8: \
if (!FP_REG_P (X)) fputs ("e", FILE); \
case 2: \
fputs (hi_reg_name[REGNO (X)], FILE); \
break; \
case 1: \
fputs (qi_reg_name[REGNO (X)], FILE); \
break; \
case 0: \
fputs (qi_high_reg_name[REGNO (X)], FILE); \
break; \
} \
} while (0)
/* Meaning of CODE:
f -- float insn (print a CONST_DOUBLE as a float rather than in hex).
D,L,W,B,Q,S -- print the opcode suffix for specified size of operand.
R -- print the prefix for register names.
z -- print the opcode suffix for the size of the current operand.
* -- print a star (in certain assembler syntax)
w -- print the operand as if it's a "word" (HImode) even if it isn't.
c -- don't print special prefixes before constant operands.
*/
void
print_operand
(
file
,
x
,
code
)
FILE
*
file
;
rtx
x
;
int
code
;
{
if
(
code
)
{
switch
(
code
)
{
case
'*'
:
if
(
USE_STAR
)
putc
(
'*'
,
file
);
return
;
case
'D'
:
PUT_OP_SIZE
(
code
,
'l'
,
file
);
case
'L'
:
PUT_OP_SIZE
(
code
,
'l'
,
file
);
return
;
case
'W'
:
PUT_OP_SIZE
(
code
,
'w'
,
file
);
return
;
case
'B'
:
PUT_OP_SIZE
(
code
,
'b'
,
file
);
return
;
case
'Q'
:
PUT_OP_SIZE
(
code
,
'l'
,
file
);
return
;
case
'S'
:
PUT_OP_SIZE
(
code
,
's'
,
file
);
return
;
case
'R'
:
fprintf
(
file
,
"%s"
,
RP
);
return
;
case
'z'
:
/* 387 opcodes don't get size suffixes if the operands are
registers. */
if
(
STACK_REG_P
(
x
))
return
;
/* this is the size of op from size of operand */
switch
(
GET_MODE_SIZE
(
GET_MODE
(
x
)))
{
case
1
:
PUT_OP_SIZE
(
'B'
,
'b'
,
file
);
return
;
case
2
:
PUT_OP_SIZE
(
'W'
,
'w'
,
file
);
return
;
case
4
:
if
(
GET_MODE
(
x
)
==
SFmode
)
{
PUT_OP_SIZE
(
'S'
,
's'
,
file
);
return
;
}
else
PUT_OP_SIZE
(
'L'
,
'l'
,
file
);
return
;
case
8
:
if
(
GET_MODE_CLASS
(
GET_MODE
(
x
))
==
MODE_INT
)
PUT_OP_SIZE
(
'Q'
,
'l'
,
file
);
PUT_OP_SIZE
(
'Q'
,
'l'
,
file
);
return
;
}
}
}
if
(
GET_CODE
(
x
)
==
REG
)
{
PRINT_REG
(
x
,
code
,
file
);
}
else
if
(
GET_CODE
(
x
)
==
MEM
)
{
PRINT_PTR
(
x
,
file
);
if
(
CONSTANT_ADDRESS_P
(
XEXP
(
x
,
0
)))
{
if
(
flag_pic
)
output_pic_addr_const
(
file
,
XEXP
(
x
,
0
),
code
);
else
output_addr_const
(
file
,
XEXP
(
x
,
0
));
}
else
output_address
(
XEXP
(
x
,
0
));
}
else
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE
(
x
)
==
SFmode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
union
{
float
f
;
int
i
;
}
u1
;
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
u1
.
f
=
u
.
d
;
if
(
code
==
'f'
)
fprintf
(
file
,
"%.22e"
,
u1
.
f
);
else
{
PRINT_IMMED_PREFIX
(
file
);
fprintf
(
file
,
"0x%x"
,
u1
.
i
);
}
}
else
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE
(
x
)
==
DFmode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
fprintf
(
file
,
"%.22e"
,
u
.
d
);
}
else
{
if
(
code
!=
'c'
&&
code
!=
'P'
)
{
if
(
GET_CODE
(
x
)
==
CONST_INT
)
PRINT_IMMED_PREFIX
(
file
);
else
if
(
GET_CODE
(
x
)
==
CONST
||
GET_CODE
(
x
)
==
SYMBOL_REF
||
GET_CODE
(
x
)
==
LABEL_REF
)
PRINT_OFFSET_PREFIX
(
file
);
}
if
(
flag_pic
)
output_pic_addr_const
(
file
,
x
,
code
);
else
output_addr_const
(
file
,
x
);
}
}
/* Print a memory operand whose address is ADDR. */
void
print_operand_address
(
file
,
addr
)
FILE
*
file
;
register
rtx
addr
;
{
register
rtx
reg1
,
reg2
,
breg
,
ireg
;
rtx
offset
;
switch
(
GET_CODE
(
addr
))
{
case
REG
:
ADDR_BEG
(
file
);
fprintf
(
file
,
"%se"
,
RP
);
fputs
(
hi_reg_name
[
REGNO
(
addr
)],
file
);
ADDR_END
(
file
);
break
;
case
PLUS
:
reg1
=
0
;
reg2
=
0
;
ireg
=
0
;
breg
=
0
;
offset
=
0
;
if
(
CONSTANT_ADDRESS_P
(
XEXP
(
addr
,
0
)))
{
offset
=
XEXP
(
addr
,
0
);
addr
=
XEXP
(
addr
,
1
);
}
else
if
(
CONSTANT_ADDRESS_P
(
XEXP
(
addr
,
1
)))
{
offset
=
XEXP
(
addr
,
1
);
addr
=
XEXP
(
addr
,
0
);
}
if
(
GET_CODE
(
addr
)
!=
PLUS
)
;
else
if
(
GET_CODE
(
XEXP
(
addr
,
0
))
==
MULT
)
{
reg1
=
XEXP
(
addr
,
0
);
addr
=
XEXP
(
addr
,
1
);
}
else
if
(
GET_CODE
(
XEXP
(
addr
,
1
))
==
MULT
)
{
reg1
=
XEXP
(
addr
,
1
);
addr
=
XEXP
(
addr
,
0
);
}
else
if
(
GET_CODE
(
XEXP
(
addr
,
0
))
==
REG
)
{
reg1
=
XEXP
(
addr
,
0
);
addr
=
XEXP
(
addr
,
1
);
}
else
if
(
GET_CODE
(
XEXP
(
addr
,
1
))
==
REG
)
{
reg1
=
XEXP
(
addr
,
1
);
addr
=
XEXP
(
addr
,
0
);
}
if
(
GET_CODE
(
addr
)
==
REG
||
GET_CODE
(
addr
)
==
MULT
)
{
if
(
reg1
==
0
)
reg1
=
addr
;
else
reg2
=
addr
;
addr
=
0
;
}
if
(
offset
!=
0
)
{
if
(
addr
!=
0
)
abort
();
addr
=
offset
;
}
if
((
reg1
&&
GET_CODE
(
reg1
)
==
MULT
)
||
(
reg2
!=
0
&&
REGNO_OK_FOR_BASE_P
(
REGNO
(
reg2
))))
{
breg
=
reg2
;
ireg
=
reg1
;
}
else
if
(
reg1
!=
0
&&
REGNO_OK_FOR_BASE_P
(
REGNO
(
reg1
)))
{
breg
=
reg1
;
ireg
=
reg2
;
}
if
(
ireg
!=
0
||
breg
!=
0
)
{
int
scale
=
1
;
if
(
addr
!=
0
)
{
if
(
GET_CODE
(
addr
)
==
LABEL_REF
)
output_asm_label
(
addr
);
else
{
if
(
flag_pic
)
output_pic_addr_const
(
file
,
addr
,
0
);
else
output_addr_const
(
file
,
addr
);
}
}
if
(
ireg
!=
0
&&
GET_CODE
(
ireg
)
==
MULT
)
{
scale
=
INTVAL
(
XEXP
(
ireg
,
1
));
ireg
=
XEXP
(
ireg
,
0
);
}
/* The stack pointer can only appear as a base register,
never an index register, so exchange the regs if it is wrong. */
if
(
scale
==
1
&&
ireg
&&
REGNO
(
ireg
)
==
STACK_POINTER_REGNUM
)
{
rtx
tmp
;
tmp
=
breg
;
breg
=
ireg
;
ireg
=
tmp
;
}
/* output breg+ireg*scale */
PRINT_B_I_S
(
breg
,
ireg
,
scale
,
file
);
break
;
}
case
MULT
:
{
int
scale
;
if
(
GET_CODE
(
XEXP
(
addr
,
0
))
==
CONST_INT
)
{
scale
=
INTVAL
(
XEXP
(
addr
,
0
));
ireg
=
XEXP
(
addr
,
1
);
}
else
{
scale
=
INTVAL
(
XEXP
(
addr
,
1
));
ireg
=
XEXP
(
addr
,
0
);
}
output_addr_const
(
file
,
const0_rtx
);
PRINT_B_I_S
((
rtx
)
0
,
ireg
,
scale
,
file
);
}
break
;
default
:
if
(
GET_CODE
(
addr
)
==
CONST_INT
&&
INTVAL
(
addr
)
<
0x8000
&&
INTVAL
(
addr
)
>=
-
0x8000
)
fprintf
(
file
,
"%d"
,
INTVAL
(
addr
));
else
{
if
(
flag_pic
)
output_pic_addr_const
(
file
,
addr
,
0
);
else
output_addr_const
(
file
,
addr
);
}
}
}
/* Set the cc_status for the results of an insn whose pattern is EXP.
On the 80386, we assume that only test and compare insns, as well
as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT,
ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully.
Also, we assume that jumps and moves don't affect the condition codes.
All else, clobbers the condition codes, by assumption.
We assume that ALL add, minus, etc. instructions effect the condition
codes. This MUST be consistent with i386.md. */
void
notice_update_cc
(
exp
)
rtx
exp
;
{
if
(
GET_CODE
(
exp
)
==
SET
)
{
/* Jumps do not alter the cc's. */
if
(
SET_DEST
(
exp
)
==
pc_rtx
)
return
;
/* Moving register or memory into a register:
it doesn't alter the cc's, but it might invalidate
the RTX's which we remember the cc's came from.
(Note that moving a constant 0 or 1 MAY set the cc's). */
if
(
REG_P
(
SET_DEST
(
exp
))
&&
(
REG_P
(
SET_SRC
(
exp
))
||
GET_CODE
(
SET_SRC
(
exp
))
==
MEM
))
{
if
(
cc_status
.
value1
&&
reg_overlap_mentioned_p
(
SET_DEST
(
exp
),
cc_status
.
value1
))
cc_status
.
value1
=
0
;
if
(
cc_status
.
value2
&&
reg_overlap_mentioned_p
(
SET_DEST
(
exp
),
cc_status
.
value2
))
cc_status
.
value2
=
0
;
return
;
}
/* Moving register into memory doesn't alter the cc's.
It may invalidate the RTX's which we remember the cc's came from. */
if
(
GET_CODE
(
SET_DEST
(
exp
))
==
MEM
&&
REG_P
(
SET_SRC
(
exp
)))
{
if
(
cc_status
.
value1
&&
GET_CODE
(
cc_status
.
value1
)
==
MEM
)
cc_status
.
value1
=
0
;
if
(
cc_status
.
value2
&&
GET_CODE
(
cc_status
.
value2
)
==
MEM
)
cc_status
.
value2
=
0
;
return
;
}
/* Function calls clobber the cc's. */
else
if
(
GET_CODE
(
SET_SRC
(
exp
))
==
CALL
)
{
CC_STATUS_INIT
;
return
;
}
/* Tests and compares set the cc's in predictable ways. */
else
if
(
SET_DEST
(
exp
)
==
cc0_rtx
)
{
CC_STATUS_INIT
;
cc_status
.
value1
=
SET_SRC
(
exp
);
return
;
}
/* Certain instructions effect the condition codes. */
else
if
(
GET_MODE
(
SET_SRC
(
exp
))
==
SImode
||
GET_MODE
(
SET_SRC
(
exp
))
==
HImode
||
GET_MODE
(
SET_SRC
(
exp
))
==
QImode
)
switch
(
GET_CODE
(
SET_SRC
(
exp
)))
{
case
ASHIFTRT
:
case
LSHIFTRT
:
case
ASHIFT
:
case
LSHIFT
:
/* Shifts on the 386 don't set the condition codes if the
shift count is zero. */
if
(
GET_CODE
(
XEXP
(
SET_SRC
(
exp
),
1
))
!=
CONST_INT
)
{
CC_STATUS_INIT
;
break
;
}
/* We assume that the CONST_INT is non-zero (this rtx would
have been deleted if it were zero. */
case
PLUS
:
case
MINUS
:
case
NEG
:
case
AND
:
case
IOR
:
case
XOR
:
cc_status
.
flags
=
CC_NO_OVERFLOW
;
cc_status
.
value1
=
SET_SRC
(
exp
);
cc_status
.
value2
=
SET_DEST
(
exp
);
break
;
default
:
CC_STATUS_INIT
;
}
else
{
CC_STATUS_INIT
;
}
}
else
if
(
GET_CODE
(
exp
)
==
PARALLEL
&&
GET_CODE
(
XVECEXP
(
exp
,
0
,
0
))
==
SET
)
{
if
(
SET_DEST
(
XVECEXP
(
exp
,
0
,
0
))
==
pc_rtx
)
return
;
if
(
SET_DEST
(
XVECEXP
(
exp
,
0
,
0
))
==
cc0_rtx
)
{
CC_STATUS_INIT
;
cc_status
.
value1
=
SET_SRC
(
XVECEXP
(
exp
,
0
,
0
));
return
;
}
CC_STATUS_INIT
;
}
else
{
CC_STATUS_INIT
;
}
}
/* Split one or more DImode RTL references into pairs of SImode
references. The RTL can be REG, offsettable MEM, integer constant, or
CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
split and "num" is its length. lo_half and hi_half are output arrays
that parallel "operands". */
void
split_di
(
operands
,
num
,
lo_half
,
hi_half
)
rtx
operands
[];
int
num
;
rtx
lo_half
[],
hi_half
[];
{
while
(
num
--
)
{
if
(
GET_CODE
(
operands
[
num
])
==
REG
)
{
lo_half
[
num
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
num
]));
hi_half
[
num
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
num
])
+
1
);
}
else
if
(
CONSTANT_P
(
operands
[
num
]))
{
split_double
(
operands
[
num
],
&
lo_half
[
num
],
&
hi_half
[
num
]);
}
else
if
(
offsettable_memref_p
(
operands
[
num
]))
{
lo_half
[
num
]
=
operands
[
num
];
hi_half
[
num
]
=
adj_offsettable_operand
(
operands
[
num
],
4
);
}
else
abort
();
}
}
/* Return 1 if this is a valid binary operation on a 387.
OP is the expression matched, and MODE is its mode. */
int
binary_387_op
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
mode
!=
VOIDmode
&&
mode
!=
GET_MODE
(
op
))
return
0
;
switch
(
GET_CODE
(
op
))
{
case
PLUS
:
case
MINUS
:
case
MULT
:
case
DIV
:
return
GET_MODE_CLASS
(
GET_MODE
(
op
))
==
MODE_FLOAT
;
default
:
return
0
;
}
}
/* Return 1 if this is a valid conversion operation on a 387.
OP is the expression matched, and MODE is its mode. */
int
convert_387_op
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
mode
!=
VOIDmode
&&
mode
!=
GET_MODE
(
op
))
return
0
;
switch
(
GET_CODE
(
op
))
{
case
FLOAT
:
return
GET_MODE
(
XEXP
(
op
,
0
))
==
SImode
;
case
FLOAT_EXTEND
:
return
mode
==
DFmode
&&
GET_MODE
(
XEXP
(
op
,
0
))
==
SFmode
;
default
:
return
0
;
}
}
/* Return 1 if this is a valid "float from int" operation on a 387.
OP is the expression matched, and MODE is its mode. */
int
float_op
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
mode
!=
VOIDmode
&&
mode
!=
GET_MODE
(
op
))
return
0
;
return
GET_CODE
(
op
)
==
FLOAT
&&
GET_MODE_CLASS
(
GET_MODE
(
op
))
==
MODE_FLOAT
;
}
/* Return 1 if this is a valid shift or rotate operation on a 386.
OP is the expression matched, and MODE is its mode. */
int
shift_op
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
rtx
operand
=
XEXP
(
op
,
0
);
if
(
mode
!=
VOIDmode
&&
mode
!=
GET_MODE
(
op
))
return
0
;
if
(
GET_MODE
(
operand
)
!=
GET_MODE
(
op
)
||
GET_MODE_CLASS
(
GET_MODE
(
op
))
!=
MODE_INT
)
return
0
;
return
(
GET_CODE
(
op
)
==
ASHIFT
||
GET_CODE
(
op
)
==
ASHIFTRT
||
GET_CODE
(
op
)
==
LSHIFTRT
||
GET_CODE
(
op
)
==
ROTATE
||
GET_CODE
(
op
)
==
ROTATERT
);
}
/* Output code to perform a 387 binary operation in INSN, one of PLUS,
MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3]
is the expression of the binary operation. The output may either be
emitted here, or returned to the caller, like all output_* functions.
There is no guarantee that the operands are the same mode, as they
might be within FLOAT or FLOAT_EXTEND expressions. */
char
*
output_387_binary_op
(
insn
,
operands
)
rtx
insn
;
rtx
*
operands
;
{
rtx
temp
;
char
*
base_op
;
static
char
buf
[
100
];
switch
(
GET_CODE
(
operands
[
3
]))
{
case
PLUS
:
if
(
GET_MODE_CLASS
(
GET_MODE
(
operands
[
1
]))
==
MODE_INT
||
GET_MODE_CLASS
(
GET_MODE
(
operands
[
2
]))
==
MODE_INT
)
base_op
=
"fiadd"
;
else
base_op
=
"fadd"
;
break
;
case
MINUS
:
if
(
GET_MODE_CLASS
(
GET_MODE
(
operands
[
1
]))
==
MODE_INT
||
GET_MODE_CLASS
(
GET_MODE
(
operands
[
2
]))
==
MODE_INT
)
base_op
=
"fisub"
;
else
base_op
=
"fsub"
;
break
;
case
MULT
:
if
(
GET_MODE_CLASS
(
GET_MODE
(
operands
[
1
]))
==
MODE_INT
||
GET_MODE_CLASS
(
GET_MODE
(
operands
[
2
]))
==
MODE_INT
)
base_op
=
"fimul"
;
else
base_op
=
"fmul"
;
break
;
case
DIV
:
if
(
GET_MODE_CLASS
(
GET_MODE
(
operands
[
1
]))
==
MODE_INT
||
GET_MODE_CLASS
(
GET_MODE
(
operands
[
2
]))
==
MODE_INT
)
base_op
=
"fidiv"
;
else
base_op
=
"fdiv"
;
break
;
default
:
abort
();
}
strcpy
(
buf
,
base_op
);
switch
(
GET_CODE
(
operands
[
3
]))
{
case
MULT
:
case
PLUS
:
if
(
REG_P
(
operands
[
2
])
&&
REGNO
(
operands
[
0
])
==
REGNO
(
operands
[
2
]))
{
temp
=
operands
[
2
];
operands
[
2
]
=
operands
[
1
];
operands
[
1
]
=
temp
;
}
if
(
GET_CODE
(
operands
[
2
])
==
MEM
)
return
strcat
(
buf
,
AS1
(
%
z2
,
%
2
));
if
(
NON_STACK_REG_P
(
operands
[
1
]))
{
output_op_from_reg
(
operands
[
1
],
strcat
(
buf
,
AS1
(
%
z0
,
%
1
)));
RET
;
}
else
if
(
NON_STACK_REG_P
(
operands
[
2
]))
{
output_op_from_reg
(
operands
[
2
],
strcat
(
buf
,
AS1
(
%
z0
,
%
1
)));
RET
;
}
if
(
find_regno_note
(
insn
,
REG_DEAD
,
REGNO
(
operands
[
2
])))
return
strcat
(
buf
,
AS2
(
p
,
%
2
,
%
0
));
if
(
STACK_TOP_P
(
operands
[
0
]))
return
strcat
(
buf
,
AS2
(,
%
y2
,
%
0
));
else
return
strcat
(
buf
,
AS2
(,
%
2
,
%
0
));
case
MINUS
:
case
DIV
:
if
(
GET_CODE
(
operands
[
1
])
==
MEM
)
return
strcat
(
buf
,
AS1
(
r
%
z1
,
%
1
));
if
(
GET_CODE
(
operands
[
2
])
==
MEM
)
return
strcat
(
buf
,
AS1
(
%
z2
,
%
2
));
if
(
NON_STACK_REG_P
(
operands
[
1
]))
{
output_op_from_reg
(
operands
[
1
],
strcat
(
buf
,
AS1
(
r
%
z0
,
%
1
)));
RET
;
}
else
if
(
NON_STACK_REG_P
(
operands
[
2
]))
{
output_op_from_reg
(
operands
[
2
],
strcat
(
buf
,
AS1
(
%
z0
,
%
1
)));
RET
;
}
if
(
!
STACK_REG_P
(
operands
[
1
])
||
!
STACK_REG_P
(
operands
[
2
]))
abort
();
if
(
find_regno_note
(
insn
,
REG_DEAD
,
REGNO
(
operands
[
2
])))
return
strcat
(
buf
,
AS2
(
rp
,
%
2
,
%
0
));
if
(
find_regno_note
(
insn
,
REG_DEAD
,
REGNO
(
operands
[
1
])))
return
strcat
(
buf
,
AS2
(
p
,
%
1
,
%
0
));
if
(
STACK_TOP_P
(
operands
[
0
]))
{
if
(
STACK_TOP_P
(
operands
[
1
]))
return
strcat
(
buf
,
AS2
(,
%
y2
,
%
0
));
else
return
strcat
(
buf
,
AS2
(
r
,
%
y1
,
%
0
));
}
else
if
(
STACK_TOP_P
(
operands
[
1
]))
return
strcat
(
buf
,
AS2
(,
%
1
,
%
0
));
else
return
strcat
(
buf
,
AS2
(
r
,
%
2
,
%
0
));
default
:
abort
();
}
}
/* Output code for INSN to convert a float to a signed int. OPERANDS
are the insn operands. The output may be SFmode or DFmode and the
input operand may be SImode or DImode. As a special case, make sure
that the 387 stack top dies if the output mode is DImode, because the
hardware requires this. */
char
*
output_fix_trunc
(
insn
,
operands
)
rtx
insn
;
rtx
*
operands
;
{
int
stack_top_dies
=
find_regno_note
(
insn
,
REG_DEAD
,
FIRST_STACK_REG
)
!=
0
;
rtx
xops
[
6
];
if
(
!
STACK_TOP_P
(
operands
[
1
])
||
(
GET_MODE
(
operands
[
0
])
==
DImode
&&
!
stack_top_dies
))
abort
();
xops
[
0
]
=
stack_pointer_rtx
;
xops
[
1
]
=
AT_SP
(
SImode
);
xops
[
2
]
=
adj_offsettable_operand
(
xops
[
1
],
2
);
xops
[
3
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
4
);
xops
[
4
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
0xc00
);
xops
[
5
]
=
operands
[
2
];
output_asm_insn
(
AS2
(
sub
%
L0
,
%
3
,
%
0
),
xops
);
output_asm_insn
(
AS1
(
fnstc
%
W5
,
%
1
),
xops
);
output_asm_insn
(
AS2
(
mov
%
W5
,
%
1
,
%
5
),
xops
);
output_asm_insn
(
AS2
(
or
%
W5
,
%
4
,
%
5
),
xops
);
output_asm_insn
(
AS2
(
mov
%
W5
,
%
5
,
%
2
),
xops
);
output_asm_insn
(
AS1
(
fldc
%
W5
,
%
2
),
xops
);
if
(
NON_STACK_REG_P
(
operands
[
0
]))
output_to_reg
(
operands
[
0
],
stack_top_dies
);
else
if
(
GET_CODE
(
operands
[
0
])
==
MEM
)
{
/* If frame pointer elimination is being done, the MEM reference
might be an index off of the stack pointer. In that case,
since we have already adjusted %esp above, adjust the operand
address so it points where it should. */
if
(
!
frame_pointer_needed
&&
reg_mentioned_p
(
stack_pointer_rtx
,
operands
[
0
]))
operands
[
0
]
=
adj_offsettable_operand
(
operands
[
0
],
4
);
if
(
stack_top_dies
)
output_asm_insn
(
AS1
(
fistp
%
z0
,
%
0
),
operands
);
else
output_asm_insn
(
AS1
(
fist
%
z0
,
%
0
),
operands
);
}
else
abort
();
output_asm_insn
(
AS1
(
fldc
%
W5
,
%
1
),
xops
);
output_asm_insn
(
AS2
(
add
%
L0
,
%
3
,
%
0
),
xops
);
RET
;
}
/* Output code for INSN to compare OPERANDS. The two operands might
not have the same mode: one might be within a FLOAT or FLOAT_EXTEND
expression. */
char
*
output_float_compare
(
insn
,
operands
)
rtx
insn
;
rtx
*
operands
;
{
int
stack_top_dies
;
if
(
!
STACK_TOP_P
(
operands
[
0
]))
abort
();
stack_top_dies
=
find_regno_note
(
insn
,
REG_DEAD
,
FIRST_STACK_REG
)
!=
0
;
if
(
STACK_REG_P
(
operands
[
1
])
&&
stack_top_dies
&&
find_regno_note
(
insn
,
REG_DEAD
,
REGNO
(
operands
[
1
]))
&&
REGNO
(
operands
[
1
])
!=
FIRST_STACK_REG
)
{
/* If both the top of the 387 stack dies, and the other operand
is also a stack register that dies, then this must be a
`fcompp' float compare */
output_asm_insn
(
"fcompp"
,
operands
);
}
else
{
static
char
buf
[
100
];
/* Decide if this is the integer or float compare opcode. */
if
(
GET_MODE_CLASS
(
GET_MODE
(
operands
[
1
]))
==
MODE_FLOAT
)
strcpy
(
buf
,
"fcom"
);
else
strcpy
(
buf
,
"ficom"
);
/* Modify the opcode if the 387 stack is to be popped. */
if
(
stack_top_dies
)
strcat
(
buf
,
"p"
);
if
(
NON_STACK_REG_P
(
operands
[
1
]))
output_op_from_reg
(
operands
[
1
],
strcat
(
buf
,
AS1
(
%
z0
,
%
1
)));
else
output_asm_insn
(
strcat
(
buf
,
AS1
(
%
z1
,
%
y1
)),
operands
);
}
/* Now retrieve the condition code. */
output_asm_insn
(
AS1
(
fnsts
%
W2
,
%
2
),
operands
);
cc_status
.
flags
|=
CC_IN_80387
;
return
"sahf"
;
}
#ifdef HANDLE_PRAGMA
/* When structure field packing is in effect, this variable is the
number of bits to use as the maximum alignment. When packing is not
in effect, this is zero. */
int
maximum_field_alignment
=
0
;
/* Handle a pragma directive. HANDLE_PRAGMA conspires to parse the
input following #pragma into tokens based on yylex. TOKEN is the
current token, and STRING is its printable form. */
void
handle_pragma_token
(
string
,
token
)
char
*
string
;
tree
token
;
{
static
enum
pragma_state
{
ps_start
,
ps_done
,
ps_bad
,
ps_weak
,
ps_name
,
ps_equals
,
ps_value
,
ps_pack
,
ps_left
,
ps_align
,
ps_right
}
state
=
ps_start
,
type
;
static
char
*
name
;
static
char
*
value
;
static
int
align
;
if
(
string
==
0
)
{
if
(
type
==
ps_pack
)
{
if
(
state
==
ps_right
)
maximum_field_alignment
=
align
*
8
;
else
warning
(
"ignoring malformed #pragma pack( [ 1 | 2 | 4 ] )"
);
}
#ifdef WEAK_ASM_OP
else
if
(
type
==
ps_weak
)
{
if
(
state
==
ps_name
||
state
==
ps_value
)
{
fprintf
(
asm_out_file
,
"
\t
%s
\t
"
,
WEAK_ASM_OP
);
ASM_OUTPUT_LABELREF
(
asm_out_file
,
name
);
fputc
(
'\n'
,
asm_out_file
);
if
(
state
==
ps_value
)
{
fprintf
(
asm_out_file
,
"
\t
%s
\t
"
,
DEF_ASM_OP
);
ASM_OUTPUT_LABELREF
(
asm_out_file
,
name
);
fputc
(
','
,
asm_out_file
);
ASM_OUTPUT_LABELREF
(
asm_out_file
,
value
);
fputc
(
'\n'
,
asm_out_file
);
}
}
else
if
(
!
(
state
==
ps_done
||
state
==
ps_start
))
warning
(
"ignoring malformed #pragma weak symbol [=value]"
);
}
#endif
/* WEAK_ASM_OP */
type
=
state
=
ps_start
;
return
;
}
switch
(
state
)
{
case
ps_start
:
if
(
token
&&
TREE_CODE
(
token
)
==
IDENTIFIER_NODE
)
{
if
(
strcmp
(
IDENTIFIER_POINTER
(
token
),
"pack"
)
==
0
)
type
=
state
=
ps_pack
;
#ifdef WEAK_ASM_OP
else
if
(
strcmp
(
IDENTIFIER_POINTER
(
token
),
"weak"
)
==
0
)
type
=
state
=
ps_weak
;
#endif
else
type
=
state
=
ps_done
;
}
else
type
=
state
=
ps_done
;
break
;
#ifdef WEAK_ASM_OP
case
ps_weak
:
if
(
token
&&
TREE_CODE
(
token
)
==
IDENTIFIER_NODE
)
{
name
=
IDENTIFIER_POINTER
(
token
);
state
=
ps_name
;
}
else
state
=
ps_bad
;
break
;
case
ps_name
:
state
=
(
strcmp
(
string
,
"="
)
?
ps_bad
:
ps_equals
);
break
;
case
ps_equals
:
if
(
token
&&
TREE_CODE
(
token
)
==
IDENTIFIER_NODE
)
{
value
=
IDENTIFIER_POINTER
(
token
);
state
=
ps_value
;
}
else
state
=
ps_bad
;
break
;
case
ps_value
:
state
=
ps_bad
;
break
;
#endif
/* WEAK_ASM_OP */
case
ps_pack
:
if
(
strcmp
(
string
,
"("
)
==
0
)
state
=
ps_left
;
else
state
=
ps_bad
;
break
;
case
ps_left
:
if
(
token
&&
TREE_CODE
(
token
)
==
INTEGER_CST
&&
TREE_INT_CST_HIGH
(
token
)
==
0
)
switch
(
TREE_INT_CST_LOW
(
token
))
{
case
1
:
case
2
:
case
4
:
align
=
TREE_INT_CST_LOW
(
token
);
state
=
ps_align
;
break
;
default
:
state
=
ps_bad
;
}
else
if
(
!
token
&&
strcmp
(
string
,
")"
)
==
0
)
{
align
=
0
;
state
=
ps_right
;
}
else
state
=
ps_bad
;
break
;
case
ps_align
:
if
(
strcmp
(
string
,
")"
)
==
0
)
state
=
ps_right
;
else
state
=
ps_bad
;
break
;
case
ps_right
:
state
=
ps_bad
;
break
;
case
ps_bad
:
case
ps_done
:
break
;
default
:
abort
();
}
}
#endif
/* HANDLE_PRAGMA */
gcc/config/svr4.h
0 → 100644
View file @
2a2ab3f9
/* svr4.h -- operating system specific defines to be used when
targeting GCC for some generic System V Release 4 system.
Copyright (C) 1991 Free Software Foundation, Inc.
Written by Ron Guilmette (rfg@ncd.com).
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.
To use this file, make up a file with a name like:
?????svr4.h
where ????? is replaced by the name of the basic hardware that you
are targeting for. Then, in the file ?????svr4.h, put something
like:
#include "?????.h"
#include "svr4.h"
followed by any really system-specific defines (or overrides of
defines) which you find that you need. For example, CPP_PREDEFINES
is defined here with only the defined -Dunix and -DSVR4. You should
probably override that in your target-specific ?????svr4.h file
with a set of defines that includes these, but also contains an
appropriate define for the type of hardware that you are targeting.
*/
/* Define a symbol so that libgcc* can know what sort of operating
environment and assembler syntax we are targeting for. */
#ifndef SVR4
#define SVR4
#endif
/* For the sake of libgcc2.c, indicate target supports atexit. */
#define HAVE_ATEXIT
/* Cpp, assembler, linker, library, and startfile spec's. */
/* This defines which switch letters take arguments. On svr4, most of
the normal cases (defined in gcc.c) apply, and we also have -h* and
-z* options (for the linker). Note however that there is no such
thing as a -T option for svr4. */
#define SWITCH_TAKES_ARG(CHAR) \
( (CHAR) == 'D' \
|| (CHAR) == 'U' \
|| (CHAR) == 'o' \
|| (CHAR) == 'e' \
|| (CHAR) == 'u' \
|| (CHAR) == 'I' \
|| (CHAR) == 'm' \
|| (CHAR) == 'L' \
|| (CHAR) == 'A' \
|| (CHAR) == 'h' \
|| (CHAR) == 'z')
/* This defines which multi-letter switches take arguments. On svr4,
there are no such switches except those implemented by GCC itself. */
#define WORD_SWITCH_TAKES_ARG(STR) \
(!strcmp (STR, "include") || !strcmp (STR, "imacros"))
/* You should redefine CPP_PREDEFINES in any file which includes this one.
The definition should be appropriate for the type of target system
involved, and it should include any -A (assertion) options which are
appropriate for the given target system. */
#undef CPP_PREDEFINES
/* Provide an ASM_SPEC appropriate for svr4. Here we try to support as
many of the specialized svr4 assembler options as seems reasonable,
given that there are certain options which we can't (or shouldn't)
support directly due to the fact that they conflict with other options
for other svr4 tools (e.g. ld) or with other options for GCC itself.
For example, we don't support the -o (output file) or -R (remove
input file) options because GCC already handles these things. We
also don't support the -m (run m4) option for the assembler because
that conflicts with the -m (produce load map) option of the svr4
linker. We do however allow passing arbitrary options to the svr4
assembler via the -Wa, option.
Note that gcc doesn't allow a space to follow -Y in a -Ym,* or -Yd,*
option.
*/
#undef ASM_SPEC
#define ASM_SPEC \
"%{V} %{v:%{!V:-V}} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Yd,*} %{Wa,*:%*}"
/* svr4 assemblers need the `-' (indicating input from stdin) to come after
the -o option (and its argument) for some reason. If we try to put it
before the -o option, the assembler will try to read the file named as
the output file in the -o option as an input file (after it has already
written some stuff to it) and the binary stuff contained therein will
cause totally confuse the assembler, resulting in many spurious error
messages. */
#undef ASM_FINAL_SPEC
#define ASM_FINAL_SPEC "%{pipe:-}"
/* Under svr4, the normal location of the various *crt*.o files is the
/usr/ccs/lib directory. */
#undef MD_STARTFILE_PREFIX
#define MD_STARTFILE_PREFIX "/usr/ccs/lib/"
/* Provide a LIB_SPEC appropropriate for svr4. Here we tack on the default
standard C library (unless we are building a shared library) followed by
our own magical crtend.o file (see crtstuff.c) which provides part of
the support for getting C++ file-scope static object constructed before
entering `main', followed by the normal svr3/svr4 "finalizer" file,
which is either `gcrtn.o' or `crtn.o'. */
#undef LIB_SPEC
#define LIB_SPEC \
"%{!shared:%{!symbolic:-lc}} \
crtend.o%s \
%{!shared:%{!symbolic:%{pg:gcrtn.o}%{!pg:crtn.o%s}}}"
/* Provide a LINK_SPEC appropriate for svr4. Here we provide support
for the special GCC options -static, -shared, and -symbolic which
allow us to link things in one of these three modes by applying the
appropriate combinations of options at link-time. We also provide
support here for as many of the other svr4 linker options as seems
reasonable, given that some of them conflict with options for other
svr4 tools (e.g. the assembler). In particular, we do support the
-h*, -z*, -V, -b, -t, -Qy, -Qn, and -YP* options here, and the -e*,
-l*, -o*, -r, -s, -u*, and -L* options are directly supported
by gcc.c itself. We don't directly support the -m (generate load
map) option because that conflicts with the -m (run m4) option of
the svr4 assembler. We also don't directly support the svr4 linker's
-I* or -M* options because these conflict with existing GCC options.
We do however allow passing arbitrary options to the svr4 linker
via the -Wl, option. We don't support the svr4 linker's -a option
at all because it is totally useless and because it conflicts with
GCC's own -a option.
Note that gcc doesn't allow a space to follow -Y in a -YP,* option.
When the -G link option is used (-shared and -symbolic) a final link is
not being done. */
#undef LINK_SPEC
#define LINK_SPEC "%{z*} %{h*} %{V} %{v:%{!V:-V}} \
%{b} %{t} %{Wl,*:%*} \
%{static:-dn -Bstatic} \
%{shared:-G -dy} \
%{symbolic:-Bsymbolic -G -dy} \
%{YP,*} \
%{!YP,*:%{p:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
%{!p:-Y P,/usr/ccs/lib:/usr/lib}} \
%{Qy:} %{!Qn:-Qy}"
/* Gcc automatically adds in one of the files /usr/ccs/lib/values-Xc.o,
/usr/ccs/lib/values-Xa.o, or /usr/ccs/lib/values-Xt.o for each final
link step (depending upon the other gcc options selected, such as
-traditional and -ansi). These files each contain one (initialized)
copy of a special variable called `_lib_version'. Each one of these
files has `_lib_version' initialized to a different (enum) value.
The SVR4 library routines query the value of `_lib_version' at run
to decide how they should behave. Specifically, they decide (based
upon the value of `_lib_version') if they will act in a strictly ANSI
conformant manner or not.
*/
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "%{!shared: \
%{!symbolic: \
%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}} \
%{pg:gcrti.o%s}%{!pg:crti.o%s} \
%{ansi:values-Xc.o%s} \
%{!ansi: \
%{traditional:values-Xt.o%s} \
%{!traditional:values-Xa.o%s}}}} crtbegin.o%s"
/* Attach a sepcial .ident directive to the end of the file to identify
the version of GCC which compiled this code. The format of the
.ident string is patterened after the ones produced by native svr4
C compilers. */
#define ASM_FILE_END(FILE) \
do { \
fprintf ((FILE), "\t.ident\t\"GCC: (GNU) %s\"\n", \
version_string); \
} while (0)
/* Allow #sccs in preprocessor. */
#define SCCS_DIRECTIVE
/* Output #ident as a .ident. */
#define ASM_OUTPUT_IDENT(FILE, NAME) \
fprintf (FILE, "\t.ident \"%s\"\n", NAME);
/* Use periods rather than dollar signs in special g++ assembler names. */
#define NO_DOLLAR_IN_LABEL
/* Writing `int' for a bitfield forces int alignment for the structure. */
#define PCC_BITFIELD_TYPE_MATTERS 1
/* Implicit library calls should use memcpy, not bcopy, etc. */
#define TARGET_MEM_FUNCTIONS
/* System V Release 4 uses DWARF debugging info. */
#define DWARF_DEBUGGING_INFO
/* The numbers used to denote specific machine registers in the System V
Release 4 DWARF debugging information are quite likely to be totally
different from the numbers used in BSD stabs debugging information
for the same kind of target machine. Thus, we undefine the macro
DBX_REGISTER_NUMBER here as an extra inducement to get people to
provide proper machine-specific definitions of DBX_REGISTER_NUMBER
(which is also used to provide DWARF registers numbers in dwarfout.c)
in their tm.h files which include this file. */
#undef DBX_REGISTER_NUMBER
/* Define the actual types of some ANSI-mandated types. (These
definitions should work for most SVR4 systems). */
#undef SIZE_TYPE
#define SIZE_TYPE "unsigned int"
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"
#undef WCHAR_TYPE
#define WCHAR_TYPE "long int"
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE BITS_PER_WORD
#undef ASM_BYTE_OP
#define ASM_BYTE_OP "\t.byte"
/* This is how to begin an assembly language file. Most svr4 assemblers want
at least a .file directive to come first, and some want to see a .version
directive come right after that. Here we just establish a default
which generates only the .file directive. If you need a .version
directive for any specific target, you should override this definition
in the target-specific file which includes this one. */
#undef ASM_FILE_START
#define ASM_FILE_START(FILE) \
output_file_directive ((FILE), main_input_filename)
/* This is how to allocate empty space in some section. The .zero
pseudo-op is used for this on most svr4 assemblers. */
#undef ASM_OUTPUT_SKIP
#define ASM_OUTPUT_SKIP(FILE,SIZE) fprintf (FILE, "\t.zero\t%u\n", (SIZE))
/* This is how to output a reference to a user-level label named NAME.
`assemble_name' uses this.
For System V Release 4 the convention is *not* to prepend a leading
underscore onto user-level symbol names. */
#undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(FILE,NAME) fprintf (FILE, "%s", NAME)
/* The standard SVR4 assembler seems to require that certain builtin
library routines (e.g. .udiv) be explicitly declared as .globl
in each assembly file where they are referenced. */
#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \
ASM_GLOBALIZE_LABEL (FILE, XSTR (FUN, 0))
/* This says how to output assembler code to declare an
uninitialized external linkage data object. Under SVR4,
the linker seems to want the alignment of data objects
to depend on their types. We do exactly that here. */
#undef ASM_OUTPUT_ALIGNED_COMMON
#define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \
do { \
fputs ("\t.comm\t", (FILE)); \
assemble_name ((FILE), (NAME)); \
fprintf ((FILE), ",%u,%u\n", (SIZE), (ALIGN) / BITS_PER_UNIT); \
} while (0)
/* This says how to output assembler code to declare an
uninitialized internal linkage data object. Under SVR4,
the linker seems to want the alignment of data objects
to depend on their types. We do exactly that here. */
#define BSS_ASM_OP "\t.bss"
#undef ASM_OUTPUT_ALIGNED_LOCAL
#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \
do { \
fprintf ((FILE), "%s\t%s,%u,%u\n", \
BSS_ASM_OP, (NAME), (SIZE), (ALIGN) / BITS_PER_UNIT); \
} while (0)
/* This is the pseudo-op used to generate a 32-bit word of data with a
specific value in some section. This is the same for all known svr4
assemblers. */
#define INT_ASM_OP "\t.long\t"
/* This is the pseudo-op used to generate a contiguous sequence of byte
values from a double-quoted string WITHOUT HAVING A TERMINATING NUL
AUTOMATICALLY APPENDED. This is the same for most svr4 assemblers. */
#undef ASCII_DATA_ASM_OP
#define ASCII_DATA_ASM_OP ".ascii"
/* Support const sections and the ctors and dtors sections for g++.
Note that there appears to be two different ways to support const
sections at the moment. You can either #define the symbol
READONLY_DATA_SECTION (giving it some code which switches to the
readonly data section) or else you can #define the symbols
EXTRA_SECTIONS, EXTRA_SECTION_FUNCTIONS, SELECT_SECTION, and
SELECT_RTX_SECTION. We do both here just to be on the safe side. */
#define USE_CONST_SECTION 1
#define CONST_SECTION_ASM_OP "\t.section\t.rodata"
#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"a\",@progbits\n"
#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"a\",@progbits\n"
/* On svr4, we *do* have support for the .init section, and we can put
stuff in there to be executed before `main'. We let crtstuff.c and
other files know this by defining the following symbol. The definition
says how to change sections to the .init section. This is the same
for all know svr4 assemblers. */
#define INIT_SECTION_ASM_OP "\t.section\t.init"
/* A default list of other sections which we might be "in" at any given
time. For targets that use additional sections (e.g. .tdesc) you
should override this definition in the target-specific file which
includes this file. */
#undef EXTRA_SECTIONS
#define EXTRA_SECTIONS in_const, in_ctors, in_dtors
/* A default list of extra section function definitions. For targets
that use additional sections (e.g. .tdesc) you should override this
definition in the target-specific file which includes this file. */
#undef EXTRA_SECTION_FUNCTIONS
#define EXTRA_SECTION_FUNCTIONS \
CONST_SECTION_FUNCTION \
CTORS_SECTION_FUNCTION \
DTORS_SECTION_FUNCTION
#define READONLY_DATA_SECTION() const_section ()
extern
void
text_section
();
#define CONST_SECTION_FUNCTION \
void \
const_section () \
{ \
if (!USE_CONST_SECTION) \
text_section(); \
else if (in_section != in_const) \
{ \
fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \
in_section = in_const; \
} \
}
#define CTORS_SECTION_FUNCTION \
void \
ctors_section () \
{ \
if (in_section != in_ctors) \
{ \
fprintf (asm_out_file, CTORS_SECTION_ASM_OP); \
in_section = in_ctors; \
} \
}
#define DTORS_SECTION_FUNCTION \
void \
dtors_section () \
{ \
if (in_section != in_dtors) \
{ \
fprintf (asm_out_file, DTORS_SECTION_ASM_OP); \
in_section = in_dtors; \
} \
}
/* A C statement (sans semicolon) to output an element in the table of
global constructors. */
#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \
do { \
ctors_section (); \
fprintf (FILE, "%s\t ", INT_ASM_OP); \
assemble_name (FILE, NAME); \
fprintf (FILE, "\n"); \
} while (0)
/* A C statement (sans semicolon) to output an element in the table of
global destructors. */
#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \
do { \
dtors_section (); \
fprintf (FILE, "%s\t ", INT_ASM_OP); \
assemble_name (FILE, NAME); \
fprintf (FILE, "\n"); \
} while (0)
/* A C statement or statements to switch to the appropriate
section for output of DECL. DECL is either a `VAR_DECL' node
or a constant of some sort. RELOC indicates whether forming
the initial value of DECL requires link-time relocations. */
#define SELECT_SECTION(DECL,RELOC) \
{ \
if (TREE_CODE (DECL) == STRING_CST) \
{ \
if (! flag_writable_strings) \
const_section (); \
else \
data_section (); \
} \
else if (TREE_CODE (DECL) == VAR_DECL) \
{ \
if ((flag_pic && RELOC) \
|| !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL)) \
data_section (); \
else \
const_section (); \
} \
else \
const_section (); \
}
/* A C statement or statements to switch to the appropriate
section for output of RTX in mode MODE. RTX is some kind
of constant in RTL. The argument MODE is redundant except
in the case of a `const_int' rtx. Currently, these always
go into the const section. */
#undef SELECT_RTX_SECTION
#define SELECT_RTX_SECTION(MODE,RTX) const_section()
/* Define the strings used for the special svr4 .type and .size directives.
These strings generally do not vary from one system running svr4 to
another, but if a given system (e.g. m88k running svr) needs to use
different pseudo-op names for these, they may be overridden in the
file which includes this one. */
#define TYPE_ASM_OP "\t.type"
#define SIZE_ASM_OP "\t.size"
/* The following macro defines the format used to output the second
operand of the .type assembler directive. Different svr4 assemblers
expect various different forms for this operand. The one given here
is just a default. You may need to override it in your machine-
specific tm.h file (depending upon the particulars of your assembler). */
#define TYPE_OPERAND_FMT "@%s"
/* These macros generate the special .type and .size directives which
are used to set the corresponding fields of the linker symbol table
entries in an ELF object file under SVR4. */
/* Write the extra assembler code needed to declare a function properly. */
#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
do { \
fprintf (FILE, "%s\t ", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
putc (',', FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
putc ('\n', FILE); \
ASM_OUTPUT_LABEL(FILE, NAME); \
} while (0)
/* Write the extra assembler code needed to declare an object properly. */
#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
do { \
fprintf (FILE, "%s\t ", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
putc (',', FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "object"); \
putc ('\n', FILE); \
if (!flag_inhibit_size_directive) \
{ \
fprintf (FILE, "%s\t ", SIZE_ASM_OP); \
assemble_name (FILE, NAME); \
fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (decl))); \
} \
ASM_OUTPUT_LABEL(FILE, NAME); \
} while (0)
/* This is how to declare the size of a function. */
#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
do { \
if (!flag_inhibit_size_directive) \
{ \
char label[256]; \
static int labelno; \
labelno++; \
ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \
ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \
fprintf (FILE, "%s\t ", SIZE_ASM_OP); \
assemble_name (FILE, (FNAME)); \
fprintf (FILE, ","); \
assemble_name (FILE, label); \
fprintf (FILE, "-"); \
ASM_OUTPUT_LABELREF (FILE, (FNAME)); \
putc ('\n', FILE); \
} \
} while (0)
/* A table of bytes codes used by the ASM_OUTPUT_ASCII and
ASM_OUTPUT_LIMITED_STRING macros. Each byte in the table
corresponds to a particular byte value [0..255]. For any
given byte value, if the value in the corresponding table
position is zero, the given character can be output directly.
If the table value is 1, the byte must be output as a \ooo
octal escape. If the tables value is anything else, then the
byte value should be output as a \ followed by the value
in the table. Note that we can use standard UN*X escape
sequences for many control characters, but we don't use
\a to represent BEL because some svr4 assemblers (e.g. on
the i386) don't know about that. */
#define ESCAPES \
"\1\1\1\1\1\1\1\1btnvfr\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\
\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"
/* Some svr4 assemblers have a limit on the number of characters which
can appear in the operand of a .string directive. If your assembler
has such a limitation, you should define STRING_LIMIT to reflect that
limit. Note that at least some svr4 assemblers have a limit on the
actual number of bytes in the double-quoted string, and that they
count each chanacter in an escape sequence as one byte. Thus, an
escape sequence like \377 would count as four bytes.
If your target assembler doesn't support the .string directive, you
should define this to zero.
*/
#define STRING_LIMIT ((unsigned) 256)
#define STRING_ASM_OP ".string"
/* The routine used to output NUL terminated strings. We use a special
version of this for most svr4 targets because doing so makes the
generated assembly code more compact (and thus faster to assemble)
as well as more readable, especially for targets like the i386
(where the only alternative is to output character sequences as
comma separated lists of numbers). */
#define ASM_OUTPUT_LIMITED_STRING(FILE, STR) \
do \
{ \
register unsigned char *_limited_str = (unsigned char *) (STR); \
register unsigned ch; \
fprintf ((FILE), "\t%s\t\"", STRING_ASM_OP); \
for (; ch = *_limited_str; _limited_str++) \
{ \
register int escape; \
switch (escape = ESCAPES[ch]) \
{ \
case 0: \
putc (ch, (FILE)); \
break; \
case 1: \
fprintf ((FILE), "\\%03o", ch); \
break; \
default: \
putc ('\\', (FILE)); \
putc (escape, (FILE)); \
break; \
} \
} \
fprintf ((FILE), "\"\n"); \
} \
while (0)
/* The routine used to output sequences of byte values. We use a special
version of this for most svr4 targets because doing so makes the
generated assembly code more compact (and thus faster to assemble)
as well as more readable. Note that if we find subparts of the
character sequence which end with NUL (and which are shorter than
STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */
#undef ASM_OUTPUT_ASCII
#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \
do \
{ \
register unsigned char *_ascii_bytes = (unsigned char *) (STR); \
register unsigned char *limit = _ascii_bytes + (LENGTH); \
register unsigned bytes_in_chunk = 0; \
for (; _ascii_bytes < limit; _ascii_bytes++) \
{ \
register unsigned char *p; \
if (bytes_in_chunk >= 60) \
{ \
fprintf ((FILE), "\"\n"); \
bytes_in_chunk = 0; \
} \
for (p = _ascii_bytes; p < limit && *p != '\0'; p++) \
continue; \
if (p < limit && (p - _ascii_bytes) <= STRING_LIMIT) \
{ \
if (bytes_in_chunk > 0) \
{ \
fprintf ((FILE), "\"\n"); \
bytes_in_chunk = 0; \
} \
ASM_OUTPUT_LIMITED_STRING ((FILE), _ascii_bytes); \
_ascii_bytes = p; \
} \
else \
{ \
register int escape; \
register unsigned ch; \
if (bytes_in_chunk == 0) \
fprintf ((FILE), "\t%s\t\"", ASCII_DATA_ASM_OP); \
switch (escape = ESCAPES[ch = *_ascii_bytes]) \
{ \
case 0: \
putc (ch, (FILE)); \
bytes_in_chunk++; \
break; \
case 1: \
fprintf ((FILE), "\\%03o", ch); \
bytes_in_chunk += 4; \
break; \
default: \
putc ('\\', (FILE)); \
putc (escape, (FILE)); \
bytes_in_chunk += 2; \
break; \
} \
} \
} \
if (bytes_in_chunk > 0) \
fprintf ((FILE), "\"\n"); \
} \
while (0)
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