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
188538df
Commit
188538df
authored
Apr 10, 1992
by
Torbjorn Granlund
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial revision
From-SVN: r724
parent
ebf9e246
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
2399 additions
and
0 deletions
+2399
-0
gcc/config/pa/pa.c
+2399
-0
No files found.
gcc/config/pa/pa.c
0 → 100644
View file @
188538df
/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "tree.h"
#include "c-tree.h"
#include "expr.h"
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
rtx
hppa_compare_op0
,
hppa_compare_op1
;
enum
cmp_type
hppa_branch_type
;
/* Set by the FUNCTION_PROFILER macro. */
int
hp_profile_labelno
;
/* Global variables set by FUNCTION_PROLOGUE. */
/* Size of frame. Need to know this to emit return insns from
leaf procedures. */
int
apparent_fsize
;
int
actual_fsize
;
int
local_fsize
,
save_fregs
;
/* Name of where we pretend to think the frame pointer points.
Normally, this is "4", but if we are in a leaf procedure,
this is "something(30)". Will this work? */
char
*
frame_base_name
;
static
rtx
find_addr_reg
();
/* Return non-zero only if OP is a register of mode MODE,
or const0_rtx. */
int
reg_or_0_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
op
==
const0_rtx
||
register_operand
(
op
,
mode
));
}
int
call_operand_address
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
REG_P
(
op
)
||
CONSTANT_P
(
op
));
}
int
symbolic_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
switch
(
GET_CODE
(
op
))
{
case
SYMBOL_REF
:
case
LABEL_REF
:
return
1
;
case
CONST
:
op
=
XEXP
(
op
,
0
);
return
((
GET_CODE
(
XEXP
(
op
,
0
))
==
SYMBOL_REF
||
GET_CODE
(
XEXP
(
op
,
0
))
==
LABEL_REF
)
&&
GET_CODE
(
XEXP
(
op
,
1
))
==
CONST_INT
);
default
:
return
0
;
}
}
/* Return truth value of statement that OP is a symbolic memory
operand of mode MODE. */
int
symbolic_memory_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
if
(
GET_CODE
(
op
)
==
SUBREG
)
op
=
SUBREG_REG
(
op
);
if
(
GET_CODE
(
op
)
!=
MEM
)
return
0
;
op
=
XEXP
(
op
,
0
);
return
(
GET_CODE
(
op
)
==
SYMBOL_REF
||
GET_CODE
(
op
)
==
CONST
||
GET_CODE
(
op
)
==
HIGH
||
GET_CODE
(
op
)
==
LABEL_REF
);
}
/* Return 1 if the operand is either a register or a memory operand that is
not symbolic. */
int
reg_or_nonsymb_mem_operand
(
op
,
mode
)
register
rtx
op
;
enum
machine_mode
mode
;
{
if
(
register_operand
(
op
,
mode
))
return
1
;
if
(
memory_operand
(
op
,
mode
)
&&
!
symbolic_memory_operand
(
op
,
mode
))
return
1
;
return
0
;
}
int
move_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
if
(
register_operand
(
op
,
mode
))
return
1
;
if
(
op
==
CONST0_RTX
(
mode
))
return
1
;
if
(
GET_MODE
(
op
)
!=
mode
)
return
0
;
if
(
GET_CODE
(
op
)
==
SUBREG
)
op
=
SUBREG_REG
(
op
);
if
(
GET_CODE
(
op
)
!=
MEM
)
return
0
;
op
=
XEXP
(
op
,
0
);
if
(
GET_CODE
(
op
)
==
LO_SUM
)
return
(
register_operand
(
XEXP
(
op
,
0
),
Pmode
)
&&
CONSTANT_P
(
XEXP
(
op
,
1
)));
return
memory_address_p
(
mode
,
op
);
}
int
pic_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
flag_pic
&&
GET_CODE
(
op
)
==
LABEL_REF
;
}
int
short_memory_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
if
(
GET_CODE
(
op
)
==
MEM
)
{
if
(
GET_CODE
(
XEXP
(
op
,
0
))
==
REG
)
return
1
;
else
if
(
GET_CODE
(
XEXP
(
op
,
0
))
==
PLUS
)
{
rtx
op1
=
XEXP
(
XEXP
(
op
,
0
),
0
);
rtx
op2
=
XEXP
(
XEXP
(
op
,
0
),
1
);
if
(
GET_CODE
(
op1
)
==
REG
)
return
(
GET_CODE
(
op2
)
==
CONST_INT
&&
INT_5_BITS
(
op2
));
else
if
(
GET_CODE
(
op2
)
==
REG
)
return
(
GET_CODE
(
op1
)
==
CONST_INT
&&
INT_5_BITS
(
op1
));
}
}
return
0
;
}
int
register_or_short_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
if
(
register_operand
(
op
,
mode
))
return
1
;
if
(
GET_CODE
(
op
)
==
SUBREG
)
op
=
SUBREG_REG
(
op
);
return
short_memory_operand
(
op
,
mode
);
}
int
fp_reg_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
reg_renumber
&&
FP_REG_P
(
op
);
}
extern
int
current_function_uses_pic_offset_table
;
extern
rtx
force_reg
(),
validize_mem
();
/* The rtx for the global offset table which is a special form
that *is* a position independent symbolic constant. */
rtx
pic_pc_rtx
;
/* Ensure that we are not using patterns that are not OK with PIC. */
int
check_pic
(
i
)
int
i
;
{
extern
rtx
recog_operand
[];
switch
(
flag_pic
)
{
case
1
:
if
(
GET_CODE
(
recog_operand
[
i
])
==
SYMBOL_REF
||
(
GET_CODE
(
recog_operand
[
i
])
==
CONST
&&
!
rtx_equal_p
(
pic_pc_rtx
,
recog_operand
[
i
])))
abort
();
case
2
:
default
:
return
1
;
}
}
/* Return truth value of whether OP is EQ or NE. */
int
eq_or_neq
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
EQ
||
GET_CODE
(
op
)
==
NE
);
}
/* Return truth value of whether OP can be used as an operand in a
three operand arithmetic insn that accepts registers of mode MODE
or 14-bit signed integers. */
int
arith_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
INT_14_BITS
(
op
)));
}
/* Return truth value of whether OP can be used as an operand in a
three operand arithmetic insn that accepts registers of mode MODE
or 11-bit signed integers. */
int
arith11_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
INT_11_BITS
(
op
)));
}
int
arith_double_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
register_operand
(
op
,
mode
)
||
(
GET_CODE
(
op
)
==
CONST_DOUBLE
&&
GET_MODE
(
op
)
==
mode
&&
VAL_14_BITS_P
(
CONST_DOUBLE_LOW
(
op
))
&&
(
CONST_DOUBLE_HIGH
(
op
)
>=
0
==
((
CONST_DOUBLE_LOW
(
op
)
&
0x1000
)
==
0
))));
}
/* Return truth value of whether OP is a integer which fits the
range constraining immediate operands in three-address insns. */
int
int5_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
CONST_INT
&&
INT_5_BITS
(
op
));
}
int
uint5_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
CONST_INT
&&
INT_U5_BITS
(
op
));
}
int
int11_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
CONST_INT
&&
INT_11_BITS
(
op
));
}
int
arith5_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
register_operand
(
op
,
mode
)
||
int5_operand
(
op
,
mode
);
}
/* Return truth value of statement that OP is a call-clobbered register. */
int
clobbered_register
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
GET_CODE
(
op
)
==
REG
&&
call_used_regs
[
REGNO
(
op
)]);
}
/* True iff OP can be the source of a move to a general register. */
int
srcsi_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
/* Not intended for other modes than SImode. */
if
(
mode
!=
SImode
)
return
0
;
/* Accept any register or memory reference. */
if
(
nonimmediate_operand
(
op
,
mode
))
return
1
;
/* OK if ldo or ldil can be used. */
return
(
GET_CODE
(
op
)
==
CONST_INT
&&
(
INT_14_BITS
(
op
)
||
(
INTVAL
(
op
)
&
0x7ff
)
==
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. */
rtx
legitimize_pic_address
(
orig
,
mode
,
reg
)
rtx
orig
,
reg
;
enum
machine_mode
mode
;
{
rtx
pic_ref
=
orig
;
if
(
GET_CODE
(
orig
)
==
SYMBOL_REF
)
{
if
(
reg
==
0
)
abort
();
if
(
flag_pic
==
2
)
{
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
reg
,
gen_rtx
(
HIGH
,
Pmode
,
orig
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
reg
,
gen_rtx
(
LO_SUM
,
Pmode
,
reg
,
orig
)));
orig
=
reg
;
}
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
;
if
(
GET_CODE
(
XEXP
(
orig
,
0
))
==
PLUS
&&
XEXP
(
XEXP
(
orig
,
0
),
0
)
==
pic_offset_table_rtx
)
return
orig
;
if
(
reg
==
0
)
abort
();
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
)
{
if
(
SMALL_INT
(
orig
))
return
plus_constant_for_output
(
base
,
INTVAL
(
orig
));
orig
=
force_reg
(
Pmode
,
orig
);
}
pic_ref
=
gen_rtx
(
PLUS
,
Pmode
,
base
,
orig
);
/* Likewise, should we set special REG_NOTEs here? */
}
return
pic_ref
;
}
/* Set up PIC-specific rtl. This should not cause any insns
to be emitted. */
void
initialize_pic
()
{
}
/* Emit special PIC prologues and epilogues. */
void
finalize_pic
()
{
/* The table we use to reference PIC data. */
rtx
global_offset_table
;
/* Labels to get the PC in the prologue of this function. */
rtx
l1
,
l2
;
rtx
seq
;
int
orig_flag_pic
=
flag_pic
;
if
(
current_function_uses_pic_offset_table
==
0
)
return
;
if
(
!
flag_pic
)
abort
();
flag_pic
=
0
;
l1
=
gen_label_rtx
();
l2
=
gen_label_rtx
();
start_sequence
();
emit_label
(
l1
);
/* Note that we pun calls and jumps here! */
emit_jump_insn
(
gen_rtx
(
PARALLEL
,
VOIDmode
,
gen_rtvec
(
2
,
gen_rtx
(
SET
,
VOIDmode
,
pc_rtx
,
gen_rtx
(
LABEL_REF
,
VOIDmode
,
l2
)),
gen_rtx
(
SET
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
15
),
gen_rtx
(
LABEL_REF
,
VOIDmode
,
l2
)))));
emit_label
(
l2
);
/* Initialize every time through, since we can't easily
know this to be permanent. */
global_offset_table
=
gen_rtx
(
SYMBOL_REF
,
Pmode
,
"*__GLOBAL_OFFSET_TABLE_"
);
pic_pc_rtx
=
gen_rtx
(
CONST
,
Pmode
,
gen_rtx
(
MINUS
,
Pmode
,
global_offset_table
,
gen_rtx
(
CONST
,
Pmode
,
gen_rtx
(
MINUS
,
Pmode
,
gen_rtx
(
LABEL_REF
,
VOIDmode
,
l1
),
pc_rtx
))));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
pic_offset_table_rtx
,
gen_rtx
(
HIGH
,
Pmode
,
pic_pc_rtx
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
pic_offset_table_rtx
,
gen_rtx
(
LO_SUM
,
Pmode
,
pic_offset_table_rtx
,
pic_pc_rtx
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
pic_offset_table_rtx
,
gen_rtx
(
PLUS
,
SImode
,
pic_offset_table_rtx
,
gen_rtx
(
REG
,
SImode
,
15
))));
/* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */
LABEL_PRESERVE_P
(
l1
)
=
1
;
LABEL_PRESERVE_P
(
l2
)
=
1
;
flag_pic
=
orig_flag_pic
;
seq
=
gen_sequence
();
end_sequence
();
emit_insn_after
(
seq
,
get_insns
());
/* Need to emit this whether or not we obey regdecls,
since setjmp/longjmp can cause life info to screw up. */
emit_insn
(
gen_rtx
(
USE
,
VOIDmode
,
pic_offset_table_rtx
));
}
/* For the HPPA, REG and REG+CONST is cost 0
and addresses involving symbolic constants are cost 2.
PIC addresses are very expensive.
It is no coincidence that this has the same structure
as GO_IF_LEGITIMATE_ADDRESS. */
int
hppa_address_cost
(
X
)
rtx
X
;
{
if
(
GET_CODE
(
X
)
==
PLUS
)
return
1
;
else
if
(
GET_CODE
(
X
)
==
LO_SUM
)
return
1
;
else
if
(
GET_CODE
(
X
)
==
HIGH
)
return
2
;
return
4
;
}
/* Emit insns to move operands[1] into operands[0].
Return 1 if we have written out everything that needs to be done to
do the move. Otherwise, return 0 and the caller will emit the move
normally. */
int
emit_move_sequence
(
operands
,
mode
)
rtx
*
operands
;
enum
machine_mode
mode
;
{
register
rtx
operand0
=
operands
[
0
];
register
rtx
operand1
=
operands
[
1
];
/* Handle most common case first: storing into a register. */
if
(
register_operand
(
operand0
,
mode
))
{
if
(
register_operand
(
operand1
,
mode
)
||
(
GET_CODE
(
operand1
)
==
CONST_INT
&&
SMALL_INT
(
operand1
))
||
(
GET_CODE
(
operand1
)
==
HIGH
&&
!
symbolic_operand
(
XEXP
(
operand1
,
0
)))
/* Only `general_operands' can come here, so MEM is ok. */
||
GET_CODE
(
operand1
)
==
MEM
)
{
/* Run this case quickly. */
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
operand1
));
return
1
;
}
}
else
if
(
GET_CODE
(
operand0
)
==
MEM
)
{
if
(
register_operand
(
operand1
,
mode
)
||
operand1
==
const0_rtx
)
{
/* Run this case quickly. */
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
operand1
));
return
1
;
}
if
(
!
reload_in_progress
)
{
operands
[
0
]
=
validize_mem
(
operand0
);
operands
[
1
]
=
operand1
=
force_reg
(
mode
,
operand1
);
}
}
/* Simplify the source if we need to. */
#if 0
if (GET_CODE (operand1) == HIGH
&& symbolic_operand (XEXP (operand1, 0), mode)
&& !read_only_operand (XEXP (operand1, 0)))
{
rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (mode);
emit_insn (gen_rtx (SET, VOIDmode, temp, operand1));
emit_insn (gen_rtx (SET, VOIDmode,
operand0,
gen_rtx (PLUS, mode,
temp, gen_rtx (REG, mode, 27))));
return 1;
}
#endif
if
(
GET_CODE
(
operand1
)
!=
HIGH
&&
immediate_operand
(
operand1
,
mode
))
{
if
(
symbolic_operand
(
operand1
,
mode
))
{
if
(
flag_pic
)
{
rtx
temp
=
reload_in_progress
?
operand0
:
gen_reg_rtx
(
Pmode
);
operands
[
1
]
=
legitimize_pic_address
(
operand1
,
mode
,
temp
);
}
/* On the HPPA, references to data space are supposed to */
/* use dp, register 27. */
else
if
(
read_only_operand
(
operand1
))
{
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
HIGH
,
mode
,
operand1
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
LO_SUM
,
mode
,
operand0
,
operand1
)));
return
1
;
}
else
{
/* If reload_in_progress, we can't use addil and r1; we */
/* have to use the more expensive ldil sequence. */
if
(
reload_in_progress
)
{
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
HIGH
,
mode
,
operand1
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
PLUS
,
mode
,
operand0
,
gen_rtx
(
REG
,
mode
,
27
))));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
LO_SUM
,
mode
,
operand0
,
operand1
)));
}
else
{
rtx
temp1
=
gen_reg_rtx
(
mode
),
temp2
=
gen_reg_rtx
(
mode
);
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
temp1
,
gen_rtx
(
HIGH
,
mode
,
operand1
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
temp2
,
gen_rtx
(
PLUS
,
mode
,
gen_rtx
(
REG
,
mode
,
27
),
temp1
)));
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
LO_SUM
,
mode
,
temp2
,
operand1
)));
}
return
1
;
}
}
else
if
(
GET_CODE
(
operand1
)
==
CONST_INT
?
(
!
SMALL_INT
(
operand1
)
&&
(
INTVAL
(
operand1
)
&
0x7ff
)
!=
0
)
:
1
)
{
rtx
temp
=
reload_in_progress
?
operand0
:
gen_reg_rtx
(
mode
);
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
temp
,
gen_rtx
(
HIGH
,
mode
,
operand1
)));
operands
[
1
]
=
gen_rtx
(
LO_SUM
,
mode
,
temp
,
operand1
);
}
}
/* Now have insn-emit do whatever it normally does. */
return
0
;
}
/* Does operand (which is a symbolic_operand) live in text space? If
so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true.*/
int
read_only_operand
(
operand
)
rtx
operand
;
{
if
(
GET_CODE
(
operand
)
==
CONST
)
operand
=
XEXP
(
XEXP
(
operand
,
0
),
0
);
if
(
GET_CODE
(
operand
)
==
SYMBOL_REF
)
return
SYMBOL_REF_FLAG
(
operand
)
||
CONSTANT_POOL_ADDRESS_P
(
operand
);
return
1
;
}
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
static
char
*
singlemove_string
(
operands
)
rtx
*
operands
;
{
if
(
GET_CODE
(
operands
[
0
])
==
MEM
)
return
"stw %r1,%0"
;
if
(
GET_CODE
(
operands
[
1
])
==
MEM
)
return
"ldw %1,%0"
;
if
(
GET_CODE
(
operands
[
1
])
==
CONST_INT
)
if
(
INT_14_BITS
(
operands
[
1
]))
return
(
INTVAL
(
operands
[
1
])
==
0
?
"copy 0,%0"
:
"ldi %1,%0"
);
else
return
"ldil L'%1,%0
\n\t
ldo R'%1(%0),%0"
;
return
"copy %1,%0"
;
}
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
char
*
output_move_double
(
operands
)
rtx
*
operands
;
{
enum
{
REGOP
,
OFFSOP
,
MEMOP
,
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
(
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
(
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
!=
REGOP
&&
optype1
!=
REGOP
)
abort
();
/* Handle auto decrementing and incrementing loads and stores
specifically, since the structure of the function doesn't work
for them without major modification. Do it better when we learn
this port about the general inc/dec addressing of PA.
(This was written by tege. Chide him if it doesn't work.) */
if
(
optype0
==
MEMOP
)
{
rtx
addr
=
XEXP
(
operands
[
0
],
0
);
if
(
GET_CODE
(
addr
)
==
POST_INC
||
GET_CODE
(
addr
)
==
POST_DEC
||
GET_CODE
(
addr
)
==
PRE_INC
||
GET_CODE
(
addr
)
==
PRE_DEC
)
{
operands
[
0
]
=
gen_rtx
(
MEM
,
SImode
,
addr
);
return
"stw%M0 %1,%0
\n\t
stw%M0 %1,%0"
;
}
}
if
(
optype1
==
MEMOP
)
{
/* We have to output the address syntax ourselves, since print_operand
doesn't deal with the addresses we want to use. Fix this later. */
rtx
addr
=
XEXP
(
operands
[
1
],
0
);
if
(
GET_CODE
(
addr
)
==
POST_INC
||
GET_CODE
(
addr
)
==
POST_DEC
)
{
rtx
high_reg
=
gen_rtx
(
SUBREG
,
SImode
,
operands
[
0
],
0
);
operands
[
1
]
=
XEXP
(
addr
,
0
);
if
(
GET_CODE
(
operands
[
0
])
!=
REG
||
GET_CODE
(
operands
[
1
])
!=
REG
)
abort
();
if
(
!
reg_overlap_mentioned_p
(
high_reg
,
addr
))
{
/* No overlap between high target register and address
register. (We do this in an non-obious way to
save a register file writeback) */
if
(
GET_CODE
(
addr
)
==
POST_INC
)
return
"ldws,ma 8(0,%1),%0
\n\t
ldw -4(0,%1),%R0"
;
return
"ldws,ma -8(0,%1),%0
\n\t
ldw 12(0,%1),%R0"
;
}
else
{
/* This is an undefined situation. We should load into the
address register *and* update that register. Probably
we don't need to handle this at all. */
if
(
GET_CODE
(
addr
)
==
POST_INC
)
return
"ldw 4(0,%1),%R0
\n\t
ldws,ma 8(0,%1),%0"
;
return
"ldw 4(0,%1),%R0
\n\t
ldws,ma -8(0,%1),%0"
;
}
}
else
if
(
GET_CODE
(
addr
)
==
PRE_INC
||
GET_CODE
(
addr
)
==
PRE_DEC
)
{
rtx
high_reg
=
gen_rtx
(
SUBREG
,
SImode
,
operands
[
0
],
0
);
operands
[
1
]
=
XEXP
(
addr
,
0
);
if
(
GET_CODE
(
operands
[
0
])
!=
REG
||
GET_CODE
(
operands
[
1
])
!=
REG
)
abort
();
if
(
!
reg_overlap_mentioned_p
(
high_reg
,
addr
))
{
/* No overlap between high target register and address
register. (We do this in an non-obious way to
save a register file writeback) */
if
(
GET_CODE
(
addr
)
==
PRE_INC
)
return
"ldws,mb 8(0,%1),%0
\n\t
ldw 4(0,%1),%R0"
;
return
"ldws,mb -8(0,%1),%0
\n\t
ldw 4(0,%1),%R0"
;
}
else
{
/* This is an undefined situation. We should load into the
address register *and* update that register. Probably
we don't need to handle this at all. */
if
(
GET_CODE
(
addr
)
==
PRE_INC
)
return
"ldw 12(0,%1),%R0
\n\t
ldws,mb 8(0,%1),%0"
;
return
"ldw -4(0,%1),%R0
\n\t
ldws,mb -8(0,%1),%0"
;
}
}
}
/* 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.
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 the first move would clobber the source of the second one,
do them in the other order.
RMS says "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."
but it happens on the HP-PA when loading parameter registers,
so I am going to define that circumstance, and make it work
as expected. */
if
(
optype0
==
REGOP
&&
(
optype1
==
MEMOP
||
optype1
==
OFFSOP
)
&&
reg_overlap_mentioned_p
(
operands
[
0
],
XEXP
(
operands
[
1
],
0
)))
{
/* XXX THIS PROBABLY DOESN'T WORK. */
/* Do the late half first. */
if
(
addreg1
)
output_asm_insn
(
"addi 4,%0"
,
&
addreg1
);
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
if
(
addreg1
)
output_asm_insn
(
"addi -4,%0"
,
&
addreg1
);
/* Then clobber. */
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
(
"addi 4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"addi 4,%0"
,
&
addreg1
);
/* Do that word. */
output_asm_insn
(
singlemove_string
(
latehalf
),
latehalf
);
/* Undo the adds we just did. */
if
(
addreg0
)
output_asm_insn
(
"addi -4,%0"
,
&
addreg0
);
if
(
addreg1
)
output_asm_insn
(
"addi -4,%0"
,
&
addreg1
);
return
""
;
}
char
*
output_fp_move_double
(
operands
)
rtx
*
operands
;
{
if
(
FP_REG_P
(
operands
[
0
]))
{
if
(
FP_REG_P
(
operands
[
1
]))
output_asm_insn
(
"fcpy,dbl %1,%0"
,
operands
);
else
if
(
GET_CODE
(
operands
[
1
])
==
REG
)
{
rtx
xoperands
[
3
];
xoperands
[
0
]
=
operands
[
0
];
xoperands
[
1
]
=
operands
[
1
];
xoperands
[
2
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
1
])
+
1
);
output_asm_insn
(
"stw %1,-16(0,30)
\n\t
stw %2,-12(0,30)
\n\t
fldds -16(0,30),%0"
,
xoperands
);
}
else
output_asm_insn
(
"fldds%F1 %1,%0"
,
operands
);
}
else
if
(
FP_REG_P
(
operands
[
1
]))
{
if
(
GET_CODE
(
operands
[
0
])
==
REG
)
{
rtx
xoperands
[
3
];
xoperands
[
2
]
=
operands
[
1
];
xoperands
[
1
]
=
gen_rtx
(
REG
,
SImode
,
REGNO
(
operands
[
0
])
+
1
);
xoperands
[
0
]
=
operands
[
0
];
output_asm_insn
(
"fstds %2,-16(0,30)
\n\t
ldw -12(0,30),%1
\n\t
ldw -16(0,30),%0"
,
xoperands
);
}
else
output_asm_insn
(
"fstds%F0 %1,%0"
,
operands
);
}
else
abort
();
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
();
}
/* Load the address specified by OPERANDS[3] into the register
specified by OPERANDS[0].
OPERANDS[3] may be the result of a sum, hence it could either be:
(1) CONST
(2) REG
(2) REG + CONST_INT
(3) REG + REG + CONST_INT
(4) REG + REG (special case of 3).
Note that (3) is not a legitimate address.
All cases are handled here. */
void
output_load_address
(
operands
)
rtx
*
operands
;
{
rtx
base
,
offset
;
if
(
CONSTANT_P
(
operands
[
3
]))
{
output_asm_insn
(
"ldi %3,%0"
,
operands
);
return
;
}
if
(
REG_P
(
operands
[
3
]))
{
if
(
REGNO
(
operands
[
0
])
!=
REGNO
(
operands
[
3
]))
output_asm_insn
(
"copy %3,%0"
,
operands
);
return
;
}
if
(
GET_CODE
(
operands
[
3
])
!=
PLUS
)
abort
();
base
=
XEXP
(
operands
[
3
],
0
);
offset
=
XEXP
(
operands
[
3
],
1
);
if
(
GET_CODE
(
base
)
==
CONST_INT
)
{
rtx
tmp
=
base
;
base
=
offset
;
offset
=
tmp
;
}
if
(
GET_CODE
(
offset
)
!=
CONST_INT
)
{
/* Operand is (PLUS (REG) (REG)). */
base
=
operands
[
3
];
offset
=
const0_rtx
;
}
if
(
REG_P
(
base
))
{
operands
[
6
]
=
base
;
operands
[
7
]
=
offset
;
if
(
INT_14_BITS
(
offset
))
output_asm_insn
(
"ldo %7(%6),%0"
,
operands
);
else
output_asm_insn
(
"addil L'%7,%6
\n\t
ldo R'%7(1),%0"
,
operands
);
}
else
if
(
GET_CODE
(
base
)
==
PLUS
)
{
operands
[
6
]
=
XEXP
(
base
,
0
);
operands
[
7
]
=
XEXP
(
base
,
1
);
operands
[
8
]
=
offset
;
if
(
offset
==
const0_rtx
)
output_asm_insn
(
"add %6,%7,%0"
,
operands
);
else
if
(
INT_14_BITS
(
offset
))
output_asm_insn
(
"add %6,%7,%0
\n\t
addi %8,%0"
,
operands
);
else
output_asm_insn
(
"addil L'%8,%6
\n\t
ldo R'%8(1),%0
\n\t
add %0,%7,%0"
,
operands
);
}
else
abort
();
}
/* Emit code to perform a block move.
Restriction: If the length argument is non-constant, alignment
must be 4.
OPERANDS[0] is the destination pointer as a REG, clobbered.
OPERANDS[1] is the source pointer as a REG, clobbered.
if SIZE_IS_CONSTANT
OPERANDS[2] is a register for temporary storage.
OPERANDS[4] is the size as a CONST_INT
else
OPERANDS[2] is a REG which will contain the size, clobbered.
OPERANDS[3] is a register for temporary storage.
OPERANDS[5] is the alignment safe to use, as a CONST_INT. */
char
*
output_block_move
(
operands
,
size_is_constant
)
rtx
*
operands
;
int
size_is_constant
;
{
int
align
=
INTVAL
(
operands
[
5
]);
unsigned
long
n_bytes
;
/* We can't move more than four bytes at a time because the PA
has no longer integer move insns. (Could use fp mem ops?) */
if
(
align
>
4
)
align
=
4
;
if
(
size_is_constant
)
{
unsigned
long
n_items
;
unsigned
long
offset
;
rtx
temp
;
n_bytes
=
INTVAL
(
operands
[
4
]);
if
(
n_bytes
==
0
)
return
""
;
if
(
align
>=
4
)
{
/* Don't unroll too large blocks. */
if
(
n_bytes
>
64
)
goto
copy_with_loop
;
/* Read and store using two registers, and hide latency
by defering the stores until three instructions after
the corresponding load. The last load insn will read
the entire word were the last bytes are, possibly past
the end of the source block, but since loads are aligned,
this is harmless. */
output_asm_insn
(
"ldws,ma 4(0,%1),%2"
,
operands
);
for
(
offset
=
4
;
offset
<
n_bytes
;
offset
+=
4
)
{
output_asm_insn
(
"ldws,ma 4(0,%1),%3"
,
operands
);
output_asm_insn
(
"stws,ma %2,4(0,%0)"
,
operands
);
temp
=
operands
[
2
];
operands
[
2
]
=
operands
[
3
];
operands
[
3
]
=
temp
;
}
if
(
n_bytes
%
4
==
0
)
/* Store the last word. */
output_asm_insn
(
"stw %2,0(0,%0)"
,
operands
);
else
{
/* Store the last, partial word. */
operands
[
4
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
n_bytes
%
4
);
output_asm_insn
(
"stbys,e %2,%4(0,%0)"
,
operands
);
}
return
""
;
}
if
(
align
>=
2
&&
n_bytes
>=
2
)
{
output_asm_insn
(
"ldhs,ma 2(0,%1),%2"
,
operands
);
for
(
offset
=
2
;
offset
+
2
<=
n_bytes
;
offset
+=
2
)
{
output_asm_insn
(
"ldhs,ma 2(0,%1),%3"
,
operands
);
output_asm_insn
(
"sths,ma %2,2(0,%0)"
,
operands
);
temp
=
operands
[
2
];
operands
[
2
]
=
operands
[
3
];
operands
[
3
]
=
temp
;
}
if
(
n_bytes
%
2
!=
0
)
output_asm_insn
(
"ldb 0(0,%1),%3"
,
operands
);
output_asm_insn
(
"sths,ma %2,2(0,%0)"
,
operands
);
if
(
n_bytes
%
2
!=
0
)
output_asm_insn
(
"stb %3,0(0,%0)"
,
operands
);
return
""
;
}
output_asm_insn
(
"ldbs,ma 1(0,%1),%2"
,
operands
);
for
(
offset
=
1
;
offset
+
1
<=
n_bytes
;
offset
+=
1
)
{
output_asm_insn
(
"ldbs,ma 1(0,%1),%3"
,
operands
);
output_asm_insn
(
"stbs,ma %2,1(0,%0)"
,
operands
);
temp
=
operands
[
2
];
operands
[
2
]
=
operands
[
3
];
operands
[
3
]
=
temp
;
}
output_asm_insn
(
"stb %2,0(0,%0)"
,
operands
);
return
""
;
}
if
(
align
!=
4
)
abort
();
copy_with_loop
:
if
(
size_is_constant
)
{
/* Size is an compile-time determined, and also not
very small (such small cases are handled above). */
operands
[
4
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
n_bytes
-
4
);
output_asm_insn
(
"ldo %4(0),%2"
,
operands
);
}
else
{
/* Decrement counter by 4, and if it becomes negative, jump past the
word copying loop. */
output_asm_insn
(
"addib,<,n -4,%2,.+16"
,
operands
);
}
/* Copying loop. Note that the first load is in the anulled delay slot
of addib. Is it OK on PA to have a load in a delay slot, i.e. is a
possible page fault stopped in time? */
output_asm_insn
(
"ldws,ma 4(0,%1),%3"
,
operands
);
output_asm_insn
(
"addib,>= -4,%2,.-4"
,
operands
);
output_asm_insn
(
"stws,ma %3,4(0,%0)"
,
operands
);
/* The counter is negative, >= -4. The remaining number of bytes are
determined by the two least significant bits. */
if
(
size_is_constant
)
{
if
(
n_bytes
%
4
!=
0
)
{
/* Read the entire word of the source block tail. */
output_asm_insn
(
"ldw 0(0,%1),%3"
,
operands
);
operands
[
4
]
=
gen_rtx
(
CONST_INT
,
VOIDmode
,
n_bytes
%
4
);
output_asm_insn
(
"stbys,e %3,%4(0,%0)"
,
operands
);
}
}
else
{
/* Add 4 to counter. If it becomes zero, we're done. */
output_asm_insn
(
"addib,=,n 4,%2,.+16"
,
operands
);
/* Read the entire word of the source block tail. (Also this
load is in an anulled delay slot.) */
output_asm_insn
(
"ldw 0(0,%1),%3"
,
operands
);
/* Make %0 point at the first byte after the destination block. */
output_asm_insn
(
"add %2,%0,%0"
,
operands
);
/* Store the leftmost bytes, up to, but not including, the address
in %0. */
output_asm_insn
(
"stbys,e %3,0(0,%0)"
,
operands
);
}
return
""
;
}
/* Output an ascii string. */
output_ascii
(
file
,
p
,
size
)
FILE
*
file
;
unsigned
char
*
p
;
int
size
;
{
int
i
;
int
chars_output
;
unsigned
char
partial_output
[
16
];
/* Max space 4 chars can occupy. */
/* The HP assembler can only take strings of 256 characters at one
time. This is a limitation on input line length, *not* the
length of the string. Sigh. Even worse, it seems that the
restriction is in number of input characters (see \xnn &
\whatever). So we have to do this very carefully. */
fprintf
(
file
,
"
\t
.STRING
\"
"
);
chars_output
=
0
;
for
(
i
=
0
;
i
<
size
;
i
+=
4
)
{
int
co
=
0
;
int
io
=
0
;
for
(
io
=
0
,
co
=
0
;
io
<
MIN
(
4
,
size
-
i
);
io
++
)
{
register
unsigned
int
c
=
p
[
i
+
io
];
if
(
c
==
'\"'
||
c
==
'\\'
)
partial_output
[
co
++
]
=
'\\'
;
if
(
c
>=
' '
&&
c
<
0177
)
partial_output
[
co
++
]
=
c
;
else
{
unsigned
int
hexd
;
partial_output
[
co
++
]
=
'\\'
;
partial_output
[
co
++
]
=
'x'
;
hexd
=
c
/
16
-
0
+
'0'
;
if
(
hexd
>
'9'
)
hexd
-=
'9'
-
'a'
+
1
;
partial_output
[
co
++
]
=
hexd
;
hexd
=
c
%
16
-
0
+
'0'
;
if
(
hexd
>
'9'
)
hexd
-=
'9'
-
'a'
+
1
;
partial_output
[
co
++
]
=
hexd
;
}
}
if
(
chars_output
+
co
>
243
)
{
fprintf
(
file
,
"
\"\n\t
.STRING
\"
"
);
chars_output
=
0
;
}
fwrite
(
partial_output
,
1
,
co
,
file
);
chars_output
+=
co
;
co
=
0
;
}
fprintf
(
file
,
"
\"\n
"
);
}
/* You may have trouble believing this, but this is the HP825 stack
layout. Wow.
Offset Contents
Variable arguments (optional; any number may be allocated)
SP-(4*(N+9)) arg word N
: :
SP-56 arg word 5
SP-52 arg word 4
Fixed arguments (must be allocated; may remain unused)
SP-48 arg word 3
SP-44 arg word 2
SP-40 arg word 1
SP-36 arg word 0
Frame Marker
SP-32 External Data Pointer (DP)
SP-28 External sr4
SP-24 External/stub RP (RP')
SP-20 Current RP
SP-16 Static Link
SP-12 Clean up
SP-8 Calling Stub RP (RP'')
SP-4 Previous SP
Top of Frame
SP-0 Stack Pointer (points to next available address)
*/
/* This function saves registers as follows. Registers marked with ' are
this function's registers (as opposed to the previous function's).
If a frame_pointer isn't needed, r4 is saved as a general register;
the space for the frame pointer is still allocated, though, to keep
things simple.
Top of Frame
SP (FP') Previous FP
SP + 4 Alignment filler (sigh)
SP + 8 Space for locals reserved here.
.
.
.
SP + n All call saved register used.
.
.
.
SP + o All call saved fp registers used.
.
.
.
SP + p (SP') points to next available address.
*/
/* Helper functions */
void
print_stw
(
file
,
r
,
disp
,
base
)
FILE
*
file
;
int
r
,
disp
,
base
;
{
if
(
VAL_14_BITS_P
(
disp
))
fprintf
(
file
,
"
\t
stw %d,%d(0,%d)
\n
"
,
r
,
disp
,
base
);
else
fprintf
(
file
,
"
\t
addil L'%d,%d
\n\t
stw %d,R'%d(0,1)
\n
"
,
disp
,
base
,
r
,
disp
);
}
void
print_ldw
(
file
,
r
,
disp
,
base
)
FILE
*
file
;
int
r
,
disp
,
base
;
{
if
(
VAL_14_BITS_P
(
disp
))
fprintf
(
file
,
"
\t
ldw %d(0,%d),%d
\n
"
,
disp
,
base
,
r
);
else
fprintf
(
file
,
"
\t
addil L'%d,%d
\n\t
ldw R'%d(0,1),%d
\n
"
,
disp
,
base
,
disp
,
r
);
}
int
compute_frame_size
(
size
,
leaf_function
)
int
size
;
int
leaf_function
;
{
extern
int
current_function_outgoing_args_size
;
int
i
;
/* 8 is space for frame pointer + filler */
local_fsize
=
actual_fsize
=
size
+
8
;
/* fp is stored in a special place. */
for
(
i
=
18
;
i
>=
5
;
i
--
)
if
(
regs_ever_live
[
i
])
actual_fsize
+=
4
;
if
(
regs_ever_live
[
3
])
actual_fsize
+=
4
;
actual_fsize
=
(
actual_fsize
+
7
)
&
~
7
;
if
(
!
TARGET_SNAKE
)
{
for
(
i
=
47
;
i
>=
44
;
i
--
)
if
(
regs_ever_live
[
i
])
{
actual_fsize
+=
8
;
save_fregs
++
;
}
}
else
{
for
(
i
=
90
;
i
>=
72
;
i
-=
2
)
if
(
regs_ever_live
[
i
]
||
regs_ever_live
[
i
+
1
])
{
actual_fsize
+=
8
;
save_fregs
++
;
}
}
return
actual_fsize
+
current_function_outgoing_args_size
;
}
void
output_function_prologue
(
file
,
size
,
leaf_function
)
FILE
*
file
;
int
size
;
int
leaf_function
;
{
extern
char
call_used_regs
[];
extern
int
frame_pointer_needed
;
int
i
,
offset
;
actual_fsize
=
compute_frame_size
(
size
,
leaf_function
)
+
32
;
if
(
TARGET_SNAKE
)
actual_fsize
=
(
actual_fsize
+
63
)
&
~
63
;
/* Let's not try to bullshit more than we need to here. */
/* This might be right a lot of the time */
fprintf
(
file
,
"
\t
.PROC
\n\t
.CALLINFO FRAME=%d"
,
actual_fsize
);
if
(
regs_ever_live
[
2
])
fprintf
(
file
,
",CALLS,SAVE_RP
\n
"
);
else
fprintf
(
file
,
",NO_CALLS
\n
"
);
fprintf
(
file
,
"
\t
.ENTRY
\n
"
);
/* Instead of taking one argument, the counter label, as most normal
mcounts do, _mcount appears to behave differently on the HPPA. It
takes the return address of the caller, the address of this
routine, and the address of the label. Also, it isn't magic, so
caller saves have to be preserved. We get around this by calling
our own gcc_mcount, which takes arguments on the stack and saves
argument registers. */
if
(
profile_flag
)
{
fprintf
(
file
,
"
\t
stw 2,-20(30)
\n\t
ldo 48(30),30
\n
\
\t
addil L'LP$%04d-$global$,27
\n\t
ldo R'LP$%04d-$global$(1),1
\n
\
\t
bl __gcc_mcount,2
\n\t
stw 1,-16(30)
\n\t
ldo -48(30),30
\n\t
ldw -20(30),2
\n
"
,
hp_profile_labelno
,
hp_profile_labelno
);
}
/* Some registers have places to go in the current stack
structure. */
#if 0
/* However, according to the hp docs, there's no need to save the
sp. */
fprintf (file, "\tstw 30,-4(30)\n");
#endif
if
(
regs_ever_live
[
2
])
fprintf
(
file
,
"
\t
stw 2,-20(0,30)
\n
"
);
/* Reserve space for local variables. */
if
(
frame_pointer_needed
)
{
if
(
VAL_14_BITS_P
(
actual_fsize
))
fprintf
(
file
,
"
\t
copy 4,1
\n\t
copy 30,4
\n\t
stwm 1,%d(0,30)
\n
"
,
actual_fsize
);
else
{
fprintf
(
file
,
"
\t
copy 4,1
\n\t
copy 30,4
\n\t
stw 1,0(0,4)
\n
"
);
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
ldo R'%d(1),30
\n
"
,
actual_fsize
,
actual_fsize
);
}
}
else
/* Used to be abort (); */
{
if
(
VAL_14_BITS_P
(
actual_fsize
))
fprintf
(
file
,
"
\t
ldo %d(30),30
\n
"
,
actual_fsize
);
else
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
ldo R'%d(1),30
\n
"
,
actual_fsize
,
actual_fsize
);
}
/* Normal register save. */
if
(
frame_pointer_needed
)
{
for
(
i
=
18
,
offset
=
local_fsize
;
i
>=
5
;
i
--
)
if
(
regs_ever_live
[
i
]
&&
!
call_used_regs
[
i
])
{
print_stw
(
file
,
i
,
offset
,
4
);
offset
+=
4
;
}
if
(
regs_ever_live
[
3
]
&&
!
call_used_regs
[
3
])
{
print_stw
(
file
,
3
,
offset
,
4
);
offset
+=
4
;
}
}
else
{
for
(
i
=
18
,
offset
=
local_fsize
-
actual_fsize
;
i
>=
5
;
i
--
)
if
(
regs_ever_live
[
i
]
&&
!
call_used_regs
[
i
])
{
print_stw
(
file
,
i
,
offset
,
30
);
offset
+=
4
;
}
if
(
regs_ever_live
[
3
]
&&
!
call_used_regs
[
3
])
{
print_stw
(
file
,
3
,
offset
,
30
);
offset
+=
4
;
}
}
/* Align pointer properly (doubleword boundary). */
offset
=
(
offset
+
7
)
&
~
7
;
/* Floating point register store. */
if
(
save_fregs
)
if
(
frame_pointer_needed
)
{
if
(
VAL_14_BITS_P
(
offset
))
fprintf
(
file
,
"
\t
ldo %d(4),1
\n
"
,
offset
);
else
fprintf
(
file
,
"
\t
addil L'%d,4
\n\t
ldo R'%d(1),1
\n
"
,
offset
,
offset
);
}
else
{
if
(
VAL_14_BITS_P
(
offset
))
fprintf
(
file
,
"
\t
ldo %d(30),1
\n
"
,
offset
);
else
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
ldo R'%d(1),1
\n
"
,
offset
,
offset
);
}
if
(
!
TARGET_SNAKE
)
{
for
(
i
=
47
;
i
>=
44
;
i
--
)
{
if
(
regs_ever_live
[
i
])
fprintf
(
file
,
"
\t
fstds,ma %s,8(0,1)
\n
"
,
reg_names
[
i
]);
}
}
else
{
for
(
i
=
90
;
i
>=
72
;
i
-=
2
)
if
(
regs_ever_live
[
i
]
||
regs_ever_live
[
i
+
1
])
{
fprintf
(
file
,
"
\t
fstds,ma %s,8(0,1)
\n
"
,
reg_names
[
i
]);
}
}
}
void
output_function_epilogue
(
file
,
size
,
leaf_function
)
FILE
*
file
;
int
size
;
int
leaf_function
;
{
extern
char
call_used_regs
[];
extern
int
frame_pointer_needed
;
int
i
,
offset
;
if
(
frame_pointer_needed
)
{
for
(
i
=
18
,
offset
=
local_fsize
;
i
>=
5
;
i
--
)
if
(
regs_ever_live
[
i
]
&&
!
call_used_regs
[
i
])
{
print_ldw
(
file
,
i
,
offset
,
4
);
offset
+=
4
;
}
if
(
regs_ever_live
[
3
]
&&
!
call_used_regs
[
3
])
{
print_ldw
(
file
,
3
,
offset
,
4
);
offset
+=
4
;
}
}
else
{
for
(
i
=
18
,
offset
=
local_fsize
-
actual_fsize
;
i
>=
5
;
i
--
)
if
(
regs_ever_live
[
i
]
&&
!
call_used_regs
[
i
])
{
print_ldw
(
file
,
i
,
offset
,
30
);
offset
+=
4
;
}
if
(
regs_ever_live
[
3
]
&&
!
call_used_regs
[
3
])
{
print_ldw
(
file
,
3
,
offset
,
30
);
offset
+=
4
;
}
}
/* Align pointer properly (doubleword boundary). */
offset
=
(
offset
+
7
)
&
~
7
;
/* Floating point register restore. */
if
(
save_fregs
)
if
(
frame_pointer_needed
)
{
if
(
VAL_14_BITS_P
(
offset
))
fprintf
(
file
,
"
\t
ldo %d(4),1
\n
"
,
offset
);
else
fprintf
(
file
,
"
\t
addil L'%d,4
\n\t
ldo R'%d(1),1
\n
"
,
offset
,
offset
);
}
else
{
if
(
VAL_14_BITS_P
(
offset
))
fprintf
(
file
,
"
\t
ldo %d(30),1
\n
"
,
offset
);
else
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
ldo R'%d(1),1
\n
"
,
offset
,
offset
);
}
if
(
!
TARGET_SNAKE
)
{
for
(
i
=
47
;
i
>=
44
;
i
--
)
{
if
(
regs_ever_live
[
i
])
fprintf
(
file
,
"
\t
fldds,ma 8(0,1),%s
\n
"
,
reg_names
[
i
]);
}
}
else
{
for
(
i
=
90
;
i
>=
72
;
i
-=
2
)
if
(
regs_ever_live
[
i
]
||
regs_ever_live
[
i
+
1
])
{
fprintf
(
file
,
"
\t
fldds,ma 8(0,1),%s
\n
"
,
reg_names
[
i
]);
}
}
/* Reset stack pointer (and possibly frame pointer). The stack */
/* pointer is initially set to fp + 8 to avoid a race condition. */
if
(
frame_pointer_needed
)
{
fprintf
(
file
,
"
\t
ldo 8(4),30
\n
"
);
if
(
regs_ever_live
[
2
])
fprintf
(
file
,
"
\t
ldw -28(0,30),2
\n
"
);
fprintf
(
file
,
"
\t
bv 0(2)
\n\t
ldwm -8(30),4
\n
"
);
}
else
if
(
actual_fsize
)
{
if
(
regs_ever_live
[
2
]
&&
VAL_14_BITS_P
(
actual_fsize
+
20
))
fprintf
(
file
,
"
\t
ldw %d(30),2
\n\t
bv 0(2)
\n\t
ldo %d(30),30
\n
"
,
-
(
actual_fsize
+
20
),
-
actual_fsize
);
else
if
(
regs_ever_live
[
2
])
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
ldw %d(1),2
\n\t
bv 0(2)
\n\t
ldo R'%d(1),30
\n
"
,
-
actual_fsize
,
-
(
actual_fsize
+
20
+
((
-
actual_fsize
)
&
~
0x7ff
)),
/* - ((actual_fsize + 20) - (actual_fsize & ~0x7ff)), */
-
actual_fsize
);
else
if
(
VAL_14_BITS_P
(
actual_fsize
))
fprintf
(
file
,
"
\t
bv 0(2)
\n\t
ldo %d(30),30
\n
"
,
-
actual_fsize
);
else
fprintf
(
file
,
"
\t
addil L'%d,30
\n\t
bv 0(2)
\n\t
ldo R'%d(1),30
\n
"
);
}
else
if
(
current_function_epilogue_delay_list
)
{
fprintf
(
file
,
"
\t
bv 0(2)
\n
"
);
final_scan_insn
(
XEXP
(
current_function_epilogue_delay_list
,
0
),
file
,
write_symbols
,
1
,
0
,
1
);
}
else
fprintf
(
file
,
"
\t
bv,n 0(2)
\n
"
);
fprintf
(
file
,
"
\t
.EXIT
\n\t
.PROCEND
\n
"
);
}
rtx
gen_compare_reg
(
code
,
x
,
y
)
enum
rtx_code
code
;
rtx
x
,
y
;
{
enum
machine_mode
mode
=
SELECT_CC_MODE
(
code
,
x
);
rtx
cc_reg
=
gen_rtx
(
REG
,
mode
,
0
);
emit_insn
(
gen_rtx
(
SET
,
VOIDmode
,
cc_reg
,
gen_rtx
(
COMPARE
,
mode
,
x
,
y
)));
return
cc_reg
;
}
/* Return nonzero if TRIAL can go into the function epilogue's
delay slot. SLOT is the slot we are trying to fill. */
int
eligible_for_epilogue_delay
(
trial
,
slot
)
rtx
trial
;
int
slot
;
{
if
(
slot
>=
1
)
return
0
;
if
(
GET_CODE
(
trial
)
!=
INSN
||
GET_CODE
(
PATTERN
(
trial
))
!=
SET
)
return
0
;
if
(
get_attr_length
(
trial
)
!=
1
)
return
0
;
return
(
leaf_function
&&
get_attr_in_branch_delay
(
trial
)
==
IN_BRANCH_DELAY_TRUE
);
}
rtx
gen_scond_fp
(
code
,
operand0
)
enum
rtx_code
code
;
rtx
operand0
;
{
return
gen_rtx
(
SET
,
VOIDmode
,
operand0
,
gen_rtx
(
code
,
CCFPmode
,
gen_rtx
(
REG
,
CCFPmode
,
0
),
const0_rtx
));
}
void
emit_bcond_fp
(
code
,
operand0
)
enum
rtx_code
code
;
rtx
operand0
;
{
emit_jump_insn
(
gen_rtx
(
SET
,
VOIDmode
,
pc_rtx
,
gen_rtx
(
IF_THEN_ELSE
,
VOIDmode
,
gen_rtx
(
code
,
VOIDmode
,
gen_rtx
(
REG
,
CCFPmode
,
0
),
const0_rtx
),
gen_rtx
(
LABEL_REF
,
VOIDmode
,
operand0
),
pc_rtx
)));
}
rtx
gen_cmp_fp
(
code
,
operand0
,
operand1
)
enum
rtx_code
code
;
rtx
operand0
,
operand1
;
{
return
gen_rtx
(
SET
,
VOIDmode
,
gen_rtx
(
REG
,
CCFPmode
,
0
),
gen_rtx
(
code
,
CCFPmode
,
operand0
,
operand1
));
}
/* 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. */
void
print_operand
(
file
,
x
,
code
)
FILE
*
file
;
rtx
x
;
int
code
;
{
switch
(
code
)
{
case
'#'
:
/* Output a 'nop' if there's nothing for the delay slot. */
if
(
dbr_sequence_length
()
==
0
)
fputs
(
"
\n\t
nop"
,
file
);
return
;
case
'*'
:
/* Output an nullification completer if there's nothing for the */
/* delay slot or nullification is requested. */
if
(
dbr_sequence_length
()
==
0
||
(
final_sequence
&&
INSN_ANNULLED_BRANCH_P
(
XVECEXP
(
final_sequence
,
0
,
0
))))
fputs
(
",n"
,
file
);
return
;
case
'R'
:
/* Print out the second register name of a register pair.
I.e., R (6) => 7. */
fputs
(
reg_names
[
REGNO
(
x
)
+
1
],
file
);
return
;
case
'r'
:
/* A register or zero. */
if
(
x
==
const0_rtx
)
{
fputs
(
"0"
,
file
);
return
;
}
else
break
;
case
'O'
:
switch
(
GET_CODE
(
x
))
{
case
PLUS
:
fprintf
(
file
,
"add%s"
,
GET_CODE
(
XEXP
(
x
,
1
))
==
CONST_INT
?
"i"
:
""
);
break
;
case
MINUS
:
fprintf
(
file
,
"sub%s"
,
GET_CODE
(
XEXP
(
x
,
0
))
==
CONST_INT
?
"i"
:
""
);
break
;
case
AND
:
fprintf
(
file
,
"and%s"
,
GET_CODE
(
XEXP
(
x
,
1
))
==
NOT
?
"cm"
:
""
);
break
;
case
IOR
:
fprintf
(
file
,
"or"
);
break
;
case
XOR
:
fprintf
(
file
,
"xor"
);
break
;
case
ASHIFT
:
fprintf
(
file
,
"sh%dadd"
,
INTVAL
(
XEXP
(
x
,
1
)));
break
;
/* Too lazy to handle bitfield conditions yet. */
default
:
printf
(
"Can't grok '%c' operator:
\n
"
,
code
);
debug_rtx
(
x
);
abort
();
}
return
;
case
'C'
:
case
'X'
:
switch
(
GET_CODE
(
x
))
{
case
EQ
:
fprintf
(
file
,
"="
);
break
;
case
NE
:
if
(
code
==
'C'
)
fprintf
(
file
,
"<>"
);
else
fprintf
(
file
,
"!="
);
break
;
case
GT
:
fprintf
(
file
,
">"
);
break
;
case
GE
:
fprintf
(
file
,
">="
);
break
;
case
GEU
:
fprintf
(
file
,
">>="
);
break
;
case
GTU
:
fprintf
(
file
,
">>"
);
break
;
case
LT
:
fprintf
(
file
,
"<"
);
break
;
case
LE
:
fprintf
(
file
,
"<="
);
break
;
case
LEU
:
fprintf
(
file
,
"<<="
);
break
;
case
LTU
:
fprintf
(
file
,
"<<"
);
break
;
default
:
printf
(
"Can't grok '%c' operator:
\n
"
,
code
);
debug_rtx
(
x
);
abort
();
}
return
;
case
'N'
:
case
'Y'
:
switch
(
GET_CODE
(
x
))
{
case
EQ
:
if
(
code
==
'N'
)
fprintf
(
file
,
"<>"
);
else
fprintf
(
file
,
"!="
);
break
;
case
NE
:
fprintf
(
file
,
"="
);
break
;
case
GT
:
fprintf
(
file
,
"<="
);
break
;
case
GE
:
fprintf
(
file
,
"<"
);
break
;
case
GEU
:
fprintf
(
file
,
"<<"
);
break
;
case
GTU
:
fprintf
(
file
,
"<<="
);
break
;
case
LT
:
fprintf
(
file
,
">="
);
break
;
case
LE
:
fprintf
(
file
,
">"
);
break
;
case
LEU
:
fprintf
(
file
,
">>"
);
break
;
case
LTU
:
fprintf
(
file
,
">>="
);
break
;
default
:
printf
(
"Can't grok '%c' operator:
\n
"
,
code
);
debug_rtx
(
x
);
abort
();
}
return
;
case
'M'
:
switch
(
GET_CODE
(
XEXP
(
x
,
0
)))
{
case
PRE_DEC
:
case
PRE_INC
:
fprintf
(
file
,
"s,mb"
);
break
;
case
POST_DEC
:
case
POST_INC
:
fprintf
(
file
,
"s,ma"
);
break
;
default
:
break
;
}
return
;
case
'F'
:
switch
(
GET_CODE
(
XEXP
(
x
,
0
)))
{
case
PRE_DEC
:
case
PRE_INC
:
fprintf
(
file
,
",mb"
);
break
;
case
POST_DEC
:
case
POST_INC
:
fprintf
(
file
,
",ma"
);
break
;
default
:
break
;
}
return
;
case
'G'
:
output_global_address
(
file
,
x
);
return
;
case
0
:
/* Don't do anything special */
break
;
default
:
abort
();
}
if
(
GET_CODE
(
x
)
==
REG
)
fprintf
(
file
,
"%s"
,
reg_names
[
REGNO
(
x
)]);
else
if
(
GET_CODE
(
x
)
==
MEM
)
{
int
size
=
GET_MODE_SIZE
(
GET_MODE
(
x
));
rtx
base
=
XEXP
(
XEXP
(
x
,
0
),
0
);
switch
(
GET_CODE
(
XEXP
(
x
,
0
)))
{
case
PRE_DEC
:
case
POST_DEC
:
fprintf
(
file
,
"-%d(0,%s)"
,
size
,
reg_names
[
REGNO
(
base
)]);
break
;
case
PRE_INC
:
case
POST_INC
:
fprintf
(
file
,
"%d(0,%s)"
,
size
,
reg_names
[
REGNO
(
base
)]);
break
;
default:
output_address
(
XEXP
(
x
,
0
));
break
;
}
}
else
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE
(
x
)
==
SFmode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
union
{
float
f
;
int
i
;
}
u1
;
u
.
i
[
0
]
=
XINT
(
x
,
0
);
u
.
i
[
1
]
=
XINT
(
x
,
1
);
u1
.
f
=
u
.
d
;
if
(
code
==
'f'
)
fprintf
(
file
,
"0r%.9g"
,
u1
.
f
);
else
fprintf
(
file
,
"0x%x"
,
u1
.
i
);
}
else
if
(
GET_CODE
(
x
)
==
CONST_DOUBLE
&&
GET_MODE
(
x
)
!=
DImode
)
{
union
{
double
d
;
int
i
[
2
];
}
u
;
u
.
i
[
0
]
=
XINT
(
x
,
0
);
u
.
i
[
1
]
=
XINT
(
x
,
1
);
fprintf
(
file
,
"0r%.20g"
,
u
.
d
);
}
else
output_addr_const
(
file
,
x
);
}
/* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF. */
void
output_global_address
(
file
,
x
)
FILE
*
file
;
rtx
x
;
{
if
(
GET_CODE
(
x
)
==
SYMBOL_REF
&&
read_only_operand
(
x
))
assemble_name
(
file
,
XSTR
(
x
,
0
));
else
if
(
GET_CODE
(
x
)
==
SYMBOL_REF
)
{
assemble_name
(
file
,
XSTR
(
x
,
0
));
fprintf
(
file
,
"-$global$"
);
}
else
if
(
GET_CODE
(
x
)
==
CONST
)
{
char
*
sep
=
""
;
int
offset
=
0
;
/* assembler wants -$global$ at end */
rtx
base
;
if
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
SYMBOL_REF
)
{
base
=
XEXP
(
XEXP
(
x
,
0
),
0
);
output_addr_const
(
file
,
base
);
}
else
if
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
CONST_INT
)
offset
=
INTVAL
(
XEXP
(
XEXP
(
x
,
0
),
0
));
else
abort
();
if
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
1
))
==
SYMBOL_REF
)
{
base
=
XEXP
(
XEXP
(
x
,
0
),
1
);
output_addr_const
(
file
,
base
);
}
else
if
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
1
))
==
CONST_INT
)
offset
=
INTVAL
(
XEXP
(
XEXP
(
x
,
0
),
1
));
else
abort
();
if
(
GET_CODE
(
XEXP
(
x
,
0
))
==
PLUS
)
{
if
(
offset
<
0
)
{
offset
=
-
offset
;
sep
=
"-"
;
}
else
sep
=
"+"
;
}
else
if
(
GET_CODE
(
XEXP
(
x
,
0
))
==
MINUS
&&
(
GET_CODE
(
XEXP
(
XEXP
(
x
,
0
),
0
))
==
SYMBOL_REF
))
sep
=
"-"
;
else
abort
();
if
(
!
read_only_operand
(
base
))
fprintf
(
file
,
"-$global$"
);
fprintf
(
file
,
"%s"
,
sep
);
if
(
offset
)
fprintf
(
file
,
"%d"
,
offset
);
}
else
output_addr_const
(
file
,
x
);
}
/* MEM rtls here are never SYMBOL_REFs (I think), so fldws is safe. */
char
*
output_floatsisf2
(
operands
)
rtx
*
operands
;
{
if
(
GET_CODE
(
operands
[
1
])
==
MEM
)
return
"fldws %1,%0
\n\t
fcnvxf,sgl,sgl %0,%0"
;
else
if
(
FP_REG_P
(
operands
[
1
]))
return
"fcnvxf,sgl,sgl %1,%0"
;
return
"stwm %r1,4(0,30)
\n\t
fldws,mb -4(0,30),%0
\n\t
fcnvxf,sgl,sgl %0,%0"
;
}
char
*
output_floatsidf2
(
operands
)
rtx
*
operands
;
{
if
(
GET_CODE
(
operands
[
1
])
==
MEM
)
return
"fldws %1,%0
\n\t
fcnvxf,sgl,dbl %0,%0"
;
else
if
(
FP_REG_P
(
operands
[
1
]))
return
"fcnvxf,sgl,dbl %1,%0"
;
return
"stwm %r1,4(0,30)
\n\t
fldws,mb -4(0,30),%0
\n\t
fcnvxf,sgl,dbl %0,%0"
;
}
enum
rtx_code
reverse_relop
(
code
)
enum
rtx_code
code
;
{
switch
(
code
)
{
case
GT
:
return
LT
;
case
LT
:
return
GT
;
case
GE
:
return
LE
;
case
LE
:
return
GE
;
case
LTU
:
return
GTU
;
case
GTU
:
return
LTU
;
case
GEU
:
return
LEU
;
case
LEU
:
return
GEU
;
default
:
abort
();
}
}
/* HP's millicode routines mean something special to the assembler.
Keep track of which ones we have used. */
enum
millicodes
{
remI
,
remU
,
divI
,
divU
,
mulI
,
mulU
,
end1000
};
static
char
imported
[(
int
)
end1000
];
static
char
*
milli_names
[]
=
{
"remI"
,
"remU"
,
"divI"
,
"divU"
,
"mulI"
,
"mulU"
};
static
char
import_string
[]
=
".IMPORT $$....,MILLICODE"
;
#define MILLI_START 10
static
int
import_milli
(
code
)
enum
millicodes
code
;
{
char
str
[
sizeof
(
import_string
)];
if
(
!
imported
[(
int
)
code
])
{
imported
[(
int
)
code
]
=
1
;
strcpy
(
str
,
import_string
);
strncpy
(
str
+
MILLI_START
,
milli_names
[(
int
)
code
],
4
);
output_asm_insn
(
str
,
0
);
}
}
/* The register constraints have put the operands and return value in
the proper registers. */
char
*
output_mul_insn
(
unsignedp
)
int
unsignedp
;
{
if
(
unsignedp
)
{
import_milli
(
mulU
);
return
"bl $$mulU,31
\n\t
nop"
;
}
else
{
import_milli
(
mulI
);
return
"bl $$mulI,31
\n\t
nop"
;
}
}
/* If operands isn't NULL, then it's a CONST_INT with which we can do
something */
/* Emit the rtl for doing a division by a constant. */
/* Do magic division millicodes exist for this value? */
static
int
magic_milli
[]
=
{
0
,
0
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
0
,
1
,
1
};
/* We'll use an array to keep track of the magic millicodes and
whether or not we've used them already. [n][0] is signed, [n][1] is
unsigned. */
static
int
div_milli
[
16
][
2
];
int
div_operand
(
op
,
mode
)
rtx
op
;
enum
machine_mode
mode
;
{
return
(
mode
==
SImode
&&
((
GET_CODE
(
op
)
==
REG
&&
REGNO
(
op
)
==
25
)
||
(
GET_CODE
(
op
)
==
CONST_INT
&&
INTVAL
(
op
)
>
0
&&
INTVAL
(
op
)
<
16
&&
magic_milli
[
INTVAL
(
op
)])));
}
int
emit_hpdiv_const
(
operands
,
unsignedp
)
rtx
*
operands
;
int
unsignedp
;
{
if
(
GET_CODE
(
operands
[
2
])
==
CONST_INT
&&
INTVAL
(
operands
[
2
])
>
0
&&
INTVAL
(
operands
[
2
])
<
16
&&
magic_milli
[
INTVAL
(
operands
[
2
])])
{
emit_move_insn
(
gen_rtx
(
REG
,
SImode
,
26
),
operands
[
1
]);
emit
(
gen_rtx
(
PARALLEL
,
VOIDmode
,
gen_rtvec
(
5
,
gen_rtx
(
SET
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
29
),
gen_rtx
(
unsignedp
?
UDIV
:
DIV
,
SImode
,
gen_rtx
(
REG
,
SImode
,
26
),
operands
[
2
])),
gen_rtx
(
CLOBBER
,
VOIDmode
,
gen_rtx
(
SCRATCH
,
SImode
,
0
)),
gen_rtx
(
CLOBBER
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
26
)),
gen_rtx
(
CLOBBER
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
25
)),
gen_rtx
(
CLOBBER
,
VOIDmode
,
gen_rtx
(
REG
,
SImode
,
31
)))));
emit_move_insn
(
operands
[
0
],
gen_rtx
(
REG
,
SImode
,
29
));
return
1
;
}
return
0
;
}
char
*
output_div_insn
(
operands
,
unsignedp
)
rtx
*
operands
;
int
unsignedp
;
{
int
divisor
;
/* If the divisor is a constant, try to use one of the special
opcodes .*/
if
(
GET_CODE
(
operands
[
0
])
==
CONST_INT
)
{
divisor
=
INTVAL
(
operands
[
0
]);
if
(
!
div_milli
[
divisor
][
unsignedp
])
{
if
(
unsignedp
)
output_asm_insn
(
".IMPORT $$divU_%0,MILLICODE"
,
operands
);
else
output_asm_insn
(
".IMPORT $$divI_%0,MILLICODE"
,
operands
);
div_milli
[
divisor
][
unsignedp
]
=
1
;
}
if
(
unsignedp
)
return
"bl $$divU_%0,31%#"
;
return
"bl $$divI_%0,31%#"
;
}
/* Divisor isn't a special constant. */
else
{
if
(
unsignedp
)
{
import_milli
(
divU
);
return
"bl $$divU,31%#"
;
}
else
{
import_milli
(
divI
);
return
"bl $$divI,31%#"
;
}
}
}
/* Output a $$rem millicode to do mod. */
char
*
output_mod_insn
(
unsignedp
)
int
unsignedp
;
{
if
(
unsignedp
)
{
import_milli
(
remU
);
return
"bl $$remU,31%#"
;
}
else
{
import_milli
(
remI
);
return
"bl $$remI,31%#"
;
}
}
void
output_arg_descriptor
(
insn
)
rtx
insn
;
{
char
*
arg_regs
[
4
];
enum
machine_mode
arg_mode
;
rtx
prev_insn
;
int
i
,
output_flag
=
0
;
int
regno
;
for
(
i
=
0
;
i
<
4
;
i
++
)
arg_regs
[
i
]
=
0
;
for
(
prev_insn
=
PREV_INSN
(
insn
);
GET_CODE
(
prev_insn
)
==
INSN
;
prev_insn
=
PREV_INSN
(
prev_insn
))
{
if
(
!
(
GET_CODE
(
PATTERN
(
prev_insn
))
==
USE
&&
GET_CODE
(
XEXP
(
PATTERN
(
prev_insn
),
0
))
==
REG
&&
FUNCTION_ARG_REGNO_P
(
REGNO
(
XEXP
(
PATTERN
(
prev_insn
),
0
)))))
break
;
arg_mode
=
GET_MODE
(
XEXP
(
PATTERN
(
prev_insn
),
0
));
regno
=
REGNO
(
XEXP
(
PATTERN
(
prev_insn
),
0
));
if
(
regno
>=
23
&&
regno
<=
26
)
arg_regs
[
26
-
regno
]
=
"GR"
;
else
if
(
!
TARGET_SNAKE
)
/* fp args */
{
if
(
arg_mode
==
SFmode
)
arg_regs
[
regno
-
36
]
=
"FR"
;
else
{
#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED
arg_regs
[
regno
-
37
]
=
"FR"
;
arg_regs
[
regno
-
36
]
=
"FU"
;
#else
arg_regs
[
regno
-
37
]
=
"FU"
;
arg_regs
[
regno
-
36
]
=
"FR"
;
#endif
}
}
else
{
if
(
arg_mode
==
SFmode
)
arg_regs
[(
regno
-
56
)
/
2
]
=
"FR"
;
else
{
#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED
arg_regs
[(
regno
-
58
)
/
2
]
=
"FR"
;
arg_regs
[(
regno
-
58
)
/
2
+
1
]
=
"FU"
;
#else
arg_regs
[(
regno
-
58
)
/
2
]
=
"FU"
;
arg_regs
[(
regno
-
58
)
/
2
+
1
]
=
"FR"
;
#endif
}
}
}
fputs
(
"
\t
.CALL "
,
asm_out_file
);
for
(
i
=
0
;
i
<
4
;
i
++
)
{
if
(
arg_regs
[
i
])
{
if
(
output_flag
++
)
fputc
(
','
,
asm_out_file
);
fprintf
(
asm_out_file
,
"ARGW%d=%s"
,
i
,
arg_regs
[
i
]);
}
}
fputc
(
'\n'
,
asm_out_file
);
}
/* Memory loads/stores to/from fp registers may need a scratch
register in which to reload the address. */
enum
reg_class
secondary_reload_class
(
class
,
mode
,
in
)
enum
reg_class
class
;
enum
machine_mode
mode
;
rtx
in
;
{
int
regno
=
true_regnum
(
in
);
if
(
regno
>=
FIRST_PSEUDO_REGISTER
)
regno
=
-
1
;
if
(
class
==
FP_REGS
||
class
==
SNAKE_FP_REGS
||
class
==
HI_SNAKE_FP_REGS
)
{
if
(
regno
==
-
1
||
!
REGNO_OK_FOR_FP_P
(
regno
))
return
GENERAL_REGS
;
}
return
NO_REGS
;
}
enum
direction
function_arg_padding
(
mode
,
type
)
enum
machine_mode
mode
;
tree
type
;
{
int
size
;
if
(
mode
==
BLKmode
)
{
if
(
type
&&
TREE_CODE
(
TYPE_SIZE
(
type
))
==
INTEGER_CST
)
size
=
int_size_in_bytes
(
type
)
*
BITS_PER_UNIT
;
else
return
upward
;
/* Don't know if this is right, but */
/* same as old definition. */
}
else
size
=
GET_MODE_BITSIZE
(
mode
);
if
(
size
<
PARM_BOUNDARY
)
return
downward
;
else
if
(
size
%
PARM_BOUNDARY
)
return
upward
;
else
return
none
;
}
int
use_milli_regs
(
insn
)
rtx
insn
;
{
return
(
reg_mentioned_p
(
gen_rtx
(
REG
,
SImode
,
1
),
insn
)
||
reg_mentioned_p
(
gen_rtx
(
REG
,
SImode
,
25
),
insn
)
||
reg_mentioned_p
(
gen_rtx
(
REG
,
SImode
,
26
),
insn
)
||
reg_mentioned_p
(
gen_rtx
(
REG
,
SImode
,
29
),
insn
)
||
reg_mentioned_p
(
gen_rtx
(
REG
,
SImode
,
31
),
insn
));
}
/* Do what is necessary for `va_start'. The argument is ignored;
We look at the current function to determine if stdargs or varargs
is used and fill in an initial va_list. A pointer to this constructor
is returned. */
struct
rtx_def
*
hppa_builtin_saveregs
(
arglist
)
tree
arglist
;
{
rtx
block
,
float_addr
,
offset
,
float_mem
;
tree
fntype
=
TREE_TYPE
(
current_function_decl
);
int
argadj
=
((
!
(
TYPE_ARG_TYPES
(
fntype
)
!=
0
&&
(
TREE_VALUE
(
tree_last
(
TYPE_ARG_TYPES
(
fntype
)))
!=
void_type_node
)))
?
UNITS_PER_WORD
:
0
);
if
(
argadj
)
offset
=
plus_constant
(
current_function_arg_offset_rtx
,
argadj
);
else
offset
=
current_function_arg_offset_rtx
;
/* Allocate the va_list structure. */
block
=
assign_stack_local
(
BLKmode
,
4
*
UNITS_PER_WORD
,
BITS_PER_UNIT
);
RTX_UNCHANGING_P
(
block
)
=
1
;
RTX_UNCHANGING_P
(
XEXP
(
block
,
0
))
=
1
;
/*
* Store a pointer to where arguments should begin on the stack in
* __va_stack_start.
*/
emit_move_insn
(
change_address
(
block
,
Pmode
,
XEXP
(
block
,
0
)),
copy_to_reg
(
plus_constant
(
current_function_internal_arg_pointer
,
-
16
)));
/* Store where to start getting args from in the __va_int member. */
emit_move_insn
(
change_address
(
block
,
Pmode
,
plus_constant
(
XEXP
(
block
,
0
),
UNITS_PER_WORD
)),
copy_to_reg
(
expand_binop
(
Pmode
,
add_optab
,
current_function_internal_arg_pointer
,
offset
,
0
,
0
,
OPTAB_LIB_WIDEN
)));
/* Store general registers on the stack. */
move_block_from_reg
(
23
,
gen_rtx
(
MEM
,
BLKmode
,
plus_constant
(
current_function_internal_arg_pointer
,
-
16
)),
4
);
/*
* Allocate space for the float args, and store it in the
* __va_float member.
*/
float_addr
=
copy_to_reg
(
XEXP
(
float_mem
=
assign_stack_local
(
BLKmode
,
4
*
UNITS_PER_WORD
,
-
1
),
0
));
MEM_IN_STRUCT_P
(
float_mem
)
=
1
;
RTX_UNCHANGING_P
(
float_mem
)
=
1
;
RTX_UNCHANGING_P
(
XEXP
(
float_mem
,
0
))
=
1
;
emit_move_insn
(
change_address
(
block
,
Pmode
,
plus_constant
(
XEXP
(
block
,
0
),
2
*
UNITS_PER_WORD
)),
copy_to_reg
(
expand_binop
(
Pmode
,
add_optab
,
float_addr
,
plus_constant
(
offset
,
4
*
UNITS_PER_WORD
),
0
,
0
,
OPTAB_LIB_WIDEN
)));
/* Store fp registers. */
emit_move_insn
(
gen_rtx
(
MEM
,
SFmode
,
float_addr
),
gen_rtx
(
REG
,
SFmode
,
TARGET_SNAKE
?
60
:
39
));
emit_move_insn
(
gen_rtx
(
MEM
,
SFmode
,
gen_rtx
(
PLUS
,
Pmode
,
float_addr
,
gen_rtx
(
CONST_INT
,
Pmode
,
4
))),
gen_rtx
(
REG
,
SFmode
,
TARGET_SNAKE
?
58
:
38
));
emit_move_insn
(
gen_rtx
(
MEM
,
SFmode
,
gen_rtx
(
PLUS
,
Pmode
,
float_addr
,
gen_rtx
(
CONST_INT
,
Pmode
,
8
))),
gen_rtx
(
REG
,
SFmode
,
TARGET_SNAKE
?
56
:
37
));
emit_move_insn
(
gen_rtx
(
MEM
,
SFmode
,
gen_rtx
(
PLUS
,
Pmode
,
float_addr
,
gen_rtx
(
CONST_INT
,
Pmode
,
12
))),
gen_rtx
(
REG
,
SFmode
,
TARGET_SNAKE
?
54
:
36
));
/*
* Allocate space for the double args, and store it in the
* __va_double member.
*/
float_addr
=
copy_to_reg
(
XEXP
(
float_mem
=
assign_stack_local
(
BLKmode
,
4
*
UNITS_PER_WORD
,
-
1
),
0
));
MEM_IN_STRUCT_P
(
float_mem
)
=
1
;
RTX_UNCHANGING_P
(
float_mem
)
=
1
;
RTX_UNCHANGING_P
(
XEXP
(
float_mem
,
0
))
=
1
;
emit_move_insn
(
change_address
(
block
,
Pmode
,
plus_constant
(
XEXP
(
block
,
0
),
3
*
UNITS_PER_WORD
)),
copy_to_reg
(
expand_binop
(
Pmode
,
add_optab
,
float_addr
,
plus_constant
(
offset
,
4
*
UNITS_PER_WORD
),
0
,
0
,
OPTAB_LIB_WIDEN
)));
/* Store fp registers as doubles. */
emit_move_insn
(
gen_rtx
(
MEM
,
DFmode
,
float_addr
),
(
gen_rtx
(
REG
,
DFmode
,
TARGET_SNAKE
?
60
:
39
)));
emit_move_insn
(
gen_rtx
(
MEM
,
DFmode
,
gen_rtx
(
PLUS
,
Pmode
,
float_addr
,
gen_rtx
(
CONST_INT
,
Pmode
,
8
))),
gen_rtx
(
REG
,
DFmode
,
TARGET_SNAKE
?
56
:
37
));
return
copy_to_reg
(
XEXP
(
block
,
0
));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment