#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "except.h"
#include "function.h"
#include "recog.h"
#include "expr.h"
#include "reload.h"
#include "toplev.h"
#include "basic-block.h"
#include "integrate.h"
#include "ggc.h"
#include "target.h"
#include "target-def.h"
#include "debug.h"
static bool s390_assemble_integer PARAMS ((rtx, unsigned int, int));
static int s390_adjust_cost PARAMS ((rtx, rtx, rtx, int));
static int s390_adjust_priority PARAMS ((rtx, int));
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
#undef TARGET_ASM_ALIGNED_DI_OP
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER s390_assemble_integer
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE s390_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE s390_function_epilogue
#undef TARGET_ASM_OPEN_PAREN
#define TARGET_ASM_OPEN_PAREN ""
#undef TARGET_ASM_CLOSE_PAREN
#define TARGET_ASM_CLOSE_PAREN ""
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST s390_adjust_cost
#undef TARGET_SCHED_ADJUST_PRIORITY
#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
struct gcc_target targetm = TARGET_INITIALIZER;
extern int reload_completed;
static int s390_sr_alias_set = 0;
int s390_function_count = 0;
rtx s390_compare_op0, s390_compare_op1;
struct s390_address
{
rtx base;
rtx indx;
rtx disp;
int pointer;
};
struct s390_frame
{
int frame_pointer_p;
int return_reg_saved_p;
int save_fprs_p;
int first_save_gpr;
int first_restore_gpr;
int last_save_gpr;
int arg_frame_offset;
HOST_WIDE_INT frame_size;
};
static int s390_match_ccmode_set PARAMS ((rtx, enum machine_mode));
static int s390_branch_condition_mask PARAMS ((rtx));
static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int));
static int check_mode PARAMS ((rtx, enum machine_mode *));
static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
static int s390_decompose_address PARAMS ((rtx, struct s390_address *, int));
static int reg_used_in_mem_p PARAMS ((int, rtx));
static int addr_generation_dependency_p PARAMS ((rtx, rtx));
static void s390_split_branches PARAMS ((void));
static void s390_chunkify_pool PARAMS ((void));
static int save_fprs_p PARAMS ((void));
static int find_unused_clobbered_reg PARAMS ((void));
static void s390_frame_info PARAMS ((struct s390_frame *));
static rtx save_fpr PARAMS ((rtx, int, int));
static rtx restore_fpr PARAMS ((rtx, int, int));
static int s390_function_arg_size PARAMS ((enum machine_mode, tree));
static int
s390_match_ccmode_set (set, req_mode)
rtx set;
enum machine_mode req_mode;
{
enum machine_mode set_mode;
if (GET_CODE (set) != SET)
abort ();
if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
return 1;
set_mode = GET_MODE (SET_DEST (set));
switch (set_mode)
{
case CCSmode:
if (req_mode != CCSmode)
return 0;
break;
case CCUmode:
if (req_mode != CCUmode)
return 0;
break;
case CCLmode:
if (req_mode != CCLmode)
return 0;
break;
case CCZmode:
if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode)
return 0;
break;
default:
abort ();
}
return (GET_MODE (SET_SRC (set)) == set_mode);
}
int
s390_match_ccmode (insn, req_mode)
rtx insn;
enum machine_mode req_mode;
{
int i;
if (GET_CODE (PATTERN (insn)) == SET)
return s390_match_ccmode_set (PATTERN (insn), req_mode);
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
rtx set = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (set) == SET)
if (!s390_match_ccmode_set (set, req_mode))
return 0;
}
return 1;
}
enum machine_mode
s390_select_ccmode (code, op0, op1)
enum rtx_code code;
rtx op0;
rtx op1;
{
switch (code)
{
case EQ:
case NE:
if (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|| GET_CODE (op1) == NEG)
return CCLmode;
return CCZmode;
case LE:
case LT:
case GE:
case GT:
case UNORDERED:
case ORDERED:
case UNEQ:
case UNLE:
case UNLT:
case UNGE:
case UNGT:
case LTGT:
return CCSmode;
case LEU:
case LTU:
case GEU:
case GTU:
return CCUmode;
default:
abort ();
}
}
static int
s390_branch_condition_mask (code)
rtx code;
{
const int CC0 = 1 << 3;
const int CC1 = 1 << 2;
const int CC2 = 1 << 1;
const int CC3 = 1 << 0;
if (GET_CODE (XEXP (code, 0)) != REG
|| REGNO (XEXP (code, 0)) != CC_REGNUM
|| XEXP (code, 1) != const0_rtx)
abort ();
switch (GET_MODE (XEXP (code, 0)))
{
case CCZmode:
switch (GET_CODE (code))
{
case EQ: return CC0;
case NE: return CC1 | CC2 | CC3;
default:
abort ();
}
break;
case CCLmode:
switch (GET_CODE (code))
{
case EQ: return CC0 | CC2;
case NE: return CC1 | CC3;
case UNORDERED: return CC2 | CC3;
case ORDERED: return CC0 | CC1;
default:
abort ();
}
break;
case CCUmode:
switch (GET_CODE (code))
{
case EQ: return CC0;
case NE: return CC1 | CC2 | CC3;
case LTU: return CC1;
case GTU: return CC2;
case LEU: return CC0 | CC1;
case GEU: return CC0 | CC2;
default:
abort ();
}
break;
case CCSmode:
switch (GET_CODE (code))
{
case EQ: return CC0;
case NE: return CC1 | CC2 | CC3;
case LT: return CC1;
case GT: return CC2;
case LE: return CC0 | CC1;
case GE: return CC0 | CC2;
case UNORDERED: return CC3;
case ORDERED: return CC0 | CC1 | CC2;
case UNEQ: return CC0 | CC3;
case UNLT: return CC1 | CC3;
case UNGT: return CC2 | CC3;
case UNLE: return CC0 | CC1 | CC3;
case UNGE: return CC0 | CC2 | CC3;
case LTGT: return CC1 | CC2;
default:
abort ();
}
default:
abort ();
}
}
static const char *
s390_branch_condition_mnemonic (code, inv)
rtx code;
int inv;
{
static const char *mnemonic[16] =
{
NULL, "o", "h", "nle",
"l", "nhe", "lh", "ne",
"e", "nlh", "he", "nl",
"le", "nh", "no", NULL
};
int mask = s390_branch_condition_mask (code);
if (inv)
mask ^= 15;
if (mask < 1 || mask > 14)
abort ();
return mnemonic[mask];
}
int
s390_single_hi (op, mode, def)
rtx op;
enum machine_mode mode;
int def;
{
if (GET_CODE (op) == CONST_INT)
{
unsigned HOST_WIDE_INT value;
int n_parts = GET_MODE_SIZE (mode) / 2;
int i, part = -1;
for (i = 0; i < n_parts; i++)
{
if (i == 0)
value = (unsigned HOST_WIDE_INT) INTVAL (op);
else
value >>= 16;
if ((value & 0xffff) != (unsigned)(def & 0xffff))
{
if (part != -1)
return -1;
else
part = i;
}
}
return part == -1 ? 0 : (n_parts - 1 - part);
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode)
{
unsigned HOST_WIDE_INT value;
int n_parts = GET_MODE_SIZE (mode) / 2;
int i, part = -1;
for (i = 0; i < n_parts; i++)
{
if (i == 0)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
else if (i == HOST_BITS_PER_WIDE_INT / 16)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
else
value >>= 16;
if ((value & 0xffff) != (unsigned)(def & 0xffff))
{
if (part != -1)
return -1;
else
part = i;
}
}
return part == -1 ? 0 : (n_parts - 1 - part);
}
return -1;
}
int
s390_extract_hi (op, mode, part)
rtx op;
enum machine_mode mode;
int part;
{
int n_parts = GET_MODE_SIZE (mode) / 2;
if (part < 0 || part >= n_parts)
abort();
else
part = n_parts - 1 - part;
if (GET_CODE (op) == CONST_INT)
{
unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
return ((value >> (16 * part)) & 0xffff);
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode)
{
unsigned HOST_WIDE_INT value;
if (part < HOST_BITS_PER_WIDE_INT / 16)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
else
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
part -= HOST_BITS_PER_WIDE_INT / 16;
return ((value >> (16 * part)) & 0xffff);
}
abort ();
}
int
s390_single_qi (op, mode, def)
rtx op;
enum machine_mode mode;
int def;
{
if (GET_CODE (op) == CONST_INT)
{
unsigned HOST_WIDE_INT value;
int n_parts = GET_MODE_SIZE (mode);
int i, part = -1;
for (i = 0; i < n_parts; i++)
{
if (i == 0)
value = (unsigned HOST_WIDE_INT) INTVAL (op);
else
value >>= 8;
if ((value & 0xff) != (unsigned)(def & 0xff))
{
if (part != -1)
return -1;
else
part = i;
}
}
return part == -1 ? 0 : (n_parts - 1 - part);
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode)
{
unsigned HOST_WIDE_INT value;
int n_parts = GET_MODE_SIZE (mode);
int i, part = -1;
for (i = 0; i < n_parts; i++)
{
if (i == 0)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
else if (i == HOST_BITS_PER_WIDE_INT / 8)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
else
value >>= 8;
if ((value & 0xff) != (unsigned)(def & 0xff))
{
if (part != -1)
return -1;
else
part = i;
}
}
return part == -1 ? 0 : (n_parts - 1 - part);
}
return -1;
}
int
s390_extract_qi (op, mode, part)
rtx op;
enum machine_mode mode;
int part;
{
int n_parts = GET_MODE_SIZE (mode);
if (part < 0 || part >= n_parts)
abort();
else
part = n_parts - 1 - part;
if (GET_CODE (op) == CONST_INT)
{
unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
return ((value >> (8 * part)) & 0xff);
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode)
{
unsigned HOST_WIDE_INT value;
if (part < HOST_BITS_PER_WIDE_INT / 8)
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
else
value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
part -= HOST_BITS_PER_WIDE_INT / 8;
return ((value >> (8 * part)) & 0xff);
}
abort ();
}
void
optimization_options (level, size)
int level ATTRIBUTE_UNUSED;
int size ATTRIBUTE_UNUSED;
{
#ifdef HAVE_decrement_and_branch_on_count
if (level >= 1)
flag_branch_on_count_reg = 1;
#endif
}
void
override_options ()
{
s390_sr_alias_set = new_alias_set ();
}
enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
ADDR_REGS, NO_REGS, ADDR_REGS
};
int
const0_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return op == CONST0_RTX (mode);
}
static int
check_mode (op, mode)
register rtx op;
enum machine_mode *mode;
{
if (*mode == VOIDmode)
*mode = GET_MODE (op);
else
{
if (GET_MODE (op) != VOIDmode && GET_MODE (op) != *mode)
return 0;
}
return 1;
}
int
larl_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (! check_mode (op, &mode))
return 0;
if (GET_CODE (op) == LABEL_REF)
return 1;
if (GET_CODE (op) == SYMBOL_REF
&& (!flag_pic || SYMBOL_REF_FLAG (op)
|| CONSTANT_POOL_ADDRESS_P (op)))
return 1;
if (GET_CODE (op) != CONST)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) == PLUS)
{
if (GET_CODE (XEXP (op, 1)) != CONST_INT
|| (INTVAL (XEXP (op, 1)) & 1) != 0)
return 0;
op = XEXP (op, 0);
}
if (GET_CODE (op) == LABEL_REF)
return 1;
if (GET_CODE (op) == SYMBOL_REF
&& (!flag_pic || SYMBOL_REF_FLAG (op)
|| CONSTANT_POOL_ADDRESS_P (op)))
return 1;
if (GET_CODE (op) == UNSPEC
&& XINT (op, 1) == 111)
return 1;
if (GET_CODE (op) == UNSPEC
&& XINT (op, 1) == 113)
return 1;
return 0;
}
int
fp_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
register enum rtx_code code = GET_CODE (op);
if (! check_mode (op, &mode))
return 0;
if (code == REG && REGNO_OK_FOR_FP_P (REGNO (op)))
return 1;
else
return 0;
}
static int
general_s_operand (op, mode, allow_immediate)
register rtx op;
enum machine_mode mode;
int allow_immediate;
{
struct s390_address addr;
if (!general_operand (op, mode))
return 0;
if (reload_completed
&& GET_CODE (op) == SUBREG
&& GET_CODE (SUBREG_REG (op)) == MEM)
op = SUBREG_REG (op);
switch (GET_CODE (op))
{
case CONST_INT:
case CONST_DOUBLE:
if (!allow_immediate || reload_completed)
break;
if (!legitimate_reload_constant_p (op))
return 1;
if (!TARGET_64BIT)
return 1;
break;
case MEM:
if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
return 1;
if (s390_decompose_address (XEXP (op, 0), &addr, FALSE)
&& !addr.indx)
return 1;
break;
default:
break;
}
return 0;
}
int
s_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return general_s_operand (op, mode, 0);
}
int
s_imm_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return general_s_operand (op, mode, 1);
}
int
bras_sym_operand (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
register enum rtx_code code = GET_CODE (op);
if (code == SYMBOL_REF)
return 1;
if (code == CONST
&& GET_CODE (XEXP (op, 0)) == UNSPEC
&& XINT (XEXP (op, 0), 1) == 113)
return 1;
return 0;
}
int
load_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int count = XVECLEN (op, 0);
unsigned int dest_regno;
rtx src_addr;
int i, off;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
return 0;
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
if (GET_CODE (src_addr) == REG)
off = 0;
else if (GET_CODE (src_addr) == PLUS
&& GET_CODE (XEXP (src_addr, 0)) == REG
&& GET_CODE (XEXP (src_addr, 1)) == CONST_INT)
{
off = INTVAL (XEXP (src_addr, 1));
src_addr = XEXP (src_addr, 0);
}
else
return 0;
if (src_addr == frame_pointer_rtx || src_addr == arg_pointer_rtx)
return 0;
for (i = 1; i < count; i++)
{
rtx elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != Pmode
|| REGNO (SET_DEST (elt)) != dest_regno + i
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != Pmode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1))
!= off + i * UNITS_PER_WORD)
return 0;
}
return 1;
}
int
store_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int count = XVECLEN (op, 0);
unsigned int src_regno;
rtx dest_addr;
int i, off;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
return 0;
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
if (GET_CODE (dest_addr) == REG)
off = 0;
else if (GET_CODE (dest_addr) == PLUS
&& GET_CODE (XEXP (dest_addr, 0)) == REG
&& GET_CODE (XEXP (dest_addr, 1)) == CONST_INT)
{
off = INTVAL (XEXP (dest_addr, 1));
dest_addr = XEXP (dest_addr, 0);
}
else
return 0;
if (dest_addr == frame_pointer_rtx || dest_addr == arg_pointer_rtx)
return 0;
for (i = 1; i < count; i++)
{
rtx elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != Pmode
|| REGNO (SET_SRC (elt)) != src_regno + i
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != Pmode
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1))
!= off + i * UNITS_PER_WORD)
return 0;
}
return 1;
}
int
symbolic_reference_mentioned_p (op)
rtx op;
{
register const char *fmt;
register int i;
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (op));
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
return 1;
}
else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
return 1;
}
return 0;
}
int
legitimate_pic_operand_p (op)
register rtx op;
{
if (!SYMBOLIC_CONST (op))
return 1;
if (TARGET_64BIT)
return larl_operand (op, VOIDmode);
return 0;
}
int
legitimate_constant_p (op)
register rtx op;
{
if (!SYMBOLIC_CONST (op))
return 1;
if (flag_pic)
return 1;
if (TARGET_64BIT)
return larl_operand (op, VOIDmode);
return 0;
}
int
legitimate_reload_constant_p (op)
register rtx op;
{
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'K'))
return 1;
if (TARGET_64BIT
&& s390_single_hi (op, DImode, 0) >= 0)
return 1;
if (TARGET_64BIT
&& larl_operand (op, VOIDmode))
return 1;
if (reload_completed && !regs_ever_live[BASE_REGISTER])
abort ();
return 0;
}
enum reg_class
s390_preferred_reload_class (op, class)
rtx op;
enum reg_class class;
{
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& class != FP_REGS)
return class;
switch (GET_CODE (op))
{
case CONST_DOUBLE:
case CONST_INT:
if (!legitimate_reload_constant_p (op))
return NO_REGS;
if (TARGET_64BIT && current_function_is_leaf)
return class;
return NO_REGS;
case PLUS:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
if (reg_class_subset_p (ADDR_REGS, class))
return ADDR_REGS;
else
return NO_REGS;
default:
break;
}
return class;
}
enum reg_class
s390_secondary_input_reload_class (class, mode, in)
enum reg_class class ATTRIBUTE_UNUSED;
enum machine_mode mode;
rtx in;
{
if (s390_plus_operand (in, mode))
return ADDR_REGS;
return NO_REGS;
}
int
s390_plus_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (!check_mode (op, &mode) || mode != Pmode)
return FALSE;
if (GET_CODE (op) != PLUS)
return FALSE;
if (legitimate_la_operand_p (op))
return FALSE;
return TRUE;
}
void
s390_expand_plus_operand (target, src, scratch_in)
register rtx target;
register rtx src;
register rtx scratch_in;
{
rtx sum1, sum2, scratch;
scratch = gen_rtx_REG (Pmode, REGNO (scratch_in));
if (rtx_equal_p (scratch, target))
scratch = gen_rtx_REG (Pmode, REGNO (scratch_in) + 1);
if (GET_CODE (src) != PLUS || GET_MODE (src) != Pmode)
abort ();
sum1 = find_replacement (&XEXP (src, 0));
sum2 = find_replacement (&XEXP (src, 1));
if (rtx_equal_p (target, sum2)
|| GET_CODE (sum1) == CONST_INT)
{
rtx tem = sum2;
sum2 = sum1;
sum1 = tem;
}
if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
{
emit_move_insn (target, sum1);
sum1 = target;
}
if ((true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
&& !(GET_CODE (sum2) == CONST_INT
&& INTVAL (sum2) >= 0 && INTVAL (sum2) < 4096))
{
if (!rtx_equal_p (target, sum1))
{
emit_move_insn (target, sum2);
sum2 = target;
}
else
{
emit_move_insn (scratch, sum2);
sum2 = scratch;
}
}
src = gen_rtx_PLUS (Pmode, sum1, sum2);
src = legitimize_la_operand (src);
emit_insn (gen_rtx_SET (VOIDmode, target, src));
}
static int
s390_decompose_address (addr, out, strict)
register rtx addr;
struct s390_address *out;
int strict;
{
rtx base = NULL_RTX;
rtx indx = NULL_RTX;
rtx disp = NULL_RTX;
int pointer = FALSE;
if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
base = addr;
else if (GET_CODE (addr) == PLUS)
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
enum rtx_code code0 = GET_CODE (op0);
enum rtx_code code1 = GET_CODE (op1);
if (code0 == REG || code0 == UNSPEC)
{
if (code1 == REG || code1 == UNSPEC)
{
indx = op0;
base = op1;
}
else
{
base = op0;
disp = op1;
}
}
else if (code0 == PLUS)
{
indx = XEXP (op0, 0);
base = XEXP (op0, 1);
disp = op1;
}
else
{
return FALSE;
}
}
else
disp = addr;
if (base)
{
if (GET_CODE (base) == UNSPEC)
{
if (XVECLEN (base, 0) != 1 || XINT (base, 1) != 101)
return FALSE;
base = XVECEXP (base, 0, 0);
pointer = TRUE;
}
if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
return FALSE;
if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base))
|| (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base)))
return FALSE;
if (REGNO (base) == BASE_REGISTER
|| REGNO (base) == STACK_POINTER_REGNUM
|| REGNO (base) == FRAME_POINTER_REGNUM
|| ((reload_completed || reload_in_progress)
&& frame_pointer_needed
&& REGNO (base) == HARD_FRAME_POINTER_REGNUM)
|| (flag_pic
&& REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
pointer = TRUE;
}
if (indx)
{
if (GET_CODE (indx) == UNSPEC)
{
if (XVECLEN (indx, 0) != 1 || XINT (indx, 1) != 101)
return FALSE;
indx = XVECEXP (indx, 0, 0);
pointer = TRUE;
}
if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
return FALSE;
if ((strict && ! REG_OK_FOR_BASE_STRICT_P (indx))
|| (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (indx)))
return FALSE;
if (REGNO (indx) == BASE_REGISTER
|| REGNO (indx) == STACK_POINTER_REGNUM
|| REGNO (indx) == FRAME_POINTER_REGNUM
|| ((reload_completed || reload_in_progress)
&& frame_pointer_needed
&& REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
|| (flag_pic
&& REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
pointer = TRUE;
}
if (disp)
{
if (GET_CODE (disp) == CONST_INT)
{
if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096)
return FALSE;
}
else if (GET_CODE (disp) == CONST
&& GET_CODE (XEXP (disp, 0)) == UNSPEC
&& XINT (XEXP (disp, 0), 1) == 110)
{
if (flag_pic != 1)
return FALSE;
pointer = TRUE;
}
else
{
unsigned int offset = 0;
if (GET_CODE (disp) == CONST
&& GET_CODE (XEXP (disp, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
{
offset = INTVAL (XEXP (XEXP (disp, 0), 1));
disp = XEXP (XEXP (disp, 0), 0);
}
if (GET_CODE (disp) != SYMBOL_REF
|| !CONSTANT_POOL_ADDRESS_P (disp))
return FALSE;
if (TARGET_64BIT && flag_pic
&& SYMBOLIC_CONST (get_pool_constant (disp)))
return FALSE;
if (offset && offset >= GET_MODE_SIZE (get_pool_mode (disp)))
return FALSE;
if (base && indx)
return FALSE;
if (base)
indx = gen_rtx_REG (Pmode, BASE_REGISTER);
else
base = gen_rtx_REG (Pmode, BASE_REGISTER);
disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), 100);
disp = gen_rtx_CONST (Pmode, disp);
if (offset)
disp = plus_constant (disp, offset);
pointer = TRUE;
}
}
if (!base && !indx)
pointer = TRUE;
if (out)
{
out->base = base;
out->indx = indx;
out->disp = disp;
out->pointer = pointer;
}
return TRUE;
}
int
legitimate_address_p (mode, addr, strict)
enum machine_mode mode ATTRIBUTE_UNUSED;
register rtx addr;
int strict;
{
return s390_decompose_address (addr, NULL, strict);
}
int
legitimate_la_operand_p (op)
register rtx op;
{
struct s390_address addr;
if (!s390_decompose_address (op, &addr, FALSE))
return FALSE;
if (TARGET_64BIT || addr.pointer)
return TRUE;
return FALSE;
}
rtx
legitimize_la_operand (op)
register rtx op;
{
struct s390_address addr;
if (!s390_decompose_address (op, &addr, FALSE))
abort ();
if (TARGET_64BIT || addr.pointer)
return op;
if (!addr.base)
abort ();
op = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr.base), 101);
if (addr.indx)
op = gen_rtx_PLUS (Pmode, op, addr.indx);
if (addr.disp)
op = gen_rtx_PLUS (Pmode, op, addr.disp);
return op;
}
rtx
legitimize_pic_address (orig, reg)
rtx orig;
rtx reg;
{
rtx addr = orig;
rtx new = orig;
rtx base;
if (GET_CODE (addr) == LABEL_REF
|| (GET_CODE (addr) == SYMBOL_REF
&& (SYMBOL_REF_FLAG (addr)
|| CONSTANT_POOL_ADDRESS_P (addr))))
{
if (TARGET_64BIT)
{
}
else
{
rtx temp = reg? reg : gen_reg_rtx (Pmode);
addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 100);
addr = gen_rtx_CONST (SImode, addr);
addr = force_const_mem (SImode, addr);
emit_move_insn (temp, addr);
base = gen_rtx_REG (Pmode, BASE_REGISTER);
base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
new = gen_rtx_PLUS (Pmode, base, temp);
if (reg != 0)
{
emit_move_insn (reg, new);
new = reg;
}
}
}
else if (GET_CODE (addr) == SYMBOL_REF)
{
if (reg == 0)
reg = gen_reg_rtx (Pmode);
if (flag_pic == 1)
{
current_function_uses_pic_offset_table = 1;
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 110);
new = gen_rtx_CONST (Pmode, new);
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
new = gen_rtx_MEM (Pmode, new);
RTX_UNCHANGING_P (new) = 1;
emit_move_insn (reg, new);
new = reg;
}
else if (TARGET_64BIT)
{
rtx temp = gen_reg_rtx (Pmode);
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 111);
new = gen_rtx_CONST (Pmode, new);
emit_move_insn (temp, new);
new = gen_rtx_MEM (Pmode, temp);
RTX_UNCHANGING_P (new) = 1;
emit_move_insn (reg, new);
new = reg;
}
else
{
rtx temp = gen_reg_rtx (Pmode);
current_function_uses_pic_offset_table = 1;
addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 112);
addr = gen_rtx_CONST (SImode, addr);
addr = force_const_mem (SImode, addr);
emit_move_insn (temp, addr);
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
new = gen_rtx_MEM (Pmode, new);
RTX_UNCHANGING_P (new) = 1;
emit_move_insn (reg, new);
new = reg;
}
}
else
{
if (GET_CODE (addr) == CONST)
{
addr = XEXP (addr, 0);
if (GET_CODE (addr) == UNSPEC)
{
if (XVECLEN (addr, 0) != 1)
abort ();
switch (XINT (addr, 1))
{
case 100:
case 112:
case 114:
new = force_const_mem (SImode, orig);
break;
case 111:
break;
case 113:
if (!TARGET_64BIT)
{
rtx temp = reg? reg : gen_reg_rtx (Pmode);
addr = XVECEXP (addr, 0, 0);
addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 114);
addr = gen_rtx_CONST (SImode, addr);
addr = force_const_mem (SImode, addr);
emit_move_insn (temp, addr);
base = gen_rtx_REG (Pmode, BASE_REGISTER);
base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
new = gen_rtx_PLUS (Pmode, base, temp);
if (reg != 0)
{
emit_move_insn (reg, new);
new = reg;
}
}
break;
default:
abort ();
}
}
else if (GET_CODE (addr) != PLUS)
abort ();
}
if (GET_CODE (addr) == PLUS)
{
rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
if ((GET_CODE (op0) == LABEL_REF
|| (GET_CODE (op0) == SYMBOL_REF
&& (SYMBOL_REF_FLAG (op0)
|| CONSTANT_POOL_ADDRESS_P (op0))))
&& GET_CODE (op1) == CONST_INT)
{
if (TARGET_64BIT)
{
if (INTVAL (op1) & 1)
{
rtx temp = reg? reg : gen_reg_rtx (Pmode);
if (INTVAL (op1) < 0 || INTVAL (op1) >= 4096)
{
int even = INTVAL (op1) - 1;
op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
op0 = gen_rtx_CONST (Pmode, op0);
op1 = GEN_INT (1);
}
emit_move_insn (temp, op0);
new = gen_rtx_PLUS (Pmode, temp, op1);
if (reg != 0)
{
emit_move_insn (reg, new);
new = reg;
}
}
else
{
}
}
else
{
rtx temp = reg? reg : gen_reg_rtx (Pmode);
addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), 100);
addr = gen_rtx_PLUS (SImode, addr, op1);
addr = gen_rtx_CONST (SImode, addr);
addr = force_const_mem (SImode, addr);
emit_move_insn (temp, addr);
base = gen_rtx_REG (Pmode, BASE_REGISTER);
base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
new = gen_rtx_PLUS (Pmode, base, temp);
if (reg != 0)
{
emit_move_insn (reg, new);
new = reg;
}
}
}
else if (GET_CODE (op0) == UNSPEC
&& GET_CODE (op1) == CONST_INT)
{
if (XVECLEN (op0, 0) != 1)
abort ();
if (XINT (op0, 1) != 100)
abort ();
new = force_const_mem (SImode, orig);
}
else
{
base = legitimize_pic_address (XEXP (addr, 0), reg);
new = legitimize_pic_address (XEXP (addr, 1),
base == reg ? NULL_RTX : reg);
if (GET_CODE (new) == CONST_INT)
new = plus_constant (base, INTVAL (new));
else
{
if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
{
base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
new = XEXP (new, 1);
}
new = gen_rtx_PLUS (Pmode, base, new);
}
if (GET_CODE (new) == CONST)
new = XEXP (new, 0);
new = force_operand (new, 0);
}
}
}
return new;
}
void
emit_pic_move (operands, mode)
rtx *operands;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
operands[1] = force_reg (Pmode, operands[1]);
else
operands[1] = legitimize_pic_address (operands[1], temp);
}
rtx
legitimize_address (x, oldx, mode)
register rtx x;
register rtx oldx ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx constant_term = const0_rtx;
if (flag_pic)
{
if (SYMBOLIC_CONST (x)
|| (GET_CODE (x) == PLUS
&& (SYMBOLIC_CONST (XEXP (x, 0))
|| SYMBOLIC_CONST (XEXP (x, 1)))))
x = legitimize_pic_address (x, 0);
if (legitimate_address_p (mode, x, FALSE))
return x;
}
x = eliminate_constant_term (x, &constant_term);
if (GET_CODE (x) == PLUS)
{
if (GET_CODE (XEXP (x, 0)) == REG)
{
register rtx temp = gen_reg_rtx (Pmode);
register rtx val = force_operand (XEXP (x, 1), temp);
if (val != temp)
emit_move_insn (temp, val);
x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp);
}
else if (GET_CODE (XEXP (x, 1)) == REG)
{
register rtx temp = gen_reg_rtx (Pmode);
register rtx val = force_operand (XEXP (x, 0), temp);
if (val != temp)
emit_move_insn (temp, val);
x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1));
}
}
if (constant_term != const0_rtx)
x = gen_rtx_PLUS (Pmode, x, constant_term);
return x;
}
rtx
s390_simplify_dwarf_addr (orig_x)
rtx orig_x;
{
rtx x = orig_x, y;
if (GET_CODE (x) != MEM)
return orig_x;
x = XEXP (x, 0);
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 1)) == CONST
&& GET_CODE (XEXP (x, 0)) == REG
&& REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM)
{
y = XEXP (XEXP (x, 1), 0);
if (GET_CODE (y) == UNSPEC
&& XINT (y, 1) == 110)
return XVECEXP (y, 0, 0);
return orig_x;
}
if (GET_CODE (x) == CONST)
{
y = XEXP (x, 0);
if (GET_CODE (y) == UNSPEC
&& XINT (y, 1) == 111)
return XVECEXP (y, 0, 0);
return orig_x;
}
return orig_x;
}
void
s390_output_symbolic_const (file, x)
FILE *file;
rtx x;
{
switch (GET_CODE (x))
{
case CONST:
case ZERO_EXTEND:
case SIGN_EXTEND:
s390_output_symbolic_const (file, XEXP (x, 0));
break;
case PLUS:
s390_output_symbolic_const (file, XEXP (x, 0));
fprintf (file, "+");
s390_output_symbolic_const (file, XEXP (x, 1));
break;
case MINUS:
s390_output_symbolic_const (file, XEXP (x, 0));
fprintf (file, "-");
s390_output_symbolic_const (file, XEXP (x, 1));
break;
case CONST_INT:
output_addr_const (file, x);
break;
case LABEL_REF:
case CODE_LABEL:
output_addr_const (file, x);
break;
case SYMBOL_REF:
output_addr_const (file, x);
if (CONSTANT_POOL_ADDRESS_P (x) && s390_pool_count != 0)
fprintf (file, "_%X", s390_pool_count);
break;
case UNSPEC:
if (XVECLEN (x, 0) != 1)
output_operand_lossage ("invalid UNSPEC as operand (1)");
switch (XINT (x, 1))
{
case 100:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "-.LT%X_%X",
s390_function_count, s390_pool_count);
break;
case 110:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOT12");
break;
case 111:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOTENT");
break;
case 112:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOT");
break;
case 113:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@PLT");
break;
case 114:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@PLT-.LT%X_%X",
s390_function_count, s390_pool_count);
break;
default:
output_operand_lossage ("invalid UNSPEC as operand (2)");
break;
}
break;
default:
fatal_insn ("UNKNOWN in s390_output_symbolic_const !?", x);
break;
}
}
void
print_operand_address (file, addr)
FILE *file;
rtx addr;
{
struct s390_address ad;
if (!s390_decompose_address (addr, &ad, TRUE))
output_operand_lossage ("Cannot decompose address.");
if (ad.disp)
s390_output_symbolic_const (file, ad.disp);
else
fprintf (file, "0");
if (ad.base && ad.indx)
fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)],
reg_names[REGNO (ad.base)]);
else if (ad.base)
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
}
void
print_operand (file, x, code)
FILE *file;
rtx x;
int code;
{
switch (code)
{
case 'C':
fprintf (file, s390_branch_condition_mnemonic (x, FALSE));
return;
case 'D':
fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
return;
case 'Y':
fprintf (file, ".LT%X_%X-.", s390_function_count, s390_pool_count);
return;
case 'y':
fprintf (file, ".LT%X_%X", s390_function_count, s390_pool_count);
return;
case 'O':
{
struct s390_address ad;
if (GET_CODE (x) != MEM
|| !s390_decompose_address (XEXP (x, 0), &ad, TRUE)
|| ad.indx)
abort ();
if (ad.disp)
s390_output_symbolic_const (file, ad.disp);
else
fprintf (file, "0");
}
return;
case 'R':
{
struct s390_address ad;
if (GET_CODE (x) != MEM
|| !s390_decompose_address (XEXP (x, 0), &ad, TRUE)
|| ad.indx)
abort ();
if (ad.base)
fprintf (file, "%s", reg_names[REGNO (ad.base)]);
else
fprintf (file, "0");
}
return;
case 'N':
if (GET_CODE (x) == REG)
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
else if (GET_CODE (x) == MEM)
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
else
abort ();
break;
case 'M':
if (GET_CODE (x) == REG)
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
else if (GET_CODE (x) == MEM)
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
else
abort ();
break;
}
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
case MEM:
output_address (XEXP (x, 0));
break;
case CONST:
case CODE_LABEL:
case LABEL_REF:
case SYMBOL_REF:
s390_output_symbolic_const (file, x);
break;
case CONST_INT:
if (code == 'b')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
else if (code == 'x')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
else if (code == 'h')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
else
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
break;
case CONST_DOUBLE:
if (GET_MODE (x) != VOIDmode)
abort ();
if (code == 'b')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
else if (code == 'x')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
else if (code == 'h')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
else
abort ();
break;
default:
fatal_insn ("UNKNOWN in print_operand !?", x);
break;
}
}
static bool
s390_assemble_integer (x, size, aligned_p)
rtx x;
unsigned int size;
int aligned_p;
{
if (size == 8 && aligned_p
&& GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
{
fputs ("\t.quad\t", asm_out_file);
fprintf (asm_out_file, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
putc ('\n', asm_out_file);
return true;
}
return default_assemble_integer (x, size, aligned_p);
}
#define DEBUG_SCHED 0
static int
reg_used_in_mem_p (regno, x)
int regno;
rtx x;
{
enum rtx_code code = GET_CODE (x);
int i, j;
const char *fmt;
if (code == MEM)
{
if (refers_to_regno_p (regno, regno+1,
XEXP (x, 0), 0))
return 1;
}
else if (code == SET
&& GET_CODE (SET_DEST (x)) == PC)
{
if (refers_to_regno_p (regno, regno+1,
SET_SRC (x), 0))
return 1;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e'
&& reg_used_in_mem_p (regno, XEXP (x, i)))
return 1;
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
return 1;
}
return 0;
}
static int
addr_generation_dependency_p (dep_rtx, insn)
rtx dep_rtx;
rtx insn;
{
rtx target, pat;
if (GET_CODE (dep_rtx) == SET)
{
target = SET_DEST (dep_rtx);
if (GET_CODE (target) == REG)
{
int regno = REGNO (target);
if (get_attr_type (insn) == TYPE_LA)
{
pat = PATTERN (insn);
if (GET_CODE (pat) == PARALLEL)
{
if (XVECLEN (pat, 0) != 2)
abort();
pat = XVECEXP (pat, 0, 0);
}
if (GET_CODE (pat) == SET)
return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
else
abort();
}
else if (get_attr_atype (insn) == ATYPE_MEM)
return reg_used_in_mem_p (regno, PATTERN (insn));
}
}
return 0;
}
static int
s390_adjust_cost (insn, link, dep_insn, cost)
rtx insn;
rtx link;
rtx dep_insn;
int cost;
{
rtx dep_rtx;
int i;
if (REG_NOTE_KIND (link) != 0)
return 0;
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
return cost;
dep_rtx = PATTERN (dep_insn);
if (GET_CODE (dep_rtx) == SET)
{
if (addr_generation_dependency_p (dep_rtx, insn))
{
cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;
if (DEBUG_SCHED)
{
fprintf (stderr, "\n\nAddress dependency detected: cost %d\n",
cost);
debug_rtx (dep_insn);
debug_rtx (insn);
}
}
}
else if (GET_CODE (dep_rtx) == PARALLEL)
{
for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
{
if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i),
insn))
{
cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;
if (DEBUG_SCHED)
{
fprintf (stderr, "\n\nAddress dependency detected: cost %d\n"
,cost);
debug_rtx (dep_insn);
debug_rtx (insn);
}
}
}
}
return cost;
}
static int
s390_adjust_priority (insn, priority)
rtx insn ATTRIBUTE_UNUSED;
int priority;
{
if (! INSN_P (insn))
return priority;
if (GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER)
return priority;
switch (get_attr_type (insn))
{
default:
break;
case TYPE_LA:
if (priority >= 0 && priority < 0x01000000)
priority <<= 3;
break;
case TYPE_LM:
priority = 0x7fffffff;
break;
}
return priority;
}
int s390_pool_count = -1;
rtx s390_pool_start_insn = NULL_RTX;
void
s390_asm_output_pool_prologue (file, fname, fndecl, size)
FILE *file;
const char *fname ATTRIBUTE_UNUSED;
tree fndecl;
int size ATTRIBUTE_UNUSED;
{
if (s390_pool_count>0) {
if (TARGET_64BIT)
{
fprintf (file, "\tlarl\t%s,.LT%X_%X\n",
reg_names[BASE_REGISTER],
s390_function_count, s390_pool_count);
readonly_data_section ();
ASM_OUTPUT_ALIGN (file, floor_log2 (3));
fprintf (file, ".LT%X_%X:\t# Pool %d\n",
s390_function_count, s390_pool_count, s390_pool_count);
}
else
fprintf (file,"\t.align 4\n\tbras\t%s,0f\n.LT%X_%X:\t# Pool %d \n",
reg_names[BASE_REGISTER],
s390_function_count, s390_pool_count, s390_pool_count);
}
if (!TARGET_64BIT)
function_section (fndecl);
}
static void
s390_split_branches (void)
{
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
rtx insn, pat, label, target, jump, tmp;
if (TARGET_64BIT)
return;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) != JUMP_INSN)
continue;
pat = PATTERN (insn);
if (GET_CODE (pat) != SET)
continue;
if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
{
label = SET_SRC (pat);
}
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
{
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
label = XEXP (SET_SRC (pat), 1);
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
label = XEXP (SET_SRC (pat), 2);
else
continue;
}
else
continue;
if (get_attr_length (insn) == 4)
continue;
if (flag_pic)
{
target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, label), 100);
target = gen_rtx_CONST (SImode, target);
target = force_const_mem (SImode, target);
jump = gen_rtx_REG (Pmode, BASE_REGISTER);
jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
}
else
{
target = force_const_mem (Pmode, label);
jump = temp_reg;
}
if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
{
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
jump, pc_rtx);
else
jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
pc_rtx, jump);
}
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
INSN_ADDRESSES_NEW (tmp, -1);
tmp = emit_jump_insn_before (gen_rtx_SET (VOIDmode, pc_rtx, jump), insn);
INSN_ADDRESSES_NEW (tmp, -1);
remove_insn (insn);
insn = tmp;
}
}
static void
s390_chunkify_pool (void)
{
int *ltorg_uids, max_ltorg, chunk, last_addr, next_addr;
rtx insn;
if (get_pool_size () <= S390_POOL_MAX)
return;
ltorg_uids = alloca (insn_current_address / 1024 + 1024);
max_ltorg = 0;
last_addr = 0;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (INSN_ADDRESSES (INSN_UID (insn)) - last_addr < S390_CHUNK_MAX)
continue;
if (INSN_ADDRESSES (INSN_UID (insn)) - last_addr > S390_CHUNK_OV)
abort ();
if (GET_CODE (insn) == CODE_LABEL
&& !(GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
|| GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC)))
{
ltorg_uids[max_ltorg++] = INSN_UID (prev_real_insn (insn));
last_addr = INSN_ADDRESSES (ltorg_uids[max_ltorg-1]);
continue;
}
if (GET_CODE (insn) == CALL_INSN)
{
ltorg_uids[max_ltorg++] = INSN_UID (insn);
last_addr = INSN_ADDRESSES (ltorg_uids[max_ltorg-1]);
continue;
}
}
ltorg_uids[max_ltorg] = -1;
chunk = last_addr = 0;
next_addr = ltorg_uids[chunk] == -1 ? insn_current_address + 1
: INSN_ADDRESSES (ltorg_uids[chunk]);
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == SET)
{
rtx label = 0;
if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
{
label = XEXP (SET_SRC (pat), 0);
}
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
{
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (pat), 1), 0);
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (pat), 2), 0);
}
if (label)
{
if (INSN_ADDRESSES (INSN_UID (label)) <= last_addr
|| INSN_ADDRESSES (INSN_UID (label)) > next_addr)
SYMBOL_REF_USED (label) = 1;
}
}
else if (GET_CODE (pat) == ADDR_VEC
|| GET_CODE (pat) == ADDR_DIFF_VEC)
{
int i, diff_p = GET_CODE (pat) == ADDR_DIFF_VEC;
for (i = 0; i < XVECLEN (pat, diff_p); i++)
{
rtx label = XEXP (XVECEXP (pat, diff_p, i), 0);
if (INSN_ADDRESSES (INSN_UID (label)) <= last_addr
|| INSN_ADDRESSES (INSN_UID (label)) > next_addr)
SYMBOL_REF_USED (label) = 1;
}
}
}
if (INSN_UID (insn) == ltorg_uids[chunk])
{
last_addr = INSN_ADDRESSES (ltorg_uids[chunk++]);
next_addr = ltorg_uids[chunk] == -1 ? insn_current_address + 1
: INSN_ADDRESSES (ltorg_uids[chunk]);
}
}
chunk = 0;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (INSN_UID (insn) == ltorg_uids[chunk])
{
rtx new_insn = gen_ltorg (GEN_INT (chunk++));
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
if (GET_CODE (insn) == CODE_LABEL && SYMBOL_REF_USED (insn))
{
rtx new_insn = gen_reload_base (insn);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
}
init_insn_lengths ();
shorten_branches (get_insns ());
}
int
s390_stop_dump_lit_p (insn)
rtx insn;
{
rtx body=PATTERN (insn);
if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (XVECEXP (body, 0, 1)) == USE
&& GET_CODE (XEXP ((XVECEXP (body, 0, 1)),0)) == CONST_INT
&& GET_CODE (SET_DEST (XVECEXP (body, 0, 0))) == REG
&& REGNO (SET_DEST (XVECEXP (body, 0, 0))) == BASE_REGISTER
&& SET_SRC (XVECEXP (body, 0, 0)) == pc_rtx) {
return 1;
}
else
return 0;
}
void
s390_dump_literal_pool (act_insn, stop)
rtx act_insn;
rtx stop;
{
s390_pool_start_insn = act_insn;
s390_pool_count++;
output_constant_pool (current_function_name, current_function_decl);
function_section (current_function_decl);
}
int s390_nr_constants;
static int
save_fprs_p ()
{
int i;
if (!TARGET_64BIT)
return 0;
for (i=24; i<=31; i++)
{
if (regs_ever_live[i] == 1)
return 1;
}
return 0;
}
void
s390_output_constant_pool (file)
FILE *file;
{
if (s390_nr_constants)
{
s390_pool_count = 0;
if (TARGET_64BIT)
{
fprintf (file, "\tlarl\t%s,.LT%X_%X\n", reg_names[BASE_REGISTER],
s390_function_count, s390_pool_count);
readonly_data_section ();
ASM_OUTPUT_ALIGN (file, floor_log2 (3));
}
else
{
fprintf (file, "\tbras\t%s,.LTN%X_%X\n", reg_names[BASE_REGISTER],
s390_function_count, s390_pool_count);
}
fprintf (file, ".LT%X_%X:\n", s390_function_count, s390_pool_count);
output_constant_pool (current_function_name, current_function_decl);
fprintf (file, ".LTN%X_%X:\n", s390_function_count,
s390_pool_count);
if (TARGET_64BIT)
function_section (current_function_decl);
}
}
static int
find_unused_clobbered_reg ()
{
int i;
for (i = 0; i < 6; i++)
if (!regs_ever_live[i])
return i;
return 0;
}
static void
s390_frame_info (frame)
struct s390_frame *frame;
{
int i, j;
HOST_WIDE_INT fsize = get_frame_size ();
if (fsize > 0x7fff0000)
fatal_error ("Total size of local variables exceeds architecture limit.");
frame->save_fprs_p = save_fprs_p ();
frame->frame_size = fsize + frame->save_fprs_p * 64;
if (! current_function_is_leaf
|| frame->frame_size > 0
|| current_function_calls_alloca
|| current_function_stdarg
|| current_function_varargs)
frame->frame_size += STARTING_FRAME_OFFSET;
if (frame->frame_size > 0)
regs_ever_live[STACK_POINTER_REGNUM] = 1;
if (get_pool_size ()
|| !CONST_OK_FOR_LETTER_P (frame->frame_size, 'K')
|| (!TARGET_64BIT && current_function_uses_pic_offset_table))
regs_ever_live[BASE_REGISTER] = 1;
if (current_function_uses_pic_offset_table)
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
frame->frame_pointer_p = frame_pointer_needed;
for (i = 6; i < 16; i++)
if (regs_ever_live[i])
break;
for (j = 15; j > i; j--)
if (regs_ever_live[j])
break;
if (i == 16)
{
frame->first_save_gpr = -1;
frame->first_restore_gpr = -1;
frame->last_save_gpr = -1;
frame->return_reg_saved_p = 0;
}
else
{
frame->first_save_gpr = i;
frame->first_restore_gpr = i;
frame->last_save_gpr = j;
frame->return_reg_saved_p = (j >= RETURN_REGNUM && i <= RETURN_REGNUM);
}
if (current_function_stdarg || current_function_varargs)
{
frame->first_save_gpr = 2;
}
}
int
s390_arg_frame_offset ()
{
struct s390_frame frame;
s390_frame_info (&frame);
return frame.frame_size + STACK_POINTER_OFFSET;
}
static rtx
save_fpr (base, offset, regnum)
rtx base;
int offset;
int regnum;
{
rtx addr;
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
set_mem_alias_set (addr, s390_sr_alias_set);
return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
}
static rtx
restore_fpr (base, offset, regnum)
rtx base;
int offset;
int regnum;
{
rtx addr;
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
set_mem_alias_set (addr, s390_sr_alias_set);
return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
}
void
s390_function_prologue (file, lsize)
FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT lsize ATTRIBUTE_UNUSED;
{
s390_chunkify_pool ();
s390_split_branches ();
}
void
s390_function_epilogue (file, lsize)
FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT lsize ATTRIBUTE_UNUSED;
{
current_function_uses_pic_offset_table = 0;
s390_pool_start_insn = NULL_RTX;
s390_pool_count = -1;
s390_function_count++;
}
void
s390_emit_prologue ()
{
struct s390_frame frame;
rtx insn, addr;
rtx temp_reg;
int i;
s390_frame_info (&frame);
if (frame.return_reg_saved_p
&& !has_hard_reg_initial_val (Pmode, RETURN_REGNUM))
temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
else
temp_reg = gen_rtx_REG (Pmode, 1);
if (frame.first_save_gpr != -1)
{
addr = plus_constant (stack_pointer_rtx,
frame.first_save_gpr * UNITS_PER_WORD);
addr = gen_rtx_MEM (Pmode, addr);
set_mem_alias_set (addr, s390_sr_alias_set);
if (frame.first_save_gpr != frame.last_save_gpr )
{
insn = emit_insn (gen_store_multiple (addr,
gen_rtx_REG (Pmode, frame.first_save_gpr),
GEN_INT (frame.last_save_gpr
- frame.first_save_gpr + 1)));
if (frame.first_save_gpr >= 6)
{
rtx pat = PATTERN (insn);
for (i = 0; i < XVECLEN (pat, 0); i++)
if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
RTX_FRAME_RELATED_P (insn) = 1;
}
else if (frame.last_save_gpr >= 6)
{
rtx note, naddr;
naddr = plus_constant (stack_pointer_rtx, 6 * UNITS_PER_WORD);
note = gen_store_multiple (gen_rtx_MEM (Pmode, naddr),
gen_rtx_REG (Pmode, 6),
GEN_INT (frame.last_save_gpr - 6 + 1));
REG_NOTES (insn) =
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
note, REG_NOTES (insn));
for (i = 0; i < XVECLEN (note, 0); i++)
if (GET_CODE (XVECEXP (note, 0, i)) == SET)
RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
RTX_FRAME_RELATED_P (insn) = 1;
}
}
else
{
insn = emit_move_insn (addr,
gen_rtx_REG (Pmode, frame.first_save_gpr));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
insn = emit_insn (gen_lit ());
if (current_function_stdarg || current_function_varargs)
{
save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 32, 16);
save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 24, 17);
if (TARGET_64BIT)
{
save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18);
save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19);
}
}
if (!TARGET_64BIT)
{
if (regs_ever_live[18])
{
insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (regs_ever_live[19])
{
insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19);
RTX_FRAME_RELATED_P (insn) = 1;
}
}
if (frame.frame_size > 0)
{
rtx frame_off = GEN_INT (-frame.frame_size);
if (TARGET_BACKCHAIN || frame.save_fprs_p)
{
insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
}
frame_off = GEN_INT (-frame.frame_size);
if (!CONST_OK_FOR_LETTER_P (-frame.frame_size, 'K'))
frame_off = force_const_mem (Pmode, frame_off);
insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) =
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode, stack_pointer_rtx,
gen_rtx_PLUS (Pmode, stack_pointer_rtx,
GEN_INT (-frame.frame_size))),
REG_NOTES (insn));
if (TARGET_BACKCHAIN)
{
addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
set_mem_alias_set (addr, s390_sr_alias_set);
insn = emit_insn (gen_move_insn (addr, temp_reg));
}
}
if (frame.save_fprs_p)
{
insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64)));
for (i = 24; i < 32; i++)
if (regs_ever_live[i])
{
rtx addr = plus_constant (stack_pointer_rtx,
frame.frame_size - 64 + (i-24)*8);
insn = save_fpr (temp_reg, (i-24)*8, i);
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) =
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode,
gen_rtx_MEM (DFmode, addr),
gen_rtx_REG (DFmode, i)),
REG_NOTES (insn));
}
}
if (frame.frame_pointer_p)
{
insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (current_function_uses_pic_offset_table)
{
rtx got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
SYMBOL_REF_FLAG (got_symbol) = 1;
if (TARGET_64BIT)
{
insn = emit_insn (gen_movdi (pic_offset_table_rtx,
got_symbol));
REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
REG_NOTES (insn));
}
else
{
got_symbol = gen_rtx_UNSPEC (VOIDmode,
gen_rtvec (1, got_symbol), 100);
got_symbol = gen_rtx_CONST (VOIDmode, got_symbol);
got_symbol = force_const_mem (Pmode, got_symbol);
insn = emit_move_insn (pic_offset_table_rtx,
got_symbol);
REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
REG_NOTES (insn));
insn = emit_insn (gen_add2_insn (pic_offset_table_rtx,
gen_rtx_REG (Pmode, BASE_REGISTER)));
REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
REG_NOTES (insn));
}
}
}
void
s390_emit_epilogue ()
{
struct s390_frame frame;
rtx frame_pointer, return_reg;
int area_bottom, area_top, offset;
rtvec p;
s390_frame_info (&frame);
frame_pointer = frame.frame_pointer_p ?
hard_frame_pointer_rtx : stack_pointer_rtx;
if (frame.first_restore_gpr != -1)
{
area_bottom = frame.first_restore_gpr * UNITS_PER_WORD;
area_top = (frame.last_save_gpr + 1) * UNITS_PER_WORD;
}
else
{
area_bottom = INT_MAX;
area_top = INT_MIN;
}
if (TARGET_64BIT)
{
if (frame.save_fprs_p)
{
if (area_bottom > -64)
area_bottom = -64;
if (area_top < 0)
area_top = 0;
}
}
else
{
if (regs_ever_live[18])
{
if (area_bottom > STACK_POINTER_OFFSET - 16)
area_bottom = STACK_POINTER_OFFSET - 16;
if (area_top < STACK_POINTER_OFFSET - 8)
area_top = STACK_POINTER_OFFSET - 8;
}
if (regs_ever_live[19])
{
if (area_bottom > STACK_POINTER_OFFSET - 8)
area_bottom = STACK_POINTER_OFFSET - 8;
if (area_top < STACK_POINTER_OFFSET)
area_top = STACK_POINTER_OFFSET;
}
}
if (area_top <= area_bottom)
{
}
else if (frame.frame_size + area_bottom >= 0
&& frame.frame_size + area_top <= 4096)
{
offset = frame.frame_size;
}
else
{
rtx insn, frame_off;
offset = area_bottom < 0 ? -area_bottom : 0;
frame_off = GEN_INT (frame.frame_size - offset);
if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
frame_off = force_const_mem (Pmode, frame_off);
insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) =
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode, frame_pointer,
gen_rtx_PLUS (Pmode, frame_pointer,
GEN_INT (frame.frame_size - offset))),
REG_NOTES (insn));
}
if (TARGET_64BIT)
{
int i;
if (frame.save_fprs_p)
for (i = 24; i < 32; i++)
if (regs_ever_live[i] && !global_regs[i])
restore_fpr (frame_pointer,
offset - 64 + (i-24) * 8, i);
}
else
{
if (regs_ever_live[18] && !global_regs[18])
restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 16, 18);
if (regs_ever_live[19] && !global_regs[19])
restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 8, 19);
}
return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
if (frame.first_restore_gpr != -1)
{
rtx addr;
int i;
for (i = frame.first_restore_gpr;
i <= frame.last_save_gpr;
i++)
{
if (global_regs[i])
{
addr = plus_constant (frame_pointer,
offset + i * UNITS_PER_WORD);
addr = gen_rtx_MEM (Pmode, addr);
set_mem_alias_set (addr, s390_sr_alias_set);
emit_move_insn (addr, gen_rtx_REG (Pmode, i));
}
}
if (frame.last_save_gpr >= RETURN_REGNUM
&& frame.first_restore_gpr < RETURN_REGNUM)
{
int return_regnum = find_unused_clobbered_reg();
if (!return_regnum)
return_regnum = 4;
return_reg = gen_rtx_REG (Pmode, return_regnum);
addr = plus_constant (frame_pointer,
offset + RETURN_REGNUM * UNITS_PER_WORD);
addr = gen_rtx_MEM (Pmode, addr);
set_mem_alias_set (addr, s390_sr_alias_set);
emit_move_insn (return_reg, addr);
}
emit_insn (gen_blockage());
addr = plus_constant (frame_pointer,
offset + frame.first_restore_gpr * UNITS_PER_WORD);
addr = gen_rtx_MEM (Pmode, addr);
set_mem_alias_set (addr, s390_sr_alias_set);
if (frame.first_restore_gpr != frame.last_save_gpr)
{
emit_insn (gen_load_multiple (
gen_rtx_REG (Pmode, frame.first_restore_gpr),
addr,
GEN_INT (frame.last_save_gpr - frame.first_restore_gpr + 1)));
}
else
{
emit_move_insn (gen_rtx_REG (Pmode, frame.first_restore_gpr),
addr);
}
}
p = rtvec_alloc (2);
RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
}
static int
s390_function_arg_size (mode, type)
enum machine_mode mode;
tree type;
{
if (type)
return int_size_in_bytes (type);
if (mode != BLKmode)
return GET_MODE_SIZE (mode);
abort ();
}
int
s390_function_arg_pass_by_reference (mode, type)
enum machine_mode mode;
tree type;
{
int size = s390_function_arg_size (mode, type);
if (type)
{
if (AGGREGATE_TYPE_P (type) &&
size != 1 && size != 2 && size != 4 && size != 8)
return 1;
if (TREE_CODE (type) == COMPLEX_TYPE)
return 1;
}
return 0;
}
void
s390_function_arg_advance (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
{
cum->fprs++;
}
else if (s390_function_arg_pass_by_reference (mode, type))
{
cum->gprs += 1;
}
else
{
int size = s390_function_arg_size (mode, type);
cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
}
}
rtx
s390_function_arg (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
if (s390_function_arg_pass_by_reference (mode, type))
return 0;
if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
{
if (cum->fprs + 1 > (TARGET_64BIT? 4 : 2))
return 0;
else
return gen_rtx (REG, mode, cum->fprs + 16);
}
else
{
int size = s390_function_arg_size (mode, type);
int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
if (cum->gprs + n_gprs > 5)
return 0;
else
return gen_rtx (REG, mode, cum->gprs + 2);
}
}
tree
s390_build_va_list ()
{
tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
record = make_lang_type (RECORD_TYPE);
type_decl =
build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
long_integer_type_node);
f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
long_integer_type_node);
f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
ptr_type_node);
f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
ptr_type_node);
DECL_FIELD_CONTEXT (f_gpr) = record;
DECL_FIELD_CONTEXT (f_fpr) = record;
DECL_FIELD_CONTEXT (f_ovf) = record;
DECL_FIELD_CONTEXT (f_sav) = record;
TREE_CHAIN (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_gpr;
TREE_CHAIN (f_gpr) = f_fpr;
TREE_CHAIN (f_fpr) = f_ovf;
TREE_CHAIN (f_ovf) = f_sav;
layout_type (record);
return build_array_type (record, build_index_type (size_zero_node));
}
void
s390_va_start (stdarg_p, valist, nextarg)
int stdarg_p;
tree valist;
rtx nextarg ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT n_gpr, n_fpr;
int off;
tree f_gpr, f_fpr, f_ovf, f_sav;
tree gpr, fpr, ovf, sav, t;
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
f_fpr = TREE_CHAIN (f_gpr);
f_ovf = TREE_CHAIN (f_fpr);
f_sav = TREE_CHAIN (f_ovf);
valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
n_gpr = current_function_args_info.gprs;
n_fpr = current_function_args_info.fprs;
t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
off = INTVAL (current_function_arg_offset_rtx);
off = off < 0 ? 0 : off;
if (! stdarg_p)
off = off > 0 ? off - UNITS_PER_WORD : off;
if (TARGET_DEBUG_ARG)
fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
(int)n_gpr, (int)n_fpr, off);
t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_2 (off, 0));
t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = make_tree (TREE_TYPE (sav), virtual_incoming_args_rtx);
t = build (PLUS_EXPR, TREE_TYPE (sav), t,
build_int_2 (-STACK_POINTER_OFFSET, -1));
t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
rtx
s390_va_arg (valist, type)
tree valist;
tree type;
{
tree f_gpr, f_fpr, f_ovf, f_sav;
tree gpr, fpr, ovf, sav, reg, t, u;
int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
rtx lab_false, lab_over, addr_rtx, r;
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
f_fpr = TREE_CHAIN (f_gpr);
f_ovf = TREE_CHAIN (f_fpr);
f_sav = TREE_CHAIN (f_ovf);
valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
size = int_size_in_bytes (type);
if (s390_function_arg_pass_by_reference (TYPE_MODE (type), type))
{
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "va_arg: aggregate type");
debug_tree (type);
}
indirect_p = 1;
reg = gpr;
n_reg = 1;
sav_ofs = 2 * UNITS_PER_WORD;
sav_scale = UNITS_PER_WORD;
size = UNITS_PER_WORD;
max_reg = 4;
}
else if (FLOAT_TYPE_P (type) && ! TARGET_SOFT_FLOAT)
{
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "va_arg: float type");
debug_tree (type);
}
indirect_p = 0;
reg = fpr;
n_reg = 1;
sav_ofs = 16 * UNITS_PER_WORD;
sav_scale = 8;
max_reg = TARGET_64BIT ? 3 : 1;
}
else
{
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "va_arg: other type");
debug_tree (type);
}
indirect_p = 0;
reg = gpr;
n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
sav_ofs = 2 * UNITS_PER_WORD;
if (TARGET_64BIT)
sav_ofs += TYPE_MODE (type) == SImode ? 4 :
TYPE_MODE (type) == HImode ? 6 :
TYPE_MODE (type) == QImode ? 7 : 0;
else
sav_ofs += TYPE_MODE (type) == HImode ? 2 :
TYPE_MODE (type) == QImode ? 3 : 0;
sav_scale = UNITS_PER_WORD;
if (n_reg > 1)
max_reg = 3;
else
max_reg = 4;
}
lab_false = gen_label_rtx ();
lab_over = gen_label_rtx ();
addr_rtx = gen_reg_rtx (Pmode);
emit_cmp_and_jump_insns (expand_expr (reg, NULL_RTX, Pmode, EXPAND_NORMAL),
GEN_INT (max_reg),
GT, const1_rtx, Pmode, 0, lab_false);
if (sav_ofs)
t = build (PLUS_EXPR, ptr_type_node, sav, build_int_2 (sav_ofs, 0));
else
t = sav;
u = build (MULT_EXPR, long_integer_type_node,
reg, build_int_2 (sav_scale, 0));
TREE_SIDE_EFFECTS (u) = 1;
t = build (PLUS_EXPR, ptr_type_node, t, u);
TREE_SIDE_EFFECTS (t) = 1;
r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
emit_jump_insn (gen_jump (lab_over));
emit_barrier ();
emit_label (lab_false);
t = save_expr (ovf);
if (size < UNITS_PER_WORD)
{
t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (UNITS_PER_WORD-size, 0));
t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
t = save_expr (ovf);
}
r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (size, 0));
t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
emit_label (lab_over);
u = build (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
build_int_2 (n_reg, 0));
TREE_SIDE_EFFECTS (u) = 1;
expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (indirect_p)
{
r = gen_rtx_MEM (Pmode, addr_rtx);
set_mem_alias_set (r, get_varargs_alias_set ());
emit_move_insn (addr_rtx, r);
}
return addr_rtx;
}
void
s390_trampoline_template (file)
FILE *file;
{
if (TARGET_64BIT)
{
fprintf (file, "larl\t%s,0f\n", reg_names[1]);
fprintf (file, "lg\t%s,0(%s)\n", reg_names[0], reg_names[1]);
fprintf (file, "lg\t%s,8(%s)\n", reg_names[1], reg_names[1]);
fprintf (file, "br\t%s\n", reg_names[1]);
fprintf (file, "0:\t.quad\t0\n");
fprintf (file, ".quad\t0\n");
}
else
{
fprintf (file, "basr\t%s,0\n", reg_names[1]);
fprintf (file, "l\t%s,10(%s)\n", reg_names[0], reg_names[1]);
fprintf (file, "l\t%s,14(%s)\n", reg_names[1], reg_names[1]);
fprintf (file, "br\t%s\n", reg_names[1]);
fprintf (file, ".long\t0\n");
fprintf (file, ".long\t0\n");
}
}
void
s390_initialize_trampoline (addr, fnaddr, cxt)
rtx addr;
rtx fnaddr;
rtx cxt;
{
emit_move_insn (gen_rtx
(MEM, Pmode,
memory_address (Pmode,
plus_constant (addr, (TARGET_64BIT ? 20 : 12) ))), cxt);
emit_move_insn (gen_rtx
(MEM, Pmode,
memory_address (Pmode,
plus_constant (addr, (TARGET_64BIT ? 28 : 16) ))), fnaddr);
}
rtx
s390_gen_rtx_const_DI (high, low)
int high;
int low;
{
#if HOST_BITS_PER_WIDE_INT >= 64
HOST_WIDE_INT val;
val = (HOST_WIDE_INT)high;
val <<= 32;
val |= (HOST_WIDE_INT)low;
return GEN_INT (val);
#else
#if HOST_BITS_PER_WIDE_INT >= 32
return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
#else
abort ();
#endif
#endif
}
void
s390_function_profiler (file, labelno)
FILE *file;
int labelno;
{
rtx op[7];
char label[128];
sprintf (label, "%sP%d", LPREFIX, labelno);
fprintf (file, "# function profiler \n");
op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
op[2] = gen_rtx_REG (Pmode, 1);
op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
SYMBOL_REF_FLAG (op[3]) = 1;
op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
if (flag_pic)
{
op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), 113);
op[4] = gen_rtx_CONST (Pmode, op[4]);
}
if (TARGET_64BIT)
{
output_asm_insn ("stg\t%0,%1", op);
output_asm_insn ("larl\t%2,%3", op);
output_asm_insn ("brasl\t%0,%4", op);
output_asm_insn ("lg\t%0,%1", op);
}
else if (!flag_pic)
{
op[6] = gen_label_rtx ();
output_asm_insn ("st\t%0,%1", op);
output_asm_insn ("bras\t%2,%l6", op);
output_asm_insn (".long\t%4", op);
output_asm_insn (".long\t%3", op);
ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6]));
output_asm_insn ("l\t%0,0(%2)", op);
output_asm_insn ("l\t%2,4(%2)", op);
output_asm_insn ("basr\t%0,%0", op);
output_asm_insn ("l\t%0,%1", op);
}
else
{
op[5] = gen_label_rtx ();
op[6] = gen_label_rtx ();
output_asm_insn ("st\t%0,%1", op);
output_asm_insn ("bras\t%2,%l6", op);
ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5]));
output_asm_insn (".long\t%4-%l5", op);
output_asm_insn (".long\t%3-%l5", op);
ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6]));
output_asm_insn ("lr\t%0,%2", op);
output_asm_insn ("a\t%0,0(%2)", op);
output_asm_insn ("a\t%2,4(%2)", op);
output_asm_insn ("basr\t%0,%0", op);
output_asm_insn ("l\t%0,%1", op);
}
}