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
79e68feb
Commit
79e68feb
authored
Feb 06, 1992
by
Richard Stallman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial revision
From-SVN: r281
parent
17448690
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
6823 additions
and
0 deletions
+6823
-0
gcc/config/m68k/m68k.c
+1834
-0
gcc/config/m88k/m88k.h
+2223
-0
gcc/varasm.c
+2766
-0
No files found.
gcc/config/m68k/m68k.c
0 → 100644
View file @
79e68feb
/* Subroutines for insn-output.c for Motorola 68000 family.
Copyright (C) 1987 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. */
/* Some output-actions in m68k.md need these. */
#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"
/* Needed for use_return_insn. */
#include "flags.h"
#ifdef SUPPORT_SUN_FPA
/* Index into this array by (register number >> 3) to find the
smallest class which contains that register. */
enum
reg_class
regno_reg_class
[]
=
{
DATA_REGS
,
ADDR_REGS
,
FP_REGS
,
LO_FPA_REGS
,
LO_FPA_REGS
,
FPA_REGS
,
FPA_REGS
};
#endif
/* defined SUPPORT_SUN_FPA */
static
rtx
find_addr_reg
();
rtx
legitimize_pic_address
();
/* Emit a (use pic_offset_table_rtx) if we used PIC relocation in the
function at any time during the compilation process. In the future
we should try and eliminate the USE if we can easily deterine that
all PIC references were deleted from the current function. That would
save an address register */
finalize_pic
()
{
if
(
flag_pic
&&
current_function_uses_pic_offset_table
)
emit_insn
(
gen_rtx
(
USE
,
VOIDmode
,
pic_offset_table_rtx
));
}
/* This function generates the assembly code for function entry.
STREAM is a stdio stream to output the code to.
SIZE is an int: how many units of temporary storage to allocate.
Refer to the array `regs_ever_live' to determine which registers
to save; `regs_ever_live[I]' is nonzero if register number I
is ever used in the function. This function is responsible for
knowing which registers should not be saved even if used. */
/* Note that the order of the bit mask for fmovem is the opposite
of the order for movem! */
void
output_function_prologue
(
stream
,
size
)
FILE
*
stream
;
int
size
;
{
register
int
regno
;
register
int
mask
=
0
;
int
num_saved_regs
=
0
;
extern
char
call_used_regs
[];
int
fsize
=
(
size
+
3
)
&
-
4
;
if
(
frame_pointer_needed
)
{
/* Adding negative number is faster on the 68040. */
if
(
fsize
<
0x8000
&&
!
TARGET_68040
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
link.w %s,%I%d
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#else
asm_fprintf
(
stream
,
"
\t
link %s,%I%d
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#endif
}
else
if
(
TARGET_68020
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
link.l %s,%I%d
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#else
asm_fprintf
(
stream
,
"
\t
link %s,%I%d
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
link.w %s,%I0
\n\t
add.l %I%d,%Rsp
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#else
asm_fprintf
(
stream
,
"
\t
link %s,%I0
\n\t
addl %I%d,%Rsp
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
-
fsize
);
#endif
}
}
else
if
(
fsize
)
{
/* Adding negative number is faster on the 68040. */
if
(
fsize
+
4
<
0x8000
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
add.w %I%d,%Rsp
\n
"
,
-
(
fsize
+
4
));
#else
asm_fprintf
(
stream
,
"
\t
addw %I%d,%Rsp
\n
"
,
-
(
fsize
+
4
));
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
add.l %I%d,%Rsp
\n
"
,
-
(
fsize
+
4
));
#else
asm_fprintf
(
stream
,
"
\t
addl %I%d,%Rsp
\n
"
,
-
(
fsize
+
4
));
#endif
}
}
#ifdef SUPPORT_SUN_FPA
for
(
regno
=
24
;
regno
<
56
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fpmovd %s,-(%Rsp)
\n
"
,
reg_names
[
regno
]);
#else
asm_fprintf
(
stream
,
"
\t
fpmoved %s,%Rsp@-
\n
"
,
reg_names
[
regno
]);
#endif
}
#endif
for
(
regno
=
16
;
regno
<
24
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
mask
|=
1
<<
(
regno
-
16
);
if
((
mask
&
0xff
)
!=
0
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fmovm %I0x%x,-(%Rsp)
\n
"
,
mask
&
0xff
);
#else
asm_fprintf
(
stream
,
"
\t
fmovem %I0x%x,%Rsp@-
\n
"
,
mask
&
0xff
);
#endif
}
mask
=
0
;
for
(
regno
=
0
;
regno
<
16
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
{
mask
|=
1
<<
(
15
-
regno
);
num_saved_regs
++
;
}
if
(
frame_pointer_needed
)
{
mask
&=
~
(
1
<<
(
15
-
FRAME_POINTER_REGNUM
));
num_saved_regs
--
;
}
if
(
num_saved_regs
<=
2
)
{
/* Store each separately in the same order moveml uses.
Using two movel instructions instead of a single moveml
is about 15% faster for the 68020 and 68030 at no expense
in code size */
int
i
;
/* Undo the work from above. */
for
(
i
=
0
;
i
<
16
;
i
++
)
if
(
mask
&
(
1
<<
i
))
asm_fprintf
(
stream
,
#ifdef MOTOROLA
"
\t
mov.l %s,-(%Rsp)
\n
"
,
#else
"
\t
movel %s,%Rsp@-
\n
"
,
#endif
reg_names
[
15
-
i
]);
}
else
if
(
mask
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
movm.l %I0x%x,-(%Rsp)
\n
"
,
mask
);
#else
asm_fprintf
(
stream
,
"
\t
moveml %I0x%x,%Rsp@-
\n
"
,
mask
);
#endif
}
if
(
flag_pic
&&
current_function_uses_pic_offset_table
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
mov.l %I__GLOBAL_OFFSET_TABLE_, %s
\n
"
,
reg_names
[
PIC_OFFSET_TABLE_REGNUM
]);
asm_fprintf
(
stream
,
"
\t
lea.l (%Rpc,%s.l),%s
\n
"
,
reg_names
[
PIC_OFFSET_TABLE_REGNUM
],
reg_names
[
PIC_OFFSET_TABLE_REGNUM
]);
#else
asm_fprintf
(
stream
,
"
\t
movel %I__GLOBAL_OFFSET_TABLE_, %s
\n
"
,
reg_names
[
PIC_OFFSET_TABLE_REGNUM
]);
asm_fprintf
(
stream
,
"
\t
lea %Rpc@(0,%s:l),%s
\n
"
,
reg_names
[
PIC_OFFSET_TABLE_REGNUM
],
reg_names
[
PIC_OFFSET_TABLE_REGNUM
]);
#endif
}
}
/* Return true if this function's epilogue can be output as RTL. */
int
use_return_insn
()
{
int
regno
;
if
(
!
reload_completed
||
frame_pointer_needed
||
get_frame_size
()
!=
0
)
return
0
;
/* Copied from output_function_epilogue (). We should probably create a
separate layout routine to perform the common work. */
for
(
regno
=
0
;
regno
<
FIRST_PSEUDO_REGISTER
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
return
0
;
return
1
;
}
/* This function generates the assembly code for function exit,
on machines that need it. Args are same as for FUNCTION_PROLOGUE.
The function epilogue should not depend on the current stack pointer!
It should use the frame pointer only, if there is a frame pointer.
This is mandatory because of alloca; we also take advantage of it to
omit stack adjustments before returning. */
void
output_function_epilogue
(
stream
,
size
)
FILE
*
stream
;
int
size
;
{
register
int
regno
;
register
int
mask
,
fmask
;
register
int
nregs
;
int
offset
,
foffset
,
fpoffset
;
extern
char
call_used_regs
[];
int
fsize
=
(
size
+
3
)
&
-
4
;
int
big
=
0
;
rtx
insn
=
get_last_insn
();
/* If the last insn was a BARRIER, we don't have to write any code. */
if
(
GET_CODE
(
insn
)
==
NOTE
)
insn
=
prev_nonnote_insn
(
insn
);
if
(
insn
&&
GET_CODE
(
insn
)
==
BARRIER
)
return
;
#ifdef FUNCTION_EXTRA_EPILOGUE
FUNCTION_EXTRA_EPILOGUE
(
stream
,
size
);
#endif
nregs
=
0
;
fmask
=
0
;
fpoffset
=
0
;
#ifdef SUPPORT_SUN_FPA
for
(
regno
=
24
;
regno
<
56
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
nregs
++
;
fpoffset
=
nregs
*
8
;
#endif
nregs
=
0
;
for
(
regno
=
16
;
regno
<
24
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
{
nregs
++
;
fmask
|=
1
<<
(
23
-
regno
);
}
foffset
=
fpoffset
+
nregs
*
12
;
nregs
=
0
;
mask
=
0
;
if
(
frame_pointer_needed
)
regs_ever_live
[
FRAME_POINTER_REGNUM
]
=
0
;
for
(
regno
=
0
;
regno
<
16
;
regno
++
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
{
nregs
++
;
mask
|=
1
<<
regno
;
}
offset
=
foffset
+
nregs
*
4
;
if
(
offset
+
fsize
>=
0x8000
&&
frame_pointer_needed
&&
(
mask
||
fmask
||
fpoffset
))
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
mov.l %I%d,%Ra0
\n
"
,
-
fsize
);
#else
asm_fprintf
(
stream
,
"
\t
movel %I%d,%Ra0
\n
"
,
-
fsize
);
#endif
fsize
=
0
,
big
=
1
;
}
if
(
nregs
<=
2
)
{
/* Restore each separately in the same order moveml does.
Using two movel instructions instead of a single moveml
is about 15% faster for the 68020 and 68030 at no expense
in code size. */
int
i
;
/* Undo the work from above. */
for
(
i
=
0
;
i
<
16
;
i
++
)
if
(
mask
&
(
1
<<
i
))
{
if
(
big
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
mov.l -%d(%s,%Ra0.l),%s
\n
"
,
offset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
reg_names
[
i
]);
#else
asm_fprintf
(
stream
,
"
\t
movel %s@(-%d,%Ra0:l),%s
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
offset
+
fsize
,
reg_names
[
i
]);
#endif
}
else
if
(
!
frame_pointer_needed
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
mov.l (%Rsp)+,%s
\n
"
,
reg_names
[
i
]);
#else
asm_fprintf
(
stream
,
"
\t
movel %Rsp@+,%s
\n
"
,
reg_names
[
i
]);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
mov.l -%d(%s),%s
\n
"
,
offset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
reg_names
[
i
]);
#else
asm_fprintf
(
stream
,
"
\t
movel %s@(-%d),%s
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
offset
+
fsize
,
reg_names
[
i
]);
#endif
}
offset
=
offset
-
4
;
}
}
else
if
(
mask
)
{
if
(
big
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
movm.l -%d(%s,%Ra0.l),%I0x%x
\n
"
,
offset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
mask
);
#else
asm_fprintf
(
stream
,
"
\t
moveml %s@(-%d,%Ra0:l),%I0x%x
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
offset
+
fsize
,
mask
);
#endif
}
else
if
(
!
frame_pointer_needed
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
movm.l (%Rsp)+,%I0x%x
\n
"
,
mask
);
#else
asm_fprintf
(
stream
,
"
\t
moveml %Rsp@+,%I0x%x
\n
"
,
mask
);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
movm.l -%d(%s),%I0x%x
\n
"
,
offset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
mask
);
#else
asm_fprintf
(
stream
,
"
\t
moveml %s@(-%d),%I0x%x
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
offset
+
fsize
,
mask
);
#endif
}
}
if
(
fmask
)
{
if
(
big
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fmovm -%d(%s,%Ra0.l),%I0x%x
\n
"
,
foffset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
fmask
);
#else
asm_fprintf
(
stream
,
"
\t
fmovem %s@(-%d,%Ra0:l),%I0x%x
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
foffset
+
fsize
,
fmask
);
#endif
}
else
if
(
!
frame_pointer_needed
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fmovm (%Rsp)+,%I0x%x
\n
"
,
fmask
);
#else
asm_fprintf
(
stream
,
"
\t
fmovem %Rsp@+,%I0x%x
\n
"
,
fmask
);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fmovm -%d(%s),%I0x%x
\n
"
,
foffset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
fmask
);
#else
asm_fprintf
(
stream
,
"
\t
fmovem %s@(-%d),%I0x%x
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
foffset
+
fsize
,
fmask
);
#endif
}
}
if
(
fpoffset
!=
0
)
for
(
regno
=
55
;
regno
>=
24
;
regno
--
)
if
(
regs_ever_live
[
regno
]
&&
!
call_used_regs
[
regno
])
{
if
(
big
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fpmovd -%d(%s,%Ra0.l), %s
\n
"
,
fpoffset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
reg_names
[
regno
]);
#else
asm_fprintf
(
stream
,
"
\t
fpmoved %s@(-%d,%Ra0:l), %s
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
fpoffset
+
fsize
,
reg_names
[
regno
]);
#endif
}
else
if
(
!
frame_pointer_needed
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fpmovd (%Rsp)+,%s
\n
"
,
reg_names
[
regno
]);
#else
asm_fprintf
(
stream
,
"
\t
fpmoved %Rsp@+, %s
\n
"
,
reg_names
[
regno
]);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
fpmovd -%d(%s), %s
\n
"
,
fpoffset
+
fsize
,
reg_names
[
FRAME_POINTER_REGNUM
],
reg_names
[
regno
]);
#else
asm_fprintf
(
stream
,
"
\t
fpmoved %s@(-%d), %s
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
],
fpoffset
+
fsize
,
reg_names
[
regno
]);
#endif
}
fpoffset
-=
8
;
}
if
(
frame_pointer_needed
)
fprintf
(
stream
,
"
\t
unlk %s
\n
"
,
reg_names
[
FRAME_POINTER_REGNUM
]);
else
if
(
fsize
)
{
if
(
fsize
+
4
<
0x8000
)
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
add.w %I%d,%Rsp
\n
"
,
fsize
+
4
);
#else
asm_fprintf
(
stream
,
"
\t
addw %I%d,%Rsp
\n
"
,
fsize
+
4
);
#endif
}
else
{
#ifdef MOTOROLA
asm_fprintf
(
stream
,
"
\t
add.l %I%d,%Rsp
\n
"
,
fsize
+
4
);
#else
asm_fprintf
(
stream
,
"
\t
addl %I%d,%Rsp
\n
"
,
fsize
+
4
);
#endif
}
}
if
(
current_function_pops_args
)
asm_fprintf
(
stream
,
"
\t
rtd %I%d
\n
"
,
current_function_pops_args
);
else
fprintf
(
stream
,
"
\t
rts
\n
"
);
}
/* Similar to general_operand, but exclude stack_pointer_rtx. */
int
not_sp_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
return
op
!=
stack_pointer_rtx
&&
general_operand
(
op
,
mode
);
}
char
*
output_btst
(
operands
,
countop
,
dataop
,
insn
,
signpos
)
rtx
*
operands
;
rtx
countop
,
dataop
;
rtx
insn
;
int
signpos
;
{
operands
[
0
]
=
countop
;
operands
[
1
]
=
dataop
;
if
(
GET_CODE
(
countop
)
==
CONST_INT
)
{
register
int
count
=
INTVAL
(
countop
);
/* If COUNT is bigger than size of storage unit in use,
advance to the containing unit of same size. */
if
(
count
>
signpos
)
{
int
offset
=
(
count
&
~
signpos
)
/
8
;
count
=
count
&
signpos
;
operands
[
1
]
=
dataop
=
adj_offsettable_operand
(
dataop
,
offset
);
}
if
(
count
==
signpos
)
cc_status
.
flags
=
CC_NOT_POSITIVE
|
CC_Z_IN_NOT_N
;
else
cc_status
.
flags
=
CC_NOT_NEGATIVE
|
CC_Z_IN_NOT_N
;
/* These three statements used to use next_insns_test_no...
but it appears that this should do the same job. */
if
(
count
==
31
&&
next_insn_tests_no_inequality
(
insn
))
return
"tst%.l %1"
;
if
(
count
==
15
&&
next_insn_tests_no_inequality
(
insn
))
return
"tst%.w %1"
;
if
(
count
==
7
&&
next_insn_tests_no_inequality
(
insn
))
return
"tst%.b %1"
;
cc_status
.
flags
=
CC_NOT_NEGATIVE
;
}
return
"btst %0,%1"
;
}
/* 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
);
#if 0 /* Deleted, with corresponding change in m68k.h,
so as to fit the specs. No CONST_DOUBLE is ever symbolic. */
case CONST_DOUBLE:
return GET_MODE (op) == mode;
#endif
default
:
return
0
;
}
}
/* Legitimize PIC addresses. If the address is already
position-independent, we return ORIG. Newly generated
position-independent addresses go to REG. If we need more
than one register, we lose.
An address is legitimized by making an indirect reference
through the Global Offset Table with the name of the symbol
used as an offset.
The assembler and linker are responsible for placing the
address of the symbol in the GOT. The function prologue
is responsible for initializing a5 to the starting address
of the GOT.
The assembler is also responsible for translating a symbol name
into a constant displacement from the start of the GOT.
A quick example may make things a little clearer:
When not generating PIC code to store the value 12345 into _foo
we would generate the following code:
movel #12345, _foo
When generating PIC two transformations are made. First, the compiler
loads the address of foo into a register. So the first transformation makes:
lea _foo, a0
movel #12345, a0@
The code in movsi will intercept the lea instruction and call this
routine which will transform the instructions into:
movel a5@(_foo:w), a0
movel #12345, a0@
That (in a nutshell) is how *all* symbol and label references are
handled. */
rtx
legitimize_pic_address
(
orig
,
mode
,
reg
)
rtx
orig
,
reg
;
enum
machine_mode
mode
;
{
rtx
pic_ref
=
orig
;
/* First handle a simple SYMBOL_REF or LABEL_REF */
if
(
GET_CODE
(
orig
)
==
SYMBOL_REF
||
GET_CODE
(
orig
)
==
LABEL_REF
)
{
if
(
reg
==
0
)
abort
();
pic_ref
=
gen_rtx
(
MEM
,
Pmode
,
gen_rtx
(
PLUS
,
Pmode
,
pic_offset_table_rtx
,
orig
));
current_function_uses_pic_offset_table
=
1
;
RTX_UNCHANGING_P
(
pic_ref
)
=
1
;
emit_move_insn
(
reg
,
pic_ref
);
return
reg
;
}
else
if
(
GET_CODE
(
orig
)
==
CONST
)
{
rtx
base
,
offset
;
/* Make sure this is CONST has not already been legitimized */
if
(
GET_CODE
(
XEXP
(
orig
,
0
))
==
PLUS
&&
XEXP
(
XEXP
(
orig
,
0
),
0
)
==
pic_offset_table_rtx
)
return
orig
;
if
(
reg
==
0
)
abort
();
/* legitimize both operands of the PLUS */
if
(
GET_CODE
(
XEXP
(
orig
,
0
))
==
PLUS
)
{
base
=
legitimize_pic_address
(
XEXP
(
XEXP
(
orig
,
0
),
0
),
Pmode
,
reg
);
orig
=
legitimize_pic_address
(
XEXP
(
XEXP
(
orig
,
0
),
1
),
Pmode
,
base
==
reg
?
0
:
reg
);
}
else
abort
();
if
(
GET_CODE
(
orig
)
==
CONST_INT
)
return
plus_constant_for_output
(
base
,
INTVAL
(
orig
));
pic_ref
=
gen_rtx
(
PLUS
,
Pmode
,
base
,
orig
);
/* Likewise, should we set special REG_NOTEs here? */
}
return
pic_ref
;
}
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
static
char
*
singlemove_string
(
operands
)
rtx
*
operands
;
{
#ifdef SUPPORT_SUN_FPA
if
(
FPA_REG_P
(
operands
[
0
])
||
FPA_REG_P
(
operands
[
1
]))
return
"fpmoves %1,%0"
;
#endif
if
(
DATA_REG_P
(
operands
[
0
])
&&
GET_CODE
(
operands
[
1
])
==
CONST_INT
&&
INTVAL
(
operands
[
1
])
<
128
&&
INTVAL
(
operands
[
1
])
>=
-
128
)
{
#if defined(MOTOROLA) && !defined(CRDS)
return
"moveq%.l %1,%0"
;
#else
return
"moveq %1,%0"
;
#endif
}
if
(
operands
[
1
]
!=
const0_rtx
)
return
"move%.l %1,%0"
;
if
(
!
ADDRESS_REG_P
(
operands
[
0
]))
return
"clr%.l %0"
;
return
"sub%.l %0,%0"
;
}
/* 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
);
output_asm_insn
(
"subq%.l %#8,%0"
,
operands
);
operands
[
0
]
=
gen_rtx
(
MEM
,
DImode
,
operands
[
0
]);
optype0
=
OFFSOP
;
}
if
(
optype0
==
POPOP
&&
optype1
==
PUSHOP
)
{
operands
[
1
]
=
XEXP
(
XEXP
(
operands
[
1
],
0
),
0
);
output_asm_insn
(
"subq%.l %#8,%1"
,
operands
);
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
)
split_double
(
operands
[
1
],
&
operands
[
1
],
&
latehalf
[
1
]);
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
)
output_asm_insn
(
"addql %#4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"addql %#4,%0"
,
&
addreg1
);
/* Do that word. */
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
/* Undo the adds we just did. */
if
(
addreg0
)
output_asm_insn
(
"subql %#4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"subql %#4,%0"
,
&
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
)
output_asm_insn
(
"addql %#4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"addql %#4,%0"
,
&
addreg1
);
/* Do that word. */
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
/* Undo the adds we just did. */
if
(
addreg0
)
output_asm_insn
(
"subql %#4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"subql %#4,%0"
,
&
addreg1
);
return
""
;
}
/* 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
();
}
/* Store in cc_status the expressions that the condition codes will
describe after execution of an instruction whose pattern is EXP.
Do not alter them if the instruction would not alter the cc's. */
/* On the 68000, all the insns to store in an address register fail to
set the cc's. However, in some cases these instructions can make it
possibly invalid to use the saved cc's. In those cases we clear out
some or all of the saved cc's so they won't be used. */
notice_update_cc
(
exp
,
insn
)
rtx
exp
;
rtx
insn
;
{
/* If the cc is being set from the fpa and the expression is not an
explicit floating point test instruction (which has code to deal with
this), reinit the CC. */
if
(((
cc_status
.
value1
&&
FPA_REG_P
(
cc_status
.
value1
))
||
(
cc_status
.
value2
&&
FPA_REG_P
(
cc_status
.
value2
)))
&&
!
(
GET_CODE
(
exp
)
==
PARALLEL
&&
GET_CODE
(
XVECEXP
(
exp
,
0
,
0
))
==
SET
&&
XEXP
(
XVECEXP
(
exp
,
0
,
0
),
0
)
==
cc0_rtx
))
{
CC_STATUS_INIT
;
}
else
if
(
GET_CODE
(
exp
)
==
SET
)
{
if
(
GET_CODE
(
SET_SRC
(
exp
))
==
CALL
)
{
CC_STATUS_INIT
;
}
else
if
(
ADDRESS_REG_P
(
SET_DEST
(
exp
)))
{
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
;
}
else
if
(
!
FP_REG_P
(
SET_DEST
(
exp
))
&&
SET_DEST
(
exp
)
!=
cc0_rtx
&&
(
FP_REG_P
(
SET_SRC
(
exp
))
||
GET_CODE
(
SET_SRC
(
exp
))
==
FIX
||
GET_CODE
(
SET_SRC
(
exp
))
==
FLOAT_TRUNCATE
||
GET_CODE
(
SET_SRC
(
exp
))
==
FLOAT_EXTEND
))
{
CC_STATUS_INIT
;
}
/* A pair of move insns doesn't produce a useful overall cc. */
else
if
(
!
FP_REG_P
(
SET_DEST
(
exp
))
&&
!
FP_REG_P
(
SET_SRC
(
exp
))
&&
GET_MODE_SIZE
(
GET_MODE
(
SET_SRC
(
exp
)))
>
4
&&
(
GET_CODE
(
SET_SRC
(
exp
))
==
REG
||
GET_CODE
(
SET_SRC
(
exp
))
==
MEM
||
GET_CODE
(
SET_SRC
(
exp
))
==
CONST_DOUBLE
))
{
CC_STATUS_INIT
;
}
else
if
(
GET_CODE
(
SET_SRC
(
exp
))
==
CALL
)
{
CC_STATUS_INIT
;
}
else
if
(
XEXP
(
exp
,
0
)
!=
pc_rtx
)
{
cc_status
.
flags
=
0
;
cc_status
.
value1
=
XEXP
(
exp
,
0
);
cc_status
.
value2
=
XEXP
(
exp
,
1
);
}
}
else
if
(
GET_CODE
(
exp
)
==
PARALLEL
&&
GET_CODE
(
XVECEXP
(
exp
,
0
,
0
))
==
SET
)
{
if
(
ADDRESS_REG_P
(
XEXP
(
XVECEXP
(
exp
,
0
,
0
),
0
)))
CC_STATUS_INIT
;
else
if
(
XEXP
(
XVECEXP
(
exp
,
0
,
0
),
0
)
!=
pc_rtx
)
{
cc_status
.
flags
=
0
;
cc_status
.
value1
=
XEXP
(
XVECEXP
(
exp
,
0
,
0
),
0
);
cc_status
.
value2
=
XEXP
(
XVECEXP
(
exp
,
0
,
0
),
1
);
}
}
else
CC_STATUS_INIT
;
if
(
cc_status
.
value2
!=
0
&&
ADDRESS_REG_P
(
cc_status
.
value2
)
&&
GET_MODE
(
cc_status
.
value2
)
==
QImode
)
CC_STATUS_INIT
;
if
(
cc_status
.
value2
!=
0
&&
!
(
cc_status
.
value1
&&
FPA_REG_P
(
cc_status
.
value1
)))
switch
(
GET_CODE
(
cc_status
.
value2
))
{
case
PLUS
:
case
MINUS
:
case
MULT
:
case
DIV
:
case
UDIV
:
case
MOD
:
case
UMOD
:
case
NEG
:
case
ASHIFT
:
case
LSHIFT
:
case
ASHIFTRT
:
case
LSHIFTRT
:
case
ROTATE
:
case
ROTATERT
:
if
(
GET_MODE
(
cc_status
.
value2
)
!=
VOIDmode
)
cc_status
.
flags
|=
CC_NO_OVERFLOW
;
break
;
case
ZERO_EXTEND
:
/* (SET r1 (ZERO_EXTEND r2)) on this machine
ends with a move insn moving r2 in r2's mode.
Thus, the cc's are set for r2.
This can set N bit spuriously. */
cc_status
.
flags
|=
CC_NOT_NEGATIVE
;
}
if
(
cc_status
.
value1
&&
GET_CODE
(
cc_status
.
value1
)
==
REG
&&
cc_status
.
value2
&&
reg_overlap_mentioned_p
(
cc_status
.
value1
,
cc_status
.
value2
))
cc_status
.
value2
=
0
;
if
(((
cc_status
.
value1
&&
FP_REG_P
(
cc_status
.
value1
))
||
(
cc_status
.
value2
&&
FP_REG_P
(
cc_status
.
value2
)))
&&
!
((
cc_status
.
value1
&&
FPA_REG_P
(
cc_status
.
value1
))
||
(
cc_status
.
value2
&&
FPA_REG_P
(
cc_status
.
value2
))))
cc_status
.
flags
=
CC_IN_68881
;
}
char
*
output_move_const_double
(
operands
)
rtx
*
operands
;
{
#ifdef SUPPORT_SUN_FPA
if
(
TARGET_FPA
&&
FPA_REG_P
(
operands
[
0
]))
{
int
code
=
standard_sun_fpa_constant_p
(
operands
[
1
]);
if
(
code
!=
0
)
{
static
char
buf
[
40
];
sprintf
(
buf
,
"fpmove%%.d %%%%%d,%%0"
,
code
&
0x1ff
);
return
buf
;
}
return
"fpmove%.d %1,%0"
;
}
else
#endif
{
int
code
=
standard_68881_constant_p
(
operands
[
1
]);
if
(
code
!=
0
)
{
static
char
buf
[
40
];
sprintf
(
buf
,
"fmovecr %%#0x%x,%%0"
,
code
&
0xff
);
return
buf
;
}
return
"fmove%.d %1,%0"
;
}
}
char
*
output_move_const_single
(
operands
)
rtx
*
operands
;
{
#ifdef SUPPORT_SUN_FPA
if
(
TARGET_FPA
)
{
int
code
=
standard_sun_fpa_constant_p
(
operands
[
1
]);
if
(
code
!=
0
)
{
static
char
buf
[
40
];
sprintf
(
buf
,
"fpmove%%.s %%%%%d,%%0"
,
code
&
0x1ff
);
return
buf
;
}
return
"fpmove%.s %1,%0"
;
}
else
#endif
/* defined SUPPORT_SUN_FPA */
{
int
code
=
standard_68881_constant_p
(
operands
[
1
]);
if
(
code
!=
0
)
{
static
char
buf
[
40
];
sprintf
(
buf
,
"fmovecr %%#0x%x,%%0"
,
code
&
0xff
);
return
buf
;
}
return
"fmove%.s %f1,%0"
;
}
}
/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
from the "fmovecr" instruction.
The value, anded with 0xff, gives the code to use in fmovecr
to get the desired constant. */
/* ??? This code should be fixed for cross-compilation. */
int
standard_68881_constant_p
(
x
)
rtx
x
;
{
union
{
double
d
;
int
i
[
2
];}
u
;
register
double
d
;
/* fmovecr must be emulated on the 68040, so it shoudn't be used at all. */
if
(
TARGET_68040
)
return
0
;
#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
if
(
!
flag_pretend_float
)
return
0
;
#endif
#ifdef HOST_WORDS_BIG_ENDIAN
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
#else
u
.
i
[
0
]
=
CONST_DOUBLE_HIGH
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_LOW
(
x
);
#endif
d
=
u
.
d
;
if
(
d
==
0
)
return
0x0f
;
/* Note: there are various other constants available
but it is a nuisance to put in their values here. */
if
(
d
==
1
)
return
0x32
;
if
(
d
==
10
)
return
0x33
;
if
(
d
==
100
)
return
0x34
;
if
(
d
==
10000
)
return
0x35
;
if
(
d
==
1e8
)
return
0x36
;
if
(
GET_MODE
(
x
)
==
SFmode
)
return
0
;
if
(
d
==
1e16
)
return
0x37
;
/* larger powers of ten in the constants ram are not used
because they are not equal to a `double' C constant. */
return
0
;
}
/* If X is a floating-point constant, return the logarithm of X base 2,
or 0 if X is not a power of 2. */
int
floating_exact_log2
(
x
)
rtx
x
;
{
union
{
double
d
;
int
i
[
2
];}
u
;
register
double
d
,
d1
;
int
i
;
#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
if
(
!
flag_pretend_float
)
return
0
;
#endif
#ifdef HOST_WORDS_BIG_ENDIAN
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
#else
u
.
i
[
0
]
=
CONST_DOUBLE_HIGH
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_LOW
(
x
);
#endif
d
=
u
.
d
;
if
(
!
(
d
>
0
))
return
0
;
for
(
d1
=
1
.
0
,
i
=
0
;
d1
<
d
;
d1
*=
2
.
0
,
i
++
)
;
if
(
d
==
d1
)
return
i
;
return
0
;
}
#ifdef SUPPORT_SUN_FPA
/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
from the Sun FPA's constant RAM.
The value returned, anded with 0x1ff, gives the code to use in fpmove
to get the desired constant. */
#define S_E (2.718281745910644531)
#define D_E (2.718281828459045091)
#define S_PI (3.141592741012573242)
#define D_PI (3.141592653589793116)
#define S_SQRT2 (1.414213538169860840)
#define D_SQRT2 (1.414213562373095145)
#define S_LOG2ofE (1.442695021629333496)
#define D_LOG2ofE (1.442695040888963387)
#define S_LOG2of10 (3.321928024291992188)
#define D_LOG2of10 (3.321928024887362182)
#define S_LOGEof2 (0.6931471824645996094)
#define D_LOGEof2 (0.6931471805599452862)
#define S_LOGEof10 (2.302585124969482442)
#define D_LOGEof10 (2.302585092994045901)
#define S_LOG10of2 (0.3010300099849700928)
#define D_LOG10of2 (0.3010299956639811980)
#define S_LOG10ofE (0.4342944920063018799)
#define D_LOG10ofE (0.4342944819032518167)
/* This code should be fixed for cross-compilation. */
int
standard_sun_fpa_constant_p
(
x
)
rtx
x
;
{
union
{
double
d
;
int
i
[
2
];}
u
;
register
double
d
;
#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
if
(
!
flag_pretend_float
)
return
0
;
#endif
u
.
i
[
0
]
=
CONST_DOUBLE_LOW
(
x
);
u
.
i
[
1
]
=
CONST_DOUBLE_HIGH
(
x
);
d
=
u
.
d
;
if
(
d
==
0
.
0
)
return
0x200
;
/* 0 once 0x1ff is anded with it */
if
(
d
==
1
.
0
)
return
0xe
;
if
(
d
==
0
.
5
)
return
0xf
;
if
(
d
==
-
1
.
0
)
return
0x10
;
if
(
d
==
2
.
0
)
return
0x11
;
if
(
d
==
3
.
0
)
return
0xB1
;
if
(
d
==
4
.
0
)
return
0x12
;
if
(
d
==
8
.
0
)
return
0x13
;
if
(
d
==
0
.
25
)
return
0x15
;
if
(
d
==
0
.
125
)
return
0x16
;
if
(
d
==
10
.
0
)
return
0x17
;
if
(
d
==
-
(
1
.
0
/
2
.
0
))
return
0x2E
;
/*
* Stuff that looks different if it's single or double
*/
if
(
GET_MODE
(
x
)
==
SFmode
)
{
if
(
d
==
S_E
)
return
0x8
;
if
(
d
==
(
2
*
S_PI
))
return
0x9
;
if
(
d
==
S_PI
)
return
0xA
;
if
(
d
==
(
S_PI
/
2
.
0
))
return
0xB
;
if
(
d
==
S_SQRT2
)
return
0xC
;
if
(
d
==
(
1
.
0
/
S_SQRT2
))
return
0xD
;
/* Large powers of 10 in the constant
ram are not used because they are
not equal to a C double constant */
if
(
d
==
-
(
S_PI
/
2
.
0
))
return
0x27
;
if
(
d
==
S_LOG2ofE
)
return
0x28
;
if
(
d
==
S_LOG2of10
)
return
0x29
;
if
(
d
==
S_LOGEof2
)
return
0x2A
;
if
(
d
==
S_LOGEof10
)
return
0x2B
;
if
(
d
==
S_LOG10of2
)
return
0x2C
;
if
(
d
==
S_LOG10ofE
)
return
0x2D
;
}
else
{
if
(
d
==
D_E
)
return
0x8
;
if
(
d
==
(
2
*
D_PI
))
return
0x9
;
if
(
d
==
D_PI
)
return
0xA
;
if
(
d
==
(
D_PI
/
2
.
0
))
return
0xB
;
if
(
d
==
D_SQRT2
)
return
0xC
;
if
(
d
==
(
1
.
0
/
D_SQRT2
))
return
0xD
;
/* Large powers of 10 in the constant
ram are not used because they are
not equal to a C double constant */
if
(
d
==
-
(
D_PI
/
2
.
0
))
return
0x27
;
if
(
d
==
D_LOG2ofE
)
return
0x28
;
if
(
d
==
D_LOG2of10
)
return
0x29
;
if
(
d
==
D_LOGEof2
)
return
0x2A
;
if
(
d
==
D_LOGEof10
)
return
0x2B
;
if
(
d
==
D_LOG10of2
)
return
0x2C
;
if
(
d
==
D_LOG10ofE
)
return
0x2D
;
}
return
0x0
;
}
#endif
/* define SUPPORT_SUN_FPA */
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand X. X is an RTL
expression.
CODE is a value that can be used to specify one of several ways
of printing the operand. It is used when identical operands
must be printed differently depending on the context. CODE
comes from the `%' specification that was used to request
printing of the operand. If the specification was just `%DIGIT'
then CODE is 0; if the specification was `%LTR DIGIT' then CODE
is the ASCII code for LTR.
If X is a register, this macro should print the register's name.
The names can be found in an array `reg_names' whose type is
`char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
When the machine description has a specification `%PUNCT' (a `%'
followed by a punctuation character), this macro is called with
a null pointer for X and the punctuation character for CODE.
The m68k specific codes are:
'.' for dot needed in Motorola-style opcode names.
'-' for an operand pushing on the stack:
sp@-, -(sp) or -(%sp) depending on the style of syntax.
'+' for an operand pushing on the stack:
sp@+, (sp)+ or (%sp)+ depending on the style of syntax.
'@' for a reference to the top word on the stack:
sp@, (sp) or (%sp) depending on the style of syntax.
'#' for an immediate operand prefix (# in MIT and Motorola syntax
but & in SGS syntax).
'!' for the cc register (used in an `and to cc' insn).
'$' for the letter `s' in an op code, but only on the 68040.
'&' for the letter `d' in an op code, but only on the 68040.
'b' for byte insn (no effect, on the Sun; this is for the ISI).
'd' to force memory addressing to be absolute, not relative.
'f' for float insn (print a CONST_DOUBLE as a float rather than in hex)
'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather
than directly). Second part of 'y' below.
'x' for float insn (print a CONST_DOUBLE as a float rather than in hex),
or print pair of registers as rx:ry.
'y' for a FPA insn (print pair of registers as rx:ry). This also outputs
CONST_DOUBLE's as SunFPA constant RAM registers if
possible, so it should not be used except for the SunFPA.
*/
void
print_operand
(
file
,
op
,
letter
)
FILE
*
file
;
/* file to write to */
rtx
op
;
/* operand to print */
int
letter
;
/* %<letter> or 0 */
{
int
i
;
if
(
letter
==
'.'
)
{
#ifdef MOTOROLA
asm_fprintf
(
file
,
"."
);
#endif
}
else
if
(
letter
==
'#'
)
{
asm_fprintf
(
file
,
"%I"
);
}
else
if
(
letter
==
'-'
)
{
#ifdef MOTOROLA
asm_fprintf
(
file
,
"-(%Rsp)"
);
#else
asm_fprintf
(
file
,
"%Rsp@-"
);
#endif
}
else
if
(
letter
==
'+'
)
{
#ifdef MOTOROLA
asm_fprintf
(
file
,
"(%Rsp)+"
);
#else
asm_fprintf
(
file
,
"%Rsp@+"
);
#endif
}
else
if
(
letter
==
'@'
)
{
#ifdef MOTOROLA
asm_fprintf
(
file
,
"(%Rsp)"
);
#else
asm_fprintf
(
file
,
"%Rsp@"
);
#endif
}
else
if
(
letter
==
'!'
)
{
#ifdef MOTOROLA
asm_fprintf
(
file
,
"(%Rcc)"
);
#else
asm_fprintf
(
file
,
"%Rcc"
);
#endif
}
else
if
(
letter
==
'$'
)
{
if
(
TARGET_68040_ONLY
)
{
fprintf
(
file
,
"s"
);
}
}
else
if
(
letter
==
'&'
)
{
if
(
TARGET_68040_ONLY
)
{
fprintf
(
file
,
"d"
);
}
}
else
if
(
GET_CODE
(
op
)
==
REG
)
{
if
(
REGNO
(
op
)
<
16
&&
(
letter
==
'y'
||
letter
==
'x'
)
&&
GET_MODE
(
op
)
==
DFmode
)
{
fprintf
(
file
,
"%s:%s"
,
reg_names
[
REGNO
(
op
)],
reg_names
[
REGNO
(
op
)
+
1
]);
}
else
{
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
op
)]);
}
}
else
if
(
GET_CODE
(
op
)
==
MEM
)
{
output_address
(
XEXP
(
op
,
0
));
if
(
letter
==
'd'
&&
!
TARGET_68020
&&
CONSTANT_ADDRESS_P
(
XEXP
(
op
,
0
))
&&
!
(
GET_CODE
(
XEXP
(
op
,
0
))
==
CONST_INT
&&
INTVAL
(
XEXP
(
op
,
0
))
<
0x8000
&&
INTVAL
(
XEXP
(
op
,
0
))
>=
-
0x8000
))
{
fprintf
(
file
,
":l"
);
}
}
#ifdef SUPPORT_SUN_FPA
else
if
((
letter
==
'y'
||
letter
==
'w'
)
&&
GET_CODE
(
op
)
==
CONST_DOUBLE
&&
(
i
=
standard_sun_fpa_constant_p
(
op
)))
{
fprintf
(
file
,
"%%%d"
,
i
&
0x1ff
);
}
#endif
else
if
(
GET_CODE
(
op
)
==
CONST_DOUBLE
&&
GET_MODE
(
op
)
==
SFmode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
union
{
float
f
;
int
i
;
}
u1
;
PRINT_OPERAND_EXTRACT_FLOAT
(
op
);
u1
.
f
=
u
.
d
;
PRINT_OPERAND_PRINT_FLOAT
(
letter
,
file
);
}
else
if
(
GET_CODE
(
op
)
==
CONST_DOUBLE
&&
GET_MODE
(
op
)
!=
DImode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
PRINT_OPERAND_EXTRACT_FLOAT
(
op
);
ASM_OUTPUT_DOUBLE_OPERAND
(
file
,
u
.
d
);
}
else
{
asm_fprintf
(
file
,
"%I"
);
output_addr_const
(
file
,
op
);
}
}
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand that is a memory
reference whose address is ADDR. ADDR is an RTL expression.
Note that this contains a kludge that knows that the only reason
we have an address (plus (label_ref...) (reg...)) when not generating
PIC code is in the insn before a tablejump, and we know that m68k.md
generates a label LInnn: on such an insn.
It is possible for PIC to generate a (plus (label_ref...) (reg...))
and we handle that just like we would a (plus (symbol_ref...) (reg...)).
Some SGS assemblers have a bug such that "Lnnn-LInnn-2.b(pc,d0.l*2)"
fails to assemble. Luckily "Lnnn(pc,d0.l*2)" produces the results
we want. This difference can be accommodated by using an assembler
define such "LDnnn" to be either "Lnnn-LInnn-2.b", "Lnnn", or any other
string, as necessary. This is accomplished via the ASM_OUTPUT_CASE_END
macro. See m68ksgs.h for an example; for versions without the bug.
They also do not like things like "pea 1.w", so we simple leave off
the .w on small constants.
This routine is responsible for distinguishing between -fpic and -fPIC
style relocations in an address. When generating -fpic code the
offset is output in word mode (eg movel a5@(_foo:w), a0). When generating
-fPIC code the offset is output in long mode (eg movel a5@(_foo:l), a0) */
void
print_operand_address
(
file
,
addr
)
FILE
*
file
;
rtx
addr
;
{
register
rtx
reg1
,
reg2
,
breg
,
ireg
;
rtx
offset
;
switch
(
GET_CODE
(
addr
))
{
case
REG
:
#ifdef MOTOROLA
fprintf
(
file
,
"(%s)"
,
reg_names
[
REGNO
(
addr
)]);
#else
fprintf
(
file
,
"%s@"
,
reg_names
[
REGNO
(
addr
)]);
#endif
break
;
case
PRE_DEC
:
#ifdef MOTOROLA
fprintf
(
file
,
"-(%s)"
,
reg_names
[
REGNO
(
XEXP
(
addr
,
0
))]);
#else
fprintf
(
file
,
"%s@-"
,
reg_names
[
REGNO
(
XEXP
(
addr
,
0
))]);
#endif
break
;
case
POST_INC
:
#ifdef MOTOROLA
fprintf
(
file
,
"(%s)+"
,
reg_names
[
REGNO
(
XEXP
(
addr
,
0
))]);
#else
fprintf
(
file
,
"%s@+"
,
reg_names
[
REGNO
(
XEXP
(
addr
,
0
))]);
#endif
break
;
case
PLUS
:
reg1
=
reg2
=
ireg
=
breg
=
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
))
==
SIGN_EXTEND
)
{
reg1
=
XEXP
(
addr
,
0
);
addr
=
XEXP
(
addr
,
1
);
}
else
if
(
GET_CODE
(
XEXP
(
addr
,
1
))
==
SIGN_EXTEND
)
{
reg1
=
XEXP
(
addr
,
1
);
addr
=
XEXP
(
addr
,
0
);
}
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
||
GET_CODE
(
addr
)
==
SIGN_EXTEND
)
{
if
(
reg1
==
0
)
{
reg1
=
addr
;
}
else
{
reg2
=
addr
;
}
addr
=
0
;
}
#if 0 /* for OLD_INDEXING */
else if (GET_CODE (addr) == PLUS)
{
if (GET_CODE (XEXP (addr, 0)) == REG)
{
reg2 = XEXP (addr, 0);
addr = XEXP (addr, 1);
}
else if (GET_CODE (XEXP (addr, 1)) == REG)
{
reg2 = XEXP (addr, 1);
addr = XEXP (addr, 0);
}
}
#endif
if
(
offset
!=
0
)
{
if
(
addr
!=
0
)
{
abort
();
}
addr
=
offset
;
}
if
((
reg1
&&
(
GET_CODE
(
reg1
)
==
SIGN_EXTEND
||
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
&&
GET_CODE
(
addr
)
==
LABEL_REF
&&
!
(
flag_pic
&&
ireg
==
pic_offset_table_rtx
))
{
int
scale
=
1
;
if
(
GET_CODE
(
ireg
)
==
MULT
)
{
scale
=
INTVAL
(
XEXP
(
ireg
,
1
));
ireg
=
XEXP
(
ireg
,
0
);
}
if
(
GET_CODE
(
ireg
)
==
SIGN_EXTEND
)
{
#ifdef MOTOROLA
#ifdef SGS
asm_fprintf
(
file
,
"%LLD%d(%Rpc,%s.w"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
XEXP
(
ireg
,
0
))]);
#else
asm_fprintf
(
file
,
"%LL%d-%LLI%d-2.b(%Rpc,%s.w"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
XEXP
(
ireg
,
0
))]);
#endif
#else
asm_fprintf
(
file
,
"%Rpc@(%LL%d-%LLI%d-2:b,%s:w"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
XEXP
(
ireg
,
0
))]);
#endif
}
else
{
#ifdef MOTOROLA
#ifdef SGS
asm_fprintf
(
file
,
"%LLD%d(%Rpc,%s.l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
ireg
)]);
#else
asm_fprintf
(
file
,
"%LL%d-%LLI%d-2.b(%Rpc,%s.l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
ireg
)]);
#endif
#else
asm_fprintf
(
file
,
"%Rpc@(%LL%d-%LLI%d-2:b,%s:l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
ireg
)]);
#endif
}
if
(
scale
!=
1
)
{
#ifdef MOTOROLA
fprintf
(
file
,
"*%d"
,
scale
);
#else
fprintf
(
file
,
":%d"
,
scale
);
#endif
}
putc
(
')'
,
file
);
break
;
}
if
(
breg
!=
0
&&
ireg
==
0
&&
GET_CODE
(
addr
)
==
LABEL_REF
&&
!
(
flag_pic
&&
breg
==
pic_offset_table_rtx
))
{
#ifdef MOTOROLA
#ifdef SGS
asm_fprintf
(
file
,
"%LLD%d(%Rpc,%s.l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
breg
)]);
#else
asm_fprintf
(
file
,
"%LL%d-%LLI%d-2.b(%Rpc,%s.l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
breg
)]);
#endif
#else
asm_fprintf
(
file
,
"%Rpc@(%LL%d-%LLI%d-2:b,%s:l"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
breg
)]);
#endif
putc
(
')'
,
file
);
break
;
}
if
(
ireg
!=
0
||
breg
!=
0
)
{
int
scale
=
1
;
if
(
breg
==
0
)
{
abort
();
}
if
(
!
flag_pic
&&
addr
&&
GET_CODE
(
addr
)
==
LABEL_REF
)
{
abort
();
}
#ifdef MOTOROLA
if
(
addr
!=
0
)
{
output_addr_const
(
file
,
addr
);
if
((
flag_pic
==
1
)
&&
(
breg
==
pic_offset_table_rtx
))
fprintf
(
file
,
":w"
);
if
((
flag_pic
==
2
)
&&
(
breg
==
pic_offset_table_rtx
))
fprintf
(
file
,
":l"
);
}
fprintf
(
file
,
"(%s"
,
reg_names
[
REGNO
(
breg
)]);
if
(
ireg
!=
0
)
{
putc
(
','
,
file
);
}
#else
fprintf
(
file
,
"%s@("
,
reg_names
[
REGNO
(
breg
)]);
if
(
addr
!=
0
)
{
output_addr_const
(
file
,
addr
);
if
((
flag_pic
==
1
)
&&
(
breg
==
pic_offset_table_rtx
))
fprintf
(
file
,
":w"
);
if
((
flag_pic
==
2
)
&&
(
breg
==
pic_offset_table_rtx
))
fprintf
(
file
,
":l"
);
}
if
(
addr
!=
0
&&
ireg
!=
0
)
{
putc
(
','
,
file
);
}
#endif
if
(
ireg
!=
0
&&
GET_CODE
(
ireg
)
==
MULT
)
{
scale
=
INTVAL
(
XEXP
(
ireg
,
1
));
ireg
=
XEXP
(
ireg
,
0
);
}
if
(
ireg
!=
0
&&
GET_CODE
(
ireg
)
==
SIGN_EXTEND
)
{
#ifdef MOTOROLA
fprintf
(
file
,
"%s.w"
,
reg_names
[
REGNO
(
XEXP
(
ireg
,
0
))]);
#else
fprintf
(
file
,
"%s:w"
,
reg_names
[
REGNO
(
XEXP
(
ireg
,
0
))]);
#endif
}
else
if
(
ireg
!=
0
)
{
#ifdef MOTOROLA
fprintf
(
file
,
"%s.l"
,
reg_names
[
REGNO
(
ireg
)]);
#else
fprintf
(
file
,
"%s:l"
,
reg_names
[
REGNO
(
ireg
)]);
#endif
}
if
(
scale
!=
1
)
{
#ifdef MOTOROLA
fprintf
(
file
,
"*%d"
,
scale
);
#else
fprintf
(
file
,
":%d"
,
scale
);
#endif
}
putc
(
')'
,
file
);
break
;
}
else
if
(
reg1
!=
0
&&
GET_CODE
(
addr
)
==
LABEL_REF
&&
!
(
flag_pic
&&
reg1
==
pic_offset_table_rtx
))
{
#ifdef MOTOROLA
#ifdef SGS
asm_fprintf
(
file
,
"%LLD%d(%Rpc,%s.l)"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
reg1
)]);
#else
asm_fprintf
(
file
,
"%LL%d-%LLI%d-2.b(%Rpc,%s.l)"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
reg1
)]);
#endif
#else
asm_fprintf
(
file
,
"%Rpc@(%LL%d-%LLI%d-2:b,%s:l)"
,
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
CODE_LABEL_NUMBER
(
XEXP
(
addr
,
0
)),
reg_names
[
REGNO
(
reg1
)]);
#endif
break
;
}
/* FALL-THROUGH (is this really what we want? */
default
:
if
(
GET_CODE
(
addr
)
==
CONST_INT
&&
INTVAL
(
addr
)
<
0x8000
&&
INTVAL
(
addr
)
>=
-
0x8000
)
{
#ifdef MOTOROLA
#ifdef SGS
/* Many SGS assemblers croak on size specifiers for constants. */
fprintf
(
file
,
"%d"
,
INTVAL
(
addr
));
#else
fprintf
(
file
,
"%d.w"
,
INTVAL
(
addr
));
#endif
#else
fprintf
(
file
,
"%d:w"
,
INTVAL
(
addr
));
#endif
}
else
{
output_addr_const
(
file
,
addr
);
}
break
;
}
}
gcc/config/m88k/m88k.h
0 → 100644
View file @
79e68feb
/* Definitions of target machine for GNU compiler.
Motorola m88100 in an 88open OCS/BCS environment.
Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@mcc.com)
Enhanced by Michael Meissner (meissner@osf.org)
Currently supported by Tom Wood (wood@dg-rtp.dg.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. */
/* The m88100 port of GNU CC adheres to the various standards from 88open.
These documents are available by writing:
88open Consortium Ltd.
100 Homeland Court, Suite 800
San Jose, CA 95112
(408) 436-6600
In brief, the current standards are:
Binary Compatibility Standard, Release 1.1A, May 1991
This provides for portability of application-level software at the
executable level for AT&T System V Release 3.2.
Object Compatibility Standard, Release 1.1A, May 1991
This provides for portability of application-level software at the
object file and library level for C, Fortran, and Cobol, and again,
largely for SVR3.
Under development are standards for AT&T System V Release 4, based on the
[generic] System V Application Binary Interface from AT&T. These include:
System V Application Binary Interface, Motorola 88000 Processor Supplement
Another document from AT&T for SVR4 specific to the m88100.
Available from Prentice Hall.
System V Application Binary Interface, Motorola 88000 Processor Supplement,
Release 1.1, Draft H, May 6, 1991
A proposed update to the AT&T document from 88open.
System V ABI Implementation Guide for the M88000 Processor,
Release 1.0, January 1991
A companion ABI document from 88open. */
/* Other m88k*.h files include this one and override certain items.
At present, these are m88kv3.h, m88kv4.h, m88kdgux.h, and m88kluna.h.
Additionally, m88kv4.h and m88kdgux.h include svr4.h first. All other
m88k targets except m88kluna.h are based on svr3.h. */
/* Choose SVR3 as the default. */
#if !defined(DBX_DEBUGGING_INFO) && !defined(DWARF_DEBUGGING_INFO)
#include "svr3.h"
#endif
/* External types used. */
/* What instructions are needed to manufacture an integer constant. */
enum
m88k_instruction
{
m88k_zero
,
m88k_or
,
m88k_subu
,
m88k_or_lo16
,
m88k_or_lo8
,
m88k_set
,
m88k_oru_hi16
,
m88k_oru_or
};
/* External variables/functions defined in m88k.c. */
extern
char
*
m88k_pound_sign
;
extern
char
*
m88k_short_data
;
extern
int
m88k_gp_threshold
;
extern
int
m88k_prologue_done
;
extern
int
m88k_function_number
;
extern
int
m88k_fp_offset
;
extern
int
m88k_stack_size
;
extern
int
m88k_case_index
;
extern
struct
rtx_def
*
m88k_compare_reg
;
extern
struct
rtx_def
*
m88k_compare_op0
;
extern
struct
rtx_def
*
m88k_compare_op1
;
extern
int
null_epilogue
();
extern
int
integer_ok_for_set
();
extern
int
m88k_debugger_offset
();
extern
void
m88k_handle_pragma_token
();
extern
void
emit_bcnd
();
extern
void
expand_block_move
();
extern
void
check_float_value
();
extern
void
m88k_layout_frame
();
extern
void
m88k_output_prologue
();
extern
void
m88k_output_epilogue
();
extern
void
output_function_profiler
();
extern
void
output_function_block_profiler
();
extern
void
output_block_profiler
();
extern
void
output_file_start
();
extern
void
output_ascii
();
extern
void
output_label
();
extern
void
print_operand
();
extern
void
print_operand_address
();
extern
char
*
output_load_const_int
();
extern
char
*
output_load_const_float
();
extern
char
*
output_load_const_double
();
extern
char
*
output_load_const_dimode
();
extern
char
*
output_and
();
extern
char
*
output_ior
();
extern
char
*
output_xor
();
extern
char
*
output_call
();
extern
struct
rtx_def
*
emit_test
();
extern
struct
rtx_def
*
legitimize_address
();
extern
struct
rtx_def
*
legitimize_operand
();
extern
struct
rtx_def
*
m88k_function_arg
();
extern
struct
rtx_def
*
m88k_builtin_saveregs
();
extern
enum
m88k_instruction
classify_integer
();
/* external variables defined elsewhere in the compiler */
extern
int
target_flags
;
/* -m compiler switches */
extern
int
frame_pointer_needed
;
/* current function has a FP */
extern
int
current_function_pretend_args_size
;
/* args size without ... */
extern
int
flag_delayed_branch
;
/* -fdelayed-branch */
extern
int
flag_pic
;
/* -fpic */
extern
char
*
reg_names
[];
/* Specify the default monitors. The meaning of these values can
be obtained by doing "grep MONITOR_GCC *m88k*". Generally, the
values downward from 0x8000 are tests that will soon go away.
values upward from 0x1 are generally useful tests that will remain. */
#ifndef MONITOR_GCC
#define MONITOR_GCC 0
#endif
/*** Controlling the Compilation Driver, `gcc' ***/
/* Some machines may desire to change what optimizations are performed for
various optimization levels. This macro, if defined, is executed once
just after the optimization level is determined and before the remainder
of the command options have been parsed. Values set in this macro are
used as the default values for the other command line options.
LEVEL is the optimization level specified; 2 if -O2 is specified,
1 if -O is specified, and 0 if neither is specified. */
/* This macro used to store 0 in flag_signed_bitfields.
Not only is that misuse of this macro; the whole idea is wrong.
The GNU C dialect makes bitfields signed by default,
regardless of machine type. Making any machine inconsistent in this
regard is bad for portability.
I chose to make bitfields signed by default because this is consistent
with the way ordinary variables are handled: `int' equals `signed int'.
If there is a good reason to prefer making bitfields unsigned by default,
it cannot have anything to do with the choice of machine.
If the reason is good enough, we should change the convention for all machines.
-- rms, 20 July 1991. */
#define OPTIMIZATION_OPTIONS(LEVEL) \
do { \
if (LEVEL) \
{ \
flag_omit_frame_pointer = 1; \
} \
} while (0)
/* LIB_SPEC, LINK_SPEC, and STARTFILE_SPEC defined in svr3.h.
ASM_SPEC, ASM_FINAL_SPEC, LIB_SPEC, LINK_SPEC, and STARTFILE_SPEC redefined
in svr4.h.
CPP_SPEC, ASM_SPEC, ASM_FINAL_SPEC, LIB_SPEC, LINK_SPEC, and
STARTFILE_SPEC redefined in m88kdgux.h. */
/*** Run-time Target Specification ***/
/* Names to predefine in the preprocessor for this target machine.
Redefined in m88kv3.h, m88kv4.h, m88kdgux.h, and m88kluna.h. */
#define CPP_PREDEFINES "-Dm88000 -Dm88k -Dunix -D__CLASSIFY_TYPE__=2"
#define TARGET_VERSION fprintf (stderr, " (%s%s)", \
VERSION_INFO1, VERSION_INFO2)
/* Print subsidiary information on the compiler version in use.
Redefined in m88kv4.h, and m88kluna.h. */
#define VERSION_INFO1 "88open OCS/BCS, "
#define VERSION_INFO2 "03 Feb 1992"
#define VERSION_STRING version_string
#define TM_SCCS_ID "@(#)m88k.h 1.96.5.3 03 Feb 1992 09:39:25"
/* Run-time compilation parameters selecting different hardware subsets. */
/* Macro to define tables used to set the flags.
This is a list in braces of pairs in braces,
each pair being { "NAME", VALUE }
where VALUE is the bits to set or minus the bits to clear.
An empty string NAME is used to identify the default VALUE. */
#define MASK_88100 0x00000001
/* Target m88100 */
#define MASK_88110 0x00000002
/* Target m88110 */
#define MASK_OCS_DEBUG_INFO 0x00000004
/* Emit .tdesc info */
#define MASK_OCS_FRAME_POSITION 0x00000008
/* Debug frame = CFA, not r30 */
#define MASK_SVR4 0x00000010
/* Target is AT&T System V.4 */
#define MASK_VERSION_0300 0x00000020
/* Use version 03.00 syntax */
#define MASK_NO_UNDERSCORES 0x00000040
/* Don't emit a leading `_' */
#define MASK_BIG_PIC 0x00000080
/* PIC with large got-rel's -fPIC */
#define MASK_TRAP_LARGE_SHIFT 0x00000100
/* Trap if shift not <= 31 */
#define MASK_HANDLE_LARGE_SHIFT 0x00000200
/* Handle shift count >= 32 */
#define MASK_CHECK_ZERO_DIV 0x00000400
/* Check for int div. by 0 */
#define MASK_USE_DIV 0x00000800
/* No signed div. checks */
#define MASK_IDENTIFY_REVISION 0x00001000
/* Emit ident, with GCC rev */
#define MASK_WARN_PASS_STRUCT 0x00002000
/* Warn about passed structs */
#define MASK_OPTIMIZE_ARG_AREA 0x00004000
/* Save stack space */
#define MASK_88000 (MASK_88100 | MASK_88110)
#define MASK_EITHER_LARGE_SHIFT (MASK_TRAP_LARGE_SHIFT | \
MASK_HANDLE_LARGE_SHIFT)
#define TARGET_88100 ((target_flags & MASK_88000) == MASK_88100)
#define TARGET_88110 ((target_flags & MASK_88000) == MASK_88110)
#define TARGET_88000 ((target_flags & MASK_88000) == MASK_88000)
#define TARGET_OCS_DEBUG_INFO (target_flags & MASK_OCS_DEBUG_INFO)
#define TARGET_OCS_FRAME_POSITION (target_flags & MASK_OCS_FRAME_POSITION)
#define TARGET_SVR4 (target_flags & MASK_SVR4)
#define TARGET_VERSION_0300 (target_flags & MASK_VERSION_0300)
#define TARGET_NO_UNDERSCORES (target_flags & MASK_NO_UNDERSCORES)
#define TARGET_BIG_PIC (target_flags & MASK_BIG_PIC)
#define TARGET_TRAP_LARGE_SHIFT (target_flags & MASK_TRAP_LARGE_SHIFT)
#define TARGET_HANDLE_LARGE_SHIFT (target_flags & MASK_HANDLE_LARGE_SHIFT)
#define TARGET_CHECK_ZERO_DIV (target_flags & MASK_CHECK_ZERO_DIV)
#define TARGET_USE_DIV (target_flags & MASK_USE_DIV)
#define TARGET_IDENTIFY_REVISION (target_flags & MASK_IDENTIFY_REVISION)
#define TARGET_WARN_PASS_STRUCT (target_flags & MASK_WARN_PASS_STRUCT)
#define TARGET_OPTIMIZE_ARG_AREA (target_flags & MASK_OPTIMIZE_ARG_AREA)
#define TARGET_EITHER_LARGE_SHIFT (target_flags & MASK_EITHER_LARGE_SHIFT)
/* Redefined in m88kv3.h,m88kv4.h, and m88kdgux.h. */
#define TARGET_DEFAULT (MASK_CHECK_ZERO_DIV)
#define CPU_DEFAULT MASK_88100
#define TARGET_SWITCHES \
{ \
{ "88110", MASK_88110 }, \
{ "88100", MASK_88100 }, \
{ "88000", MASK_88000 }, \
{ "ocs-debug-info", MASK_OCS_DEBUG_INFO }, \
{ "no-ocs-debug-info", -MASK_OCS_DEBUG_INFO }, \
{ "ocs-frame-position", MASK_OCS_FRAME_POSITION }, \
{ "no-ocs-frame-position", -MASK_OCS_FRAME_POSITION }, \
{ "svr4", MASK_SVR4 }, \
{ "svr3", -MASK_SVR4 }, \
{ "version-03.00", MASK_VERSION_0300 }, \
{ "no-underscores", MASK_NO_UNDERSCORES }, \
{ "big-pic", MASK_BIG_PIC }, \
{ "trap-large-shift", MASK_TRAP_LARGE_SHIFT }, \
{ "handle-large-shift", MASK_HANDLE_LARGE_SHIFT }, \
{ "check-zero-division", MASK_CHECK_ZERO_DIV }, \
{ "no-check-zero-division", -MASK_CHECK_ZERO_DIV }, \
{ "use-div-instruction", MASK_USE_DIV }, \
{ "identify-revision", MASK_IDENTIFY_REVISION }, \
{ "warn-passed-structs", MASK_WARN_PASS_STRUCT }, \
{ "optimize-arg-area", MASK_OPTIMIZE_ARG_AREA }, \
{ "no-optimize-arg-area", -MASK_OPTIMIZE_ARG_AREA }, \
SUBTARGET_SWITCHES \
/* Default switches */
\
{ "", TARGET_DEFAULT }, \
}
/* Redefined in m88kdgux.h. */
#define SUBTARGET_SWITCHES
/* Macro to define table for command options with values. */
#define TARGET_OPTIONS { { "short-data-", &m88k_short_data } }
/* Do any checking or such that is needed after processing the -m switches. */
#define OVERRIDE_OPTIONS \
do { \
register int i; \
\
if ((target_flags & MASK_88000) == 0) \
target_flags |= CPU_DEFAULT; \
\
if (TARGET_BIG_PIC) \
flag_pic = 2; \
\
if ((target_flags & MASK_EITHER_LARGE_SHIFT) == MASK_EITHER_LARGE_SHIFT) \
error ("-mtrap-large-shift and -mhandle-large-shift are incompatible");\
\
if (VERSION_0300_SYNTAX) \
{ \
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) \
reg_names[i]--; \
m88k_pound_sign = "#"; \
} \
\
if (m88k_short_data) \
{ \
char *p = m88k_short_data; \
while (*p) \
if (*p >= '0' && *p <= '9') \
p++; \
else \
{ \
error ("Invalid option `-mshort-data-%s'", m88k_short_data); \
break; \
} \
m88k_gp_threshold = atoi (m88k_short_data); \
if (flag_pic) \
error ("-mshort-data-%s and PIC are incompatible", m88k_short_data); \
} \
} while (0)
/*** Storage Layout ***/
/* Sizes in bits of the various types. */
#define CHAR_TYPE_SIZE 8
#define SHORT_TYPE_SIZE 16
#define INT_TYPE_SIZE 32
#define LONG_TYPE_SIZE 32
#define LONG_LONG_TYPE_SIZE 64
#define FLOAT_TYPE_SIZE 32
#define DOUBLE_TYPE_SIZE 64
#define LONG_DOUBLE_TYPE_SIZE 64
/* Define this if most significant bit is lowest numbered
in instructions that operate on numbered bit-fields.
Somewhat arbitrary. It matches the bit field patterns. */
#define BITS_BIG_ENDIAN 1
/* Define this if most significant byte of a word is the lowest numbered.
That is true on the m88000. */
#define BYTES_BIG_ENDIAN 1
/* Define this if most significant word of a multiword number is the lowest
numbered.
For the m88000 we can decide arbitrarily since there are no machine
instructions for them. */
#define WORDS_BIG_ENDIAN 1
/* Number of bits in an addressible storage unit */
#define BITS_PER_UNIT 8
/* Width in bits of a "word", which is the contents of a machine register.
Note that this is not necessarily the width of data type `int';
if using 16-bit ints on a 68000, this would still be 32.
But on a machine with 16-bit registers, this would be 16. */
#define BITS_PER_WORD 32
/* Width of a word, in units (bytes). */
#define UNITS_PER_WORD 4
/* Width in bits of a pointer.
See also the macro `Pmode' defined below. */
#define POINTER_SIZE 32
/* Allocation boundary (in *bits*) for storing arguments in argument list. */
#define PARM_BOUNDARY 32
/* Largest alignment for stack parameters (if greater than PARM_BOUNDARY). */
#define MAX_PARM_BOUNDARY 64
/* Boundary (in *bits*) on which stack pointer should be aligned. */
#define STACK_BOUNDARY 128
/* Allocation boundary (in *bits*) for the code of a function. */
#define FUNCTION_BOUNDARY 128
/* No data type wants to be aligned rounder than this. */
#define BIGGEST_ALIGNMENT 64
/* Make strings word-aligned so strcpy from constants will be faster. */
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
(TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
/* Make arrays of chars word-aligned for the same reasons. */
#define DATA_ALIGNMENT(TYPE, ALIGN) \
(TREE_CODE (TYPE) == ARRAY_TYPE \
&& TYPE_MODE (TREE_TYPE (TYPE)) == QImode \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
/* Alignment of field after `int : 0' in a structure.
Ignored with PCC_BITFIELD_TYPE_MATTERS. */
/* #define EMPTY_FIELD_BOUNDARY 8 */
/* Every structure's size must be a multiple of this. */
#define STRUCTURE_SIZE_BOUNDARY 8
/* Define this if move instructions will actually fail to work
when given unaligned data. */
#define STRICT_ALIGNMENT
/* A bitfield declared as `int' forces `int' alignment for the struct. */
#define PCC_BITFIELD_TYPE_MATTERS 1
/* Maximum size (in bits) to use for the largest integral type that
replaces a BLKmode type. */
/* #define MAX_FIXED_MODE_SIZE 0 */
/* Report errors on floating point, if we are given NaN's, or such. Leave
the number as is, though, since we output the number in hex, and the
assemble won't choak on it. */
#define CHECK_FLOAT_VALUE(MODE,VALUE) check_float_value (MODE, VALUE)
/* A code distinguishing the floating point format of the target machine. */
/* #define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT */
/*** Register Usage ***/
/* Number of actual hardware registers.
The hardware registers are assigned numbers for the compiler
from 0 to just below FIRST_PSEUDO_REGISTER.
All registers that the compiler knows about must be given numbers,
even those that are not normally considered general registers.
The m88100 has 32 fullword registers.
The pseudo argument pointer is said to be register 0. This prohibits
the use of r0 as a general register and causes no trouble.
Using register 0 is useful, in that it keeps the number of
registers down to 32, and GNU can use a long as a bitmask
for the registers. */
#define FIRST_PSEUDO_REGISTER 32
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator.
Registers 14-25 are expected to be preserved across
function calls.
On the 88000, these are:
Reg 0 = Pseudo argument pointer (hardware fixed to 0).
Reg 1 = Subroutine return pointer (hardware).
Reg 2-9 = Parameter registers (OCS).
Reg 10 = OCS reserved temporary.
Reg 11 = Static link if needed [OCS reserved temporary].
Reg 12 = Address of structure return (OCS).
Reg 13 = OCS reserved temporary.
Reg 14-25 = Preserved register set.
Reg 26-29 = Reserved by OCS and ABI.
Reg 30 = Frame pointer (Common use).
Reg 31 = Stack pointer. */
#define FIXED_REGISTERS \
{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, 1, 1, 1, 1, 1, 1}
/* 1 for registers not available across function calls.
These must include the FIXED_REGISTERS and also any
registers that can be used without being saved.
The latter must include the registers where values are returned
and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you like. */
#define CALL_USED_REGISTERS \
{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, 1, 1, 1, 1, 1, 1}
/* Macro to conditionally modify fixed_regs/call_used_regs. */
#define CONDITIONAL_REGISTER_USAGE \
{ \
if (flag_pic) \
{ \
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
global_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
} \
}
/* These interfaces that don't apply to the m88000. */
/* OVERLAPPING_REGNO_P(REGNO) 0 */
/* INSN_CLOBBERS_REGNO_P(INSN, REGNO) 0 */
/* PRESERVE_DEATH_INFO_REGNO_P(REGNO) 0 */
/* Return number of consecutive hard regs needed starting at reg REGNO
to hold something of mode MODE.
This is ordinarily the length in words of a value of mode MODE
but can be less for certain modes in special long registers.
On the m88000, ordinary registers hold 32 bits worth;
a single floating point register is always enough for
anything that can be stored in them at all. */
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
For double integers, we never put the value into an odd register so that
the operators don't run into the situation where the high part of one of
the inputs is the low part of the result register (it's ok if the output
registers are the same as the input registers. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
(((MODE) != DImode && (MODE) != DFmode && (MODE) != DCmode) || \
((REGNO) & 1) == 0)
/* Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2.
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
for any hard reg, then this must be 0 for correct output. */
#define MODES_TIEABLE_P(MODE1, MODE2) \
(((MODE1) == DFmode || (MODE1) == DCmode || (MODE1) == DImode) \
== ((MODE2) == DFmode || (MODE2) == DCmode || (MODE2) == DImode))
/* Specify the registers used for certain standard purposes.
The values of these macros are register numbers. */
/* the m88000 pc isn't overloaded on a register that the compiler knows about. */
/* #define PC_REGNUM */
/* Register to use for pushing function arguments. */
#define STACK_POINTER_REGNUM 31
/* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM 30
/* Base register for access to arguments of the function. */
#define ARG_POINTER_REGNUM 0
/* Register used in cases where a temporary is known to be safe to use. */
#define TEMP_REGNUM 10
/* Register in which static-chain is passed to a function. */
#define STATIC_CHAIN_REGNUM 11
/* Register in which address to store a structure value
is passed to a function. */
#define STRUCT_VALUE_REGNUM 12
/* Register to hold the addressing base for position independent
code access to data items. */
#define PIC_OFFSET_TABLE_REGNUM 25
/* Order in which registers are preferred (most to least). Use temp
registers, then param registers top down. Preserve registers are
top down to maximize use of double memory ops for register save.
The 88open reserved registers (26-29) may commonly be used in most
environments with the -fcall-used- or -fcall-saved- options. */
#define REG_ALLOC_ORDER \
{13, 12, 11, 10, 29, 28, 27, 26, \
1, 9, 8, 7, 6, 5, 4, 3, \
2, 25, 24, 23, 22, 21, 20, 19, \
18, 17, 16, 15, 14, 30, 31, 0}
/*** Register Classes ***/
/* Define the classes of registers for register constraints in the
machine description. Also define ranges of constants.
One of the classes must always be named ALL_REGS and include all hard regs.
If there is more than one class, another class must be named NO_REGS
and contain no registers.
The name GENERAL_REGS must be the name of a class (or an alias for
another name such as ALL_REGS). This is the class of registers
that is allowed by "g" or "r" in a register constraint.
Also, registers outside this class are allocated only when
instructions express preferences for them.
The classes must be numbered in nondecreasing order; that is,
a larger-numbered class must never be contained completely
in a smaller-numbered class.
For any two classes, it is very desirable that there be another
class that represents their union. */
/* The m88100 hardware has one kind of register. However, we denote
the arg pointer as a separate class. */
enum
reg_class
{
NO_REGS
,
AP_REG
,
GENERAL_REGS
,
ALL_REGS
,
LIM_REG_CLASSES
};
#define N_REG_CLASSES (int) LIM_REG_CLASSES
/* Give names of register classes as strings for dump file. */
#define REG_CLASS_NAMES {"NO_REGS", "AP_REG", "GENERAL_REGS", "ALL_REGS" }
/* Define which registers fit in which classes.
This is an initializer for a vector of HARD_REG_SET
of length N_REG_CLASSES. */
#define REG_CLASS_CONTENTS {0, 1, -2, -1}
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
or could index an array. */
#define REGNO_REG_CLASS(REGNO) ((REGNO) ? GENERAL_REGS : AP_REG)
/* The class value for index registers, and the one for base regs. */
#define BASE_REG_CLASS ALL_REGS
#define INDEX_REG_CLASS GENERAL_REGS
/* Get reg_class from a letter such as appears in the machine description. */
#define REG_CLASS_FROM_LETTER(C) NO_REGS
/* Macros to check register numbers against specific register classes.
These assume that REGNO is a hard or pseudo reg number.
They give nonzero only if REGNO is a hard reg of the suitable class
or a pseudo reg currently allocated to a suitable hard reg.
Since they use reg_renumber, they are safe only once reg_renumber
has been allocated, which happens in local-alloc.c. */
#define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < FIRST_PSEUDO_REGISTER || \
(unsigned) reg_renumber[REGNO] < FIRST_PSEUDO_REGISTER)
#define REGNO_OK_FOR_INDEX_P(REGNO) \
(((REGNO) && (REGNO) < FIRST_PSEUDO_REGISTER) || \
(unsigned) reg_renumber[REGNO] < FIRST_PSEUDO_REGISTER)
/* Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use.
In general this is just CLASS; but on some machines
in some cases it is preferable to use a more restrictive class.
Double constants should be in a register iff they can be made cheaply. */
#define PREFERRED_RELOAD_CLASS(X,CLASS) (CLASS)
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. */
#define CLASS_MAX_NREGS(CLASS, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
/* Letters in the range `I' through `P' in a register constraint string can
be used to stand for particular ranges of immediate operands. The C
expression is true iff C is a known letter and VALUE is appropriate for
that letter.
For the m88000, the following contraints are used:
`I' requires a non-negative 16-bit value.
`J' requires a non-positive 16-bit value.
`K' is unused.
`L' requires a constant with only the upper 16-bits set.
`M' requires constant values that can be formed with `set'.
`N' requires a negative value.
`O' requires zero.
`P' requires a non-negative value. */
/* Quick tests for certain values. */
#define SMALL_INT(X) (SMALL_INTVAL (INTVAL (X)))
#define SMALL_INTVAL(I) ((unsigned) (I) < 0x10000)
#define ADD_INT(X) (ADD_INTVAL (INTVAL (X)))
#define ADD_INTVAL(I) ((unsigned) (I) + 0xffff < 0x1ffff)
#define POWER_OF_2(I) ((I) && POWER_OF_2_or_0(I))
#define POWER_OF_2_or_0(I) (((I) & ((unsigned)(I) - 1)) == 0)
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? SMALL_INTVAL (VALUE) \
: (C) == 'J' ? SMALL_INTVAL (-(VALUE)) \
: (C) == 'L' ? ((VALUE) & 0xffff) == 0 \
: (C) == 'M' ? integer_ok_for_set (VALUE) \
: (C) == 'N' ? (VALUE) < 0 \
: (C) == 'O' ? (VALUE) == 0 \
: (C) == 'P' ? (VALUE) >= 0 \
: 0)
/* Similar, but for floating constants, and defining letters G and H.
Here VALUE is the CONST_DOUBLE rtx itself. For the m88000, the
constraints are: `G' requires zero, and `H' requires one or two. */
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'G' ? (CONST_DOUBLE_HIGH (VALUE) == 0 \
&& CONST_DOUBLE_LOW (VALUE) == 0) \
: 0)
/* Letters in the range `Q' through `U' in a register constraint string
may be defined in a machine-dependent fashion to stand for arbitrary
operand types.
For the m88k, `Q' handles addresses in a call context. */
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'Q' ? symbolic_address_p (OP) : 0)
/*** Describing Stack Layout ***/
/* Define this if pushing a word on the stack moves the stack pointer
to a smaller address. */
#define STACK_GROWS_DOWNWARD
/* Define this if the addresses of local variable slots are at negative
offsets from the frame pointer. */
/* #define FRAME_GROWS_DOWNWARD */
/* Offset from the frame pointer to the first local variable slot to be
allocated. For the m88k, the debugger wants the return address (r1)
stored at location r30+4, and the previous frame pointer stored at
location r30. */
#define STARTING_FRAME_OFFSET 8
/* If we generate an insn to push BYTES bytes, this says how many the
stack pointer really advances by. The m88k has no push instruction. */
/* #define PUSH_ROUNDING(BYTES) */
/* If defined, the maximum amount of space required for outgoing arguments
will be computed and placed into the variable
`current_function_outgoing_args_size'. No space will be pushed
onto the stack for each call; instead, the function prologue should
increase the stack frame size by this amount. */
#define ACCUMULATE_OUTGOING_ARGS
/* Offset from the stack pointer register to the first location at which
outgoing arguments are placed. Use the default value zero. */
/* #define STACK_POINTER_OFFSET 0 */
/* Offset of first parameter from the argument pointer register value.
Using an argument pointer, this is 0 for the m88k. GCC knows
how to eliminate the argument pointer references if necessary. */
#define FIRST_PARM_OFFSET(FNDECL) 0
/* Define this if functions should assume that stack space has been
allocated for arguments even when their values are passed in
registers.
The value of this macro is the size, in bytes, of the area reserved for
arguments passed in registers.
This space can either be allocated by the caller or be a part of the
machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
says which. */
#define REG_PARM_STACK_SPACE(FNDECL) 32
/* Define this macro if REG_PARM_STACK_SPACE is defined but stack
parameters don't skip the area specified by REG_PARM_STACK_SPACE.
Normally, when a parameter is not passed in registers, it is placed on
the stack beyond the REG_PARM_STACK_SPACE area. Defining this macro
suppresses this behavior and causes the parameter to be passed on the
stack in its natural location. */
#define STACK_PARMS_IN_REG_PARM_AREA
/* Define this if it is the responsibility of the caller to allocate the
area reserved for arguments passed in registers. If
`ACCUMULATE_OUTGOING_ARGS' is also defined, the only effect of this
macro is to determine whether the space is included in
`current_function_outgoing_args_size'. */
/* #define OUTGOING_REG_PARM_STACK_SPACE */
/* Offset from the stack pointer register to an item dynamically allocated
on the stack, e.g., by `alloca'.
The default value for this macro is `STACK_POINTER_OFFSET' plus the
length of the outgoing arguments. The default is correct for most
machines. See `function.c' for details. */
/* #define STACK_DYNAMIC_OFFSET(FUNDECL) ... */
/* Value is the number of bytes of arguments automatically
popped when returning from a subroutine call.
FUNTYPE is the data type of the function (as a tree),
or for a library call it is an identifier node for the subroutine name.
SIZE is the number of bytes of arguments passed on the stack. */
#define RETURN_POPS_ARGS(FUNTYPE,SIZE) 0
/* Define how to find the value returned by a function.
VALTYPE is the data type of the value (as a tree).
If the precise function being called is known, FUNC is its FUNCTION_DECL;
otherwise, FUNC is 0. */
#define FUNCTION_VALUE(VALTYPE, FUNC) \
gen_rtx (REG, \
TYPE_MODE (VALTYPE) == BLKmode ? SImode : TYPE_MODE (VALTYPE), \
2)
/* Define this if it differs from FUNCTION_VALUE. */
/* #define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) ... */
/* Disable the promotion of some structures and unions to registers. */
#define RETURN_IN_MEMORY(TYPE) \
((TREE_CODE (TYPE) == RECORD_TYPE || TREE_CODE(TYPE) == UNION_TYPE) \
&& !(TYPE_MODE (TYPE) == SImode \
|| (TYPE_MODE (TYPE) == BLKmode \
&& TYPE_ALIGN (TYPE) == BITS_PER_WORD \
&& int_size_in_bytes (TYPE) == UNITS_PER_WORD)))
/* Define how to find the value returned by a library function
assuming the value has mode MODE. */
#define LIBCALL_VALUE(MODE) gen_rtx (REG, MODE, 2)
/* True if N is a possible register number for a function value
as seen by the caller. */
#define FUNCTION_VALUE_REGNO_P(N) ((N) == 2)
/* Determine whether a function argument is passed in a register, and
which register. See m88k.c. */
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
m88k_function_arg (CUM, MODE, TYPE, NAMED)
/* Define this if it differs from FUNCTION_ARG. */
/* #define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) ... */
/* A C expression for the number of words, at the beginning of an
argument, must be put in registers. The value must be zero for
arguments that are passed entirely in registers or that are entirely
pushed on the stack. */
#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) (0)
/* A C expression that indicates when an argument must be passed by
reference. If nonzero for an argument, a copy of that argument is
made in memory and a pointer to the argument is passed instead of the
argument itself. The pointer is passed in whatever way is appropriate
for passing a pointer to that type. */
#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) (0)
/* A C type for declaring a variable that is used as the first argument
of `FUNCTION_ARG' and other related values. It suffices to count
the number of words of argument so far. */
#define CUMULATIVE_ARGS int
/* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a
function whose data type is FNTYPE. For a library call, FNTYPE is 0. */
#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) ((CUM) = 0)
/* A C statement (sans semicolon) to update the summarizer variable
CUM to advance past an argument in the argument list. The values
MODE, TYPE and NAMED describe that argument. Once this is done,
the variable CUM is suitable for analyzing the *following* argument
with `FUNCTION_ARG', etc. (TYPE is null for libcalls where that
information may not be available.) */
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
do { \
enum machine_mode __mode = (TYPE) ? TYPE_MODE (TYPE) : (MODE); \
if ((CUM & 1) \
&& (__mode == DImode || __mode == DFmode \
|| ((TYPE) && TYPE_ALIGN (TYPE) > BITS_PER_WORD))) \
CUM++; \
CUM += (((__mode != BLKmode) \
? GET_MODE_SIZE (MODE) : int_size_in_bytes (TYPE)) \
+ 3) / 4; \
} while (0)
/* True if N is a possible register number for function argument passing.
On the m88000, these are registers 2 through 9. */
#define FUNCTION_ARG_REGNO_P(N) ((N) <= 9 && (N) >= 2)
/* A C expression which determines whether, and in which direction,
to pad out an argument with extra space. The value should be of
type `enum direction': either `upward' to pad above the argument,
`downward' to pad below, or `none' to inhibit padding.
This macro does not control the *amount* of padding; that is always
just enough to reach the next multiple of `FUNCTION_ARG_BOUNDARY'. */
#define FUNCTION_ARG_PADDING(MODE, TYPE) \
((MODE) == BLKmode \
|| ((TYPE) && (TREE_CODE (TYPE) == RECORD_TYPE \
|| TREE_CODE (TYPE) == UNION_TYPE)) \
? upward : GET_MODE_BITSIZE (MODE) < PARM_BOUNDARY ? downward : none)
/* If defined, a C expression that gives the alignment boundary, in bits,
of an argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \
(((TYPE) ? TYPE_ALIGN (TYPE) : GET_MODE_SIZE (MODE)) <= PARM_BOUNDARY \
? PARM_BOUNDARY : 2 * PARM_BOUNDARY)
/* Generate necessary RTL for __builtin_saveregs().
ARGLIST is the argument list; see expr.c. */
#define EXPAND_BUILTIN_SAVEREGS(ARGLIST) m88k_builtin_saveregs (ARGLIST)
/* Generate the assembly code for function entry. */
#define FUNCTION_PROLOGUE(FILE, SIZE) m88k_output_prologue(FILE, SIZE)
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry. Redefined in m88kv3.h, m88kv4.h and
m88kdgux.h. */
#define FUNCTION_PROFILER(FILE, LABELNO) \
output_function_profiler (FILE, LABELNO, "mcount", 1)
/* Output assembler code to FILE to initialize basic-block profiling for
the current module. LABELNO is unique to each instance. */
#define FUNCTION_BLOCK_PROFILER(FILE, LABELNO) \
output_function_block_profiler (FILE, LABELNO)
/* Output assembler code to FILE to increment the count associated with
the basic block number BLOCKNO. */
#define BLOCK_PROFILER(FILE, BLOCKNO) output_block_profiler (FILE, BLOCKNO)
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in
functions that have frame pointers.
No definition is equivalent to always zero. */
#define EXIT_IGNORE_STACK (1)
/* Generate the assembly code for function exit. */
#define FUNCTION_EPILOGUE(FILE, SIZE) m88k_output_epilogue(FILE, SIZE)
/* Define the number of delay slots needed for the function epilogue.
These are used for scheduling the function epilogue and depend on
what the epilogue looks like. */
#define DELAY_SLOTS_FOR_EPILOGUE delay_slots_for_epilogue ()
/* Define whether INSN can be placed in delay slot N for the epilogue. */
#define ELIGIBLE_FOR_EPILOGUE_DELAY(INSN,N) \
eligible_for_epilogue_delay (INSN)
/* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms
may be accessed via the stack pointer) in functions that seem suitable.
This is computed in `reload', in reload1.c. */
#define FRAME_POINTER_REQUIRED \
(frame_pointer_needed \
|| (write_symbols != NO_DEBUG && !TARGET_OCS_FRAME_POSITION))
/* Definitions for register eliminations.
We have two registers that can be eliminated on the m88k. First, the
frame pointer register can often be eliminated in favor of the stack
pointer register. Secondly, the argument pointer register can always be
eliminated; it is replaced with either the stack or frame pointer. */
/* This is an array of structures. Each structure initializes one pair
of eliminable registers. The "from" register number is given first,
followed by "to". Eliminations of the same "from" register are listed
in order of preference. */
#define ELIMINABLE_REGS \
{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
/* Given FROM and TO register numbers, say whether this elimination
is allowed. */
#define CAN_ELIMINATE(FROM, TO) \
(!((FROM) == FRAME_POINTER_REGNUM && FRAME_POINTER_REQUIRED))
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ m88k_layout_frame (); \
if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
(OFFSET) = m88k_fp_offset; \
else if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) \
(OFFSET) = m88k_stack_size - m88k_fp_offset; \
else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
(OFFSET) = m88k_stack_size; \
else \
abort (); \
}
/*** Trampolines for Nested Functions ***/
/* Output assembler code for a block containing the constant parts
of a trampoline, leaving space for the variable parts.
This block is placed on the stack and filled in. It is aligned
0 mod 128 and those portions that are executed are constant.
This should work for instruction caches that have cache lines up
to the aligned amount (128 is arbitrary), provided no other code
producer is attempting to play the same game. This of course is
in violation of any number of 88open standards. */
#define TRAMPOLINE_TEMPLATE(FILE) \
{ \
/* Save the return address (r1) in the static chain reg (r11). */
\
fprintf (FILE, "\tor\t %s,%s,0\n", reg_names[11], reg_names[1]); \
/* Locate this block; transfer to the next instruction. */
\
fprintf (FILE, "\tbsr\t 1\n"); \
/* Save r10; use it as the relative pointer; restore r1. */
\
fprintf (FILE, "\tst\t %s,%s,24\n", reg_names[10], reg_names[1]); \
fprintf (FILE, "\tor\t %s,%s,0\n", reg_names[10], reg_names[1]); \
fprintf (FILE, "\tor\t %s,%s,0\n", reg_names[1], reg_names[11]); \
/* Load the function's address and go there. */
\
fprintf (FILE, "\tld\t %s,%s,32\n", reg_names[11], reg_names[10]); \
fprintf (FILE, "\tjmp.n\t %s\n", reg_names[11]); \
/* Restore r10 and load the static chain register. */
\
fprintf (FILE, "\tld.d\t %s,%s,24\n", reg_names[10], reg_names[10]); \
/* Storage: r10 save area, static chain, function address. */
\
ASM_OUTPUT_INT (FILE, const0_rtx); \
ASM_OUTPUT_INT (FILE, const0_rtx); \
ASM_OUTPUT_INT (FILE, const0_rtx); \
}
/* Length in units of the trampoline for entering a nested function.
This is really two components. The first 32 bytes are fixed and
must be copied; the last 12 bytes are just storage that's filled
in later. So for allocation purposes, it's 32+12 bytes, but for
initializaiton purposes, it's 32 bytes. */
#define TRAMPOLINE_SIZE (32+12)
/* Alignment required for a trampoline. 128 is used to find the
beginning of a line in the instruction cache and to allow for
instruction cache lines of up to 128 bytes. */
#define TRAMPOLINE_ALIGNMENT 128
/* Emit RTL insns to initialize the variable parts of a trampoline.
FNADDR is an RTX for the address of the function's pure code.
CXT is an RTX for the static chain value for the function. */
#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
{ \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 40)), FNADDR); \
emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 36)), CXT); \
}
/*** Library Subroutine Names ***/
/* Define this macro if GNU CC should generate calls to the System V
(and ANSI C) library functions `memcpy' and `memset' rather than
the BSD functions `bcopy' and `bzero'. */
#define TARGET_MEM_FUNCTIONS
/*** Addressing Modes ***/
/* #define HAVE_POST_INCREMENT */
/* #define HAVE_POST_DECREMENT */
/* #define HAVE_PRE_DECREMENT */
/* #define HAVE_PRE_INCREMENT */
/* Recognize any constant value that is a valid address. */
#define CONSTANT_ADDRESS_P(X) (CONSTANT_P (X))
/* Maximum number of registers that can appear in a valid memory address. */
#define MAX_REGS_PER_ADDRESS 2
/* The condition for memory shift insns. */
#define SCALED_ADDRESS_P(ADDR) \
(GET_CODE (ADDR) == PLUS \
&& (GET_CODE (XEXP (ADDR, 0)) == MULT \
|| GET_CODE (XEXP (ADDR, 1)) == MULT))
/* Can the reference to X be made short? */
#define SHORT_ADDRESS_P(X,TEMP) \
((TEMP) = (GET_CODE (X) == CONST ? get_related_value (X) : X), \
((TEMP) && GET_CODE (TEMP) == SYMBOL_REF && SYMBOL_REF_FLAG (TEMP)))
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
that is a valid memory address for an instruction.
The MODE argument is the machine mode for the MEM expression
that wants to use this address.
On the m88000, a legitimate address has the form REG, REG+REG,
REG+SMALLINT, REG+(REG*modesize) (REG[REG]), or SMALLINT.
The register elimination process should deal with the argument
pointer and frame pointer changing to REG+SMALLINT. */
#define LEGITIMATE_INDEX_P(X, MODE) \
((GET_CODE (X) == CONST_INT \
&& SMALL_INT (X)) \
|| (REG_P (X) \
&& REG_OK_FOR_INDEX_P (X)) \
|| (GET_CODE (X) == MULT \
&& REG_P (XEXP (X, 0)) \
&& REG_OK_FOR_INDEX_P (XEXP (X, 0)) \
&& GET_CODE (XEXP (X, 1)) == CONST_INT \
&& INTVAL (XEXP (X, 1)) == GET_MODE_SIZE (MODE)))
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ \
register rtx _x; \
if (REG_P (X)) \
{ \
if (REG_OK_FOR_BASE_P (X)) \
goto ADDR; \
} \
else if (GET_CODE (X) == PLUS) \
{ \
register rtx _x0 = XEXP (X, 0); \
register rtx _x1 = XEXP (X, 1); \
if ((flag_pic \
&& _x0 == pic_offset_table_rtx \
&& (flag_pic == 2 \
? REG_P (_x1) \
: (GET_CODE (_x1) == SYMBOL_REF \
|| GET_CODE (_x1) == LABEL_REF))) \
|| (REG_P (_x0) \
&& (REG_OK_FOR_BASE_P (_x0) \
&& LEGITIMATE_INDEX_P (_x1, MODE))) \
|| (REG_P (_x1) \
&& (REG_OK_FOR_BASE_P (_x1) \
&& LEGITIMATE_INDEX_P (_x0, MODE)))) \
goto ADDR; \
} \
else if (GET_CODE (X) == LO_SUM) \
{ \
register rtx _x0 = XEXP (X, 0); \
register rtx _x1 = XEXP (X, 1); \
if (((REG_P (_x0) \
&& REG_OK_FOR_BASE_P (_x0)) \
|| (GET_CODE (_x0) == SUBREG \
&& REG_P (SUBREG_REG (_x0)) \
&& REG_OK_FOR_BASE_P (SUBREG_REG (_x0)))) \
&& CONSTANT_P (_x1)) \
goto ADDR; \
} \
else if (GET_CODE (X) == CONST_INT \
&& SMALL_INT (X)) \
goto ADDR; \
else if (SHORT_ADDRESS_P (X, _x)) \
goto ADDR; \
}
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class.
We have two alternate definitions for each of them.
The usual definition accepts all pseudo regs; the other rejects
them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used.
Most source files want to accept pseudo regs in the hope that
they will get allocated to the class that the insn wants them to be in.
Source files for reload pass need to be strict.
After reload, it makes no difference, since pseudo regs have
been eliminated by then. */
#ifndef REG_OK_STRICT
/* Nonzero if X is a hard reg that can be used as an index
or if it is a pseudo reg. Not the argument pointer. */
#define REG_OK_FOR_INDEX_P(X) (X)
/* Nonzero if X is a hard reg that can be used as a base reg
or if it is a pseudo reg. */
#define REG_OK_FOR_BASE_P(X) (1)
#else
/* Nonzero if X is a hard reg that can be used as an index. */
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
/* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
#endif
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
MODE and WIN are passed so that this macro can use
GO_IF_LEGITIMATE_ADDRESS.
It is always safe for this macro to do nothing. It exists to recognize
opportunities to optimize the output. */
/* On the m88000, change REG+N into REG+REG, and REG+(X*Y) into REG+REG. */
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
{ \
if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 1))) \
(X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \
copy_to_mode_reg (SImode, XEXP (X, 1))); \
if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 0))) \
(X) = gen_rtx (PLUS, SImode, XEXP (X, 1), \
copy_to_mode_reg (SImode, XEXP (X, 0))); \
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == MULT) \
(X) = gen_rtx (PLUS, SImode, XEXP (X, 1), \
force_operand (XEXP (X, 0), 0)); \
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == MULT) \
(X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \
force_operand (XEXP (X, 1), 0)); \
if (GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == CONST \
|| GET_CODE (X) == LABEL_REF) \
(X) = legitimize_address (flag_pic, X, gen_reg_rtx (Pmode)); \
if (memory_address_p (MODE, X)) \
goto WIN; }
/* Go to LABEL if ADDR (a legitimate address expression)
has an effect that depends on the machine mode it is used for.
On the the m88000 this is never true. */
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
#define LEGITIMATE_CONSTANT_P(X) (1)
/*** Condition Code Information ***/
/* C code for a data type which is used for declaring the `mdep'
component of `cc_status'. It defaults to `int'. */
/* #define CC_STATUS_MDEP int */
/* A C expression to initialize the `mdep' field to "empty". */
/* #define CC_STATUS_MDEP_INIT (cc_status.mdep = 0) */
/* Macro to zap the normal portions of CC_STATUS, but leave the
machine dependent parts (ie, literal synthesis) alone. */
/* #define CC_STATUS_INIT_NO_MDEP \
(cc_status.flags = 0, cc_status.value1 = 0, cc_status.value2 = 0) */
/* When using a register to hold the condition codes, the cc_status
mechanism cannot be used. */
#define NOTICE_UPDATE_CC(EXP, INSN) (0)
/*** Miscellaneous Parameters ***/
/* Define the codes that are matched by predicates in m88k.c. */
#define PREDICATE_CODES \
{"move_operand", {SUBREG, REG, CONST_INT, LO_SUM, MEM}}, \
{"call_address_operand", {SUBREG, REG, SYMBOL_REF, LABEL_REF, CONST}}, \
{"arith_operand", {SUBREG, REG, CONST_INT}}, \
{"arith5_operand", {SUBREG, REG, CONST_INT}}, \
{"arith32_operand", {SUBREG, REG, CONST_INT}}, \
{"arith64_operand", {SUBREG, REG, CONST_INT}}, \
{"int5_operand", {CONST_INT}}, \
{"int32_operand", {CONST_INT}}, \
{"add_operand", {SUBREG, REG, CONST_INT}}, \
{"reg_or_bbx_mask_operand", {SUBREG, REG, CONST_INT}}, \
{"real_or_0_operand", {SUBREG, REG, CONST_DOUBLE}}, \
{"relop", {EQ, NE, LT, LE, GE, GT, LTU, LEU, GEU, GTU}}, \
{"relop_no_unsigned", {EQ, NE, LT, LE, GE, GT}}, \
{"equality_op", {EQ, NE}}, \
{"pc_or_label_ref", {PC, LABEL_REF}},
/* An alias for a machine mode name. This is the machine mode that
elements of a jump-table should have. */
#define CASE_VECTOR_MODE SImode
/* Define this macro if jump-tables should contain relative addresses. */
#define CASE_VECTOR_PC_RELATIVE
/* Define this if control falls through a `case' insn when the index
value is out of range. This means the specified default-label is
actually ignored by the `case' insn proper. */
/* #define CASE_DROPS_THROUGH */
/* Specify the tree operation to be used to convert reals to integers. */
#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
/* This is the kind of divide that is easiest to do in the general case. */
#define EASY_DIV_EXPR TRUNC_DIV_EXPR
/* Define this as 1 if `char' should by default be signed; else as 0. */
#define DEFAULT_SIGNED_CHAR 1
/* The 88open ABI says size_t is unsigned int. */
#define SIZE_TYPE "unsigned int"
/* Allow and ignore #sccs directives */
#define SCCS_DIRECTIVE
/* Code to handle #pragma directives. The interface is a bit messy,
but there's no simpler way to do this while still using yylex. */
#define HANDLE_PRAGMA(FILE) \
do { \
while (c == ' ' || c == '\t') \
c = getc (FILE); \
if (c == '\n' || c == EOF) \
{ \
m88k_handle_pragma_token (0, 0); \
return c; \
} \
ungetc (c, FILE); \
switch (yylex ()) \
{ \
case IDENTIFIER: \
case TYPENAME: \
case STRING: \
case CONSTANT: \
m88k_handle_pragma_token (token_buffer, yylval.ttype); \
break; \
default: \
m88k_handle_pragma_token (token_buffer, 0); \
} \
if (nextchar >= 0) \
c = nextchar, nextchar = -1; \
else \
c = getc (FILE); \
} while (1)
/* Tell when to handle #pragma weak. This is only done for V.4. */
#define HANDLE_PRAGMA_WEAK TARGET_SVR4
/* Max number of bytes we can move from memory to memory
in one reasonably fast instruction. */
#define MOVE_MAX 64
/* Define if normal loads of shorter-than-word items from memory clears
the rest of the bigs in the register. */
#define BYTE_LOADS_ZERO_EXTEND
/* Zero if access to memory by bytes is faster. */
#define SLOW_BYTE_ACCESS 1
/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
is done just by pretending it is already truncated. */
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
/* Define this if addresses of constant functions
shouldn't be put through pseudo regs where they can be cse'd.
Desirable on machines where ordinary constants are expensive
but a CALL with constant address is cheap. */
#define NO_FUNCTION_CSE
/* Define this macro if an argument declared as `char' or
`short' in a prototype should actually be passed as an
`int'. In addition to avoiding errors in certain cases of
mismatch, it also makes for better code on certain machines. */
#define PROMOTE_PROTOTYPES
/* Define this macro if a float function always returns float
(even in traditional mode). Redefined in m88kluna.h. */
#define TRADITIONAL_RETURN_FLOAT
/* We assume that the store-condition-codes instructions store 0 for false
and some other value for true. This is the value stored for true. */
#define STORE_FLAG_VALUE -1
/* Specify the machine mode that pointers have.
After generation of rtl, the compiler makes no further distinction
between pointers and any other objects of this machine mode. */
#define Pmode SImode
/* A function address in a call instruction
is a word address (for indexing purposes)
so give the MEM rtx word mode. */
#define FUNCTION_MODE SImode
/* Compute the cost of computing a constant rtl expression RTX
whose rtx-code is CODE. The body of this macro is a portion
of a switch statement. If the code is computed here,
return it with a return statement. Otherwise, break from the switch.
We assume that any 16 bit integer can easily be recreated, so we
indicate 0 cost, in an attempt to get GCC not to optimize things
like comparison against a constant.
The cost of CONST_DOUBLE is zero (if it can be placed in an insn, it
is as good as a register; since it can't be placed in any insn, it
won't do anything in cse, but it will cause expand_binop to pass the
constant to the define_expands). */
#define CONST_COSTS(RTX,CODE) \
case CONST_INT: \
if (SMALL_INT (RTX)) \
return 0; \
else if (SMALL_INTVAL (- INTVAL (RTX))) \
return 2; \
else if (classify_integer (SImode, INTVAL (RTX)) != m88k_oru_or) \
return 4; \
return 7; \
case HIGH: \
return 2; \
case CONST: \
case LABEL_REF: \
case SYMBOL_REF: \
if (flag_pic) \
return (flag_pic == 2) ? 11 : 8; \
return 5; \
case CONST_DOUBLE: \
return 0;
/* Provide the costs of an addressing mode that contains ADDR.
If ADDR is not a valid address, it's cost is irrelavent.
REG+REG is made slightly more expensive because it might keep
a register live for longer than we might like. */
#define ADDRESS_COST(ADDR) \
(GET_CODE (ADDR) == REG ? 1 : \
GET_CODE (ADDR) == LO_SUM ? 1 : \
GET_CODE (ADDR) == HIGH ? 2 : \
GET_CODE (ADDR) == MULT ? 1 : \
GET_CODE (ADDR) != PLUS ? 4 : \
(REG_P (XEXP (ADDR, 0)) && REG_P (XEXP (ADDR, 1))) ? 2 : 1)
/* Provide the costs of a rtl expression. This is in the body of a
switch on CODE. */
#define RTX_COSTS(X,CODE) \
case MEM: \
return COSTS_N_INSNS (2); \
case MULT: \
return COSTS_N_INSNS (3); \
case DIV: \
case UDIV: \
case MOD: \
case UMOD: \
return COSTS_N_INSNS (38);
/* A C expressions returning the cost of moving data of MODE from a register
to or from memory. This is more costly than between registers. */
#define MEMORY_MOVE_COST(MODE) 4
/* Provide the cost of a branch. Exact meaning under development. */
#define BRANCH_COST (TARGET_88100 ? 1 : 2)
/* Define this to be nonzero if the character `$' should be allowed
by default in identifier names. */
#define DOLLARS_IN_IDENTIFIERS 1
/* Do not break .stabs pseudos into continuations. */
#define DBX_CONTIN_LENGTH 0
/*** Output of Assembler Code ***/
/* Control the assembler format that we output. */
/* Which assembler syntax. Redefined in m88kdgux.h. */
#define VERSION_0300_SYNTAX TARGET_SVR4
/* Allow pseudo-ops to be overridden. Override these in svr[34].h. */
#undef INT_ASM_OP
#undef ASCII_DATA_ASM_OP
#undef INIT_SECTION_ASM_OP
#undef CONST_SECTION_ASM_OP
#undef CTORS_SECTION_ASM_OP
#undef DTORS_SECTION_ASM_OP
#undef INIT_SECTION_ASM_OP
#undef FINI_SECTION_ASM_OP
#undef TYPE_ASM_OP
#undef SIZE_ASM_OP
/* These are used in varasm.c as well. */
#define TEXT_SECTION_ASM_OP "\ttext"
#define DATA_SECTION_ASM_OP "\tdata"
/* Other sections. */
#define CONST_SECTION_ASM_OP (VERSION_0300_SYNTAX \
? "\tsection\t .rodata,\"a\"\n" \
: "\tsection\t .rodata,\"x\"\n")
#define TDESC_SECTION_ASM_OP (VERSION_0300_SYNTAX \
? "\tsection\t .tdesc,\"a\"" \
: "\tsection\t .tdesc,\"x\"")
/* These must be constant strings for crtstuff.c. */
#define CTORS_SECTION_ASM_OP "\tsection\t .ctors,\"d\"\n"
#define DTORS_SECTION_ASM_OP "\tsection\t .dtors,\"d\"\n"
#define INIT_SECTION_ASM_OP "\tsection\t .init,\"x\""
#define FINI_SECTION_ASM_OP "\tsection\t .fini,\"x\""
/* These are pretty much common to all assemblers. */
#define IDENT_ASM_OP "\tident"
#define FILE_ASM_OP "\tfile"
#define SECTION_ASM_OP "\tsection"
#define DEF_ASM_OP "\tdef"
#define GLOBAL_ASM_OP "\tglobal"
#define ALIGN_ASM_OP "\talign"
#define SKIP_ASM_OP "\tzero"
#define COMMON_ASM_OP "\tcomm"
#define LOCAL_ASM_OP "\tbss"
#define FLOAT_ASM_OP "\tfloat"
#define DOUBLE_ASM_OP "\tdouble"
#define INT_ASM_OP "\tword"
#define ASM_LONG INT_ASM_OP
#define SHORT_ASM_OP "\thalf"
#define CHAR_ASM_OP "\tbyte"
#define ASCII_DATA_ASM_OP "\tstring"
/* These are particular to the global pool optimization. */
#define SBSS_ASM_OP "\tsbss"
#define SCOMM_ASM_OP "\tscomm"
#define SDATA_SECTION_ASM_OP "\tsdata"
/* These are specific to PIC. */
#define TYPE_ASM_OP "\ttype"
#define SIZE_ASM_OP "\tsize"
#define WEAK_ASM_OP "\tweak"
#ifndef AS_BUG_POUND_TYPE
/* Faulty assemblers require @ rather than #. */
#undef TYPE_OPERAND_FMT
#define TYPE_OPERAND_FMT "#%s"
#endif
/* These are specific to version 03.00 assembler syntax. */
#define INTERNAL_ASM_OP "\tlocal"
#define VERSION_ASM_OP "\tversion"
#define ASM_DWARF_POP_SECTION(FILE) fputs ("\tprevious\n", FILE)
#define UNALIGNED_SHORT_ASM_OP "\tuahalf"
#define UNALIGNED_INT_ASM_OP "\tuaword"
/* Output any initial stuff to the assembly file. Always put out
a file directive, even if not debugging.
Immediately after putting out the file, put out a "sem.<value>"
declaration. This should be harmless on other systems, and
is used in DG/UX by the debuggers to suppliment COFF. The
fields in the integer value are as follows:
Bits Value Meaning
---- ----- -------
0-1 0 No information about stack locations
1 Auto/param locations are based on r30
2 Auto/param locations are based on CFA
3-2 0 No information on dimension order
1 Array dims in sym table matches source language
2 Array dims in sym table is in reverse order
5-4 0 No information about the case of global names
1 Global names appear in the symbol table as in the source
2 Global names have been converted to lower case
3 Global names have been converted to upper case. */
#ifdef SDB_DEBUGGING_INFO
#define ASM_COFFSEM(FILE) \
if (write_symbols == SDB_DEBUG) \
{ \
fprintf (FILE, "\nsem.%x:\t\t; %s\n", \
(((TARGET_OCS_FRAME_POSITION) ? 2 : 1) << 0) + (1 << 2) + (1 << 4),\
(TARGET_OCS_FRAME_POSITION) \
? "frame is CFA, normal array dims, case unchanged" \
: "frame is r30, normal array dims, case unchanged"); \
}
#else
#define ASM_COFFSEM(FILE)
#endif
/* Output the first line of the assembly file. Redefined in m88kdgux.h. */
#define ASM_FIRST_LINE(FILE) \
do { \
if (VERSION_0300_SYNTAX) \
fprintf (FILE, "%s\t \"03.00\"\n", VERSION_ASM_OP); \
} while (0)
/* Override svr[34].h. */
#undef ASM_FILE_START
#define ASM_FILE_START(FILE) \
output_file_start (FILE, f_options, sizeof f_options / sizeof f_options[0], \
W_options, sizeof W_options / sizeof W_options[0])
#undef ASM_FILE_END
#define ASM_OUTPUT_SOURCE_FILENAME(FILE, NAME) \
fprintf (FILE, "%s\t \"%s\"\n", FILE_ASM_OP, NAME)
#ifdef SDB_DEBUGGING_INFO
#define ASM_OUTPUT_SOURCE_LINE(FILE, LINE) \
if (m88k_prologue_done) \
fprintf (FILE, "\n\tln\t %d\t\t\t\t; Real source line %d\n",\
LINE - sdb_begin_function_line, LINE)
#endif
/* Code to handle #ident directives. Override svr[34].h definition. */
#undef ASM_OUTPUT_IDENT
#ifdef DBX_DEBUGGING_INFO
#define ASM_OUTPUT_IDENT(FILE, NAME)
#else
#define ASM_OUTPUT_IDENT(FILE, NAME) \
fprintf(FILE, "%s\t \"%s\"\n", IDENT_ASM_OP, NAME)
#endif
/* Output to assembler file text saying following lines
may contain character constants, extra white space, comments, etc. */
#define ASM_APP_ON ""
/* Output to assembler file text saying following lines
no longer contain unusual constructs. */
#define ASM_APP_OFF ""
/* Format the assembly opcode so that the arguments are all aligned.
The maximum instruction size is 8 characters (fxxx.xxx), so a tab and a
space will do to align the output. Abandon the output if a `%' is
encountered. */
#define ASM_OUTPUT_OPCODE(STREAM, PTR) \
{ \
int ch; \
char *orig_ptr; \
\
for (orig_ptr = (PTR); \
(ch = *(PTR)) && ch != ' ' && ch != '\t' && ch != '\n' && ch != '%'; \
(PTR)++) \
putc (ch, STREAM); \
\
if (ch == ' ' && orig_ptr != (PTR) && (PTR) - orig_ptr < 8) \
putc ('\t', STREAM); \
}
/* How to refer to registers in assembler output.
This sequence is indexed by compiler's hard-register-number.
Updated by OVERRIDE_OPTIONS to include the # for version 03.00 syntax. */
#define REGISTER_NAMES \
{"#r0"+1, "#r1"+1, "#r2"+1, "#r3"+1, "#r4"+1, "#r5"+1, "#r6"+1, "#r7"+1, \
"#r8"+1, "#r9"+1, "#r10"+1,"#r11"+1,"#r12"+1,"#r13"+1,"#r14"+1,"#r15"+1,\
"#r16"+1,"#r17"+1,"#r18"+1,"#r19"+1,"#r20"+1,"#r21"+1,"#r22"+1,"#r23"+1,\
"#r24"+1,"#r25"+1,"#r26"+1,"#r27"+1,"#r28"+1,"#r29"+1,"#r30"+1,"#r31"+1}
/* How to renumber registers for dbx and gdb. */
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
/* Tell when to declare ASM names. Override svr4.h to provide this hook. */
#undef DECLARE_ASM_NAME
#define DECLARE_ASM_NAME TARGET_SVR4
/* Write the extra assembler code needed to declare a function properly. */
#undef ASM_DECLARE_FUNCTION_NAME
#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
do { \
if (DECLARE_ASM_NAME) \
{ \
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. */
#undef ASM_DECLARE_OBJECT_NAME
#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
do { \
if (DECLARE_ASM_NAME) \
{ \
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. */
#undef ASM_DECLARE_FUNCTION_SIZE
#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
do { \
if (DECLARE_ASM_NAME) \
{ \
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, ",%s-", &label[1]); \
assemble_name (FILE, (FNAME)); \
putc ('\n', FILE); \
} \
} \
} while (0)
/* This is how to output the definition of a user-level label named NAME,
such as the label on a static function or variable NAME. */
#define ASM_OUTPUT_LABEL(FILE,NAME) \
do { assemble_name (FILE, NAME); fputs (":\n", FILE); } while (0)
/* This is how to output a command to make the user-level label named NAME
defined for reference from other files. */
#define ASM_GLOBALIZE_LABEL(FILE,NAME) \
do { \
fprintf (FILE, "%s\t ", GLOBAL_ASM_OP); \
assemble_name (FILE, NAME); \
putc ('\n', FILE); \
} while (0)
/* This is how to output a reference to a user-level label named NAME.
Override svr[34].h. */
#undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(FILE,NAME) \
{ \
if (! TARGET_NO_UNDERSCORES && ! VERSION_0300_SYNTAX) \
fputc ('_', FILE); \
fputs (NAME, FILE); \
}
/* This is how to output an internal numbered label where
PREFIX is the class of label and NUM is the number within the class.
For V.4, labels use `.' rather than `@'. */
#ifdef AS_BUG_DOT_LABELS
/* The assembler requires a declaration of local. */
#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \
fprintf (FILE, VERSION_0300_SYNTAX ? ".%s%d:\n%s\t .%s%d\n" : "@%s%d:\n", \
PREFIX, NUM, INTERNAL_ASM_OP, PREFIX, NUM)
#else
#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \
fprintf (FILE, VERSION_0300_SYNTAX ? ".%s%d:\n" : "@%s%d:\n", PREFIX, NUM)
#endif
/* AS_BUG_DOT_LABELS */
/* This is how to store into the string LABEL
the symbol_ref name of an internal numbered label where
PREFIX is the class of label and NUM is the number within the class.
This is suitable for output with `assemble_name'. This must agree
with ASM_OUTPUT_INTERNAL_LABEL above, except for being prefixed
with an `*'. */
#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \
sprintf (LABEL, VERSION_0300_SYNTAX ? "*.%s%d" : "*@%s%d", PREFIX, NUM)
/* Internal macro to get a single precision floating point value into
an int, so we can print it's value in hex. */
#define FLOAT_TO_INT_INTERNAL( FVALUE, IVALUE ) \
{ union { \
REAL_VALUE_TYPE d; \
struct { \
unsigned sign : 1; \
unsigned exponent1 : 1; \
unsigned exponent2 : 3; \
unsigned exponent3 : 7; \
unsigned mantissa1 : 20; \
unsigned mantissa2 : 3; \
unsigned mantissa3 : 29; \
} s; \
} _u; \
\
union { \
int i; \
struct { \
unsigned sign : 1; \
unsigned exponent1 : 1; \
unsigned exponent3 : 7; \
unsigned mantissa1 : 20; \
unsigned mantissa2 : 3; \
} s; \
} _u2; \
\
_u.d = REAL_VALUE_TRUNCATE (SFmode, FVALUE); \
_u2.s.sign = _u.s.sign; \
_u2.s.exponent1 = _u.s.exponent1; \
_u2.s.exponent3 = _u.s.exponent3; \
_u2.s.mantissa1 = _u.s.mantissa1; \
_u2.s.mantissa2 = _u.s.mantissa2; \
IVALUE = _u2.i; \
}
/* This is how to output an assembler line defining a `double' constant.
Use "word" pseudos to avoid printing NaNs, infinity, etc. */
#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \
do { \
union { REAL_VALUE_TYPE d; long l[2]; } x; \
x.d = (VALUE); \
fprintf (FILE, "%s\t 0x%.8x, 0x%.8x\n", INT_ASM_OP, \
x.l[0], x.l[1]); \
} while (0)
/* This is how to output an assembler line defining a `float' constant. */
#define ASM_OUTPUT_FLOAT(FILE,VALUE) \
do { \
int i; \
FLOAT_TO_INT_INTERNAL (VALUE, i); \
fprintf (FILE, "%s\t 0x%.8x\n", INT_ASM_OP, i); \
} while (0)
/* Likewise for `int', `short', and `char' constants. */
#define ASM_OUTPUT_INT(FILE,VALUE) \
( fprintf (FILE, "%s\t ", INT_ASM_OP), \
output_addr_const (FILE, (VALUE)), \
fprintf (FILE, "\n"))
#define ASM_OUTPUT_SHORT(FILE,VALUE) \
( fprintf (FILE, "%s\t ", SHORT_ASM_OP), \
output_addr_const (FILE, (VALUE)), \
fprintf (FILE, "\n"))
#define ASM_OUTPUT_CHAR(FILE,VALUE) \
( fprintf (FILE, "%s\t ", CHAR_ASM_OP), \
output_addr_const (FILE, (VALUE)), \
fprintf (FILE, "\n"))
/* This is how to output an assembler line for a numeric constant byte. */
#define ASM_OUTPUT_BYTE(FILE,VALUE) \
fprintf (FILE, "%s\t 0x%x\n", CHAR_ASM_OP, (VALUE))
/* The singl-byte pseudo-op is the default. Override svr[34].h. */
#undef ASM_BYTE_OP
#define ASM_BYTE_OP "\tbyte"
#undef ASM_OUTPUT_ASCII
#define ASM_OUTPUT_ASCII(FILE, P, SIZE) \
output_ascii ((FILE), (P), (SIZE))
/* Epilogue for case labels. This jump instruction is called by casesi
to transfer to the appropriate branch instruction within the table.
The label `@L<n>e' is coined to mark the end of the table. */
#define ASM_OUTPUT_CASE_END(FILE, NUM, TABLE) \
do { \
char label[256]; \
ASM_GENERATE_INTERNAL_LABEL (label, "L", NUM); \
fprintf (FILE, "%se:\n", &label[1]); \
if (! flag_delayed_branch) \
fprintf (FILE, "\tlda\t %s,%s[%s]\n", reg_names[1], reg_names[1], \
reg_names[m88k_case_index]); \
fprintf (FILE, "\tjmp\t %s\n", reg_names[1]); \
} while (0)
/* This is how to output an element of a case-vector that is absolute. */
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
do { \
char buffer[256]; \
ASM_GENERATE_INTERNAL_LABEL (buffer, "L", VALUE); \
fprintf (FILE, "\tbr\t %s\n", &buffer[1]); \
} while (0)
/* This is how to output an element of a case-vector that is relative. */
#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
ASM_OUTPUT_ADDR_VEC_ELT (FILE, VALUE)
/* This is how to output an assembler line
that says to advance the location counter
to a multiple of 2**LOG bytes. */
#define ASM_OUTPUT_ALIGN(FILE,LOG) \
if ((LOG) != 0) \
fprintf (FILE, "%s\t %d\n", ALIGN_ASM_OP, 1<<(LOG))
/* Align the text address to half a cache boundary when it can only be
reached by jumping. */
#define ASM_OUTPUT_ALIGN_CODE(FILE) ASM_OUTPUT_ALIGN (FILE, 3)
/* Override svr[34].h. */
#undef ASM_OUTPUT_SKIP
#define ASM_OUTPUT_SKIP(FILE,SIZE) \
fprintf (FILE, "%s\t %u\n", SKIP_ASM_OP, (SIZE))
/* Override svr4.h. */
#undef ASM_OUTPUT_EXTERNAL_LIBCALL
/* This says how to output an assembler line to define a global common
symbol. Size can be zero for the unusual case of a `struct { int : 0; }'.
Override svr[34].h. */
#undef ASM_OUTPUT_COMMON
#undef ASM_OUTPUT_ALIGNED_COMMON
#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
( fprintf ((FILE), "%s\t ", \
(ROUNDED) <= m88k_gp_threshold ? SCOMM_ASM_OP : COMMON_ASM_OP), \
assemble_name ((FILE), (NAME)), \
fprintf ((FILE), ",%u\n", (SIZE) ? (SIZE) : 1))
/* This says how to output an assember line to define a local common
symbol. Override svr[34].h. */
#undef ASM_OUTPUT_LOCAL
#undef ASM_OUTPUT_ALIGNED_LOCAL
#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
( fprintf ((FILE), "%s\t ", \
(ROUNDED) <= m88k_gp_threshold ? SBSS_ASM_OP : LOCAL_ASM_OP), \
assemble_name ((FILE), (NAME)), \
fprintf ((FILE), ",%u,%d\n", (SIZE) ? (SIZE) : 1, (SIZE) <= 4 ? 4 : 8))
/* Store in OUTPUT a string (made with alloca) containing
an assembler-name for a local static variable named NAME.
LABELNO is an integer which is different for each call. */
#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \
sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO)))
/* This is how to output an insn to push a register on the stack.
It need not be very fast code. */
#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \
fprintf (FILE, "\tsubu\t %s,%s,%d\n\tst\t %s,%s,0\n", \
reg_names[STACK_POINTER_REGNUM], \
reg_names[STACK_POINTER_REGNUM], \
(STACK_BOUNDARY / BITS_PER_UNIT), \
reg_names[REGNO], \
reg_names[STACK_POINTER_REGNUM])
/* This is how to output an insn to pop a register from the stack. */
#define ASM_OUTPUT_REG_POP(FILE,REGNO) \
fprintf (FILE, "\tld\t %s,%s,0\n\taddu\t %s,%s,%d\n", \
reg_names[REGNO], \
reg_names[STACK_POINTER_REGNUM], \
reg_names[STACK_POINTER_REGNUM], \
reg_names[STACK_POINTER_REGNUM], \
(STACK_BOUNDARY / BITS_PER_UNIT))
/* Define the parentheses used to group arithmetic operations
in assembler code. */
#define ASM_OPEN_PAREN "("
#define ASM_CLOSE_PAREN ")"
/* Define results of standard character escape sequences. */
#define TARGET_BELL 007
#define TARGET_BS 010
#define TARGET_TAB 011
#define TARGET_NEWLINE 012
#define TARGET_VT 013
#define TARGET_FF 014
#define TARGET_CR 015
/* Macros to deal with OCS debug information */
#define OCS_START_PREFIX "Ltb"
#define OCS_END_PREFIX "Lte"
#define PUT_OCS_FUNCTION_START(FILE) \
{ ASM_OUTPUT_INTERNAL_LABEL (FILE, OCS_START_PREFIX, m88k_function_number); }
#define PUT_OCS_FUNCTION_END(FILE) \
{ ASM_OUTPUT_INTERNAL_LABEL (FILE, OCS_END_PREFIX, m88k_function_number); }
/* Macros for debug information */
#define DEBUGGER_AUTO_OFFSET(X) \
(m88k_debugger_offset (X, 0) \
+ (TARGET_OCS_FRAME_POSITION ? 0 : m88k_stack_size - m88k_fp_offset))
#define DEBUGGER_ARG_OFFSET(OFFSET, X) \
(m88k_debugger_offset (X, OFFSET) \
+ (TARGET_OCS_FRAME_POSITION ? 0 : m88k_stack_size - m88k_fp_offset))
/* Macros to deal with SDB debug information */
#ifdef SDB_DEBUGGING_INFO
/* Output structure tag names even when it causes a forward reference. */
#define SDB_ALLOW_FORWARD_REFERENCES
/* Print out extra debug information in the assembler file */
#define PUT_SDB_SCL(a) \
do { \
register int s = (a); \
register char *scl; \
switch (s) \
{ \
case C_EFCN: scl = "end of function"; break; \
case C_NULL: scl = "NULL storage class"; break; \
case C_AUTO: scl = "automatic"; break; \
case C_EXT: scl = "external"; break; \
case C_STAT: scl = "static"; break; \
case C_REG: scl = "register"; break; \
case C_EXTDEF: scl = "external definition"; break; \
case C_LABEL: scl = "label"; break; \
case C_ULABEL: scl = "undefined label"; break; \
case C_MOS: scl = "structure member"; break; \
case C_ARG: scl = "argument"; break; \
case C_STRTAG: scl = "structure tag"; break; \
case C_MOU: scl = "union member"; break; \
case C_UNTAG: scl = "union tag"; break; \
case C_TPDEF: scl = "typedef"; break; \
case C_USTATIC: scl = "uninitialized static"; break; \
case C_ENTAG: scl = "enumeration tag"; break; \
case C_MOE: scl = "member of enumeration"; break; \
case C_REGPARM: scl = "register parameter"; break; \
case C_FIELD: scl = "bit field"; break; \
case C_BLOCK: scl = "block start/end"; break; \
case C_FCN: scl = "function start/end"; break; \
case C_EOS: scl = "end of structure"; break; \
case C_FILE: scl = "filename"; break; \
case C_LINE: scl = "line"; break; \
case C_ALIAS: scl = "duplicated tag"; break; \
case C_HIDDEN: scl = "hidden"; break; \
default: scl = "unknown"; break; \
} \
\
fprintf(asm_out_file, "\tscl\t %d\t\t\t\t; %s\n", s, scl); \
} while (0)
#define PUT_SDB_TYPE(a) \
do { \
register int t = (a); \
static char buffer[100]; \
register char *p = buffer, *q; \
register int typ = t; \
register int i,d; \
\
for (i = 0; i <= 5; i++) \
{ \
switch ((typ >> ((i*N_TSHIFT) + N_BTSHFT)) & 03) \
{ \
case DT_PTR: \
strcpy (p, "ptr to "); \
p += sizeof("ptr to"); \
break; \
\
case DT_ARY: \
strcpy (p, "array of "); \
p += sizeof("array of"); \
break; \
\
case DT_FCN: \
strcpy (p, "func ret "); \
p += sizeof("func ret"); \
break; \
} \
} \
\
switch (typ & N_BTMASK) \
{ \
case T_NULL: q = "<no type>"; break; \
case T_CHAR: q = "char"; break; \
case T_SHORT: q = "short"; break; \
case T_INT: q = "int"; break; \
case T_LONG: q = "long"; break; \
case T_FLOAT: q = "float"; break; \
case T_DOUBLE: q = "double"; break; \
case T_STRUCT: q = "struct"; break; \
case T_UNION: q = "union"; break; \
case T_ENUM: q = "enum"; break; \
case T_MOE: q = "enum member"; break; \
case T_UCHAR: q = "unsigned char"; break; \
case T_USHORT: q = "unsigned short"; break; \
case T_UINT: q = "unsigned int"; break; \
case T_ULONG: q = "unsigned long"; break; \
default: q = "void"; break; \
} \
\
strcpy (p, q); \
fprintf(asm_out_file, "\ttype\t %d\t\t\t\t; %s\n", \
t, buffer); \
} while (0)
#define PUT_SDB_INT_VAL(a) \
fprintf (asm_out_file, "\tval\t %d\n", (a))
#define PUT_SDB_VAL(a) \
( fprintf (asm_out_file, "\tval\t "), \
output_addr_const (asm_out_file, (a)), \
fputc ('\n', asm_out_file))
#define PUT_SDB_DEF(a) \
do { fprintf (asm_out_file, "\tsdef\t "); \
ASM_OUTPUT_LABELREF (asm_out_file, a); \
fputc ('\n', asm_out_file); \
} while (0)
#define PUT_SDB_PLAIN_DEF(a) \
fprintf(asm_out_file,"\tsdef\t .%s\n", a)
/* Simply and endef now. */
#define PUT_SDB_ENDEF \
fputs("\tendef\n\n", asm_out_file)
#define PUT_SDB_SIZE(a) \
fprintf (asm_out_file, "\tsize\t %d\n", (a))
/* Max dimensions to store for debug information (limited by COFF). */
#define SDB_MAX_DIM 6
/* New method for dim operations. */
#define PUT_SDB_START_DIM \
fputs("\tdim\t ", asm_out_file)
/* How to end the DIM sequence. */
#define PUT_SDB_LAST_DIM(a) \
fprintf(asm_out_file, "%d\n", a)
#define PUT_SDB_TAG(a) \
do { \
fprintf (asm_out_file, "\ttag\t "); \
ASM_OUTPUT_LABELREF (asm_out_file, a); \
fputc ('\n', asm_out_file); \
} while( 0 )
#define PUT_SDB_BLOCK_OR_FUNCTION(NAME, SCL, LINE) \
do { \
fprintf (asm_out_file, "\n\tsdef\t %s\n\tval\t .\n", \
NAME); \
PUT_SDB_SCL( SCL ); \
fprintf (asm_out_file, "\tline\t %d\n\tendef\n\n", \
(LINE)); \
} while (0)
#define PUT_SDB_BLOCK_START(LINE) \
PUT_SDB_BLOCK_OR_FUNCTION (".bb", C_BLOCK, (LINE))
#define PUT_SDB_BLOCK_END(LINE) \
PUT_SDB_BLOCK_OR_FUNCTION (".eb", C_BLOCK, (LINE))
#define PUT_SDB_FUNCTION_START(LINE) \
do { \
fprintf (asm_out_file, "\tln\t 1\n"); \
PUT_SDB_BLOCK_OR_FUNCTION (".bf", C_FCN, (LINE)); \
} while (0)
#define PUT_SDB_FUNCTION_END(LINE) \
do { \
PUT_SDB_BLOCK_OR_FUNCTION (".ef", C_FCN, (LINE)); \
} while (0)
#define PUT_SDB_EPILOGUE_END(NAME) \
do { \
text_section (); \
fprintf (asm_out_file, "\n\tsdef\t "); \
ASM_OUTPUT_LABELREF(asm_out_file, (NAME)); \
fputc('\n', asm_out_file); \
PUT_SDB_SCL( C_EFCN ); \
fprintf (asm_out_file, "\tendef\n\n"); \
} while (0)
#define SDB_GENERATE_FAKE(BUFFER, NUMBER) \
sprintf ((BUFFER), ".%dfake", (NUMBER));
#endif
/* SDB_DEBUGGING_INFO */
/* Support const and tdesc sections. Generally, a const section will
be distinct from the text section whenever we do V.4-like things
and so follows DECLARE_ASM_NAME. Note that strings go in text
rather than const. Override svr[34].h. */
#undef USE_CONST_SECTION
#undef EXTRA_SECTIONS
#define USE_CONST_SECTION DECLARE_ASM_NAME
#if defined(CTORS_SECTION_FUNCTION)
/* SVR4 */
#define EXTRA_SECTIONS in_const, in_tdesc, in_sdata, in_ctors, in_dtors
#define INIT_SECTION_FUNCTION
#define FINI_SECTION_FUNCTION
#elif defined(FINI_SECTION_FUNCTION)
/* SVR3 */
#define EXTRA_SECTIONS in_const, in_tdesc, in_sdata, in_init, in_fini
#define CTORS_SECTION_FUNCTION
#define DTORS_SECTION_FUNCTION
#else
/* m88kluna or other not based on svr[34].h. */
#define EXTRA_SECTIONS in_const, in_tdesc, in_sdata
#define CONST_SECTION_FUNCTION \
void \
const_section () \
{ \
text_section(); \
}
#define CTORS_SECTION_FUNCTION
#define DTORS_SECTION_FUNCTION
#define INIT_SECTION_FUNCTION
#define FINI_SECTION_FUNCTION
#endif
/* CTORS_SECTION_FUNCTION */
#undef EXTRA_SECTION_FUNCTIONS
#define EXTRA_SECTION_FUNCTIONS \
CONST_SECTION_FUNCTION \
\
void \
tdesc_section () \
{ \
if (in_section != in_tdesc) \
{ \
fprintf (asm_out_file, "%s\n", TDESC_SECTION_ASM_OP); \
in_section = in_tdesc; \
} \
} \
\
void \
sdata_section () \
{ \
if (in_section != in_sdata) \
{ \
fprintf (asm_out_file, "%s\n", SDATA_SECTION_ASM_OP); \
in_section = in_sdata; \
} \
} \
\
CTORS_SECTION_FUNCTION \
DTORS_SECTION_FUNCTION \
INIT_SECTION_FUNCTION \
FINI_SECTION_FUNCTION
#undef READONLY_DATA_SECTION
/* 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.
For strings, the section is selected before the segment info is encoded. */
#undef SELECT_SECTION
#define SELECT_SECTION(DECL,RELOC) \
{ \
if (TREE_CODE (DECL) == STRING_CST) \
{ \
if (! flag_writable_strings) \
const_section (); \
else if (m88k_gp_threshold > 0 \
&& TREE_STRING_LENGTH (DECL) <= m88k_gp_threshold) \
sdata_section (); \
else \
data_section (); \
} \
else if (TREE_CODE (DECL) == VAR_DECL) \
{ \
if (SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0))) \
sdata_section (); \
else if ((flag_pic && RELOC) \
|| !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL)) \
data_section (); \
else \
const_section (); \
} \
else \
const_section (); \
}
/* Define this macro if references to a symbol must be treated differently
depending on something about the variable or function named by the
symbol (such as what section it is in).
The macro definition, if any, is executed immediately after the rtl for
DECL has been created and stored in `DECL_RTL (DECL)'. The value of the
rtl will be a `mem' whose address is a `symbol_ref'.
For the m88k, determine if the item should go in the global pool. */
#define ENCODE_SECTION_INFO(DECL) \
do { \
if (m88k_gp_threshold > 0) \
if (TREE_CODE (DECL) == VAR_DECL) \
{ \
if (!TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL)) \
{ \
int size = int_size_in_bytes (TREE_TYPE (DECL)); \
\
if (size > 0 && size <= m88k_gp_threshold) \
SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1; \
} \
} \
else if (TREE_CODE (DECL) == STRING_CST \
&& flag_writable_strings \
&& TREE_STRING_LENGTH (DECL) <= m88k_gp_threshold) \
SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (DECL), 0)) = 1; \
} while (0)
/* Print operand X (an rtx) in assembler syntax to file FILE.
CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
For `%' followed by punctuation, CODE is the punctuation and X is null. */
#define PRINT_OPERAND_PUNCT_VALID_P(c) \
((c) == '#' || (c) == '.' || (c) == '!' || (c) == '*' || (c) == ';')
#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE)
/* Print a memory address as an operand to reference that memory location. */
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR)
gcc/varasm.c
0 → 100644
View file @
79e68feb
/* Output variables, constants and external declarations, for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992 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. */
/* This file handles generation of all the assembler code
*except* the instructions of a function.
This includes declarations of variables and their initial values.
We also output the assembler code for constants stored in memory
and are responsible for combining constants with the same value. */
#include <stdio.h>
#include <setjmp.h>
/* #include <stab.h> */
#include "config.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "expr.h"
#include "hard-reg-set.h"
#include "regs.h"
#include "obstack.h"
#ifndef ASM_STABS_OP
#define ASM_STABS_OP ".stabs"
#endif
/* File in which assembler code is being written. */
extern
FILE
*
asm_out_file
;
/* The (assembler) name of the first globally-visible object output. */
char
*
first_global_object_name
;
extern
struct
obstack
*
current_obstack
;
extern
struct
obstack
*
saveable_obstack
;
extern
struct
obstack
permanent_obstack
;
#define obstack_chunk_alloc xmalloc
extern
int
xmalloc
();
/* Number for making the label on the next
constant that is stored in memory. */
int
const_labelno
;
/* Number for making the label on the next
static variable internal to a function. */
int
var_labelno
;
/* Nonzero if at least one function definition has been seen. */
static
int
function_defined
;
extern
FILE
*
asm_out_file
;
static
char
*
compare_constant_1
();
static
void
record_constant_1
();
void
output_constant_pool
();
void
assemble_name
();
int
output_addressed_constants
();
void
output_constant
();
void
output_constructor
();
#ifdef EXTRA_SECTIONS
static
enum
in_section
{
no_section
,
in_text
,
in_data
,
EXTRA_SECTIONS
}
in_section
=
no_section
;
#else
static
enum
in_section
{
no_section
,
in_text
,
in_data
}
in_section
=
no_section
;
#endif
/* Define functions like text_section for any extra sections. */
#ifdef EXTRA_SECTION_FUNCTIONS
EXTRA_SECTION_FUNCTIONS
#endif
/* Tell assembler to switch to text section. */
void
text_section
()
{
if
(
in_section
!=
in_text
)
{
fprintf
(
asm_out_file
,
"%s
\n
"
,
TEXT_SECTION_ASM_OP
);
in_section
=
in_text
;
}
}
/* Tell assembler to switch to read-only data section. This is normally
the text section. */
void
readonly_data_section
()
{
#ifdef READONLY_DATA_SECTION
READONLY_DATA_SECTION
();
#else
text_section
();
#endif
}
/* Tell assembler to switch to data section. */
void
data_section
()
{
if
(
in_section
!=
in_data
)
{
if
(
flag_shared_data
)
{
#ifdef SHARED_SECTION_ASM_OP
fprintf
(
asm_out_file
,
"%s
\n
"
,
SHARED_SECTION_ASM_OP
);
#else
fprintf
(
asm_out_file
,
"%s
\n
"
,
DATA_SECTION_ASM_OP
);
#endif
}
else
fprintf
(
asm_out_file
,
"%s
\n
"
,
DATA_SECTION_ASM_OP
);
in_section
=
in_data
;
}
}
/* Determine if we're in the text section. */
int
in_text_section
()
{
return
in_section
==
in_text
;
}
/* Create the rtl to represent a function, for a function definition.
DECL is a FUNCTION_DECL node which describes which function.
The rtl is stored into DECL. */
void
make_function_rtl
(
decl
)
tree
decl
;
{
char
*
name
=
IDENTIFIER_POINTER
(
DECL_ASSEMBLER_NAME
(
decl
));
/* Rename a nested function to avoid conflicts. */
if
(
decl_function_context
(
decl
)
!=
0
&&
DECL_INITIAL
(
decl
)
!=
0
&&
DECL_RTL
(
decl
)
==
0
)
{
char
*
label
;
name
=
IDENTIFIER_POINTER
(
DECL_NAME
(
decl
));
ASM_FORMAT_PRIVATE_NAME
(
label
,
name
,
var_labelno
);
name
=
obstack_copy0
(
saveable_obstack
,
label
,
strlen
(
label
));
var_labelno
++
;
}
if
(
DECL_RTL
(
decl
)
==
0
)
{
DECL_RTL
(
decl
)
=
gen_rtx
(
MEM
,
DECL_MODE
(
decl
),
gen_rtx
(
SYMBOL_REF
,
Pmode
,
name
));
/* Optionally set flags or add text to the name to record information
such as that it is a function name. If the name is changed, the macro
ASM_OUTPUT_LABELREF will have to know how to strip this information.
And if it finds a * at the beginning after doing so, it must handle
that too. */
#ifdef ENCODE_SECTION_INFO
ENCODE_SECTION_INFO
(
decl
);
#endif
}
/* Record at least one function has been defined. */
function_defined
=
1
;
}
/* Decode an `asm' spec for a declaration as a register name.
Return the register number, or -1 if nothing specified,
or -2 if the name is not a register. */
int
decode_reg_name
(
asmspec
)
char
*
asmspec
;
{
if
(
asmspec
!=
0
)
{
int
i
;
for
(
i
=
0
;
i
<
FIRST_PSEUDO_REGISTER
;
i
++
)
if
(
reg_names
[
i
][
0
]
&&
!
strcmp
(
asmspec
,
reg_names
[
i
]))
return
i
;
if
(
asmspec
[
0
]
==
'%'
)
for
(
i
=
0
;
i
<
FIRST_PSEUDO_REGISTER
;
i
++
)
if
(
reg_names
[
i
][
0
]
&&
!
strcmp
(
asmspec
+
1
,
reg_names
[
i
]))
return
i
;
#ifdef ADDITIONAL_REGISTER_NAMES
{
static
struct
{
char
*
name
;
int
number
;
}
table
[]
=
ADDITIONAL_REGISTER_NAMES
;
for
(
i
=
0
;
i
<
sizeof
(
table
)
/
sizeof
(
table
[
0
]);
i
++
)
if
(
!
strcmp
(
asmspec
,
table
[
i
].
name
))
return
table
[
i
].
number
;
if
(
asmspec
[
0
]
==
'%'
)
for
(
i
=
0
;
i
<
sizeof
(
table
)
/
sizeof
(
table
[
0
]);
i
++
)
if
(
!
strcmp
(
asmspec
+
1
,
table
[
i
].
name
))
return
table
[
i
].
number
;
}
#endif
/* ADDITIONAL_REGISTER_NAMES */
return
-
2
;
}
return
-
1
;
}
/* Create the DECL_RTL for a declaration for a static or external variable
or static or external function.
ASMSPEC, if not 0, is the string which the user specified
as the assembler symbol name.
TOP_LEVEL is nonzero if this is a file-scope variable.
This is never called for PARM_DECL nodes. */
void
make_decl_rtl
(
decl
,
asmspec
,
top_level
)
tree
decl
;
char
*
asmspec
;
int
top_level
;
{
register
char
*
name
;
int
reg_number
=
decode_reg_name
(
asmspec
);
if
(
DECL_ASSEMBLER_NAME
(
decl
)
!=
NULL_TREE
)
name
=
IDENTIFIER_POINTER
(
DECL_ASSEMBLER_NAME
(
decl
));
if
(
reg_number
==
-
2
)
{
/* ASMSPEC is given, and not the name of a register. */
name
=
(
char
*
)
obstack_alloc
(
saveable_obstack
,
strlen
(
asmspec
)
+
2
);
name
[
0
]
=
'*'
;
strcpy
(
&
name
[
1
],
asmspec
);
}
/* For a duplicate declaration, we can be called twice on the
same DECL node. Don't alter the RTL already made
unless the old mode is wrong (which can happen when
the previous rtl was made when the type was incomplete). */
if
(
DECL_RTL
(
decl
)
==
0
||
GET_MODE
(
DECL_RTL
(
decl
))
!=
DECL_MODE
(
decl
))
{
DECL_RTL
(
decl
)
=
0
;
/* First detect errors in declaring global registers. */
if
(
TREE_REGDECL
(
decl
)
&&
reg_number
==
-
1
)
error_with_decl
(
decl
,
"register name not specified for `%s'"
);
else
if
(
TREE_REGDECL
(
decl
)
&&
reg_number
==
-
2
)
error_with_decl
(
decl
,
"invalid register name for `%s'"
);
else
if
(
reg_number
>=
0
&&
!
TREE_REGDECL
(
decl
))
error_with_decl
(
decl
,
"register name given for non-register variable `%s'"
);
else
if
(
TREE_REGDECL
(
decl
)
&&
TREE_CODE
(
decl
)
==
FUNCTION_DECL
)
error
(
"function declared `register'"
);
else
if
(
TREE_REGDECL
(
decl
)
&&
TYPE_MODE
(
TREE_TYPE
(
decl
))
==
BLKmode
)
error_with_decl
(
decl
,
"data type of `%s' isn't suitable for a register"
);
/* Now handle properly declared static register variables. */
else
if
(
TREE_REGDECL
(
decl
))
{
int
nregs
;
#if 0 /* yylex should print the warning for this */
if (pedantic)
pedwarn ("ANSI C forbids global register variables");
#endif
if
(
DECL_INITIAL
(
decl
)
!=
0
&&
top_level
)
{
DECL_INITIAL
(
decl
)
=
0
;
error
(
"global register variable has initial value"
);
}
if
(
fixed_regs
[
reg_number
]
==
0
&&
function_defined
&&
top_level
)
error
(
"global register variable follows a function definition"
);
if
(
TREE_THIS_VOLATILE
(
decl
))
warning
(
"volatile register variables don't work as you might wish"
);
DECL_RTL
(
decl
)
=
gen_rtx
(
REG
,
DECL_MODE
(
decl
),
reg_number
);
REG_USERVAR_P
(
DECL_RTL
(
decl
))
=
1
;
if
(
top_level
)
{
/* Make this register fixed, so not usable for anything else. */
nregs
=
HARD_REGNO_NREGS
(
reg_number
,
DECL_MODE
(
decl
));
while
(
nregs
>
0
)
global_regs
[
reg_number
+
--
nregs
]
=
1
;
init_reg_sets_1
();
}
}
/* Now handle ordinary static variables and functions (in memory).
Also handle vars declared register invalidly. */
if
(
DECL_RTL
(
decl
)
==
0
)
{
/* Can't use just the variable's own name for a variable
whose scope is less than the whole file.
Concatenate a distinguishing number. */
if
(
!
top_level
&&
!
TREE_EXTERNAL
(
decl
)
&&
asmspec
==
0
)
{
char
*
label
;
ASM_FORMAT_PRIVATE_NAME
(
label
,
name
,
var_labelno
);
name
=
obstack_copy0
(
saveable_obstack
,
label
,
strlen
(
label
));
var_labelno
++
;
}
DECL_RTL
(
decl
)
=
gen_rtx
(
MEM
,
DECL_MODE
(
decl
),
gen_rtx
(
SYMBOL_REF
,
Pmode
,
name
));
if
(
TREE_THIS_VOLATILE
(
decl
))
MEM_VOLATILE_P
(
DECL_RTL
(
decl
))
=
1
;
if
(
TREE_READONLY
(
decl
))
RTX_UNCHANGING_P
(
DECL_RTL
(
decl
))
=
1
;
MEM_IN_STRUCT_P
(
DECL_RTL
(
decl
))
=
(
TREE_CODE
(
TREE_TYPE
(
decl
))
==
ARRAY_TYPE
||
TREE_CODE
(
TREE_TYPE
(
decl
))
==
RECORD_TYPE
||
TREE_CODE
(
TREE_TYPE
(
decl
))
==
UNION_TYPE
);
/* Optionally set flags or add text to the name to record information
such as that it is a function name.
If the name is changed, the macro ASM_OUTPUT_LABELREF
will have to know how to strip this information.
And if it finds a * at the beginning after doing so,
it must handle that too. */
#ifdef ENCODE_SECTION_INFO
ENCODE_SECTION_INFO
(
decl
);
#endif
}
}
}
/* Output a string of literal assembler code
for an `asm' keyword used between functions. */
void
assemble_asm
(
string
)
tree
string
;
{
app_enable
();
if
(
TREE_CODE
(
string
)
==
ADDR_EXPR
)
string
=
TREE_OPERAND
(
string
,
0
);
fprintf
(
asm_out_file
,
"
\t
%s
\n
"
,
TREE_STRING_POINTER
(
string
));
}
/* Tiemann: please get rid of this conditional and put appropriate
definitions in each of the files that should have them.
The type of debugging format is not the right parameter to
control how some other aspect of assembler output is done. */
#if !(defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER))
#ifndef ASM_OUTPUT_CONSTRUCTOR
#define ASM_OUTPUT_CONSTRUCTOR(file, name)
#endif
#ifndef ASM_OUTPUT_DESTRUCTOR
#define ASM_OUTPUT_DESTRUCTOR(file, name)
#endif
#endif
/* Record an element in the table of global destructors.
How this is done depends on what sort of assembler and linker
are in use.
NAME should be the name of a global function to be called
at exit time. This name is output using assemble_name. */
void
assemble_destructor
(
name
)
char
*
name
;
{
#ifdef ASM_OUTPUT_DESTRUCTOR
ASM_OUTPUT_DESTRUCTOR
(
asm_out_file
,
name
);
#else
if
(
flag_gnu_linker
)
{
/* Now tell GNU LD that this is part of the static destructor set. */
/* This code works for any machine provided you use GNU as/ld. */
fprintf
(
asm_out_file
,
"%s
\"
___DTOR_LIST__
\"
,22,0,0,"
,
ASM_STABS_OP
);
assemble_name
(
asm_out_file
,
name
);
fputc
(
'\n'
,
asm_out_file
);
}
#endif
}
/* Likewise for global constructors. */
void
assemble_constructor
(
name
)
char
*
name
;
{
#ifdef ASM_OUTPUT_CONSTRUCTOR
ASM_OUTPUT_CONSTRUCTOR
(
asm_out_file
,
name
);
#else
if
(
flag_gnu_linker
)
{
/* Now tell GNU LD that this is part of the static constructor set. */
/* This code works for any machine provided you use GNU as/ld. */
fprintf
(
asm_out_file
,
"%s
\"
___CTOR_LIST__
\"
,22,0,0,"
,
ASM_STABS_OP
);
assemble_name
(
asm_out_file
,
name
);
fputc
(
'\n'
,
asm_out_file
);
}
#endif
}
/* Likewise for entries we want to record for garbage collection.
Garbage collection is still under development. */
void
assemble_gc_entry
(
name
)
char
*
name
;
{
#ifdef ASM_OUTPUT_GC_ENTRY
ASM_OUTPUT_GC_ENTRY
(
asm_out_file
,
name
);
#else
if
(
flag_gnu_linker
)
{
/* Now tell GNU LD that this is part of the static constructor set. */
fprintf
(
asm_out_file
,
"%s
\"
___PTR_LIST__
\"
,22,0,0,"
,
ASM_STABS_OP
);
assemble_name
(
asm_out_file
,
name
);
fputc
(
'\n'
,
asm_out_file
);
}
#endif
}
/* Output assembler code for the constant pool of a function and associated
with defining the name of the function. DECL describes the function.
NAME is the function's name. For the constant pool, we use the current
constant pool data. */
void
assemble_start_function
(
decl
,
fnname
)
tree
decl
;
char
*
fnname
;
{
int
align
;
/* The following code does not need preprocessing in the assembler. */
app_disable
();
output_constant_pool
(
fnname
,
decl
);
text_section
();
/* Tell assembler to move to target machine's alignment for functions. */
align
=
floor_log2
(
FUNCTION_BOUNDARY
/
BITS_PER_UNIT
);
if
(
align
>
0
)
ASM_OUTPUT_ALIGN
(
asm_out_file
,
align
);
#ifdef ASM_OUTPUT_FUNCTION_PREFIX
ASM_OUTPUT_FUNCTION_PREFIX
(
asm_out_file
,
fnname
);
#endif
#ifdef SDB_DEBUGGING_INFO
/* Output SDB definition of the function. */
if
(
write_symbols
==
SDB_DEBUG
)
sdbout_mark_begin_function
();
#endif
#ifdef DBX_DEBUGGING_INFO
/* Output SDB definition of the function. */
if
(
write_symbols
==
DBX_DEBUG
)
dbxout_begin_function
();
#endif
/* Make function name accessible from other files, if appropriate. */
if
(
TREE_PUBLIC
(
decl
))
{
if
(
!
first_global_object_name
)
first_global_object_name
=
fnname
+
(
fnname
[
0
]
==
'*'
);
ASM_GLOBALIZE_LABEL
(
asm_out_file
,
fnname
);
}
/* Do any machine/system dependent processing of the function name */
#ifdef ASM_DECLARE_FUNCTION_NAME
ASM_DECLARE_FUNCTION_NAME
(
asm_out_file
,
fnname
,
current_function_decl
);
#else
/* Standard thing is just output label for the function. */
ASM_OUTPUT_LABEL
(
asm_out_file
,
fnname
);
#endif
/* ASM_DECLARE_FUNCTION_NAME */
}
/* Output assembler code associated with defining the size of the
function. DECL describes the function. NAME is the function's name. */
void
assemble_end_function
(
decl
,
fnname
)
tree
decl
;
char
*
fnname
;
{
#ifdef ASM_DECLARE_FUNCTION_SIZE
ASM_DECLARE_FUNCTION_SIZE
(
asm_out_file
,
fnname
,
decl
);
#endif
}
/* Assemble code to leave SIZE bytes of zeros. */
void
assemble_zeros
(
size
)
int
size
;
{
#ifdef ASM_NO_SKIP_IN_TEXT
/* The `space' pseudo in the text section outputs nop insns rather than 0s,
so we must output 0s explicitly in the text section. */
if
(
ASM_NO_SKIP_IN_TEXT
&&
in_text_section
())
{
int
i
;
for
(
i
=
0
;
i
<
size
-
20
;
i
+=
20
)
{
#ifdef ASM_BYTE_OP
fprintf
(
asm_out_file
,
"%s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
\n
"
,
ASM_BYTE_OP
);
#else
fprintf
(
asm_out_file
,
"
\t
byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
\n
"
);
#endif
}
if
(
i
<
size
)
{
#ifdef ASM_BYTE_OP
fprintf
(
asm_out_file
,
"%s 0"
,
ASM_BYTE_OP
);
#else
fprintf
(
asm_out_file
,
"
\t
byte 0"
);
#endif
i
++
;
for
(;
i
<
size
;
i
++
)
fprintf
(
asm_out_file
,
",0"
);
fprintf
(
asm_out_file
,
"
\n
"
);
}
}
else
#endif
ASM_OUTPUT_SKIP
(
asm_out_file
,
size
);
}
/* Assemble a string constant with the specified C string as contents. */
void
assemble_string
(
p
,
size
)
unsigned
char
*
p
;
int
size
;
{
register
int
i
;
int
pos
=
0
;
int
maximum
=
2000
;
/* If the string is very long, split it up. */
while
(
pos
<
size
)
{
int
thissize
=
size
-
pos
;
if
(
thissize
>
maximum
)
thissize
=
maximum
;
#ifdef ASM_OUTPUT_ASCII
ASM_OUTPUT_ASCII
(
asm_out_file
,
p
,
thissize
);
#else
fprintf
(
asm_out_file
,
"
\t
.ascii
\"
"
);
for
(
i
=
0
;
i
<
thissize
;
i
++
)
{
register
int
c
=
p
[
i
];
if
(
c
==
'\"'
||
c
==
'\\'
)
putc
(
'\\'
,
asm_out_file
);
if
(
c
>=
' '
&&
c
<
0177
)
putc
(
c
,
asm_out_file
);
else
{
fprintf
(
asm_out_file
,
"
\\
%o"
,
c
);
/* After an octal-escape, if a digit follows,
terminate one string constant and start another.
The Vax assembler fails to stop reading the escape
after three digits, so this is the only way we
can get it to parse the data properly. */
if
(
i
<
thissize
-
1
&&
p
[
i
+
1
]
>=
'0'
&&
p
[
i
+
1
]
<=
'9'
)
fprintf
(
asm_out_file
,
"
\"\n\t
.ascii
\"
"
);
}
}
fprintf
(
asm_out_file
,
"
\"\n
"
);
#endif
/* no ASM_OUTPUT_ASCII */
pos
+=
thissize
;
p
+=
thissize
;
}
}
/* Assemble everything that is needed for a variable or function declaration.
Not used for automatic variables, and not used for function definitions.
Should not be called for variables of incomplete structure type.
TOP_LEVEL is nonzero if this variable has file scope.
AT_END is nonzero if this is the special handling, at end of compilation,
to define things that have had only tentative definitions. */
void
assemble_variable
(
decl
,
top_level
,
at_end
)
tree
decl
;
int
top_level
;
int
at_end
;
{
register
char
*
name
;
int
align
;
tree
size_tree
;
int
reloc
=
0
;
if
(
GET_CODE
(
DECL_RTL
(
decl
))
==
REG
)
{
/* Do output symbol info for global register variables, but do nothing
else for them. */
if
(
TREE_ASM_WRITTEN
(
decl
))
return
;
TREE_ASM_WRITTEN
(
decl
)
=
1
;
#ifdef DBX_DEBUGGING_INFO
/* File-scope global variables are output here. */
if
(
write_symbols
==
DBX_DEBUG
&&
top_level
)
dbxout_symbol
(
decl
,
0
);
#endif
#ifdef SDB_DEBUGGING_INFO
if
(
write_symbols
==
SDB_DEBUG
&&
top_level
/* Leave initialized global vars for end of compilation;
see comment in compile_file. */
&&
(
TREE_PUBLIC
(
decl
)
==
0
||
DECL_INITIAL
(
decl
)
==
0
))
sdbout_symbol
(
decl
,
0
);
#endif
/* Don't output any DWARF debugging information for variables here.
In the case of local variables, the information for them is output
when we do our recursive traversal of the tree representation for
the entire containing function. In the case of file-scope variables,
we output information for all of them at the very end of compilation
while we are doing our final traversal of the chain of file-scope
declarations. */
return
;
}
/* Normally no need to say anything for external references,
since assembler considers all undefined symbols external. */
if
(
TREE_EXTERNAL
(
decl
))
return
;
/* Output no assembler code for a function declaration.
Only definitions of functions output anything. */
if
(
TREE_CODE
(
decl
)
==
FUNCTION_DECL
)
return
;
/* If type was incomplete when the variable was declared,
see if it is complete now. */
if
(
DECL_SIZE
(
decl
)
==
0
)
layout_decl
(
decl
,
0
);
/* Still incomplete => don't allocate it; treat the tentative defn
(which is what it must have been) as an `extern' reference. */
if
(
DECL_SIZE
(
decl
)
==
0
)
{
error_with_file_and_line
(
DECL_SOURCE_FILE
(
decl
),
DECL_SOURCE_LINE
(
decl
),
"storage size of static var `%s' isn't known"
,
IDENTIFIER_POINTER
(
DECL_NAME
(
decl
)));
return
;
}
/* The first declaration of a variable that comes through this function
decides whether it is global (in C, has external linkage)
or local (in C, has internal linkage). So do nothing more
if this function has already run. */
if
(
TREE_ASM_WRITTEN
(
decl
))
return
;
TREE_ASM_WRITTEN
(
decl
)
=
1
;
#ifdef DBX_DEBUGGING_INFO
/* File-scope global variables are output here. */
if
(
write_symbols
==
DBX_DEBUG
&&
top_level
)
dbxout_symbol
(
decl
,
0
);
#endif
#ifdef SDB_DEBUGGING_INFO
if
(
write_symbols
==
SDB_DEBUG
&&
top_level
/* Leave initialized global vars for end of compilation;
see comment in compile_file. */
&&
(
TREE_PUBLIC
(
decl
)
==
0
||
DECL_INITIAL
(
decl
)
==
0
))
sdbout_symbol
(
decl
,
0
);
#endif
/* Don't output any DWARF debugging information for variables here.
In the case of local variables, the information for them is output
when we do our recursive traversal of the tree representation for
the entire containing function. In the case of file-scope variables,
we output information for all of them at the very end of compilation
while we are doing our final traversal of the chain of file-scope
declarations. */
/* If storage size is erroneously variable, just continue.
Error message was already made. */
if
(
TREE_CODE
(
DECL_SIZE
(
decl
))
!=
INTEGER_CST
)
return
;
app_disable
();
/* This is better than explicit arithmetic, since it avoids overflow. */
size_tree
=
size_binop
(
CEIL_DIV_EXPR
,
DECL_SIZE
(
decl
),
size_int
(
BITS_PER_UNIT
));
if
(
TREE_INT_CST_HIGH
(
size_tree
)
!=
0
)
{
error_with_decl
(
decl
,
"size of variable `%s' is too large"
);
return
;
}
name
=
XSTR
(
XEXP
(
DECL_RTL
(
decl
),
0
),
0
);
/* Handle uninitialized definitions. */
/* ANSI specifies that a tentative definition which is not merged with
a non-tentative definition behaves exactly like a definition with an
initializer equal to zero. (Section 3.7.2)
-fno-common gives strict ANSI behavior. Usually you don't want it. */
if
(
!
flag_no_common
&&
(
DECL_INITIAL
(
decl
)
==
0
||
DECL_INITIAL
(
decl
)
==
error_mark_node
))
{
int
size
=
TREE_INT_CST_LOW
(
size_tree
);
int
rounded
=
size
;
if
(
TREE_INT_CST_HIGH
(
size_tree
)
!=
0
)
error_with_decl
(
decl
,
"size of variable `%s' is too large"
);
/* Don't allocate zero bytes of common,
since that means "undefined external" in the linker. */
if
(
size
==
0
)
rounded
=
1
;
/* Round size up to multiple of BIGGEST_ALIGNMENT bits
so that each uninitialized object starts on such a boundary. */
rounded
+=
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
)
-
1
;
rounded
=
(
rounded
/
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
)
*
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
));
#if 0
if (flag_shared_data)
data_section ();
#endif
if
(
TREE_PUBLIC
(
decl
))
{
#ifdef ASM_OUTPUT_SHARED_COMMON
if
(
flag_shared_data
)
ASM_OUTPUT_SHARED_COMMON
(
asm_out_file
,
name
,
size
,
rounded
);
else
#endif
#ifdef ASM_OUTPUT_ALIGNED_COMMON
ASM_OUTPUT_ALIGNED_COMMON
(
asm_out_file
,
name
,
size
,
DECL_ALIGN
(
decl
));
#else
ASM_OUTPUT_COMMON
(
asm_out_file
,
name
,
size
,
rounded
);
#endif
}
else
{
#ifdef ASM_OUTPUT_SHARED_LOCAL
if
(
flag_shared_data
)
ASM_OUTPUT_SHARED_LOCAL
(
asm_out_file
,
name
,
size
,
rounded
);
else
#endif
#ifdef ASM_OUTPUT_ALIGNED_LOCAL
ASM_OUTPUT_ALIGNED_LOCAL
(
asm_out_file
,
name
,
size
,
DECL_ALIGN
(
decl
));
#else
ASM_OUTPUT_LOCAL
(
asm_out_file
,
name
,
size
,
rounded
);
#endif
}
return
;
}
/* Handle initialized definitions. */
/* First make the assembler name(s) global if appropriate. */
if
(
TREE_PUBLIC
(
decl
)
&&
DECL_NAME
(
decl
))
{
if
(
!
first_global_object_name
)
first_global_object_name
=
name
+
(
name
[
0
]
==
'*'
);
ASM_GLOBALIZE_LABEL
(
asm_out_file
,
name
);
}
#if 0
for (d = equivalents; d; d = TREE_CHAIN (d))
{
tree e = TREE_VALUE (d);
if (TREE_PUBLIC (e) && DECL_NAME (e))
ASM_GLOBALIZE_LABEL (asm_out_file,
XSTR (XEXP (DECL_RTL (e), 0), 0));
}
#endif
/* Output any data that we will need to use the address of. */
if
(
DECL_INITIAL
(
decl
))
reloc
=
output_addressed_constants
(
DECL_INITIAL
(
decl
));
/* Switch to the proper section for this data. */
#ifdef SELECT_SECTION
SELECT_SECTION
(
decl
,
reloc
);
#else
if
(
TREE_READONLY
(
decl
)
&&
!
TREE_THIS_VOLATILE
(
decl
)
&&
!
(
flag_pic
&&
reloc
))
readonly_data_section
();
else
data_section
();
#endif
/* Compute and output the alignment of this data. */
align
=
DECL_ALIGN
(
decl
);
/* Some object file formats have a maximum alignment which they support.
In particular, a.out format supports a maximum alignment of 4. */
#ifndef MAX_OFILE_ALIGNMENT
#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT
#endif
if
(
align
>
MAX_OFILE_ALIGNMENT
)
{
warning_with_decl
(
decl
,
"alignment of `%s' is greater than maximum object file alignment"
);
align
=
MAX_OFILE_ALIGNMENT
;
}
#ifdef DATA_ALIGNMENT
/* On some machines, it is good to increase alignment sometimes. */
align
=
DATA_ALIGNMENT
(
TREE_TYPE
(
decl
),
align
);
#endif
#ifdef CONSTANT_ALIGNMENT
if
(
DECL_INITIAL
(
decl
))
align
=
CONSTANT_ALIGNMENT
(
DECL_INITIAL
(
decl
),
align
);
#endif
/* Reset the alignment in case we have made it tighter, so we can benefit
from it in get_pointer_alignment. */
DECL_ALIGN
(
decl
)
=
align
;
if
(
align
>
BITS_PER_UNIT
)
ASM_OUTPUT_ALIGN
(
asm_out_file
,
floor_log2
(
align
/
BITS_PER_UNIT
));
/* Do any machine/system dependent processing of the object. */
#ifdef ASM_DECLARE_OBJECT_NAME
ASM_DECLARE_OBJECT_NAME
(
asm_out_file
,
name
,
decl
);
#else
/* Standard thing is just output label for the object. */
ASM_OUTPUT_LABEL
(
asm_out_file
,
name
);
#endif
/* ASM_DECLARE_OBJECT_NAME */
#if 0
for (d = equivalents; d; d = TREE_CHAIN (d))
{
tree e = TREE_VALUE (d);
ASM_OUTPUT_LABEL (asm_out_file, XSTR (XEXP (DECL_RTL (e), 0), 0));
}
#endif
if
(
DECL_INITIAL
(
decl
))
/* Output the actual data. */
output_constant
(
DECL_INITIAL
(
decl
),
int_size_in_bytes
(
TREE_TYPE
(
decl
)));
else
/* Leave space for it. */
assemble_zeros
(
int_size_in_bytes
(
TREE_TYPE
(
decl
)));
}
/* Output something to declare an external symbol to the assembler.
(Most assemblers don't need this, so we normally output nothing.) */
void
assemble_external
(
decl
)
tree
decl
;
{
rtx
rtl
=
DECL_RTL
(
decl
);
#ifdef ASM_OUTPUT_EXTERNAL
if
(
TREE_PUBLIC
(
decl
)
&&
GET_CODE
(
rtl
)
==
MEM
&&
GET_CODE
(
XEXP
(
rtl
,
0
))
==
SYMBOL_REF
&&
!
SYMBOL_REF_USED
(
XEXP
(
rtl
,
0
)))
{
/* Some systems do require some output. */
SYMBOL_REF_USED
(
XEXP
(
rtl
,
0
))
=
1
;
ASM_OUTPUT_EXTERNAL
(
asm_out_file
,
decl
,
XSTR
(
XEXP
(
rtl
,
0
),
0
));
}
#endif
}
/* Similar, for calling a library function FUN. */
void
assemble_external_libcall
(
fun
)
rtx
fun
;
{
#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL
/* Declare library function name external when first used, if nec. */
if
(
!
SYMBOL_REF_USED
(
fun
))
{
SYMBOL_REF_USED
(
fun
)
=
1
;
ASM_OUTPUT_EXTERNAL_LIBCALL
(
asm_out_file
,
fun
);
}
#endif
}
/* Declare the label NAME global. */
void
assemble_global
(
name
)
char
*
name
;
{
ASM_GLOBALIZE_LABEL
(
asm_out_file
,
name
);
}
/* Assemble a label named NAME. */
void
assemble_label
(
name
)
char
*
name
;
{
ASM_OUTPUT_LABEL
(
asm_out_file
,
name
);
}
/* Output to FILE a reference to the assembler name of a C-level name NAME.
If NAME starts with a *, the rest of NAME is output verbatim.
Otherwise NAME is transformed in an implementation-defined way
(usually by the addition of an underscore).
Many macros in the tm file are defined to call this function. */
void
assemble_name
(
file
,
name
)
FILE
*
file
;
char
*
name
;
{
if
(
name
[
0
]
==
'*'
)
fputs
(
&
name
[
1
],
file
);
else
ASM_OUTPUT_LABELREF
(
file
,
name
);
}
/* Allocate SIZE bytes writable static space with a gensym name
and return an RTX to refer to its address. */
rtx
assemble_static_space
(
size
)
int
size
;
{
char
name
[
12
];
char
*
namestring
;
rtx
x
;
/* Round size up to multiple of BIGGEST_ALIGNMENT bits
so that each uninitialized object starts on such a boundary. */
int
rounded
=
((
size
+
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
)
-
1
)
/
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
)
*
(
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
));
#if 0
if (flag_shared_data)
data_section ();
#endif
ASM_GENERATE_INTERNAL_LABEL
(
name
,
"LF"
,
const_labelno
);
++
const_labelno
;
namestring
=
(
char
*
)
obstack_alloc
(
saveable_obstack
,
strlen
(
name
)
+
2
);
strcpy
(
namestring
,
name
);
x
=
gen_rtx
(
SYMBOL_REF
,
Pmode
,
namestring
);
#ifdef ASM_OUTPUT_ALIGNED_LOCAL
ASM_OUTPUT_ALIGNED_LOCAL
(
asm_out_file
,
name
,
size
,
BIGGEST_ALIGNMENT
);
#else
ASM_OUTPUT_LOCAL
(
asm_out_file
,
name
,
size
,
rounded
);
#endif
return
x
;
}
/* Assemble the static constant template for function entry trampolines.
This is done at most once per compilation.
Returns an RTX for the address of the template. */
rtx
assemble_trampoline_template
()
{
char
label
[
256
];
char
*
name
;
int
align
;
/* Write the assembler code to define one. */
align
=
floor_log2
(
FUNCTION_BOUNDARY
/
BITS_PER_UNIT
);
if
(
align
>
0
)
ASM_OUTPUT_ALIGN
(
asm_out_file
,
align
);
ASM_OUTPUT_INTERNAL_LABEL
(
asm_out_file
,
"LTRAMP"
,
0
);
TRAMPOLINE_TEMPLATE
(
asm_out_file
);
/* Record the rtl to refer to it. */
ASM_GENERATE_INTERNAL_LABEL
(
label
,
"LTRAMP"
,
0
);
name
=
(
char
*
)
obstack_copy0
(
&
permanent_obstack
,
label
,
strlen
(
label
));
return
gen_rtx
(
SYMBOL_REF
,
Pmode
,
name
);
}
/* Assemble the integer constant X into an object of SIZE bytes.
X must be either a CONST_INT or CONST_DOUBLE.
Return 1 if we were able to output the constant, otherwise 0. If FORCE is
non-zero, abort if we can't output the constant. */
int
assemble_integer
(
x
,
size
,
force
)
rtx
x
;
int
size
;
int
force
;
{
/* First try to use the standard 1, 2, 4, 8, and 16 byte
ASM_OUTPUT... macros. */
switch
(
size
)
{
#ifdef ASM_OUTPUT_CHAR
case
1
:
ASM_OUTPUT_CHAR
(
asm_out_file
,
x
);
return
1
;
#endif
#ifdef ASM_OUTPUT_SHORT
case
2
:
ASM_OUTPUT_SHORT
(
asm_out_file
,
x
);
return
1
;
#endif
#ifdef ASM_OUTPUT_INT
case
4
:
ASM_OUTPUT_INT
(
asm_out_file
,
x
);
return
1
;
#endif
#ifdef ASM_OUTPUT_DOUBLE_INT
case
8
:
ASM_OUTPUT_DOUBLE_INT
(
asm_out_file
,
x
);
return
1
;
#endif
#ifdef ASM_OUTPUT_QUADRUPLE_INT
case
16
:
ASM_OUTPUT_QUADRUPLE_INT
(
asm_out_file
,
x
);
return
1
;
#endif
}
/* If we couldn't do it that way, there are two other possibilities: First,
if the machine can output an explicit byte and this is a 1 byte constant,
we can use ASM_OUTPUT_BYTE. */
#ifdef ASM_OUTPUT_BYTE
if
(
size
==
1
&&
GET_CODE
(
x
)
==
CONST_INT
)
{
ASM_OUTPUT_BYTE
(
asm_out_file
,
INTVAL
(
x
));
return
1
;
}
#endif
/* Finally, if SIZE is larger than a single word, try to output the constant
one word at a time. */
if
(
size
>
UNITS_PER_WORD
)
{
int
i
;
enum
machine_mode
mode
=
mode_for_size
(
size
*
BITS_PER_UNIT
,
MODE_INT
,
0
);
rtx
word
;
for
(
i
=
0
;
i
<
size
/
UNITS_PER_WORD
;
i
++
)
{
word
=
operand_subword
(
x
,
i
,
0
,
mode
);
if
(
word
==
0
)
break
;
assemble_integer
(
word
,
UNITS_PER_WORD
);
}
if
(
i
==
size
/
UNITS_PER_WORD
)
return
1
;
}
if
(
force
)
abort
();
return
0
;
}
/* Assemble the floating-point constant D into an object of size MODE. */
void
assemble_real
(
d
,
mode
)
REAL_VALUE_TYPE
d
;
enum
machine_mode
mode
;
{
jmp_buf
output_constant_handler
;
if
(
setjmp
(
output_constant_handler
))
{
error
(
"floating point trap outputting a constant"
);
#ifdef REAL_IS_NOT_DOUBLE
bzero
(
&
d
,
sizeof
d
);
d
=
dconst0
;
#else
d
=
0
;
#endif
}
set_float_handler
(
output_constant_handler
);
switch
(
mode
)
{
#ifdef ASM_OUTPUT_FLOAT
case
SFmode
:
ASM_OUTPUT_FLOAT
(
asm_out_file
,
d
);
break
;
#endif
#ifdef ASM_OUTPUT_DOUBLE
case
DFmode
:
ASM_OUTPUT_DOUBLE
(
asm_out_file
,
d
);
break
;
#endif
#ifdef ASM_OUTPUT_LONG_DOUBLE
case
TFmode
:
ASM_OUTPUT_LONG_DOUBLE
(
asm_out_file
,
d
);
break
;
#endif
default
:
abort
();
}
set_float_handler
(
0
);
}
/* Here we combine duplicate floating constants to make
CONST_DOUBLE rtx's, and force those out to memory when necessary. */
/* Chain of all CONST_DOUBLE rtx's constructed for the current function.
They are chained through the CONST_DOUBLE_CHAIN.
A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain.
In that case, CONST_DOUBLE_MEM is either a MEM,
or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. */
static
rtx
const_double_chain
;
/* Return a CONST_DOUBLE for a value specified as a pair of ints.
For an integer, I0 is the low-order word and I1 is the high-order word.
For a real number, I0 is the word with the low address
and I1 is the word with the high address. */
rtx
immed_double_const
(
i0
,
i1
,
mode
)
int
i0
,
i1
;
enum
machine_mode
mode
;
{
register
rtx
r
;
int
in_current_obstack
;
if
(
GET_MODE_CLASS
(
mode
)
==
MODE_INT
)
{
/* We clear out all bits that don't belong in MODE, unless they and our
sign bit are all one. So we get either a reasonable negative value
or a reasonable unsigned value for this mode. */
int
width
=
GET_MODE_BITSIZE
(
mode
);
if
(
width
<
HOST_BITS_PER_INT
&&
((
i0
&
((
-
1
)
<<
(
width
-
1
)))
!=
((
-
1
)
<<
(
width
-
1
))))
i0
&=
(
1
<<
width
)
-
1
,
i1
=
0
;
else
if
(
width
==
HOST_BITS_PER_INT
&&
!
(
i1
==
~
0
&&
i0
<
0
))
i1
=
0
;
else
if
(
width
>
2
*
HOST_BITS_PER_INT
)
/* We cannot represent this value as a constant. */
abort
();
/* If MODE fits within HOST_BITS_PER_INT, always use a CONST_INT.
??? Strictly speaking, this is wrong if we create a CONST_INT
for a large unsigned constant with the size of MODE being
HOST_BITS_PER_INT and later try to interpret that constant in a wider
mode. In that case we will mis-interpret it as a negative number.
Unfortunately, the only alternative is to make a CONST_DOUBLE
for any constant in any mode if it is an unsigned constant larger
than the maximum signed integer in an int on the host. However,
doing this will break everyone that always expects to see a CONST_INT
for SImode and smaller.
We have always been making CONST_INTs in this case, so nothing new
is being broken. */
if
(
width
<=
HOST_BITS_PER_INT
)
i1
=
(
i0
<
0
)
?
~
0
:
0
;
/* If this integer fits in one word, return a CONST_INT. */
if
((
i1
==
0
&&
i0
>=
0
)
||
(
i1
==
~
0
&&
i0
<
0
))
return
gen_rtx
(
CONST_INT
,
VOIDmode
,
i0
);
/* We use VOIDmode for integers. */
mode
=
VOIDmode
;
}
/* Search the chain for an existing CONST_DOUBLE with the right value.
If one is found, return it. */
for
(
r
=
const_double_chain
;
r
;
r
=
CONST_DOUBLE_CHAIN
(
r
))
if
(
CONST_DOUBLE_LOW
(
r
)
==
i0
&&
CONST_DOUBLE_HIGH
(
r
)
==
i1
&&
GET_MODE
(
r
)
==
mode
)
return
r
;
/* No; make a new one and add it to the chain.
We may be called by an optimizer which may be discarding any memory
allocated during its processing (such as combine and loop). However,
we will be leaving this constant on the chain, so we cannot tolerate
freed memory. So switch to saveable_obstack for this allocation
and then switch back if we were in current_obstack. */
in_current_obstack
=
rtl_in_saveable_obstack
();
r
=
gen_rtx
(
CONST_DOUBLE
,
mode
,
0
,
i0
,
i1
);
if
(
in_current_obstack
)
rtl_in_current_obstack
();
CONST_DOUBLE_CHAIN
(
r
)
=
const_double_chain
;
const_double_chain
=
r
;
/* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain.
Actual use of mem-slot is only through force_const_mem. */
CONST_DOUBLE_MEM
(
r
)
=
const0_rtx
;
return
r
;
}
/* Return a CONST_DOUBLE for a specified `double' value
and machine mode. */
rtx
immed_real_const_1
(
d
,
mode
)
REAL_VALUE_TYPE
d
;
enum
machine_mode
mode
;
{
union
real_extract
u
;
register
rtx
r
;
int
in_current_obstack
;
/* Get the desired `double' value as a sequence of ints
since that is how they are stored in a CONST_DOUBLE. */
u
.
d
=
d
;
/* Detect special cases. */
if
(
REAL_VALUES_EQUAL
(
dconst0
,
d
))
return
CONST0_RTX
(
mode
);
else
if
(
REAL_VALUES_EQUAL
(
dconst1
,
d
))
return
CONST1_RTX
(
mode
);
if
(
sizeof
u
==
2
*
sizeof
(
int
))
return
immed_double_const
(
u
.
i
[
0
],
u
.
i
[
1
],
mode
);
/* The rest of this function handles the case where
a float value requires more than 2 ints of space.
It will be deleted as dead code on machines that don't need it. */
/* Search the chain for an existing CONST_DOUBLE with the right value.
If one is found, return it. */
for
(
r
=
const_double_chain
;
r
;
r
=
CONST_DOUBLE_CHAIN
(
r
))
if
(
!
bcmp
(
&
CONST_DOUBLE_LOW
(
r
),
&
u
,
sizeof
u
)
&&
GET_MODE
(
r
)
==
mode
)
return
r
;
/* No; make a new one and add it to the chain.
We may be called by an optimizer which may be discarding any memory
allocated during its processing (such as combine and loop). However,
we will be leaving this constant on the chain, so we cannot tolerate
freed memory. So switch to saveable_obstack for this allocation
and then switch back if we were in current_obstack. */
in_current_obstack
=
rtl_in_saveable_obstack
();
r
=
rtx_alloc
(
CONST_DOUBLE
);
PUT_MODE
(
r
,
mode
);
bcopy
(
&
u
,
&
CONST_DOUBLE_LOW
(
r
),
sizeof
u
);
if
(
in_current_obstack
)
rtl_in_current_obstack
();
CONST_DOUBLE_CHAIN
(
r
)
=
const_double_chain
;
const_double_chain
=
r
;
/* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the
chain, but has not been allocated memory. Actual use of CONST_DOUBLE_MEM
is only through force_const_mem. */
CONST_DOUBLE_MEM
(
r
)
=
const0_rtx
;
return
r
;
}
/* Return a CONST_DOUBLE rtx for a value specified by EXP,
which must be a REAL_CST tree node. */
rtx
immed_real_const
(
exp
)
tree
exp
;
{
return
immed_real_const_1
(
TREE_REAL_CST
(
exp
),
TYPE_MODE
(
TREE_TYPE
(
exp
)));
}
/* At the end of a function, forget the memory-constants
previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain.
Also clear out real_constant_chain and clear out all the chain-pointers. */
void
clear_const_double_mem
()
{
register
rtx
r
,
next
;
for
(
r
=
const_double_chain
;
r
;
r
=
next
)
{
next
=
CONST_DOUBLE_CHAIN
(
r
);
CONST_DOUBLE_CHAIN
(
r
)
=
0
;
CONST_DOUBLE_MEM
(
r
)
=
cc0_rtx
;
}
const_double_chain
=
0
;
}
/* Given an expression EXP with a constant value,
reduce it to the sum of an assembler symbol and an integer.
Store them both in the structure *VALUE.
Abort if EXP does not reduce. */
struct
addr_const
{
rtx
base
;
int
offset
;
};
static
void
decode_addr_const
(
exp
,
value
)
tree
exp
;
struct
addr_const
*
value
;
{
register
tree
target
=
TREE_OPERAND
(
exp
,
0
);
register
int
offset
=
0
;
register
rtx
x
;
while
(
1
)
{
if
(
TREE_CODE
(
target
)
==
COMPONENT_REF
&&
(
TREE_CODE
(
DECL_FIELD_BITPOS
(
TREE_OPERAND
(
target
,
1
)))
==
INTEGER_CST
))
{
offset
+=
TREE_INT_CST_LOW
(
DECL_FIELD_BITPOS
(
TREE_OPERAND
(
target
,
1
)))
/
BITS_PER_UNIT
;
target
=
TREE_OPERAND
(
target
,
0
);
}
else
if
(
TREE_CODE
(
target
)
==
ARRAY_REF
)
{
if
(
TREE_CODE
(
TREE_OPERAND
(
target
,
1
))
!=
INTEGER_CST
||
TREE_CODE
(
TYPE_SIZE
(
TREE_TYPE
(
target
)))
!=
INTEGER_CST
)
abort
();
offset
+=
((
TREE_INT_CST_LOW
(
TYPE_SIZE
(
TREE_TYPE
(
target
)))
*
TREE_INT_CST_LOW
(
TREE_OPERAND
(
target
,
1
)))
/
BITS_PER_UNIT
);
target
=
TREE_OPERAND
(
target
,
0
);
}
else
break
;
}
switch
(
TREE_CODE
(
target
))
{
case
VAR_DECL
:
case
FUNCTION_DECL
:
x
=
DECL_RTL
(
target
);
break
;
case
LABEL_DECL
:
x
=
gen_rtx
(
MEM
,
FUNCTION_MODE
,
gen_rtx
(
LABEL_REF
,
VOIDmode
,
label_rtx
(
TREE_OPERAND
(
exp
,
0
))));
break
;
case
REAL_CST
:
case
STRING_CST
:
case
COMPLEX_CST
:
case
CONSTRUCTOR
:
x
=
TREE_CST_RTL
(
target
);
break
;
default
:
abort
();
}
if
(
GET_CODE
(
x
)
!=
MEM
)
abort
();
x
=
XEXP
(
x
,
0
);
value
->
base
=
x
;
value
->
offset
=
offset
;
}
/* Uniquize all constants that appear in memory.
Each constant in memory thus far output is recorded
in `const_hash_table' with a `struct constant_descriptor'
that contains a polish representation of the value of
the constant.
We cannot store the trees in the hash table
because the trees may be temporary. */
struct
constant_descriptor
{
struct
constant_descriptor
*
next
;
char
*
label
;
char
contents
[
1
];
};
#define HASHBITS 30
#define MAX_HASH_TABLE 1009
static
struct
constant_descriptor
*
const_hash_table
[
MAX_HASH_TABLE
];
/* Compute a hash code for a constant expression. */
int
const_hash
(
exp
)
tree
exp
;
{
register
char
*
p
;
register
int
len
,
hi
,
i
;
register
enum
tree_code
code
=
TREE_CODE
(
exp
);
if
(
code
==
INTEGER_CST
)
{
p
=
(
char
*
)
&
TREE_INT_CST_LOW
(
exp
);
len
=
2
*
sizeof
TREE_INT_CST_LOW
(
exp
);
}
else
if
(
code
==
REAL_CST
)
{
p
=
(
char
*
)
&
TREE_REAL_CST
(
exp
);
len
=
sizeof
TREE_REAL_CST
(
exp
);
}
else
if
(
code
==
STRING_CST
)
p
=
TREE_STRING_POINTER
(
exp
),
len
=
TREE_STRING_LENGTH
(
exp
);
else
if
(
code
==
COMPLEX_CST
)
return
const_hash
(
TREE_REALPART
(
exp
))
*
5
+
const_hash
(
TREE_IMAGPART
(
exp
));
else
if
(
code
==
CONSTRUCTOR
)
{
register
tree
link
;
/* For record type, include the type in the hashing.
We do not do so for array types
because (1) the sizes of the elements are sufficient
and (2) distinct array types can have the same constructor. */
if
(
TREE_CODE
(
TREE_TYPE
(
exp
))
==
RECORD_TYPE
)
hi
=
((
int
)
TREE_TYPE
(
exp
)
&
((
1
<<
HASHBITS
)
-
1
))
%
MAX_HASH_TABLE
;
else
hi
=
5
;
for
(
link
=
CONSTRUCTOR_ELTS
(
exp
);
link
;
link
=
TREE_CHAIN
(
link
))
hi
=
(
hi
*
603
+
const_hash
(
TREE_VALUE
(
link
)))
%
MAX_HASH_TABLE
;
return
hi
;
}
else
if
(
code
==
ADDR_EXPR
)
{
struct
addr_const
value
;
decode_addr_const
(
exp
,
&
value
);
if
(
GET_CODE
(
value
.
base
)
==
SYMBOL_REF
)
{
/* Don't hash the address of the SYMBOL_REF;
only use the offset and the symbol name. */
hi
=
value
.
offset
;
p
=
XSTR
(
value
.
base
,
0
);
for
(
i
=
0
;
p
[
i
]
!=
0
;
i
++
)
hi
=
((
hi
*
613
)
+
(
unsigned
)(
p
[
i
]));
}
else
if
(
GET_CODE
(
value
.
base
)
==
LABEL_REF
)
hi
=
value
.
offset
+
CODE_LABEL_NUMBER
(
XEXP
(
value
.
base
,
0
))
*
13
;
hi
&=
(
1
<<
HASHBITS
)
-
1
;
hi
%=
MAX_HASH_TABLE
;
return
hi
;
}
else
if
(
code
==
PLUS_EXPR
||
code
==
MINUS_EXPR
)
return
const_hash
(
TREE_OPERAND
(
exp
,
0
))
*
9
+
const_hash
(
TREE_OPERAND
(
exp
,
1
));
else
if
(
code
==
NOP_EXPR
||
code
==
CONVERT_EXPR
)
return
const_hash
(
TREE_OPERAND
(
exp
,
0
))
*
7
+
2
;
/* Compute hashing function */
hi
=
len
;
for
(
i
=
0
;
i
<
len
;
i
++
)
hi
=
((
hi
*
613
)
+
(
unsigned
)(
p
[
i
]));
hi
&=
(
1
<<
HASHBITS
)
-
1
;
hi
%=
MAX_HASH_TABLE
;
return
hi
;
}
/* Compare a constant expression EXP with a constant-descriptor DESC.
Return 1 if DESC describes a constant with the same value as EXP. */
static
int
compare_constant
(
exp
,
desc
)
tree
exp
;
struct
constant_descriptor
*
desc
;
{
return
0
!=
compare_constant_1
(
exp
,
desc
->
contents
);
}
/* Compare constant expression EXP with a substring P of a constant descriptor.
If they match, return a pointer to the end of the substring matched.
If they do not match, return 0.
Since descriptors are written in polish prefix notation,
this function can be used recursively to test one operand of EXP
against a subdescriptor, and if it succeeds it returns the
address of the subdescriptor for the next operand. */
static
char
*
compare_constant_1
(
exp
,
p
)
tree
exp
;
char
*
p
;
{
register
char
*
strp
;
register
int
len
;
register
enum
tree_code
code
=
TREE_CODE
(
exp
);
if
(
code
!=
(
enum
tree_code
)
*
p
++
)
return
0
;
if
(
code
==
INTEGER_CST
)
{
/* Integer constants are the same only if the same width of type. */
if
(
*
p
++
!=
TYPE_PRECISION
(
TREE_TYPE
(
exp
)))
return
0
;
strp
=
(
char
*
)
&
TREE_INT_CST_LOW
(
exp
);
len
=
2
*
sizeof
TREE_INT_CST_LOW
(
exp
);
}
else
if
(
code
==
REAL_CST
)
{
/* Real constants are the same only if the same width of type. */
if
(
*
p
++
!=
TYPE_PRECISION
(
TREE_TYPE
(
exp
)))
return
0
;
strp
=
(
char
*
)
&
TREE_REAL_CST
(
exp
);
len
=
sizeof
TREE_REAL_CST
(
exp
);
}
else
if
(
code
==
STRING_CST
)
{
if
(
flag_writable_strings
)
return
0
;
strp
=
TREE_STRING_POINTER
(
exp
);
len
=
TREE_STRING_LENGTH
(
exp
);
if
(
bcmp
(
&
TREE_STRING_LENGTH
(
exp
),
p
,
sizeof
TREE_STRING_LENGTH
(
exp
)))
return
0
;
p
+=
sizeof
TREE_STRING_LENGTH
(
exp
);
}
else
if
(
code
==
COMPLEX_CST
)
{
p
=
compare_constant_1
(
TREE_REALPART
(
exp
),
p
);
if
(
p
==
0
)
return
0
;
p
=
compare_constant_1
(
TREE_IMAGPART
(
exp
),
p
);
return
p
;
}
else
if
(
code
==
CONSTRUCTOR
)
{
register
tree
link
;
int
length
=
list_length
(
CONSTRUCTOR_ELTS
(
exp
));
tree
type
;
if
(
bcmp
(
&
length
,
p
,
sizeof
length
))
return
0
;
p
+=
sizeof
length
;
/* For record constructors, insist that the types match.
For arrays, just verify both constructors are for arrays. */
if
(
TREE_CODE
(
TREE_TYPE
(
exp
))
==
RECORD_TYPE
)
type
=
TREE_TYPE
(
exp
);
else
type
=
0
;
if
(
bcmp
(
&
type
,
p
,
sizeof
type
))
return
0
;
p
+=
sizeof
type
;
for
(
link
=
CONSTRUCTOR_ELTS
(
exp
);
link
;
link
=
TREE_CHAIN
(
link
))
if
((
p
=
compare_constant_1
(
TREE_VALUE
(
link
),
p
))
==
0
)
return
0
;
return
p
;
}
else
if
(
code
==
ADDR_EXPR
)
{
struct
addr_const
value
;
decode_addr_const
(
exp
,
&
value
);
strp
=
(
char
*
)
&
value
.
offset
;
len
=
sizeof
value
.
offset
;
/* Compare the offset. */
while
(
--
len
>=
0
)
if
(
*
p
++
!=
*
strp
++
)
return
0
;
/* Compare symbol name. */
strp
=
XSTR
(
value
.
base
,
0
);
len
=
strlen
(
strp
)
+
1
;
}
else
if
(
code
==
PLUS_EXPR
||
code
==
MINUS_EXPR
)
{
p
=
compare_constant_1
(
TREE_OPERAND
(
exp
,
0
),
p
);
if
(
p
==
0
)
return
0
;
p
=
compare_constant_1
(
TREE_OPERAND
(
exp
,
1
),
p
);
return
p
;
}
else
if
(
code
==
NOP_EXPR
||
code
==
CONVERT_EXPR
)
{
p
=
compare_constant_1
(
TREE_OPERAND
(
exp
,
0
),
p
);
return
p
;
}
/* Compare constant contents. */
while
(
--
len
>=
0
)
if
(
*
p
++
!=
*
strp
++
)
return
0
;
return
p
;
}
/* Construct a constant descriptor for the expression EXP.
It is up to the caller to enter the descriptor in the hash table. */
static
struct
constant_descriptor
*
record_constant
(
exp
)
tree
exp
;
{
struct
constant_descriptor
*
ptr
=
0
;
int
buf
;
obstack_grow
(
&
permanent_obstack
,
&
ptr
,
sizeof
ptr
);
obstack_grow
(
&
permanent_obstack
,
&
buf
,
sizeof
buf
);
record_constant_1
(
exp
);
return
(
struct
constant_descriptor
*
)
obstack_finish
(
&
permanent_obstack
);
}
/* Add a description of constant expression EXP
to the object growing in `permanent_obstack'.
No need to return its address; the caller will get that
from the obstack when the object is complete. */
static
void
record_constant_1
(
exp
)
tree
exp
;
{
register
char
*
strp
;
register
int
len
;
register
enum
tree_code
code
=
TREE_CODE
(
exp
);
obstack_1grow
(
&
permanent_obstack
,
(
unsigned
int
)
code
);
if
(
code
==
INTEGER_CST
)
{
obstack_1grow
(
&
permanent_obstack
,
TYPE_PRECISION
(
TREE_TYPE
(
exp
)));
strp
=
(
char
*
)
&
TREE_INT_CST_LOW
(
exp
);
len
=
2
*
sizeof
TREE_INT_CST_LOW
(
exp
);
}
else
if
(
code
==
REAL_CST
)
{
obstack_1grow
(
&
permanent_obstack
,
TYPE_PRECISION
(
TREE_TYPE
(
exp
)));
strp
=
(
char
*
)
&
TREE_REAL_CST
(
exp
);
len
=
sizeof
TREE_REAL_CST
(
exp
);
}
else
if
(
code
==
STRING_CST
)
{
if
(
flag_writable_strings
)
return
;
strp
=
TREE_STRING_POINTER
(
exp
);
len
=
TREE_STRING_LENGTH
(
exp
);
obstack_grow
(
&
permanent_obstack
,
(
char
*
)
&
TREE_STRING_LENGTH
(
exp
),
sizeof
TREE_STRING_LENGTH
(
exp
));
}
else
if
(
code
==
COMPLEX_CST
)
{
record_constant_1
(
TREE_REALPART
(
exp
));
record_constant_1
(
TREE_IMAGPART
(
exp
));
return
;
}
else
if
(
code
==
CONSTRUCTOR
)
{
register
tree
link
;
int
length
=
list_length
(
CONSTRUCTOR_ELTS
(
exp
));
tree
type
;
obstack_grow
(
&
permanent_obstack
,
(
char
*
)
&
length
,
sizeof
length
);
/* For record constructors, insist that the types match.
For arrays, just verify both constructors are for arrays. */
if
(
TREE_CODE
(
TREE_TYPE
(
exp
))
==
RECORD_TYPE
)
type
=
TREE_TYPE
(
exp
);
else
type
=
0
;
obstack_grow
(
&
permanent_obstack
,
(
char
*
)
&
type
,
sizeof
type
);
for
(
link
=
CONSTRUCTOR_ELTS
(
exp
);
link
;
link
=
TREE_CHAIN
(
link
))
record_constant_1
(
TREE_VALUE
(
link
));
return
;
}
else
if
(
code
==
ADDR_EXPR
)
{
struct
addr_const
value
;
decode_addr_const
(
exp
,
&
value
);
/* Record the offset. */
obstack_grow
(
&
permanent_obstack
,
(
char
*
)
&
value
.
offset
,
sizeof
value
.
offset
);
/* Record the symbol name. */
obstack_grow
(
&
permanent_obstack
,
XSTR
(
value
.
base
,
0
),
strlen
(
XSTR
(
value
.
base
,
0
))
+
1
);
return
;
}
else
if
(
code
==
PLUS_EXPR
||
code
==
MINUS_EXPR
)
{
record_constant_1
(
TREE_OPERAND
(
exp
,
0
));
record_constant_1
(
TREE_OPERAND
(
exp
,
1
));
return
;
}
else
if
(
code
==
NOP_EXPR
||
code
==
CONVERT_EXPR
)
{
record_constant_1
(
TREE_OPERAND
(
exp
,
0
));
return
;
}
/* Record constant contents. */
obstack_grow
(
&
permanent_obstack
,
strp
,
len
);
}
/* Return an rtx representing a reference to constant data in memory
for the constant expression EXP.
If assembler code for such a constant has already been output,
return an rtx to refer to it.
Otherwise, output such a constant in memory and generate
an rtx for it. The TREE_CST_RTL of EXP is set up to point to that rtx.
The const_hash_table records which constants already have label strings. */
rtx
output_constant_def
(
exp
)
tree
exp
;
{
register
int
hash
,
align
;
register
struct
constant_descriptor
*
desc
;
char
label
[
256
];
char
*
found
=
0
;
int
reloc
;
register
rtx
def
;
if
(
TREE_CODE
(
exp
)
==
INTEGER_CST
)
abort
();
/* No TREE_CST_RTL slot in these. */
if
(
TREE_CST_RTL
(
exp
))
return
TREE_CST_RTL
(
exp
);
/* Make sure any other constants whose addresses appear in EXP
are assigned label numbers. */
reloc
=
output_addressed_constants
(
exp
);
/* Compute hash code of EXP. Search the descriptors for that hash code
to see if any of them describes EXP. If yes, the descriptor records
the label number already assigned. */
hash
=
const_hash
(
exp
)
%
MAX_HASH_TABLE
;
for
(
desc
=
const_hash_table
[
hash
];
desc
;
desc
=
desc
->
next
)
if
(
compare_constant
(
exp
,
desc
))
{
found
=
desc
->
label
;
break
;
}
if
(
found
==
0
)
{
/* No constant equal to EXP is known to have been output.
Make a constant descriptor to enter EXP in the hash table.
Assign the label number and record it in the descriptor for
future calls to this function to find. */
/* Create a string containing the label name, in LABEL. */
ASM_GENERATE_INTERNAL_LABEL
(
label
,
"LC"
,
const_labelno
);
desc
=
record_constant
(
exp
);
desc
->
next
=
const_hash_table
[
hash
];
desc
->
label
=
(
char
*
)
obstack_copy0
(
&
permanent_obstack
,
label
,
strlen
(
label
));
const_hash_table
[
hash
]
=
desc
;
}
/* We have a symbol name; construct the SYMBOL_REF and the MEM. */
push_obstacks_nochange
();
if
(
TREE_PERMANENT
(
exp
))
end_temporary_allocation
();
def
=
gen_rtx
(
SYMBOL_REF
,
Pmode
,
desc
->
label
);
TREE_CST_RTL
(
exp
)
=
gen_rtx
(
MEM
,
TYPE_MODE
(
TREE_TYPE
(
exp
)),
def
);
RTX_UNCHANGING_P
(
TREE_CST_RTL
(
exp
))
=
1
;
pop_obstacks
();
/* Optionally set flags or add text to the name to record information
such as that it is a function name. If the name is changed, the macro
ASM_OUTPUT_LABELREF will have to know how to strip this information.
And if it finds a * at the beginning after doing so, it must handle
that too. */
#ifdef ENCODE_SECTION_INFO
ENCODE_SECTION_INFO
(
exp
);
#endif
if
(
found
==
0
)
{
/* Now output assembler code to define that label
and follow it with the data of EXP. */
/* First switch to text section, except for writable strings. */
#ifdef SELECT_SECTION
SELECT_SECTION
(
exp
,
reloc
);
#else
if
(((
TREE_CODE
(
exp
)
==
STRING_CST
)
&&
flag_writable_strings
)
||
(
flag_pic
&&
reloc
))
data_section
();
else
readonly_data_section
();
#endif
/* Align the location counter as required by EXP's data type. */
align
=
TYPE_ALIGN
(
TREE_TYPE
(
exp
));
#ifdef CONSTANT_ALIGNMENT
align
=
CONSTANT_ALIGNMENT
(
exp
,
align
);
#endif
if
(
align
>
BITS_PER_UNIT
)
ASM_OUTPUT_ALIGN
(
asm_out_file
,
floor_log2
(
align
/
BITS_PER_UNIT
));
/* Output the label itself. */
ASM_OUTPUT_INTERNAL_LABEL
(
asm_out_file
,
"LC"
,
const_labelno
);
/* Output the value of EXP. */
output_constant
(
exp
,
(
TREE_CODE
(
exp
)
==
STRING_CST
?
TREE_STRING_LENGTH
(
exp
)
:
int_size_in_bytes
(
TREE_TYPE
(
exp
))));
++
const_labelno
;
}
return
TREE_CST_RTL
(
exp
);
}
/* Similar hash facility for making memory-constants
from constant rtl-expressions. It is used on RISC machines
where immediate integer arguments and constant addresses are restricted
so that such constants must be stored in memory.
This pool of constants is reinitialized for each function
so each function gets its own constants-pool that comes right before it.
All structures allocated here are discarded when functions are saved for
inlining, so they do not need to be allocated permanently. */
#define MAX_RTX_HASH_TABLE 61
static
struct
constant_descriptor
*
const_rtx_hash_table
[
MAX_RTX_HASH_TABLE
];
/* Structure to represent sufficient information about a constant so that
it can be output when the constant pool is output, so that function
integration can be done, and to simplify handling on machines that reference
constant pool as base+displacement. */
struct
pool_constant
{
struct
constant_descriptor
*
desc
;
struct
pool_constant
*
next
;
enum
machine_mode
mode
;
rtx
constant
;
int
labelno
;
int
align
;
int
offset
;
};
/* Pointers to first and last constant in pool. */
static
struct
pool_constant
*
first_pool
,
*
last_pool
;
/* Current offset in constant pool (does not include any machine-specific
header. */
static
int
pool_offset
;
/* Structure used to maintain hash table mapping symbols used to their
corresponding constants. */
struct
pool_sym
{
char
*
label
;
struct
pool_constant
*
pool
;
struct
pool_sym
*
next
;
};
static
struct
pool_sym
*
const_rtx_sym_hash_table
[
MAX_RTX_HASH_TABLE
];
/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true.
The argument is XSTR (... , 0) */
#define SYMHASH(LABEL) \
((((int) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE)
/* Initialize constant pool hashing for next function. */
void
init_const_rtx_hash_table
()
{
bzero
(
const_rtx_hash_table
,
sizeof
const_rtx_hash_table
);
bzero
(
const_rtx_sym_hash_table
,
sizeof
const_rtx_sym_hash_table
);
first_pool
=
last_pool
=
0
;
pool_offset
=
0
;
}
enum
kind
{
RTX_DOUBLE
,
RTX_INT
};
struct
rtx_const
{
#ifdef ONLY_INT_FIELDS
unsigned
int
kind
:
16
;
unsigned
int
mode
:
16
;
#else
enum
kind
kind
:
16
;
enum
machine_mode
mode
:
16
;
#endif
union
{
union
real_extract
du
;
struct
addr_const
addr
;
}
un
;
};
/* Express an rtx for a constant integer (perhaps symbolic)
as the sum of a symbol or label plus an explicit integer.
They are stored into VALUE. */
static
void
decode_rtx_const
(
mode
,
x
,
value
)
enum
machine_mode
mode
;
rtx
x
;
struct
rtx_const
*
value
;
{
/* Clear the whole structure, including any gaps. */
{
int
*
p
=
(
int
*
)
value
;
int
*
end
=
(
int
*
)
(
value
+
1
);
while
(
p
<
end
)
*
p
++
=
0
;
}
value
->
kind
=
RTX_INT
;
/* Most usual kind. */
value
->
mode
=
mode
;
switch
(
GET_CODE
(
x
))
{
case
CONST_DOUBLE
:
value
->
kind
=
RTX_DOUBLE
;
value
->
mode
=
GET_MODE
(
x
);
bcopy
(
&
CONST_DOUBLE_LOW
(
x
),
&
value
->
un
.
du
,
sizeof
value
->
un
.
du
);
break
;
case
CONST_INT
:
value
->
un
.
addr
.
offset
=
INTVAL
(
x
);
break
;
case
SYMBOL_REF
:
case
LABEL_REF
:
value
->
un
.
addr
.
base
=
x
;
break
;
case
CONST
:
x
=
XEXP
(
x
,
0
);
if
(
GET_CODE
(
x
)
==
PLUS
)
{
value
->
un
.
addr
.
base
=
XEXP
(
x
,
0
);
if
(
GET_CODE
(
XEXP
(
x
,
1
))
!=
CONST_INT
)
abort
();
value
->
un
.
addr
.
offset
=
INTVAL
(
XEXP
(
x
,
1
));
}
else
if
(
GET_CODE
(
x
)
==
MINUS
)
{
value
->
un
.
addr
.
base
=
XEXP
(
x
,
0
);
if
(
GET_CODE
(
XEXP
(
x
,
1
))
!=
CONST_INT
)
abort
();
value
->
un
.
addr
.
offset
=
-
INTVAL
(
XEXP
(
x
,
1
));
}
else
abort
();
break
;
default
:
abort
();
}
if
(
value
->
kind
==
RTX_INT
&&
value
->
un
.
addr
.
base
!=
0
)
switch
(
GET_CODE
(
value
->
un
.
addr
.
base
))
{
case
SYMBOL_REF
:
case
LABEL_REF
:
/* Use the string's address, not the SYMBOL_REF's address,
for the sake of addresses of library routines.
For a LABEL_REF, compare labels. */
value
->
un
.
addr
.
base
=
XEXP
(
value
->
un
.
addr
.
base
,
0
);
}
}
/* Compute a hash code for a constant RTL expression. */
int
const_hash_rtx
(
mode
,
x
)
enum
machine_mode
mode
;
rtx
x
;
{
register
int
hi
,
i
;
struct
rtx_const
value
;
decode_rtx_const
(
mode
,
x
,
&
value
);
/* Compute hashing function */
hi
=
0
;
for
(
i
=
0
;
i
<
sizeof
value
/
sizeof
(
int
);
i
++
)
hi
+=
((
int
*
)
&
value
)[
i
];
hi
&=
(
1
<<
HASHBITS
)
-
1
;
hi
%=
MAX_RTX_HASH_TABLE
;
return
hi
;
}
/* Compare a constant rtl object X with a constant-descriptor DESC.
Return 1 if DESC describes a constant with the same value as X. */
static
int
compare_constant_rtx
(
mode
,
x
,
desc
)
enum
machine_mode
mode
;
rtx
x
;
struct
constant_descriptor
*
desc
;
{
register
int
*
p
=
(
int
*
)
desc
->
contents
;
register
int
*
strp
;
register
int
len
;
struct
rtx_const
value
;
decode_rtx_const
(
mode
,
x
,
&
value
);
strp
=
(
int
*
)
&
value
;
len
=
sizeof
value
/
sizeof
(
int
);
/* Compare constant contents. */
while
(
--
len
>=
0
)
if
(
*
p
++
!=
*
strp
++
)
return
0
;
return
1
;
}
/* Construct a constant descriptor for the rtl-expression X.
It is up to the caller to enter the descriptor in the hash table. */
static
struct
constant_descriptor
*
record_constant_rtx
(
mode
,
x
)
enum
machine_mode
mode
;
rtx
x
;
{
struct
constant_descriptor
*
ptr
;
char
*
label
;
struct
rtx_const
value
;
decode_rtx_const
(
mode
,
x
,
&
value
);
obstack_grow
(
current_obstack
,
&
ptr
,
sizeof
ptr
);
obstack_grow
(
current_obstack
,
&
label
,
sizeof
label
);
/* Record constant contents. */
obstack_grow
(
current_obstack
,
&
value
,
sizeof
value
);
return
(
struct
constant_descriptor
*
)
obstack_finish
(
current_obstack
);
}
/* Given a constant rtx X, make (or find) a memory constant for its value
and return a MEM rtx to refer to it in memory. */
rtx
force_const_mem
(
mode
,
x
)
enum
machine_mode
mode
;
rtx
x
;
{
register
int
hash
;
register
struct
constant_descriptor
*
desc
;
char
label
[
256
];
char
*
found
=
0
;
rtx
def
;
/* If we want this CONST_DOUBLE in the same mode as it is in memory
(this will always be true for floating CONST_DOUBLEs that have been
placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs),
use the previous copy. Otherwise, make a new one. Note that in
the unlikely event that this same CONST_DOUBLE is used in two different
modes in an alternating fashion, we will allocate a lot of different
memory locations, but this should be extremely rare. */
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_CODE
(
CONST_DOUBLE_MEM
(
x
))
==
MEM
&&
GET_MODE
(
CONST_DOUBLE_MEM
(
x
))
==
mode
)
return
CONST_DOUBLE_MEM
(
x
);
/* Compute hash code of X. Search the descriptors for that hash code
to see if any of them describes X. If yes, the descriptor records
the label number already assigned. */
hash
=
const_hash_rtx
(
mode
,
x
);
for
(
desc
=
const_rtx_hash_table
[
hash
];
desc
;
desc
=
desc
->
next
)
if
(
compare_constant_rtx
(
mode
,
x
,
desc
))
{
found
=
desc
->
label
;
break
;
}
if
(
found
==
0
)
{
register
struct
pool_constant
*
pool
;
register
struct
pool_sym
*
sym
;
int
align
;
/* No constant equal to X is known to have been output.
Make a constant descriptor to enter X in the hash table.
Assign the label number and record it in the descriptor for
future calls to this function to find. */
desc
=
record_constant_rtx
(
mode
,
x
);
desc
->
next
=
const_rtx_hash_table
[
hash
];
const_rtx_hash_table
[
hash
]
=
desc
;
/* Align the location counter as required by EXP's data type. */
align
=
(
mode
==
VOIDmode
)
?
UNITS_PER_WORD
:
GET_MODE_SIZE
(
mode
);
if
(
align
>
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
)
align
=
BIGGEST_ALIGNMENT
/
BITS_PER_UNIT
;
pool_offset
+=
align
-
1
;
pool_offset
&=
~
(
align
-
1
);
/* Allocate a pool constant descriptor, fill it in, and chain it in. */
pool
=
(
struct
pool_constant
*
)
oballoc
(
sizeof
(
struct
pool_constant
));
pool
->
desc
=
desc
;
pool
->
constant
=
x
;
pool
->
mode
=
mode
;
pool
->
labelno
=
const_labelno
;
pool
->
align
=
align
;
pool
->
offset
=
pool_offset
;
pool
->
next
=
0
;
if
(
last_pool
==
0
)
first_pool
=
pool
;
else
last_pool
->
next
=
pool
;
last_pool
=
pool
;
pool_offset
+=
GET_MODE_SIZE
(
mode
);
/* Create a string containing the label name, in LABEL. */
ASM_GENERATE_INTERNAL_LABEL
(
label
,
"LC"
,
const_labelno
);
++
const_labelno
;
desc
->
label
=
found
=
(
char
*
)
obstack_copy0
(
saveable_obstack
,
label
,
strlen
(
label
));
/* Add label to symbol hash table. */
hash
=
SYMHASH
(
found
);
sym
=
(
struct
pool_sym
*
)
oballoc
(
sizeof
(
struct
pool_sym
));
sym
->
label
=
found
;
sym
->
pool
=
pool
;
sym
->
next
=
const_rtx_sym_hash_table
[
hash
];
const_rtx_sym_hash_table
[
hash
]
=
sym
;
}
/* We have a symbol name; construct the SYMBOL_REF and the MEM. */
def
=
gen_rtx
(
MEM
,
mode
,
gen_rtx
(
SYMBOL_REF
,
Pmode
,
found
));
RTX_UNCHANGING_P
(
def
)
=
1
;
/* Mark the symbol_ref as belonging to this constants pool. */
CONSTANT_POOL_ADDRESS_P
(
XEXP
(
def
,
0
))
=
1
;
current_function_uses_const_pool
=
1
;
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
)
{
if
(
CONST_DOUBLE_MEM
(
x
)
==
cc0_rtx
)
{
CONST_DOUBLE_CHAIN
(
x
)
=
const_double_chain
;
const_double_chain
=
x
;
}
CONST_DOUBLE_MEM
(
x
)
=
def
;
}
return
def
;
}
/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to
the corresponding pool_constant structure. */
static
struct
pool_constant
*
find_pool_constant
(
addr
)
rtx
addr
;
{
struct
pool_sym
*
sym
;
char
*
label
=
XSTR
(
addr
,
0
);
for
(
sym
=
const_rtx_sym_hash_table
[
SYMHASH
(
label
)];
sym
;
sym
=
sym
->
next
)
if
(
sym
->
label
==
label
)
return
sym
->
pool
;
abort
();
}
/* Given a constant pool SYMBOL_REF, return the corresponding constant. */
rtx
get_pool_constant
(
addr
)
rtx
addr
;
{
return
(
find_pool_constant
(
addr
))
->
constant
;
}
/* Similar, return the mode. */
enum
machine_mode
get_pool_mode
(
addr
)
rtx
addr
;
{
return
(
find_pool_constant
(
addr
))
->
mode
;
}
/* Similar, return the offset in the constant pool. */
int
get_pool_offset
(
addr
)
rtx
addr
;
{
return
(
find_pool_constant
(
addr
))
->
offset
;
}
/* Return the size of the constant pool. */
int
get_pool_size
()
{
return
pool_offset
;
}
/* Write all the constants in the constant pool. */
void
output_constant_pool
(
fnname
,
fndecl
)
char
*
fnname
;
tree
fndecl
;
{
struct
pool_constant
*
pool
;
rtx
x
;
union
real_extract
u
;
#ifdef ASM_OUTPUT_POOL_PROLOGUE
ASM_OUTPUT_POOL_PROLOGUE
(
asm_out_file
,
fnname
,
fndecl
,
pool_offset
);
#endif
for
(
pool
=
first_pool
;
pool
;
pool
=
pool
->
next
)
{
x
=
pool
->
constant
;
/* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF)
whose CODE_LABEL has been deleted. This can occur if a jump table
is eliminated by optimization. If so, write a constant of zero
instead. */
if
((
GET_CODE
(
x
)
==
LABEL_REF
&&
INSN_DELETED_P
(
XEXP
(
x
,
0
)))
||
(
GET_CODE
(
x
)
==
CONST
&&
GET_CODE
(
XEXP
(
x
,
0
))
==
PLUS
&&
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
LABEL_REF
&&
INSN_DELETED_P
(
XEXP
(
XEXP
(
XEXP
(
x
,
0
),
0
),
0
))))
x
=
const0_rtx
;
/* First switch to correct section. */
#ifdef SELECT_RTX_SECTION
SELECT_RTX_SECTION
(
pool
->
mode
,
x
);
#else
readonly_data_section
();
#endif
#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
ASM_OUTPUT_SPECIAL_POOL_ENTRY
(
asm_out_file
,
x
,
pool
->
mode
,
pool
->
align
,
pool
->
labelno
,
done
);
#endif
if
(
pool
->
align
>
1
)
ASM_OUTPUT_ALIGN
(
asm_out_file
,
exact_log2
(
pool
->
align
));
/* Output the label. */
ASM_OUTPUT_INTERNAL_LABEL
(
asm_out_file
,
"LC"
,
pool
->
labelno
);
/* Output the value of the constant itself. */
switch
(
GET_MODE_CLASS
(
pool
->
mode
))
{
case
MODE_FLOAT
:
if
(
GET_CODE
(
x
)
!=
CONST_DOUBLE
)
abort
();
bcopy
(
&
CONST_DOUBLE_LOW
(
x
),
&
u
,
sizeof
u
);
assemble_real
(
u
.
d
,
pool
->
mode
);
break
;
case
MODE_INT
:
assemble_integer
(
x
,
GET_MODE_SIZE
(
pool
->
mode
),
1
);
break
;
default
:
abort
();
}
done
:
;
}
/* Done with this pool. */
first_pool
=
last_pool
=
0
;
}
/* Find all the constants whose addresses are referenced inside of EXP,
and make sure assembler code with a label has been output for each one.
Indicate whether an ADDR_EXPR has been encountered. */
int
output_addressed_constants
(
exp
)
tree
exp
;
{
int
reloc
=
0
;
switch
(
TREE_CODE
(
exp
))
{
case
ADDR_EXPR
:
{
register
tree
constant
=
TREE_OPERAND
(
exp
,
0
);
while
(
TREE_CODE
(
constant
)
==
COMPONENT_REF
)
{
constant
=
TREE_OPERAND
(
constant
,
0
);
}
if
(
TREE_CODE_CLASS
(
TREE_CODE
(
constant
))
==
'c'
||
TREE_CODE
(
constant
)
==
CONSTRUCTOR
)
/* No need to do anything here
for addresses of variables or functions. */
output_constant_def
(
constant
);
}
reloc
=
1
;
break
;
case
PLUS_EXPR
:
case
MINUS_EXPR
:
reloc
=
output_addressed_constants
(
TREE_OPERAND
(
exp
,
0
));
reloc
|=
output_addressed_constants
(
TREE_OPERAND
(
exp
,
1
));
break
;
case
NOP_EXPR
:
case
CONVERT_EXPR
:
reloc
=
output_addressed_constants
(
TREE_OPERAND
(
exp
,
0
));
break
;
case
CONSTRUCTOR
:
{
register
tree
link
;
for
(
link
=
CONSTRUCTOR_ELTS
(
exp
);
link
;
link
=
TREE_CHAIN
(
link
))
if
(
TREE_VALUE
(
link
)
!=
0
)
reloc
|=
output_addressed_constants
(
TREE_VALUE
(
link
));
}
break
;
case
ERROR_MARK
:
break
;
}
return
reloc
;
}
/* Output assembler code for constant EXP to FILE, with no label.
This includes the pseudo-op such as ".int" or ".byte", and a newline.
Assumes output_addressed_constants has been done on EXP already.
Generate exactly SIZE bytes of assembler data, padding at the end
with zeros if necessary. SIZE must always be specified.
SIZE is important for structure constructors,
since trailing members may have been omitted from the constructor.
It is also important for initialization of arrays from string constants
since the full length of the string constant might not be wanted.
It is also needed for initialization of unions, where the initializer's
type is just one member, and that may not be as long as the union.
There a case in which we would fail to output exactly SIZE bytes:
for a structure constructor that wants to produce more than SIZE bytes.
But such constructors will never be generated for any possible input. */
void
output_constant
(
exp
,
size
)
register
tree
exp
;
register
int
size
;
{
register
enum
tree_code
code
=
TREE_CODE
(
TREE_TYPE
(
exp
));
rtx
x
;
if
(
size
==
0
)
return
;
/* Eliminate the NOP_EXPR that makes a cast not be an lvalue.
That way we get the constant (we hope) inside it. */
if
(
TREE_CODE
(
exp
)
==
NOP_EXPR
&&
TREE_TYPE
(
exp
)
==
TREE_TYPE
(
TREE_OPERAND
(
exp
,
0
)))
exp
=
TREE_OPERAND
(
exp
,
0
);
switch
(
code
)
{
case
INTEGER_TYPE
:
case
ENUMERAL_TYPE
:
case
POINTER_TYPE
:
case
REFERENCE_TYPE
:
/* ??? What about (int)((float)(int)&foo + 4) */
while
(
TREE_CODE
(
exp
)
==
NOP_EXPR
||
TREE_CODE
(
exp
)
==
CONVERT_EXPR
||
TREE_CODE
(
exp
)
==
NON_LVALUE_EXPR
)
exp
=
TREE_OPERAND
(
exp
,
0
);
if
(
!
assemble_integer
(
expand_expr
(
exp
,
0
,
VOIDmode
,
EXPAND_INITIALIZER
),
size
,
0
))
error
(
"initializer for integer value is too complicated"
);
size
=
0
;
break
;
case
REAL_TYPE
:
if
(
TREE_CODE
(
exp
)
!=
REAL_CST
)
error
(
"initializer for floating value is not a floating constant"
);
assemble_real
(
TREE_REAL_CST
(
exp
),
mode_for_size
(
size
*
BITS_PER_UNIT
,
MODE_FLOAT
,
0
));
size
=
0
;
break
;
case
COMPLEX_TYPE
:
output_constant
(
TREE_REALPART
(
exp
),
size
/
2
);
output_constant
(
TREE_IMAGPART
(
exp
),
size
/
2
);
size
-=
(
size
/
2
)
*
2
;
break
;
case
ARRAY_TYPE
:
if
(
TREE_CODE
(
exp
)
==
CONSTRUCTOR
)
{
output_constructor
(
exp
,
size
);
return
;
}
else
if
(
TREE_CODE
(
exp
)
==
STRING_CST
)
{
int
excess
=
0
;
if
(
size
>
TREE_STRING_LENGTH
(
exp
))
{
excess
=
size
-
TREE_STRING_LENGTH
(
exp
);
size
=
TREE_STRING_LENGTH
(
exp
);
}
assemble_string
(
TREE_STRING_POINTER
(
exp
),
size
);
size
=
excess
;
}
else
abort
();
break
;
case
RECORD_TYPE
:
case
UNION_TYPE
:
if
(
TREE_CODE
(
exp
)
==
CONSTRUCTOR
)
output_constructor
(
exp
,
size
);
else
abort
();
return
;
}
if
(
size
>
0
)
assemble_zeros
(
size
);
}
/* Subroutine of output_constant, used for CONSTRUCTORs
(aggregate constants).
Generate at least SIZE bytes, padding if necessary. */
void
output_constructor
(
exp
,
size
)
tree
exp
;
int
size
;
{
register
tree
link
,
field
=
0
;
/* Number of bytes output or skipped so far.
In other words, current position within the constructor. */
int
total_bytes
=
0
;
/* Non-zero means BYTE contains part of a byte, to be output. */
int
byte_buffer_in_use
=
0
;
register
int
byte
;
if
(
HOST_BITS_PER_INT
<
BITS_PER_UNIT
)
abort
();
if
(
TREE_CODE
(
TREE_TYPE
(
exp
))
==
RECORD_TYPE
)
field
=
TYPE_FIELDS
(
TREE_TYPE
(
exp
));
/* As LINK goes through the elements of the constant,
FIELD goes through the structure fields, if the constant is a structure.
if the constant is a union, then we override this,
by getting the field from the TREE_LIST element.
But the constant could also be an array. Then FIELD is zero. */
for
(
link
=
CONSTRUCTOR_ELTS
(
exp
);
link
;
link
=
TREE_CHAIN
(
link
),
field
=
field
?
TREE_CHAIN
(
field
)
:
0
)
{
tree
val
=
TREE_VALUE
(
link
);
/* the element in a union constructor specifies the proper field. */
if
(
TREE_PURPOSE
(
link
)
!=
0
)
field
=
TREE_PURPOSE
(
link
);
/* Eliminate the marker that makes a cast not be an lvalue. */
if
(
val
!=
0
&&
TREE_CODE
(
val
)
==
NON_LVALUE_EXPR
)
val
=
TREE_OPERAND
(
val
,
0
);
if
(
field
==
0
||
!
DECL_BIT_FIELD
(
field
))
{
register
int
fieldsize
;
/* Since this structure is static,
we know the positions are constant. */
int
bitpos
=
(
field
?
(
TREE_INT_CST_LOW
(
DECL_FIELD_BITPOS
(
field
))
/
BITS_PER_UNIT
)
:
0
);
/* An element that is not a bit-field.
Output any buffered-up bit-fields preceding it. */
if
(
byte_buffer_in_use
)
{
ASM_OUTPUT_BYTE
(
asm_out_file
,
byte
);
total_bytes
++
;
byte_buffer_in_use
=
0
;
}
/* Advance to offset of this element.
Note no alignment needed in an array, since that is guaranteed
if each element has the proper size. */
if
(
field
!=
0
&&
bitpos
!=
total_bytes
)
{
assemble_zeros
(
bitpos
-
total_bytes
);
total_bytes
=
bitpos
;
}
/* Determine size this element should occupy. */
if
(
field
)
{
if
(
TREE_CODE
(
DECL_SIZE
(
field
))
!=
INTEGER_CST
)
abort
();
if
(
TREE_INT_CST_LOW
(
DECL_SIZE
(
field
))
>
100000
)
{
/* This avoids overflow trouble. */
tree
size_tree
=
size_binop
(
CEIL_DIV_EXPR
,
DECL_SIZE
(
field
),
size_int
(
BITS_PER_UNIT
));
fieldsize
=
TREE_INT_CST_LOW
(
size_tree
);
}
else
{
fieldsize
=
TREE_INT_CST_LOW
(
DECL_SIZE
(
field
));
fieldsize
=
(
fieldsize
+
BITS_PER_UNIT
-
1
)
/
BITS_PER_UNIT
;
}
}
else
fieldsize
=
int_size_in_bytes
(
TREE_TYPE
(
TREE_TYPE
(
exp
)));
/* Output the element's initial value. */
if
(
val
==
0
)
assemble_zeros
(
fieldsize
);
else
output_constant
(
val
,
fieldsize
);
/* Count its size. */
total_bytes
+=
fieldsize
;
}
else
if
(
val
!=
0
&&
TREE_CODE
(
val
)
!=
INTEGER_CST
)
error
(
"invalid initial value for member `%s'"
,
IDENTIFIER_POINTER
(
DECL_NAME
(
field
)));
else
{
/* Element that is a bit-field. */
int
next_offset
=
TREE_INT_CST_LOW
(
DECL_FIELD_BITPOS
(
field
));
int
end_offset
=
(
next_offset
+
TREE_INT_CST_LOW
(
DECL_SIZE
(
field
)));
if
(
val
==
0
)
val
=
integer_zero_node
;
/* If this field does not start in this (or, next) byte,
skip some bytes. */
if
(
next_offset
/
BITS_PER_UNIT
!=
total_bytes
)
{
/* Output remnant of any bit field in previous bytes. */
if
(
byte_buffer_in_use
)
{
ASM_OUTPUT_BYTE
(
asm_out_file
,
byte
);
total_bytes
++
;
byte_buffer_in_use
=
0
;
}
/* If still not at proper byte, advance to there. */
if
(
next_offset
/
BITS_PER_UNIT
!=
total_bytes
)
{
assemble_zeros
(
next_offset
/
BITS_PER_UNIT
-
total_bytes
);
total_bytes
=
next_offset
/
BITS_PER_UNIT
;
}
}
if
(
!
byte_buffer_in_use
)
byte
=
0
;
/* We must split the element into pieces that fall within
separate bytes, and combine each byte with previous or
following bit-fields. */
/* next_offset is the offset n fbits from the begining of
the structure to the next bit of this element to be processed.
end_offset is the offset of the first bit past the end of
this element. */
while
(
next_offset
<
end_offset
)
{
int
this_time
;
int
shift
,
value
;
int
next_byte
=
next_offset
/
BITS_PER_UNIT
;
int
next_bit
=
next_offset
%
BITS_PER_UNIT
;
/* Advance from byte to byte
within this element when necessary. */
while
(
next_byte
!=
total_bytes
)
{
ASM_OUTPUT_BYTE
(
asm_out_file
,
byte
);
total_bytes
++
;
byte
=
0
;
}
/* Number of bits we can process at once
(all part of the same byte). */
this_time
=
MIN
(
end_offset
-
next_offset
,
BITS_PER_UNIT
-
next_bit
);
#if BYTES_BIG_ENDIAN
/* On big-endian machine, take the most significant bits
first (of the bits that are significant)
and put them into bytes from the most significant end. */
shift
=
end_offset
-
next_offset
-
this_time
;
/* Don't try to take a bunch of bits that cross
the word boundary in the INTEGER_CST. */
if
(
shift
<
HOST_BITS_PER_INT
&&
shift
+
this_time
>
HOST_BITS_PER_INT
)
{
this_time
-=
(
HOST_BITS_PER_INT
-
shift
);
shift
=
HOST_BITS_PER_INT
;
}
/* Now get the bits from the appropriate constant word. */
if
(
shift
<
HOST_BITS_PER_INT
)
{
value
=
TREE_INT_CST_LOW
(
val
);
}
else
if
(
shift
<
2
*
HOST_BITS_PER_INT
)
{
value
=
TREE_INT_CST_HIGH
(
val
);
shift
-=
HOST_BITS_PER_INT
;
}
else
abort
();
byte
|=
(((
value
>>
shift
)
&
((
1
<<
this_time
)
-
1
))
<<
(
BITS_PER_UNIT
-
this_time
-
next_bit
));
#else
/* On little-endian machines,
take first the least significant bits of the value
and pack them starting at the least significant
bits of the bytes. */
shift
=
(
next_offset
-
TREE_INT_CST_LOW
(
DECL_FIELD_BITPOS
(
field
)));
/* Don't try to take a bunch of bits that cross
the word boundary in the INTEGER_CST. */
if
(
shift
<
HOST_BITS_PER_INT
&&
shift
+
this_time
>
HOST_BITS_PER_INT
)
{
this_time
-=
(
HOST_BITS_PER_INT
-
shift
);
shift
=
HOST_BITS_PER_INT
;
}
/* Now get the bits from the appropriate constant word. */
if
(
shift
<
HOST_BITS_PER_INT
)
value
=
TREE_INT_CST_LOW
(
val
);
else
if
(
shift
<
2
*
HOST_BITS_PER_INT
)
{
value
=
TREE_INT_CST_HIGH
(
val
);
shift
-=
HOST_BITS_PER_INT
;
}
else
abort
();
byte
|=
((
value
>>
shift
)
&
((
1
<<
this_time
)
-
1
))
<<
next_bit
;
#endif
next_offset
+=
this_time
;
byte_buffer_in_use
=
1
;
}
}
}
if
(
byte_buffer_in_use
)
{
ASM_OUTPUT_BYTE
(
asm_out_file
,
byte
);
total_bytes
++
;
}
if
(
total_bytes
<
size
)
assemble_zeros
(
size
-
total_bytes
);
}
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