/* Subroutines used for code generation on IBM S/390 and zSeries
|
/* Subroutines used for code generation on IBM S/390 and zSeries
|
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
Free Software Foundation, Inc.
|
Free Software Foundation, Inc.
|
Contributed by Hartmut Penner (hpenner@de.ibm.com) and
|
Contributed by Hartmut Penner (hpenner@de.ibm.com) and
|
Ulrich Weigand (uweigand@de.ibm.com).
|
Ulrich Weigand (uweigand@de.ibm.com).
|
|
|
This file is part of GCC.
|
This file is part of GCC.
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
GCC 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
|
the terms of the GNU General Public License as published by the Free
|
Software Foundation; either version 3, or (at your option) any later
|
Software Foundation; either version 3, or (at your option) any later
|
version.
|
version.
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
for more details.
|
for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with GCC; see the file COPYING3. If not see
|
along with GCC; see the file COPYING3. If not see
|
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
|
|
#include "config.h"
|
#include "config.h"
|
#include "system.h"
|
#include "system.h"
|
#include "coretypes.h"
|
#include "coretypes.h"
|
#include "tm.h"
|
#include "tm.h"
|
#include "rtl.h"
|
#include "rtl.h"
|
#include "tree.h"
|
#include "tree.h"
|
#include "tm_p.h"
|
#include "tm_p.h"
|
#include "regs.h"
|
#include "regs.h"
|
#include "hard-reg-set.h"
|
#include "hard-reg-set.h"
|
#include "real.h"
|
#include "real.h"
|
#include "insn-config.h"
|
#include "insn-config.h"
|
#include "conditions.h"
|
#include "conditions.h"
|
#include "output.h"
|
#include "output.h"
|
#include "insn-attr.h"
|
#include "insn-attr.h"
|
#include "flags.h"
|
#include "flags.h"
|
#include "except.h"
|
#include "except.h"
|
#include "function.h"
|
#include "function.h"
|
#include "recog.h"
|
#include "recog.h"
|
#include "expr.h"
|
#include "expr.h"
|
#include "reload.h"
|
#include "reload.h"
|
#include "toplev.h"
|
#include "toplev.h"
|
#include "basic-block.h"
|
#include "basic-block.h"
|
#include "integrate.h"
|
#include "integrate.h"
|
#include "ggc.h"
|
#include "ggc.h"
|
#include "target.h"
|
#include "target.h"
|
#include "target-def.h"
|
#include "target-def.h"
|
#include "debug.h"
|
#include "debug.h"
|
#include "langhooks.h"
|
#include "langhooks.h"
|
#include "optabs.h"
|
#include "optabs.h"
|
#include "tree-gimple.h"
|
#include "tree-gimple.h"
|
|
|
|
|
/* Define the specific costs for a given cpu. */
|
/* Define the specific costs for a given cpu. */
|
|
|
struct processor_costs
|
struct processor_costs
|
{
|
{
|
/* multiplication */
|
/* multiplication */
|
const int m; /* cost of an M instruction. */
|
const int m; /* cost of an M instruction. */
|
const int mghi; /* cost of an MGHI instruction. */
|
const int mghi; /* cost of an MGHI instruction. */
|
const int mh; /* cost of an MH instruction. */
|
const int mh; /* cost of an MH instruction. */
|
const int mhi; /* cost of an MHI instruction. */
|
const int mhi; /* cost of an MHI instruction. */
|
const int ml; /* cost of an ML instruction. */
|
const int ml; /* cost of an ML instruction. */
|
const int mr; /* cost of an MR instruction. */
|
const int mr; /* cost of an MR instruction. */
|
const int ms; /* cost of an MS instruction. */
|
const int ms; /* cost of an MS instruction. */
|
const int msg; /* cost of an MSG instruction. */
|
const int msg; /* cost of an MSG instruction. */
|
const int msgf; /* cost of an MSGF instruction. */
|
const int msgf; /* cost of an MSGF instruction. */
|
const int msgfr; /* cost of an MSGFR instruction. */
|
const int msgfr; /* cost of an MSGFR instruction. */
|
const int msgr; /* cost of an MSGR instruction. */
|
const int msgr; /* cost of an MSGR instruction. */
|
const int msr; /* cost of an MSR instruction. */
|
const int msr; /* cost of an MSR instruction. */
|
const int mult_df; /* cost of multiplication in DFmode. */
|
const int mult_df; /* cost of multiplication in DFmode. */
|
const int mxbr;
|
const int mxbr;
|
/* square root */
|
/* square root */
|
const int sqxbr; /* cost of square root in TFmode. */
|
const int sqxbr; /* cost of square root in TFmode. */
|
const int sqdbr; /* cost of square root in DFmode. */
|
const int sqdbr; /* cost of square root in DFmode. */
|
const int sqebr; /* cost of square root in SFmode. */
|
const int sqebr; /* cost of square root in SFmode. */
|
/* multiply and add */
|
/* multiply and add */
|
const int madbr; /* cost of multiply and add in DFmode. */
|
const int madbr; /* cost of multiply and add in DFmode. */
|
const int maebr; /* cost of multiply and add in SFmode. */
|
const int maebr; /* cost of multiply and add in SFmode. */
|
/* division */
|
/* division */
|
const int dxbr;
|
const int dxbr;
|
const int dxr;
|
const int dxr;
|
const int ddbr;
|
const int ddbr;
|
const int ddr;
|
const int ddr;
|
const int debr;
|
const int debr;
|
const int der;
|
const int der;
|
const int dlgr;
|
const int dlgr;
|
const int dlr;
|
const int dlr;
|
const int dr;
|
const int dr;
|
const int dsgfr;
|
const int dsgfr;
|
const int dsgr;
|
const int dsgr;
|
};
|
};
|
|
|
const struct processor_costs *s390_cost;
|
const struct processor_costs *s390_cost;
|
|
|
static const
|
static const
|
struct processor_costs z900_cost =
|
struct processor_costs z900_cost =
|
{
|
{
|
COSTS_N_INSNS (5), /* M */
|
COSTS_N_INSNS (5), /* M */
|
COSTS_N_INSNS (10), /* MGHI */
|
COSTS_N_INSNS (10), /* MGHI */
|
COSTS_N_INSNS (5), /* MH */
|
COSTS_N_INSNS (5), /* MH */
|
COSTS_N_INSNS (4), /* MHI */
|
COSTS_N_INSNS (4), /* MHI */
|
COSTS_N_INSNS (5), /* ML */
|
COSTS_N_INSNS (5), /* ML */
|
COSTS_N_INSNS (5), /* MR */
|
COSTS_N_INSNS (5), /* MR */
|
COSTS_N_INSNS (4), /* MS */
|
COSTS_N_INSNS (4), /* MS */
|
COSTS_N_INSNS (15), /* MSG */
|
COSTS_N_INSNS (15), /* MSG */
|
COSTS_N_INSNS (7), /* MSGF */
|
COSTS_N_INSNS (7), /* MSGF */
|
COSTS_N_INSNS (7), /* MSGFR */
|
COSTS_N_INSNS (7), /* MSGFR */
|
COSTS_N_INSNS (10), /* MSGR */
|
COSTS_N_INSNS (10), /* MSGR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (7), /* multiplication in DFmode */
|
COSTS_N_INSNS (7), /* multiplication in DFmode */
|
COSTS_N_INSNS (13), /* MXBR */
|
COSTS_N_INSNS (13), /* MXBR */
|
COSTS_N_INSNS (136), /* SQXBR */
|
COSTS_N_INSNS (136), /* SQXBR */
|
COSTS_N_INSNS (44), /* SQDBR */
|
COSTS_N_INSNS (44), /* SQDBR */
|
COSTS_N_INSNS (35), /* SQEBR */
|
COSTS_N_INSNS (35), /* SQEBR */
|
COSTS_N_INSNS (18), /* MADBR */
|
COSTS_N_INSNS (18), /* MADBR */
|
COSTS_N_INSNS (13), /* MAEBR */
|
COSTS_N_INSNS (13), /* MAEBR */
|
COSTS_N_INSNS (134), /* DXBR */
|
COSTS_N_INSNS (134), /* DXBR */
|
COSTS_N_INSNS (135), /* DXR */
|
COSTS_N_INSNS (135), /* DXR */
|
COSTS_N_INSNS (30), /* DDBR */
|
COSTS_N_INSNS (30), /* DDBR */
|
COSTS_N_INSNS (30), /* DDR */
|
COSTS_N_INSNS (30), /* DDR */
|
COSTS_N_INSNS (27), /* DEBR */
|
COSTS_N_INSNS (27), /* DEBR */
|
COSTS_N_INSNS (26), /* DER */
|
COSTS_N_INSNS (26), /* DER */
|
COSTS_N_INSNS (220), /* DLGR */
|
COSTS_N_INSNS (220), /* DLGR */
|
COSTS_N_INSNS (34), /* DLR */
|
COSTS_N_INSNS (34), /* DLR */
|
COSTS_N_INSNS (34), /* DR */
|
COSTS_N_INSNS (34), /* DR */
|
COSTS_N_INSNS (32), /* DSGFR */
|
COSTS_N_INSNS (32), /* DSGFR */
|
COSTS_N_INSNS (32), /* DSGR */
|
COSTS_N_INSNS (32), /* DSGR */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs z990_cost =
|
struct processor_costs z990_cost =
|
{
|
{
|
COSTS_N_INSNS (4), /* M */
|
COSTS_N_INSNS (4), /* M */
|
COSTS_N_INSNS (2), /* MGHI */
|
COSTS_N_INSNS (2), /* MGHI */
|
COSTS_N_INSNS (2), /* MH */
|
COSTS_N_INSNS (2), /* MH */
|
COSTS_N_INSNS (2), /* MHI */
|
COSTS_N_INSNS (2), /* MHI */
|
COSTS_N_INSNS (4), /* ML */
|
COSTS_N_INSNS (4), /* ML */
|
COSTS_N_INSNS (4), /* MR */
|
COSTS_N_INSNS (4), /* MR */
|
COSTS_N_INSNS (5), /* MS */
|
COSTS_N_INSNS (5), /* MS */
|
COSTS_N_INSNS (6), /* MSG */
|
COSTS_N_INSNS (6), /* MSG */
|
COSTS_N_INSNS (4), /* MSGF */
|
COSTS_N_INSNS (4), /* MSGF */
|
COSTS_N_INSNS (4), /* MSGFR */
|
COSTS_N_INSNS (4), /* MSGFR */
|
COSTS_N_INSNS (4), /* MSGR */
|
COSTS_N_INSNS (4), /* MSGR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (1), /* multiplication in DFmode */
|
COSTS_N_INSNS (1), /* multiplication in DFmode */
|
COSTS_N_INSNS (28), /* MXBR */
|
COSTS_N_INSNS (28), /* MXBR */
|
COSTS_N_INSNS (130), /* SQXBR */
|
COSTS_N_INSNS (130), /* SQXBR */
|
COSTS_N_INSNS (66), /* SQDBR */
|
COSTS_N_INSNS (66), /* SQDBR */
|
COSTS_N_INSNS (38), /* SQEBR */
|
COSTS_N_INSNS (38), /* SQEBR */
|
COSTS_N_INSNS (1), /* MADBR */
|
COSTS_N_INSNS (1), /* MADBR */
|
COSTS_N_INSNS (1), /* MAEBR */
|
COSTS_N_INSNS (1), /* MAEBR */
|
COSTS_N_INSNS (60), /* DXBR */
|
COSTS_N_INSNS (60), /* DXBR */
|
COSTS_N_INSNS (72), /* DXR */
|
COSTS_N_INSNS (72), /* DXR */
|
COSTS_N_INSNS (40), /* DDBR */
|
COSTS_N_INSNS (40), /* DDBR */
|
COSTS_N_INSNS (44), /* DDR */
|
COSTS_N_INSNS (44), /* DDR */
|
COSTS_N_INSNS (26), /* DDBR */
|
COSTS_N_INSNS (26), /* DDBR */
|
COSTS_N_INSNS (28), /* DER */
|
COSTS_N_INSNS (28), /* DER */
|
COSTS_N_INSNS (176), /* DLGR */
|
COSTS_N_INSNS (176), /* DLGR */
|
COSTS_N_INSNS (31), /* DLR */
|
COSTS_N_INSNS (31), /* DLR */
|
COSTS_N_INSNS (31), /* DR */
|
COSTS_N_INSNS (31), /* DR */
|
COSTS_N_INSNS (31), /* DSGFR */
|
COSTS_N_INSNS (31), /* DSGFR */
|
COSTS_N_INSNS (31), /* DSGR */
|
COSTS_N_INSNS (31), /* DSGR */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs z9_109_cost =
|
struct processor_costs z9_109_cost =
|
{
|
{
|
COSTS_N_INSNS (4), /* M */
|
COSTS_N_INSNS (4), /* M */
|
COSTS_N_INSNS (2), /* MGHI */
|
COSTS_N_INSNS (2), /* MGHI */
|
COSTS_N_INSNS (2), /* MH */
|
COSTS_N_INSNS (2), /* MH */
|
COSTS_N_INSNS (2), /* MHI */
|
COSTS_N_INSNS (2), /* MHI */
|
COSTS_N_INSNS (4), /* ML */
|
COSTS_N_INSNS (4), /* ML */
|
COSTS_N_INSNS (4), /* MR */
|
COSTS_N_INSNS (4), /* MR */
|
COSTS_N_INSNS (5), /* MS */
|
COSTS_N_INSNS (5), /* MS */
|
COSTS_N_INSNS (6), /* MSG */
|
COSTS_N_INSNS (6), /* MSG */
|
COSTS_N_INSNS (4), /* MSGF */
|
COSTS_N_INSNS (4), /* MSGF */
|
COSTS_N_INSNS (4), /* MSGFR */
|
COSTS_N_INSNS (4), /* MSGFR */
|
COSTS_N_INSNS (4), /* MSGR */
|
COSTS_N_INSNS (4), /* MSGR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (4), /* MSR */
|
COSTS_N_INSNS (1), /* multiplication in DFmode */
|
COSTS_N_INSNS (1), /* multiplication in DFmode */
|
COSTS_N_INSNS (28), /* MXBR */
|
COSTS_N_INSNS (28), /* MXBR */
|
COSTS_N_INSNS (130), /* SQXBR */
|
COSTS_N_INSNS (130), /* SQXBR */
|
COSTS_N_INSNS (66), /* SQDBR */
|
COSTS_N_INSNS (66), /* SQDBR */
|
COSTS_N_INSNS (38), /* SQEBR */
|
COSTS_N_INSNS (38), /* SQEBR */
|
COSTS_N_INSNS (1), /* MADBR */
|
COSTS_N_INSNS (1), /* MADBR */
|
COSTS_N_INSNS (1), /* MAEBR */
|
COSTS_N_INSNS (1), /* MAEBR */
|
COSTS_N_INSNS (60), /* DXBR */
|
COSTS_N_INSNS (60), /* DXBR */
|
COSTS_N_INSNS (72), /* DXR */
|
COSTS_N_INSNS (72), /* DXR */
|
COSTS_N_INSNS (40), /* DDBR */
|
COSTS_N_INSNS (40), /* DDBR */
|
COSTS_N_INSNS (37), /* DDR */
|
COSTS_N_INSNS (37), /* DDR */
|
COSTS_N_INSNS (26), /* DDBR */
|
COSTS_N_INSNS (26), /* DDBR */
|
COSTS_N_INSNS (28), /* DER */
|
COSTS_N_INSNS (28), /* DER */
|
COSTS_N_INSNS (30), /* DLGR */
|
COSTS_N_INSNS (30), /* DLGR */
|
COSTS_N_INSNS (23), /* DLR */
|
COSTS_N_INSNS (23), /* DLR */
|
COSTS_N_INSNS (23), /* DR */
|
COSTS_N_INSNS (23), /* DR */
|
COSTS_N_INSNS (24), /* DSGFR */
|
COSTS_N_INSNS (24), /* DSGFR */
|
COSTS_N_INSNS (24), /* DSGR */
|
COSTS_N_INSNS (24), /* DSGR */
|
};
|
};
|
|
|
extern int reload_completed;
|
extern int reload_completed;
|
|
|
/* Save information from a "cmpxx" operation until the branch or scc is
|
/* Save information from a "cmpxx" operation until the branch or scc is
|
emitted. */
|
emitted. */
|
rtx s390_compare_op0, s390_compare_op1;
|
rtx s390_compare_op0, s390_compare_op1;
|
|
|
/* Save the result of a compare_and_swap until the branch or scc is
|
/* Save the result of a compare_and_swap until the branch or scc is
|
emitted. */
|
emitted. */
|
rtx s390_compare_emitted = NULL_RTX;
|
rtx s390_compare_emitted = NULL_RTX;
|
|
|
/* Structure used to hold the components of a S/390 memory
|
/* Structure used to hold the components of a S/390 memory
|
address. A legitimate address on S/390 is of the general
|
address. A legitimate address on S/390 is of the general
|
form
|
form
|
base + index + displacement
|
base + index + displacement
|
where any of the components is optional.
|
where any of the components is optional.
|
|
|
base and index are registers of the class ADDR_REGS,
|
base and index are registers of the class ADDR_REGS,
|
displacement is an unsigned 12-bit immediate constant. */
|
displacement is an unsigned 12-bit immediate constant. */
|
|
|
struct s390_address
|
struct s390_address
|
{
|
{
|
rtx base;
|
rtx base;
|
rtx indx;
|
rtx indx;
|
rtx disp;
|
rtx disp;
|
bool pointer;
|
bool pointer;
|
bool literal_pool;
|
bool literal_pool;
|
};
|
};
|
|
|
/* Which cpu are we tuning for. */
|
/* Which cpu are we tuning for. */
|
enum processor_type s390_tune = PROCESSOR_max;
|
enum processor_type s390_tune = PROCESSOR_max;
|
enum processor_flags s390_tune_flags;
|
enum processor_flags s390_tune_flags;
|
/* Which instruction set architecture to use. */
|
/* Which instruction set architecture to use. */
|
enum processor_type s390_arch;
|
enum processor_type s390_arch;
|
enum processor_flags s390_arch_flags;
|
enum processor_flags s390_arch_flags;
|
|
|
HOST_WIDE_INT s390_warn_framesize = 0;
|
HOST_WIDE_INT s390_warn_framesize = 0;
|
HOST_WIDE_INT s390_stack_size = 0;
|
HOST_WIDE_INT s390_stack_size = 0;
|
HOST_WIDE_INT s390_stack_guard = 0;
|
HOST_WIDE_INT s390_stack_guard = 0;
|
|
|
/* The following structure is embedded in the machine
|
/* The following structure is embedded in the machine
|
specific part of struct function. */
|
specific part of struct function. */
|
|
|
struct s390_frame_layout GTY (())
|
struct s390_frame_layout GTY (())
|
{
|
{
|
/* Offset within stack frame. */
|
/* Offset within stack frame. */
|
HOST_WIDE_INT gprs_offset;
|
HOST_WIDE_INT gprs_offset;
|
HOST_WIDE_INT f0_offset;
|
HOST_WIDE_INT f0_offset;
|
HOST_WIDE_INT f4_offset;
|
HOST_WIDE_INT f4_offset;
|
HOST_WIDE_INT f8_offset;
|
HOST_WIDE_INT f8_offset;
|
HOST_WIDE_INT backchain_offset;
|
HOST_WIDE_INT backchain_offset;
|
|
|
/* Number of first and last gpr where slots in the register
|
/* Number of first and last gpr where slots in the register
|
save area are reserved for. */
|
save area are reserved for. */
|
int first_save_gpr_slot;
|
int first_save_gpr_slot;
|
int last_save_gpr_slot;
|
int last_save_gpr_slot;
|
|
|
/* Number of first and last gpr to be saved, restored. */
|
/* Number of first and last gpr to be saved, restored. */
|
int first_save_gpr;
|
int first_save_gpr;
|
int first_restore_gpr;
|
int first_restore_gpr;
|
int last_save_gpr;
|
int last_save_gpr;
|
int last_restore_gpr;
|
int last_restore_gpr;
|
|
|
/* Bits standing for floating point registers. Set, if the
|
/* Bits standing for floating point registers. Set, if the
|
respective register has to be saved. Starting with reg 16 (f0)
|
respective register has to be saved. Starting with reg 16 (f0)
|
at the rightmost bit.
|
at the rightmost bit.
|
Bit 15 - 8 7 6 5 4 3 2 1 0
|
Bit 15 - 8 7 6 5 4 3 2 1 0
|
fpr 15 - 8 7 5 3 1 6 4 2 0
|
fpr 15 - 8 7 5 3 1 6 4 2 0
|
reg 31 - 24 23 22 21 20 19 18 17 16 */
|
reg 31 - 24 23 22 21 20 19 18 17 16 */
|
unsigned int fpr_bitmap;
|
unsigned int fpr_bitmap;
|
|
|
/* Number of floating point registers f8-f15 which must be saved. */
|
/* Number of floating point registers f8-f15 which must be saved. */
|
int high_fprs;
|
int high_fprs;
|
|
|
/* Set if return address needs to be saved.
|
/* Set if return address needs to be saved.
|
This flag is set by s390_return_addr_rtx if it could not use
|
This flag is set by s390_return_addr_rtx if it could not use
|
the initial value of r14 and therefore depends on r14 saved
|
the initial value of r14 and therefore depends on r14 saved
|
to the stack. */
|
to the stack. */
|
bool save_return_addr_p;
|
bool save_return_addr_p;
|
|
|
/* Size of stack frame. */
|
/* Size of stack frame. */
|
HOST_WIDE_INT frame_size;
|
HOST_WIDE_INT frame_size;
|
};
|
};
|
|
|
/* Define the structure for the machine field in struct function. */
|
/* Define the structure for the machine field in struct function. */
|
|
|
struct machine_function GTY(())
|
struct machine_function GTY(())
|
{
|
{
|
struct s390_frame_layout frame_layout;
|
struct s390_frame_layout frame_layout;
|
|
|
/* Literal pool base register. */
|
/* Literal pool base register. */
|
rtx base_reg;
|
rtx base_reg;
|
|
|
/* True if we may need to perform branch splitting. */
|
/* True if we may need to perform branch splitting. */
|
bool split_branches_pending_p;
|
bool split_branches_pending_p;
|
|
|
/* True during final stage of literal pool processing. */
|
/* True during final stage of literal pool processing. */
|
bool decomposed_literal_pool_addresses_ok_p;
|
bool decomposed_literal_pool_addresses_ok_p;
|
|
|
/* Some local-dynamic TLS symbol name. */
|
/* Some local-dynamic TLS symbol name. */
|
const char *some_ld_name;
|
const char *some_ld_name;
|
|
|
bool has_landing_pad_p;
|
bool has_landing_pad_p;
|
};
|
};
|
|
|
/* Few accessor macros for struct cfun->machine->s390_frame_layout. */
|
/* Few accessor macros for struct cfun->machine->s390_frame_layout. */
|
|
|
#define cfun_frame_layout (cfun->machine->frame_layout)
|
#define cfun_frame_layout (cfun->machine->frame_layout)
|
#define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs)
|
#define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs)
|
#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \
|
#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \
|
cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD)
|
cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD)
|
#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |= \
|
#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |= \
|
(1 << (BITNUM)))
|
(1 << (BITNUM)))
|
#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \
|
#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \
|
(1 << (BITNUM))))
|
(1 << (BITNUM))))
|
|
|
/* Number of GPRs and FPRs used for argument passing. */
|
/* Number of GPRs and FPRs used for argument passing. */
|
#define GP_ARG_NUM_REG 5
|
#define GP_ARG_NUM_REG 5
|
#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2)
|
#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2)
|
|
|
/* A couple of shortcuts. */
|
/* A couple of shortcuts. */
|
#define CONST_OK_FOR_J(x) \
|
#define CONST_OK_FOR_J(x) \
|
CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J")
|
CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J")
|
#define CONST_OK_FOR_K(x) \
|
#define CONST_OK_FOR_K(x) \
|
CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K")
|
CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K")
|
#define CONST_OK_FOR_Os(x) \
|
#define CONST_OK_FOR_Os(x) \
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os")
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os")
|
#define CONST_OK_FOR_Op(x) \
|
#define CONST_OK_FOR_Op(x) \
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op")
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op")
|
#define CONST_OK_FOR_On(x) \
|
#define CONST_OK_FOR_On(x) \
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On")
|
CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On")
|
|
|
#define REGNO_PAIR_OK(REGNO, MODE) \
|
#define REGNO_PAIR_OK(REGNO, MODE) \
|
(HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
|
(HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
|
|
|
/* Return true if the back end supports mode MODE. */
|
/* Return true if the back end supports mode MODE. */
|
static bool
|
static bool
|
s390_scalar_mode_supported_p (enum machine_mode mode)
|
s390_scalar_mode_supported_p (enum machine_mode mode)
|
{
|
{
|
if (DECIMAL_FLOAT_MODE_P (mode))
|
if (DECIMAL_FLOAT_MODE_P (mode))
|
return true;
|
return true;
|
else
|
else
|
return default_scalar_mode_supported_p (mode);
|
return default_scalar_mode_supported_p (mode);
|
}
|
}
|
|
|
/* Set the has_landing_pad_p flag in struct machine_function to VALUE. */
|
/* Set the has_landing_pad_p flag in struct machine_function to VALUE. */
|
|
|
void
|
void
|
s390_set_has_landing_pad_p (bool value)
|
s390_set_has_landing_pad_p (bool value)
|
{
|
{
|
cfun->machine->has_landing_pad_p = value;
|
cfun->machine->has_landing_pad_p = value;
|
}
|
}
|
|
|
/* If two condition code modes are compatible, return a condition code
|
/* If two condition code modes are compatible, return a condition code
|
mode which is compatible with both. Otherwise, return
|
mode which is compatible with both. Otherwise, return
|
VOIDmode. */
|
VOIDmode. */
|
|
|
static enum machine_mode
|
static enum machine_mode
|
s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
|
s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
|
{
|
{
|
if (m1 == m2)
|
if (m1 == m2)
|
return m1;
|
return m1;
|
|
|
switch (m1)
|
switch (m1)
|
{
|
{
|
case CCZmode:
|
case CCZmode:
|
if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode
|
if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode
|
|| m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
|
|| m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
|
return m2;
|
return m2;
|
return VOIDmode;
|
return VOIDmode;
|
|
|
case CCSmode:
|
case CCSmode:
|
case CCUmode:
|
case CCUmode:
|
case CCTmode:
|
case CCTmode:
|
case CCSRmode:
|
case CCSRmode:
|
case CCURmode:
|
case CCURmode:
|
case CCZ1mode:
|
case CCZ1mode:
|
if (m2 == CCZmode)
|
if (m2 == CCZmode)
|
return m1;
|
return m1;
|
|
|
return VOIDmode;
|
return VOIDmode;
|
|
|
default:
|
default:
|
return VOIDmode;
|
return VOIDmode;
|
}
|
}
|
return VOIDmode;
|
return VOIDmode;
|
}
|
}
|
|
|
/* Return true if SET either doesn't set the CC register, or else
|
/* Return true if SET either doesn't set the CC register, or else
|
the source and destination have matching CC modes and that
|
the source and destination have matching CC modes and that
|
CC mode is at least as constrained as REQ_MODE. */
|
CC mode is at least as constrained as REQ_MODE. */
|
|
|
static bool
|
static bool
|
s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
|
s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
|
{
|
{
|
enum machine_mode set_mode;
|
enum machine_mode set_mode;
|
|
|
gcc_assert (GET_CODE (set) == SET);
|
gcc_assert (GET_CODE (set) == SET);
|
|
|
if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
|
if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
|
return 1;
|
return 1;
|
|
|
set_mode = GET_MODE (SET_DEST (set));
|
set_mode = GET_MODE (SET_DEST (set));
|
switch (set_mode)
|
switch (set_mode)
|
{
|
{
|
case CCSmode:
|
case CCSmode:
|
case CCSRmode:
|
case CCSRmode:
|
case CCUmode:
|
case CCUmode:
|
case CCURmode:
|
case CCURmode:
|
case CCLmode:
|
case CCLmode:
|
case CCL1mode:
|
case CCL1mode:
|
case CCL2mode:
|
case CCL2mode:
|
case CCL3mode:
|
case CCL3mode:
|
case CCT1mode:
|
case CCT1mode:
|
case CCT2mode:
|
case CCT2mode:
|
case CCT3mode:
|
case CCT3mode:
|
if (req_mode != set_mode)
|
if (req_mode != set_mode)
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
case CCZmode:
|
case CCZmode:
|
if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode
|
if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode
|
&& req_mode != CCSRmode && req_mode != CCURmode)
|
&& req_mode != CCSRmode && req_mode != CCURmode)
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
case CCAPmode:
|
case CCAPmode:
|
case CCANmode:
|
case CCANmode:
|
if (req_mode != CCAmode)
|
if (req_mode != CCAmode)
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
return (GET_MODE (SET_SRC (set)) == set_mode);
|
return (GET_MODE (SET_SRC (set)) == set_mode);
|
}
|
}
|
|
|
/* Return true if every SET in INSN that sets the CC register
|
/* Return true if every SET in INSN that sets the CC register
|
has source and destination with matching CC modes and that
|
has source and destination with matching CC modes and that
|
CC mode is at least as constrained as REQ_MODE.
|
CC mode is at least as constrained as REQ_MODE.
|
If REQ_MODE is VOIDmode, always return false. */
|
If REQ_MODE is VOIDmode, always return false. */
|
|
|
bool
|
bool
|
s390_match_ccmode (rtx insn, enum machine_mode req_mode)
|
s390_match_ccmode (rtx insn, enum machine_mode req_mode)
|
{
|
{
|
int i;
|
int i;
|
|
|
/* s390_tm_ccmode returns VOIDmode to indicate failure. */
|
/* s390_tm_ccmode returns VOIDmode to indicate failure. */
|
if (req_mode == VOIDmode)
|
if (req_mode == VOIDmode)
|
return false;
|
return false;
|
|
|
if (GET_CODE (PATTERN (insn)) == SET)
|
if (GET_CODE (PATTERN (insn)) == SET)
|
return s390_match_ccmode_set (PATTERN (insn), req_mode);
|
return s390_match_ccmode_set (PATTERN (insn), req_mode);
|
|
|
if (GET_CODE (PATTERN (insn)) == PARALLEL)
|
if (GET_CODE (PATTERN (insn)) == PARALLEL)
|
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
|
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
|
{
|
{
|
rtx set = XVECEXP (PATTERN (insn), 0, i);
|
rtx set = XVECEXP (PATTERN (insn), 0, i);
|
if (GET_CODE (set) == SET)
|
if (GET_CODE (set) == SET)
|
if (!s390_match_ccmode_set (set, req_mode))
|
if (!s390_match_ccmode_set (set, req_mode))
|
return false;
|
return false;
|
}
|
}
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* If a test-under-mask instruction can be used to implement
|
/* If a test-under-mask instruction can be used to implement
|
(compare (and ... OP1) OP2), return the CC mode required
|
(compare (and ... OP1) OP2), return the CC mode required
|
to do that. Otherwise, return VOIDmode.
|
to do that. Otherwise, return VOIDmode.
|
MIXED is true if the instruction can distinguish between
|
MIXED is true if the instruction can distinguish between
|
CC1 and CC2 for mixed selected bits (TMxx), it is false
|
CC1 and CC2 for mixed selected bits (TMxx), it is false
|
if the instruction cannot (TM). */
|
if the instruction cannot (TM). */
|
|
|
enum machine_mode
|
enum machine_mode
|
s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
|
s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
|
{
|
{
|
int bit0, bit1;
|
int bit0, bit1;
|
|
|
/* ??? Fixme: should work on CONST_DOUBLE as well. */
|
/* ??? Fixme: should work on CONST_DOUBLE as well. */
|
if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT)
|
if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT)
|
return VOIDmode;
|
return VOIDmode;
|
|
|
/* Selected bits all zero: CC0.
|
/* Selected bits all zero: CC0.
|
e.g.: int a; if ((a & (16 + 128)) == 0) */
|
e.g.: int a; if ((a & (16 + 128)) == 0) */
|
if (INTVAL (op2) == 0)
|
if (INTVAL (op2) == 0)
|
return CCTmode;
|
return CCTmode;
|
|
|
/* Selected bits all one: CC3.
|
/* Selected bits all one: CC3.
|
e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */
|
e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */
|
if (INTVAL (op2) == INTVAL (op1))
|
if (INTVAL (op2) == INTVAL (op1))
|
return CCT3mode;
|
return CCT3mode;
|
|
|
/* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.:
|
/* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.:
|
int a;
|
int a;
|
if ((a & (16 + 128)) == 16) -> CCT1
|
if ((a & (16 + 128)) == 16) -> CCT1
|
if ((a & (16 + 128)) == 128) -> CCT2 */
|
if ((a & (16 + 128)) == 128) -> CCT2 */
|
if (mixed)
|
if (mixed)
|
{
|
{
|
bit1 = exact_log2 (INTVAL (op2));
|
bit1 = exact_log2 (INTVAL (op2));
|
bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2));
|
bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2));
|
if (bit0 != -1 && bit1 != -1)
|
if (bit0 != -1 && bit1 != -1)
|
return bit0 > bit1 ? CCT1mode : CCT2mode;
|
return bit0 > bit1 ? CCT1mode : CCT2mode;
|
}
|
}
|
|
|
return VOIDmode;
|
return VOIDmode;
|
}
|
}
|
|
|
/* Given a comparison code OP (EQ, NE, etc.) and the operands
|
/* Given a comparison code OP (EQ, NE, etc.) and the operands
|
OP0 and OP1 of a COMPARE, return the mode to be used for the
|
OP0 and OP1 of a COMPARE, return the mode to be used for the
|
comparison. */
|
comparison. */
|
|
|
enum machine_mode
|
enum machine_mode
|
s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
|
s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case EQ:
|
case EQ:
|
case NE:
|
case NE:
|
if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
|
if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
return CCAPmode;
|
return CCAPmode;
|
if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
|
if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
|
&& CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
|
&& CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
|
return CCAPmode;
|
return CCAPmode;
|
if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|
if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|
|| GET_CODE (op1) == NEG)
|
|| GET_CODE (op1) == NEG)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
return CCLmode;
|
return CCLmode;
|
|
|
if (GET_CODE (op0) == AND)
|
if (GET_CODE (op0) == AND)
|
{
|
{
|
/* Check whether we can potentially do it via TM. */
|
/* Check whether we can potentially do it via TM. */
|
enum machine_mode ccmode;
|
enum machine_mode ccmode;
|
ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1);
|
ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1);
|
if (ccmode != VOIDmode)
|
if (ccmode != VOIDmode)
|
{
|
{
|
/* Relax CCTmode to CCZmode to allow fall-back to AND
|
/* Relax CCTmode to CCZmode to allow fall-back to AND
|
if that turns out to be beneficial. */
|
if that turns out to be beneficial. */
|
return ccmode == CCTmode ? CCZmode : ccmode;
|
return ccmode == CCTmode ? CCZmode : ccmode;
|
}
|
}
|
}
|
}
|
|
|
if (register_operand (op0, HImode)
|
if (register_operand (op0, HImode)
|
&& GET_CODE (op1) == CONST_INT
|
&& GET_CODE (op1) == CONST_INT
|
&& (INTVAL (op1) == -1 || INTVAL (op1) == 65535))
|
&& (INTVAL (op1) == -1 || INTVAL (op1) == 65535))
|
return CCT3mode;
|
return CCT3mode;
|
if (register_operand (op0, QImode)
|
if (register_operand (op0, QImode)
|
&& GET_CODE (op1) == CONST_INT
|
&& GET_CODE (op1) == CONST_INT
|
&& (INTVAL (op1) == -1 || INTVAL (op1) == 255))
|
&& (INTVAL (op1) == -1 || INTVAL (op1) == 255))
|
return CCT3mode;
|
return CCT3mode;
|
|
|
return CCZmode;
|
return CCZmode;
|
|
|
case LE:
|
case LE:
|
case LT:
|
case LT:
|
case GE:
|
case GE:
|
case GT:
|
case GT:
|
/* The only overflow condition of NEG and ABS happens when
|
/* The only overflow condition of NEG and ABS happens when
|
-INT_MAX is used as parameter, which stays negative. So
|
-INT_MAX is used as parameter, which stays negative. So
|
we have an overflow from a positive value to a negative.
|
we have an overflow from a positive value to a negative.
|
Using CCAP mode the resulting cc can be used for comparisons. */
|
Using CCAP mode the resulting cc can be used for comparisons. */
|
if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
|
if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
return CCAPmode;
|
return CCAPmode;
|
|
|
/* If constants are involved in an add instruction it is possible to use
|
/* If constants are involved in an add instruction it is possible to use
|
the resulting cc for comparisons with zero. Knowing the sign of the
|
the resulting cc for comparisons with zero. Knowing the sign of the
|
constant the overflow behavior gets predictable. e.g.:
|
constant the overflow behavior gets predictable. e.g.:
|
int a, b; if ((b = a + c) > 0)
|
int a, b; if ((b = a + c) > 0)
|
with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP */
|
with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP */
|
if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
|
if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
|
&& CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
|
&& CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
|
{
|
{
|
if (INTVAL (XEXP((op0), 1)) < 0)
|
if (INTVAL (XEXP((op0), 1)) < 0)
|
return CCANmode;
|
return CCANmode;
|
else
|
else
|
return CCAPmode;
|
return CCAPmode;
|
}
|
}
|
/* Fall through. */
|
/* Fall through. */
|
case UNORDERED:
|
case UNORDERED:
|
case ORDERED:
|
case ORDERED:
|
case UNEQ:
|
case UNEQ:
|
case UNLE:
|
case UNLE:
|
case UNLT:
|
case UNLT:
|
case UNGE:
|
case UNGE:
|
case UNGT:
|
case UNGT:
|
case LTGT:
|
case LTGT:
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
&& GET_CODE (op1) != CONST_INT)
|
&& GET_CODE (op1) != CONST_INT)
|
return CCSRmode;
|
return CCSRmode;
|
return CCSmode;
|
return CCSmode;
|
|
|
case LTU:
|
case LTU:
|
case GEU:
|
case GEU:
|
if (GET_CODE (op0) == PLUS
|
if (GET_CODE (op0) == PLUS
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
return CCL1mode;
|
return CCL1mode;
|
|
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
&& GET_CODE (op1) != CONST_INT)
|
&& GET_CODE (op1) != CONST_INT)
|
return CCURmode;
|
return CCURmode;
|
return CCUmode;
|
return CCUmode;
|
|
|
case LEU:
|
case LEU:
|
case GTU:
|
case GTU:
|
if (GET_CODE (op0) == MINUS
|
if (GET_CODE (op0) == MINUS
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
|
return CCL2mode;
|
return CCL2mode;
|
|
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
|
&& GET_CODE (op1) != CONST_INT)
|
&& GET_CODE (op1) != CONST_INT)
|
return CCURmode;
|
return CCURmode;
|
return CCUmode;
|
return CCUmode;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
|
/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
|
that we can implement more efficiently. */
|
that we can implement more efficiently. */
|
|
|
void
|
void
|
s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
|
s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
|
{
|
{
|
/* Convert ZERO_EXTRACT back to AND to enable TM patterns. */
|
/* Convert ZERO_EXTRACT back to AND to enable TM patterns. */
|
if ((*code == EQ || *code == NE)
|
if ((*code == EQ || *code == NE)
|
&& *op1 == const0_rtx
|
&& *op1 == const0_rtx
|
&& GET_CODE (*op0) == ZERO_EXTRACT
|
&& GET_CODE (*op0) == ZERO_EXTRACT
|
&& GET_CODE (XEXP (*op0, 1)) == CONST_INT
|
&& GET_CODE (XEXP (*op0, 1)) == CONST_INT
|
&& GET_CODE (XEXP (*op0, 2)) == CONST_INT
|
&& GET_CODE (XEXP (*op0, 2)) == CONST_INT
|
&& SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
|
&& SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
|
{
|
{
|
rtx inner = XEXP (*op0, 0);
|
rtx inner = XEXP (*op0, 0);
|
HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner));
|
HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner));
|
HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1));
|
HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1));
|
HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2));
|
HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2));
|
|
|
if (len > 0 && len < modesize
|
if (len > 0 && len < modesize
|
&& pos >= 0 && pos + len <= modesize
|
&& pos >= 0 && pos + len <= modesize
|
&& modesize <= HOST_BITS_PER_WIDE_INT)
|
&& modesize <= HOST_BITS_PER_WIDE_INT)
|
{
|
{
|
unsigned HOST_WIDE_INT block;
|
unsigned HOST_WIDE_INT block;
|
block = ((unsigned HOST_WIDE_INT) 1 << len) - 1;
|
block = ((unsigned HOST_WIDE_INT) 1 << len) - 1;
|
block <<= modesize - pos - len;
|
block <<= modesize - pos - len;
|
|
|
*op0 = gen_rtx_AND (GET_MODE (inner), inner,
|
*op0 = gen_rtx_AND (GET_MODE (inner), inner,
|
gen_int_mode (block, GET_MODE (inner)));
|
gen_int_mode (block, GET_MODE (inner)));
|
}
|
}
|
}
|
}
|
|
|
/* Narrow AND of memory against immediate to enable TM. */
|
/* Narrow AND of memory against immediate to enable TM. */
|
if ((*code == EQ || *code == NE)
|
if ((*code == EQ || *code == NE)
|
&& *op1 == const0_rtx
|
&& *op1 == const0_rtx
|
&& GET_CODE (*op0) == AND
|
&& GET_CODE (*op0) == AND
|
&& GET_CODE (XEXP (*op0, 1)) == CONST_INT
|
&& GET_CODE (XEXP (*op0, 1)) == CONST_INT
|
&& SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
|
&& SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
|
{
|
{
|
rtx inner = XEXP (*op0, 0);
|
rtx inner = XEXP (*op0, 0);
|
rtx mask = XEXP (*op0, 1);
|
rtx mask = XEXP (*op0, 1);
|
|
|
/* Ignore paradoxical SUBREGs if all extra bits are masked out. */
|
/* Ignore paradoxical SUBREGs if all extra bits are masked out. */
|
if (GET_CODE (inner) == SUBREG
|
if (GET_CODE (inner) == SUBREG
|
&& SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner)))
|
&& SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner)))
|
&& (GET_MODE_SIZE (GET_MODE (inner))
|
&& (GET_MODE_SIZE (GET_MODE (inner))
|
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
|
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
|
&& ((INTVAL (mask)
|
&& ((INTVAL (mask)
|
& GET_MODE_MASK (GET_MODE (inner))
|
& GET_MODE_MASK (GET_MODE (inner))
|
& ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner))))
|
& ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner))))
|
== 0))
|
== 0))
|
inner = SUBREG_REG (inner);
|
inner = SUBREG_REG (inner);
|
|
|
/* Do not change volatile MEMs. */
|
/* Do not change volatile MEMs. */
|
if (MEM_P (inner) && !MEM_VOLATILE_P (inner))
|
if (MEM_P (inner) && !MEM_VOLATILE_P (inner))
|
{
|
{
|
int part = s390_single_part (XEXP (*op0, 1),
|
int part = s390_single_part (XEXP (*op0, 1),
|
GET_MODE (inner), QImode, 0);
|
GET_MODE (inner), QImode, 0);
|
if (part >= 0)
|
if (part >= 0)
|
{
|
{
|
mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode);
|
mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode);
|
inner = adjust_address_nv (inner, QImode, part);
|
inner = adjust_address_nv (inner, QImode, part);
|
*op0 = gen_rtx_AND (QImode, inner, mask);
|
*op0 = gen_rtx_AND (QImode, inner, mask);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Narrow comparisons against 0xffff to HImode if possible. */
|
/* Narrow comparisons against 0xffff to HImode if possible. */
|
if ((*code == EQ || *code == NE)
|
if ((*code == EQ || *code == NE)
|
&& GET_CODE (*op1) == CONST_INT
|
&& GET_CODE (*op1) == CONST_INT
|
&& INTVAL (*op1) == 0xffff
|
&& INTVAL (*op1) == 0xffff
|
&& SCALAR_INT_MODE_P (GET_MODE (*op0))
|
&& SCALAR_INT_MODE_P (GET_MODE (*op0))
|
&& (nonzero_bits (*op0, GET_MODE (*op0))
|
&& (nonzero_bits (*op0, GET_MODE (*op0))
|
& ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
|
& ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
|
{
|
{
|
*op0 = gen_lowpart (HImode, *op0);
|
*op0 = gen_lowpart (HImode, *op0);
|
*op1 = constm1_rtx;
|
*op1 = constm1_rtx;
|
}
|
}
|
|
|
|
|
/* Remove redundant UNSPEC_CMPINT conversions if possible. */
|
/* Remove redundant UNSPEC_CMPINT conversions if possible. */
|
if (GET_CODE (*op0) == UNSPEC
|
if (GET_CODE (*op0) == UNSPEC
|
&& XINT (*op0, 1) == UNSPEC_CMPINT
|
&& XINT (*op0, 1) == UNSPEC_CMPINT
|
&& XVECLEN (*op0, 0) == 1
|
&& XVECLEN (*op0, 0) == 1
|
&& GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
|
&& GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
|
&& GET_CODE (XVECEXP (*op0, 0, 0)) == REG
|
&& GET_CODE (XVECEXP (*op0, 0, 0)) == REG
|
&& REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
|
&& REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
|
&& *op1 == const0_rtx)
|
&& *op1 == const0_rtx)
|
{
|
{
|
enum rtx_code new_code = UNKNOWN;
|
enum rtx_code new_code = UNKNOWN;
|
switch (*code)
|
switch (*code)
|
{
|
{
|
case EQ: new_code = EQ; break;
|
case EQ: new_code = EQ; break;
|
case NE: new_code = NE; break;
|
case NE: new_code = NE; break;
|
case LT: new_code = GTU; break;
|
case LT: new_code = GTU; break;
|
case GT: new_code = LTU; break;
|
case GT: new_code = LTU; break;
|
case LE: new_code = GEU; break;
|
case LE: new_code = GEU; break;
|
case GE: new_code = LEU; break;
|
case GE: new_code = LEU; break;
|
default: break;
|
default: break;
|
}
|
}
|
|
|
if (new_code != UNKNOWN)
|
if (new_code != UNKNOWN)
|
{
|
{
|
*op0 = XVECEXP (*op0, 0, 0);
|
*op0 = XVECEXP (*op0, 0, 0);
|
*code = new_code;
|
*code = new_code;
|
}
|
}
|
}
|
}
|
|
|
/* Simplify cascaded EQ, NE with const0_rtx. */
|
/* Simplify cascaded EQ, NE with const0_rtx. */
|
if ((*code == NE || *code == EQ)
|
if ((*code == NE || *code == EQ)
|
&& (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE)
|
&& (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE)
|
&& GET_MODE (*op0) == SImode
|
&& GET_MODE (*op0) == SImode
|
&& GET_MODE (XEXP (*op0, 0)) == CCZ1mode
|
&& GET_MODE (XEXP (*op0, 0)) == CCZ1mode
|
&& REG_P (XEXP (*op0, 0))
|
&& REG_P (XEXP (*op0, 0))
|
&& XEXP (*op0, 1) == const0_rtx
|
&& XEXP (*op0, 1) == const0_rtx
|
&& *op1 == const0_rtx)
|
&& *op1 == const0_rtx)
|
{
|
{
|
if ((*code == EQ && GET_CODE (*op0) == NE)
|
if ((*code == EQ && GET_CODE (*op0) == NE)
|
|| (*code == NE && GET_CODE (*op0) == EQ))
|
|| (*code == NE && GET_CODE (*op0) == EQ))
|
*code = EQ;
|
*code = EQ;
|
else
|
else
|
*code = NE;
|
*code = NE;
|
*op0 = XEXP (*op0, 0);
|
*op0 = XEXP (*op0, 0);
|
}
|
}
|
|
|
/* Prefer register over memory as first operand. */
|
/* Prefer register over memory as first operand. */
|
if (MEM_P (*op0) && REG_P (*op1))
|
if (MEM_P (*op0) && REG_P (*op1))
|
{
|
{
|
rtx tem = *op0; *op0 = *op1; *op1 = tem;
|
rtx tem = *op0; *op0 = *op1; *op1 = tem;
|
*code = swap_condition (*code);
|
*code = swap_condition (*code);
|
}
|
}
|
}
|
}
|
|
|
/* Emit a compare instruction suitable to implement the comparison
|
/* Emit a compare instruction suitable to implement the comparison
|
OP0 CODE OP1. Return the correct condition RTL to be placed in
|
OP0 CODE OP1. Return the correct condition RTL to be placed in
|
the IF_THEN_ELSE of the conditional branch testing the result. */
|
the IF_THEN_ELSE of the conditional branch testing the result. */
|
|
|
rtx
|
rtx
|
s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
|
s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
|
{
|
{
|
enum machine_mode mode = s390_select_ccmode (code, op0, op1);
|
enum machine_mode mode = s390_select_ccmode (code, op0, op1);
|
rtx ret = NULL_RTX;
|
rtx ret = NULL_RTX;
|
|
|
/* Do not output a redundant compare instruction if a compare_and_swap
|
/* Do not output a redundant compare instruction if a compare_and_swap
|
pattern already computed the result and the machine modes are compatible. */
|
pattern already computed the result and the machine modes are compatible. */
|
if (s390_compare_emitted
|
if (s390_compare_emitted
|
&& (s390_cc_modes_compatible (GET_MODE (s390_compare_emitted), mode)
|
&& (s390_cc_modes_compatible (GET_MODE (s390_compare_emitted), mode)
|
== GET_MODE (s390_compare_emitted)))
|
== GET_MODE (s390_compare_emitted)))
|
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
|
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
|
else
|
else
|
{
|
{
|
rtx cc = gen_rtx_REG (mode, CC_REGNUM);
|
rtx cc = gen_rtx_REG (mode, CC_REGNUM);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
|
emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
|
ret = gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx);
|
ret = gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx);
|
}
|
}
|
s390_compare_emitted = NULL_RTX;
|
s390_compare_emitted = NULL_RTX;
|
return ret;
|
return ret;
|
}
|
}
|
|
|
/* Emit a SImode compare and swap instruction setting MEM to NEW if OLD
|
/* Emit a SImode compare and swap instruction setting MEM to NEW if OLD
|
matches CMP.
|
matches CMP.
|
Return the correct condition RTL to be placed in the IF_THEN_ELSE of the
|
Return the correct condition RTL to be placed in the IF_THEN_ELSE of the
|
conditional branch testing the result. */
|
conditional branch testing the result. */
|
|
|
static rtx
|
static rtx
|
s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new)
|
s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new)
|
{
|
{
|
rtx ret;
|
rtx ret;
|
|
|
emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new));
|
emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new));
|
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
|
ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
|
|
|
s390_compare_emitted = NULL_RTX;
|
s390_compare_emitted = NULL_RTX;
|
|
|
return ret;
|
return ret;
|
}
|
}
|
|
|
/* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an
|
/* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an
|
unconditional jump, else a conditional jump under condition COND. */
|
unconditional jump, else a conditional jump under condition COND. */
|
|
|
void
|
void
|
s390_emit_jump (rtx target, rtx cond)
|
s390_emit_jump (rtx target, rtx cond)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
target = gen_rtx_LABEL_REF (VOIDmode, target);
|
target = gen_rtx_LABEL_REF (VOIDmode, target);
|
if (cond)
|
if (cond)
|
target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx);
|
target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx);
|
|
|
insn = gen_rtx_SET (VOIDmode, pc_rtx, target);
|
insn = gen_rtx_SET (VOIDmode, pc_rtx, target);
|
emit_jump_insn (insn);
|
emit_jump_insn (insn);
|
}
|
}
|
|
|
/* Return branch condition mask to implement a branch
|
/* Return branch condition mask to implement a branch
|
specified by CODE. Return -1 for invalid comparisons. */
|
specified by CODE. Return -1 for invalid comparisons. */
|
|
|
int
|
int
|
s390_branch_condition_mask (rtx code)
|
s390_branch_condition_mask (rtx code)
|
{
|
{
|
const int CC0 = 1 << 3;
|
const int CC0 = 1 << 3;
|
const int CC1 = 1 << 2;
|
const int CC1 = 1 << 2;
|
const int CC2 = 1 << 1;
|
const int CC2 = 1 << 1;
|
const int CC3 = 1 << 0;
|
const int CC3 = 1 << 0;
|
|
|
gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
|
gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
|
gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
|
gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
|
gcc_assert (XEXP (code, 1) == const0_rtx);
|
gcc_assert (XEXP (code, 1) == const0_rtx);
|
|
|
switch (GET_MODE (XEXP (code, 0)))
|
switch (GET_MODE (XEXP (code, 0)))
|
{
|
{
|
case CCZmode:
|
case CCZmode:
|
case CCZ1mode:
|
case CCZ1mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC1 | CC2 | CC3;
|
case NE: return CC1 | CC2 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCT1mode:
|
case CCT1mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC1;
|
case EQ: return CC1;
|
case NE: return CC0 | CC2 | CC3;
|
case NE: return CC0 | CC2 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCT2mode:
|
case CCT2mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC2;
|
case EQ: return CC2;
|
case NE: return CC0 | CC1 | CC3;
|
case NE: return CC0 | CC1 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCT3mode:
|
case CCT3mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC3;
|
case EQ: return CC3;
|
case NE: return CC0 | CC1 | CC2;
|
case NE: return CC0 | CC1 | CC2;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCLmode:
|
case CCLmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0 | CC2;
|
case EQ: return CC0 | CC2;
|
case NE: return CC1 | CC3;
|
case NE: return CC1 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCL1mode:
|
case CCL1mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case LTU: return CC2 | CC3; /* carry */
|
case LTU: return CC2 | CC3; /* carry */
|
case GEU: return CC0 | CC1; /* no carry */
|
case GEU: return CC0 | CC1; /* no carry */
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCL2mode:
|
case CCL2mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case GTU: return CC0 | CC1; /* borrow */
|
case GTU: return CC0 | CC1; /* borrow */
|
case LEU: return CC2 | CC3; /* no borrow */
|
case LEU: return CC2 | CC3; /* no borrow */
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCL3mode:
|
case CCL3mode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0 | CC2;
|
case EQ: return CC0 | CC2;
|
case NE: return CC1 | CC3;
|
case NE: return CC1 | CC3;
|
case LTU: return CC1;
|
case LTU: return CC1;
|
case GTU: return CC3;
|
case GTU: return CC3;
|
case LEU: return CC1 | CC2;
|
case LEU: return CC1 | CC2;
|
case GEU: return CC2 | CC3;
|
case GEU: return CC2 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
|
|
case CCUmode:
|
case CCUmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC1 | CC2 | CC3;
|
case NE: return CC1 | CC2 | CC3;
|
case LTU: return CC1;
|
case LTU: return CC1;
|
case GTU: return CC2;
|
case GTU: return CC2;
|
case LEU: return CC0 | CC1;
|
case LEU: return CC0 | CC1;
|
case GEU: return CC0 | CC2;
|
case GEU: return CC0 | CC2;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCURmode:
|
case CCURmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC2 | CC1 | CC3;
|
case NE: return CC2 | CC1 | CC3;
|
case LTU: return CC2;
|
case LTU: return CC2;
|
case GTU: return CC1;
|
case GTU: return CC1;
|
case LEU: return CC0 | CC2;
|
case LEU: return CC0 | CC2;
|
case GEU: return CC0 | CC1;
|
case GEU: return CC0 | CC1;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCAPmode:
|
case CCAPmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC1 | CC2 | CC3;
|
case NE: return CC1 | CC2 | CC3;
|
case LT: return CC1 | CC3;
|
case LT: return CC1 | CC3;
|
case GT: return CC2;
|
case GT: return CC2;
|
case LE: return CC0 | CC1 | CC3;
|
case LE: return CC0 | CC1 | CC3;
|
case GE: return CC0 | CC2;
|
case GE: return CC0 | CC2;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCANmode:
|
case CCANmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC1 | CC2 | CC3;
|
case NE: return CC1 | CC2 | CC3;
|
case LT: return CC1;
|
case LT: return CC1;
|
case GT: return CC2 | CC3;
|
case GT: return CC2 | CC3;
|
case LE: return CC0 | CC1;
|
case LE: return CC0 | CC1;
|
case GE: return CC0 | CC2 | CC3;
|
case GE: return CC0 | CC2 | CC3;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCSmode:
|
case CCSmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC1 | CC2 | CC3;
|
case NE: return CC1 | CC2 | CC3;
|
case LT: return CC1;
|
case LT: return CC1;
|
case GT: return CC2;
|
case GT: return CC2;
|
case LE: return CC0 | CC1;
|
case LE: return CC0 | CC1;
|
case GE: return CC0 | CC2;
|
case GE: return CC0 | CC2;
|
case UNORDERED: return CC3;
|
case UNORDERED: return CC3;
|
case ORDERED: return CC0 | CC1 | CC2;
|
case ORDERED: return CC0 | CC1 | CC2;
|
case UNEQ: return CC0 | CC3;
|
case UNEQ: return CC0 | CC3;
|
case UNLT: return CC1 | CC3;
|
case UNLT: return CC1 | CC3;
|
case UNGT: return CC2 | CC3;
|
case UNGT: return CC2 | CC3;
|
case UNLE: return CC0 | CC1 | CC3;
|
case UNLE: return CC0 | CC1 | CC3;
|
case UNGE: return CC0 | CC2 | CC3;
|
case UNGE: return CC0 | CC2 | CC3;
|
case LTGT: return CC1 | CC2;
|
case LTGT: return CC1 | CC2;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
case CCSRmode:
|
case CCSRmode:
|
switch (GET_CODE (code))
|
switch (GET_CODE (code))
|
{
|
{
|
case EQ: return CC0;
|
case EQ: return CC0;
|
case NE: return CC2 | CC1 | CC3;
|
case NE: return CC2 | CC1 | CC3;
|
case LT: return CC2;
|
case LT: return CC2;
|
case GT: return CC1;
|
case GT: return CC1;
|
case LE: return CC0 | CC2;
|
case LE: return CC0 | CC2;
|
case GE: return CC0 | CC1;
|
case GE: return CC0 | CC1;
|
case UNORDERED: return CC3;
|
case UNORDERED: return CC3;
|
case ORDERED: return CC0 | CC2 | CC1;
|
case ORDERED: return CC0 | CC2 | CC1;
|
case UNEQ: return CC0 | CC3;
|
case UNEQ: return CC0 | CC3;
|
case UNLT: return CC2 | CC3;
|
case UNLT: return CC2 | CC3;
|
case UNGT: return CC1 | CC3;
|
case UNGT: return CC1 | CC3;
|
case UNLE: return CC0 | CC2 | CC3;
|
case UNLE: return CC0 | CC2 | CC3;
|
case UNGE: return CC0 | CC1 | CC3;
|
case UNGE: return CC0 | CC1 | CC3;
|
case LTGT: return CC2 | CC1;
|
case LTGT: return CC2 | CC1;
|
default: return -1;
|
default: return -1;
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
|
|
/* If INV is false, return assembler mnemonic string to implement
|
/* If INV is false, return assembler mnemonic string to implement
|
a branch specified by CODE. If INV is true, return mnemonic
|
a branch specified by CODE. If INV is true, return mnemonic
|
for the corresponding inverted branch. */
|
for the corresponding inverted branch. */
|
|
|
static const char *
|
static const char *
|
s390_branch_condition_mnemonic (rtx code, int inv)
|
s390_branch_condition_mnemonic (rtx code, int inv)
|
{
|
{
|
static const char *const mnemonic[16] =
|
static const char *const mnemonic[16] =
|
{
|
{
|
NULL, "o", "h", "nle",
|
NULL, "o", "h", "nle",
|
"l", "nhe", "lh", "ne",
|
"l", "nhe", "lh", "ne",
|
"e", "nlh", "he", "nl",
|
"e", "nlh", "he", "nl",
|
"le", "nh", "no", NULL
|
"le", "nh", "no", NULL
|
};
|
};
|
|
|
int mask = s390_branch_condition_mask (code);
|
int mask = s390_branch_condition_mask (code);
|
gcc_assert (mask >= 0);
|
gcc_assert (mask >= 0);
|
|
|
if (inv)
|
if (inv)
|
mask ^= 15;
|
mask ^= 15;
|
|
|
gcc_assert (mask >= 1 && mask <= 14);
|
gcc_assert (mask >= 1 && mask <= 14);
|
|
|
return mnemonic[mask];
|
return mnemonic[mask];
|
}
|
}
|
|
|
/* Return the part of op which has a value different from def.
|
/* Return the part of op which has a value different from def.
|
The size of the part is determined by mode.
|
The size of the part is determined by mode.
|
Use this function only if you already know that op really
|
Use this function only if you already know that op really
|
contains such a part. */
|
contains such a part. */
|
|
|
unsigned HOST_WIDE_INT
|
unsigned HOST_WIDE_INT
|
s390_extract_part (rtx op, enum machine_mode mode, int def)
|
s390_extract_part (rtx op, enum machine_mode mode, int def)
|
{
|
{
|
unsigned HOST_WIDE_INT value = 0;
|
unsigned HOST_WIDE_INT value = 0;
|
int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode);
|
int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode);
|
int part_bits = GET_MODE_BITSIZE (mode);
|
int part_bits = GET_MODE_BITSIZE (mode);
|
unsigned HOST_WIDE_INT part_mask
|
unsigned HOST_WIDE_INT part_mask
|
= ((unsigned HOST_WIDE_INT)1 << part_bits) - 1;
|
= ((unsigned HOST_WIDE_INT)1 << part_bits) - 1;
|
int i;
|
int i;
|
|
|
for (i = 0; i < max_parts; i++)
|
for (i = 0; i < max_parts; i++)
|
{
|
{
|
if (i == 0)
|
if (i == 0)
|
value = (unsigned HOST_WIDE_INT) INTVAL (op);
|
value = (unsigned HOST_WIDE_INT) INTVAL (op);
|
else
|
else
|
value >>= part_bits;
|
value >>= part_bits;
|
|
|
if ((value & part_mask) != (def & part_mask))
|
if ((value & part_mask) != (def & part_mask))
|
return value & part_mask;
|
return value & part_mask;
|
}
|
}
|
|
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* If OP is an integer constant of mode MODE with exactly one
|
/* If OP is an integer constant of mode MODE with exactly one
|
part of mode PART_MODE unequal to DEF, return the number of that
|
part of mode PART_MODE unequal to DEF, return the number of that
|
part. Otherwise, return -1. */
|
part. Otherwise, return -1. */
|
|
|
int
|
int
|
s390_single_part (rtx op,
|
s390_single_part (rtx op,
|
enum machine_mode mode,
|
enum machine_mode mode,
|
enum machine_mode part_mode,
|
enum machine_mode part_mode,
|
int def)
|
int def)
|
{
|
{
|
unsigned HOST_WIDE_INT value = 0;
|
unsigned HOST_WIDE_INT value = 0;
|
int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode);
|
int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode);
|
unsigned HOST_WIDE_INT part_mask
|
unsigned HOST_WIDE_INT part_mask
|
= ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1;
|
= ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1;
|
int i, part = -1;
|
int i, part = -1;
|
|
|
if (GET_CODE (op) != CONST_INT)
|
if (GET_CODE (op) != CONST_INT)
|
return -1;
|
return -1;
|
|
|
for (i = 0; i < n_parts; i++)
|
for (i = 0; i < n_parts; i++)
|
{
|
{
|
if (i == 0)
|
if (i == 0)
|
value = (unsigned HOST_WIDE_INT) INTVAL (op);
|
value = (unsigned HOST_WIDE_INT) INTVAL (op);
|
else
|
else
|
value >>= GET_MODE_BITSIZE (part_mode);
|
value >>= GET_MODE_BITSIZE (part_mode);
|
|
|
if ((value & part_mask) != (def & part_mask))
|
if ((value & part_mask) != (def & part_mask))
|
{
|
{
|
if (part != -1)
|
if (part != -1)
|
return -1;
|
return -1;
|
else
|
else
|
part = i;
|
part = i;
|
}
|
}
|
}
|
}
|
return part == -1 ? -1 : n_parts - 1 - part;
|
return part == -1 ? -1 : n_parts - 1 - part;
|
}
|
}
|
|
|
/* Check whether we can (and want to) split a double-word
|
/* Check whether we can (and want to) split a double-word
|
move in mode MODE from SRC to DST into two single-word
|
move in mode MODE from SRC to DST into two single-word
|
moves, moving the subword FIRST_SUBWORD first. */
|
moves, moving the subword FIRST_SUBWORD first. */
|
|
|
bool
|
bool
|
s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
|
s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
|
{
|
{
|
/* Floating point registers cannot be split. */
|
/* Floating point registers cannot be split. */
|
if (FP_REG_P (src) || FP_REG_P (dst))
|
if (FP_REG_P (src) || FP_REG_P (dst))
|
return false;
|
return false;
|
|
|
/* We don't need to split if operands are directly accessible. */
|
/* We don't need to split if operands are directly accessible. */
|
if (s_operand (src, mode) || s_operand (dst, mode))
|
if (s_operand (src, mode) || s_operand (dst, mode))
|
return false;
|
return false;
|
|
|
/* Non-offsettable memory references cannot be split. */
|
/* Non-offsettable memory references cannot be split. */
|
if ((GET_CODE (src) == MEM && !offsettable_memref_p (src))
|
if ((GET_CODE (src) == MEM && !offsettable_memref_p (src))
|
|| (GET_CODE (dst) == MEM && !offsettable_memref_p (dst)))
|
|| (GET_CODE (dst) == MEM && !offsettable_memref_p (dst)))
|
return false;
|
return false;
|
|
|
/* Moving the first subword must not clobber a register
|
/* Moving the first subword must not clobber a register
|
needed to move the second subword. */
|
needed to move the second subword. */
|
if (register_operand (dst, mode))
|
if (register_operand (dst, mode))
|
{
|
{
|
rtx subreg = operand_subword (dst, first_subword, 0, mode);
|
rtx subreg = operand_subword (dst, first_subword, 0, mode);
|
if (reg_overlap_mentioned_p (subreg, src))
|
if (reg_overlap_mentioned_p (subreg, src))
|
return false;
|
return false;
|
}
|
}
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Return true if it can be proven that [MEM1, MEM1 + SIZE]
|
/* Return true if it can be proven that [MEM1, MEM1 + SIZE]
|
and [MEM2, MEM2 + SIZE] do overlap and false
|
and [MEM2, MEM2 + SIZE] do overlap and false
|
otherwise. */
|
otherwise. */
|
|
|
bool
|
bool
|
s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size)
|
s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size)
|
{
|
{
|
rtx addr1, addr2, addr_delta;
|
rtx addr1, addr2, addr_delta;
|
HOST_WIDE_INT delta;
|
HOST_WIDE_INT delta;
|
|
|
if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
|
if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
|
return true;
|
return true;
|
|
|
if (size == 0)
|
if (size == 0)
|
return false;
|
return false;
|
|
|
addr1 = XEXP (mem1, 0);
|
addr1 = XEXP (mem1, 0);
|
addr2 = XEXP (mem2, 0);
|
addr2 = XEXP (mem2, 0);
|
|
|
addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
|
addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
|
|
|
/* This overlapping check is used by peepholes merging memory block operations.
|
/* This overlapping check is used by peepholes merging memory block operations.
|
Overlapping operations would otherwise be recognized by the S/390 hardware
|
Overlapping operations would otherwise be recognized by the S/390 hardware
|
and would fall back to a slower implementation. Allowing overlapping
|
and would fall back to a slower implementation. Allowing overlapping
|
operations would lead to slow code but not to wrong code. Therefore we are
|
operations would lead to slow code but not to wrong code. Therefore we are
|
somewhat optimistic if we cannot prove that the memory blocks are
|
somewhat optimistic if we cannot prove that the memory blocks are
|
overlapping.
|
overlapping.
|
That's why we return false here although this may accept operations on
|
That's why we return false here although this may accept operations on
|
overlapping memory areas. */
|
overlapping memory areas. */
|
if (!addr_delta || GET_CODE (addr_delta) != CONST_INT)
|
if (!addr_delta || GET_CODE (addr_delta) != CONST_INT)
|
return false;
|
return false;
|
|
|
delta = INTVAL (addr_delta);
|
delta = INTVAL (addr_delta);
|
|
|
if (delta == 0
|
if (delta == 0
|
|| (delta > 0 && delta < size)
|
|| (delta > 0 && delta < size)
|
|| (delta < 0 && -delta < size))
|
|| (delta < 0 && -delta < size))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Check whether the address of memory reference MEM2 equals exactly
|
/* Check whether the address of memory reference MEM2 equals exactly
|
the address of memory reference MEM1 plus DELTA. Return true if
|
the address of memory reference MEM1 plus DELTA. Return true if
|
we can prove this to be the case, false otherwise. */
|
we can prove this to be the case, false otherwise. */
|
|
|
bool
|
bool
|
s390_offset_p (rtx mem1, rtx mem2, rtx delta)
|
s390_offset_p (rtx mem1, rtx mem2, rtx delta)
|
{
|
{
|
rtx addr1, addr2, addr_delta;
|
rtx addr1, addr2, addr_delta;
|
|
|
if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
|
if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
|
return false;
|
return false;
|
|
|
addr1 = XEXP (mem1, 0);
|
addr1 = XEXP (mem1, 0);
|
addr2 = XEXP (mem2, 0);
|
addr2 = XEXP (mem2, 0);
|
|
|
addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
|
addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
|
if (!addr_delta || !rtx_equal_p (addr_delta, delta))
|
if (!addr_delta || !rtx_equal_p (addr_delta, delta))
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Expand logical operator CODE in mode MODE with operands OPERANDS. */
|
/* Expand logical operator CODE in mode MODE with operands OPERANDS. */
|
|
|
void
|
void
|
s390_expand_logical_operator (enum rtx_code code, enum machine_mode mode,
|
s390_expand_logical_operator (enum rtx_code code, enum machine_mode mode,
|
rtx *operands)
|
rtx *operands)
|
{
|
{
|
enum machine_mode wmode = mode;
|
enum machine_mode wmode = mode;
|
rtx dst = operands[0];
|
rtx dst = operands[0];
|
rtx src1 = operands[1];
|
rtx src1 = operands[1];
|
rtx src2 = operands[2];
|
rtx src2 = operands[2];
|
rtx op, clob, tem;
|
rtx op, clob, tem;
|
|
|
/* If we cannot handle the operation directly, use a temp register. */
|
/* If we cannot handle the operation directly, use a temp register. */
|
if (!s390_logical_operator_ok_p (operands))
|
if (!s390_logical_operator_ok_p (operands))
|
dst = gen_reg_rtx (mode);
|
dst = gen_reg_rtx (mode);
|
|
|
/* QImode and HImode patterns make sense only if we have a destination
|
/* QImode and HImode patterns make sense only if we have a destination
|
in memory. Otherwise perform the operation in SImode. */
|
in memory. Otherwise perform the operation in SImode. */
|
if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM)
|
if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM)
|
wmode = SImode;
|
wmode = SImode;
|
|
|
/* Widen operands if required. */
|
/* Widen operands if required. */
|
if (mode != wmode)
|
if (mode != wmode)
|
{
|
{
|
if (GET_CODE (dst) == SUBREG
|
if (GET_CODE (dst) == SUBREG
|
&& (tem = simplify_subreg (wmode, dst, mode, 0)) != 0)
|
&& (tem = simplify_subreg (wmode, dst, mode, 0)) != 0)
|
dst = tem;
|
dst = tem;
|
else if (REG_P (dst))
|
else if (REG_P (dst))
|
dst = gen_rtx_SUBREG (wmode, dst, 0);
|
dst = gen_rtx_SUBREG (wmode, dst, 0);
|
else
|
else
|
dst = gen_reg_rtx (wmode);
|
dst = gen_reg_rtx (wmode);
|
|
|
if (GET_CODE (src1) == SUBREG
|
if (GET_CODE (src1) == SUBREG
|
&& (tem = simplify_subreg (wmode, src1, mode, 0)) != 0)
|
&& (tem = simplify_subreg (wmode, src1, mode, 0)) != 0)
|
src1 = tem;
|
src1 = tem;
|
else if (GET_MODE (src1) != VOIDmode)
|
else if (GET_MODE (src1) != VOIDmode)
|
src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0);
|
src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0);
|
|
|
if (GET_CODE (src2) == SUBREG
|
if (GET_CODE (src2) == SUBREG
|
&& (tem = simplify_subreg (wmode, src2, mode, 0)) != 0)
|
&& (tem = simplify_subreg (wmode, src2, mode, 0)) != 0)
|
src2 = tem;
|
src2 = tem;
|
else if (GET_MODE (src2) != VOIDmode)
|
else if (GET_MODE (src2) != VOIDmode)
|
src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0);
|
src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0);
|
}
|
}
|
|
|
/* Emit the instruction. */
|
/* Emit the instruction. */
|
op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2));
|
op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2));
|
clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob)));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob)));
|
|
|
/* Fix up the destination if needed. */
|
/* Fix up the destination if needed. */
|
if (dst != operands[0])
|
if (dst != operands[0])
|
emit_move_insn (operands[0], gen_lowpart (mode, dst));
|
emit_move_insn (operands[0], gen_lowpart (mode, dst));
|
}
|
}
|
|
|
/* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR). */
|
/* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR). */
|
|
|
bool
|
bool
|
s390_logical_operator_ok_p (rtx *operands)
|
s390_logical_operator_ok_p (rtx *operands)
|
{
|
{
|
/* If the destination operand is in memory, it needs to coincide
|
/* If the destination operand is in memory, it needs to coincide
|
with one of the source operands. After reload, it has to be
|
with one of the source operands. After reload, it has to be
|
the first source operand. */
|
the first source operand. */
|
if (GET_CODE (operands[0]) == MEM)
|
if (GET_CODE (operands[0]) == MEM)
|
return rtx_equal_p (operands[0], operands[1])
|
return rtx_equal_p (operands[0], operands[1])
|
|| (!reload_completed && rtx_equal_p (operands[0], operands[2]));
|
|| (!reload_completed && rtx_equal_p (operands[0], operands[2]));
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Narrow logical operation CODE of memory operand MEMOP with immediate
|
/* Narrow logical operation CODE of memory operand MEMOP with immediate
|
operand IMMOP to switch from SS to SI type instructions. */
|
operand IMMOP to switch from SS to SI type instructions. */
|
|
|
void
|
void
|
s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
|
s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
|
{
|
{
|
int def = code == AND ? -1 : 0;
|
int def = code == AND ? -1 : 0;
|
HOST_WIDE_INT mask;
|
HOST_WIDE_INT mask;
|
int part;
|
int part;
|
|
|
gcc_assert (GET_CODE (*memop) == MEM);
|
gcc_assert (GET_CODE (*memop) == MEM);
|
gcc_assert (!MEM_VOLATILE_P (*memop));
|
gcc_assert (!MEM_VOLATILE_P (*memop));
|
|
|
mask = s390_extract_part (*immop, QImode, def);
|
mask = s390_extract_part (*immop, QImode, def);
|
part = s390_single_part (*immop, GET_MODE (*memop), QImode, def);
|
part = s390_single_part (*immop, GET_MODE (*memop), QImode, def);
|
gcc_assert (part >= 0);
|
gcc_assert (part >= 0);
|
|
|
*memop = adjust_address (*memop, QImode, part);
|
*memop = adjust_address (*memop, QImode, part);
|
*immop = gen_int_mode (mask, QImode);
|
*immop = gen_int_mode (mask, QImode);
|
}
|
}
|
|
|
|
|
/* How to allocate a 'struct machine_function'. */
|
/* How to allocate a 'struct machine_function'. */
|
|
|
static struct machine_function *
|
static struct machine_function *
|
s390_init_machine_status (void)
|
s390_init_machine_status (void)
|
{
|
{
|
return ggc_alloc_cleared (sizeof (struct machine_function));
|
return ggc_alloc_cleared (sizeof (struct machine_function));
|
}
|
}
|
|
|
/* Change optimizations to be performed, depending on the
|
/* Change optimizations to be performed, depending on the
|
optimization level.
|
optimization level.
|
|
|
LEVEL is the optimization level specified; 2 if `-O2' is
|
LEVEL is the optimization level specified; 2 if `-O2' is
|
specified, 1 if `-O' is specified, and 0 if neither is specified.
|
specified, 1 if `-O' is specified, and 0 if neither is specified.
|
|
|
SIZE is nonzero if `-Os' is specified and zero otherwise. */
|
SIZE is nonzero if `-Os' is specified and zero otherwise. */
|
|
|
void
|
void
|
optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
|
optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
|
{
|
{
|
/* ??? There are apparently still problems with -fcaller-saves. */
|
/* ??? There are apparently still problems with -fcaller-saves. */
|
flag_caller_saves = 0;
|
flag_caller_saves = 0;
|
|
|
/* By default, always emit DWARF-2 unwind info. This allows debugging
|
/* By default, always emit DWARF-2 unwind info. This allows debugging
|
without maintaining a stack frame back-chain. */
|
without maintaining a stack frame back-chain. */
|
flag_asynchronous_unwind_tables = 1;
|
flag_asynchronous_unwind_tables = 1;
|
|
|
/* Use MVCLE instructions to decrease code size if requested. */
|
/* Use MVCLE instructions to decrease code size if requested. */
|
if (size != 0)
|
if (size != 0)
|
target_flags |= MASK_MVCLE;
|
target_flags |= MASK_MVCLE;
|
}
|
}
|
|
|
/* Return true if ARG is the name of a processor. Set *TYPE and *FLAGS
|
/* Return true if ARG is the name of a processor. Set *TYPE and *FLAGS
|
to the associated processor_type and processor_flags if so. */
|
to the associated processor_type and processor_flags if so. */
|
|
|
static bool
|
static bool
|
s390_handle_arch_option (const char *arg,
|
s390_handle_arch_option (const char *arg,
|
enum processor_type *type,
|
enum processor_type *type,
|
enum processor_flags *flags)
|
enum processor_flags *flags)
|
{
|
{
|
static struct pta
|
static struct pta
|
{
|
{
|
const char *const name; /* processor name or nickname. */
|
const char *const name; /* processor name or nickname. */
|
const enum processor_type processor;
|
const enum processor_type processor;
|
const enum processor_flags flags;
|
const enum processor_flags flags;
|
}
|
}
|
const processor_alias_table[] =
|
const processor_alias_table[] =
|
{
|
{
|
{"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT},
|
{"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT},
|
{"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT},
|
{"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT},
|
{"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
|
{"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
|
{"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
|
{"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
|
| PF_LONG_DISPLACEMENT},
|
| PF_LONG_DISPLACEMENT},
|
{"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
|
{"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
|
| PF_LONG_DISPLACEMENT | PF_EXTIMM},
|
| PF_LONG_DISPLACEMENT | PF_EXTIMM},
|
};
|
};
|
size_t i;
|
size_t i;
|
|
|
for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++)
|
for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++)
|
if (strcmp (arg, processor_alias_table[i].name) == 0)
|
if (strcmp (arg, processor_alias_table[i].name) == 0)
|
{
|
{
|
*type = processor_alias_table[i].processor;
|
*type = processor_alias_table[i].processor;
|
*flags = processor_alias_table[i].flags;
|
*flags = processor_alias_table[i].flags;
|
return true;
|
return true;
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Implement TARGET_HANDLE_OPTION. */
|
/* Implement TARGET_HANDLE_OPTION. */
|
|
|
static bool
|
static bool
|
s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case OPT_march_:
|
case OPT_march_:
|
return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags);
|
return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags);
|
|
|
case OPT_mstack_guard_:
|
case OPT_mstack_guard_:
|
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1)
|
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1)
|
return false;
|
return false;
|
if (exact_log2 (s390_stack_guard) == -1)
|
if (exact_log2 (s390_stack_guard) == -1)
|
error ("stack guard value must be an exact power of 2");
|
error ("stack guard value must be an exact power of 2");
|
return true;
|
return true;
|
|
|
case OPT_mstack_size_:
|
case OPT_mstack_size_:
|
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1)
|
if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1)
|
return false;
|
return false;
|
if (exact_log2 (s390_stack_size) == -1)
|
if (exact_log2 (s390_stack_size) == -1)
|
error ("stack size must be an exact power of 2");
|
error ("stack size must be an exact power of 2");
|
return true;
|
return true;
|
|
|
case OPT_mtune_:
|
case OPT_mtune_:
|
return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
|
return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
|
|
|
case OPT_mwarn_framesize_:
|
case OPT_mwarn_framesize_:
|
return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
|
return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
|
|
|
default:
|
default:
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
void
|
void
|
override_options (void)
|
override_options (void)
|
{
|
{
|
/* Set up function hooks. */
|
/* Set up function hooks. */
|
init_machine_status = s390_init_machine_status;
|
init_machine_status = s390_init_machine_status;
|
|
|
/* Architecture mode defaults according to ABI. */
|
/* Architecture mode defaults according to ABI. */
|
if (!(target_flags_explicit & MASK_ZARCH))
|
if (!(target_flags_explicit & MASK_ZARCH))
|
{
|
{
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
target_flags |= MASK_ZARCH;
|
target_flags |= MASK_ZARCH;
|
else
|
else
|
target_flags &= ~MASK_ZARCH;
|
target_flags &= ~MASK_ZARCH;
|
}
|
}
|
|
|
/* Determine processor architectural level. */
|
/* Determine processor architectural level. */
|
if (!s390_arch_string)
|
if (!s390_arch_string)
|
{
|
{
|
s390_arch_string = TARGET_ZARCH? "z900" : "g5";
|
s390_arch_string = TARGET_ZARCH? "z900" : "g5";
|
s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags);
|
s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags);
|
}
|
}
|
|
|
/* Determine processor to tune for. */
|
/* Determine processor to tune for. */
|
if (s390_tune == PROCESSOR_max)
|
if (s390_tune == PROCESSOR_max)
|
{
|
{
|
s390_tune = s390_arch;
|
s390_tune = s390_arch;
|
s390_tune_flags = s390_arch_flags;
|
s390_tune_flags = s390_arch_flags;
|
}
|
}
|
|
|
/* Sanity checks. */
|
/* Sanity checks. */
|
if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH))
|
if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH))
|
error ("z/Architecture mode not supported on %s", s390_arch_string);
|
error ("z/Architecture mode not supported on %s", s390_arch_string);
|
if (TARGET_64BIT && !TARGET_ZARCH)
|
if (TARGET_64BIT && !TARGET_ZARCH)
|
error ("64-bit ABI not supported in ESA/390 mode");
|
error ("64-bit ABI not supported in ESA/390 mode");
|
|
|
/* Set processor cost function. */
|
/* Set processor cost function. */
|
if (s390_tune == PROCESSOR_2094_Z9_109)
|
if (s390_tune == PROCESSOR_2094_Z9_109)
|
s390_cost = &z9_109_cost;
|
s390_cost = &z9_109_cost;
|
else if (s390_tune == PROCESSOR_2084_Z990)
|
else if (s390_tune == PROCESSOR_2084_Z990)
|
s390_cost = &z990_cost;
|
s390_cost = &z990_cost;
|
else
|
else
|
s390_cost = &z900_cost;
|
s390_cost = &z900_cost;
|
|
|
if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT)
|
if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT)
|
error ("-mbackchain -mpacked-stack -mhard-float are not supported "
|
error ("-mbackchain -mpacked-stack -mhard-float are not supported "
|
"in combination");
|
"in combination");
|
|
|
if (s390_stack_size)
|
if (s390_stack_size)
|
{
|
{
|
if (!s390_stack_guard)
|
if (!s390_stack_guard)
|
error ("-mstack-size implies use of -mstack-guard");
|
error ("-mstack-size implies use of -mstack-guard");
|
else if (s390_stack_guard >= s390_stack_size)
|
else if (s390_stack_guard >= s390_stack_size)
|
error ("stack size must be greater than the stack guard value");
|
error ("stack size must be greater than the stack guard value");
|
else if (s390_stack_size > 1 << 16)
|
else if (s390_stack_size > 1 << 16)
|
error ("stack size must not be greater than 64k");
|
error ("stack size must not be greater than 64k");
|
}
|
}
|
else if (s390_stack_guard)
|
else if (s390_stack_guard)
|
error ("-mstack-guard implies use of -mstack-size");
|
error ("-mstack-guard implies use of -mstack-size");
|
|
|
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
|
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
|
if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
|
if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
|
target_flags |= MASK_LONG_DOUBLE_128;
|
target_flags |= MASK_LONG_DOUBLE_128;
|
#endif
|
#endif
|
}
|
}
|
|
|
/* Map for smallest class containing reg regno. */
|
/* Map for smallest class containing reg regno. */
|
|
|
const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
|
const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
|
{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
|
{ 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,
|
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,
|
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, CC_REGS, ADDR_REGS, ADDR_REGS,
|
ADDR_REGS, CC_REGS, ADDR_REGS, ADDR_REGS,
|
ACCESS_REGS, ACCESS_REGS
|
ACCESS_REGS, ACCESS_REGS
|
};
|
};
|
|
|
/* Return attribute type of insn. */
|
/* Return attribute type of insn. */
|
|
|
static enum attr_type
|
static enum attr_type
|
s390_safe_attr_type (rtx insn)
|
s390_safe_attr_type (rtx insn)
|
{
|
{
|
if (recog_memoized (insn) >= 0)
|
if (recog_memoized (insn) >= 0)
|
return get_attr_type (insn);
|
return get_attr_type (insn);
|
else
|
else
|
return TYPE_NONE;
|
return TYPE_NONE;
|
}
|
}
|
|
|
/* Return true if DISP is a valid short displacement. */
|
/* Return true if DISP is a valid short displacement. */
|
|
|
static bool
|
static bool
|
s390_short_displacement (rtx disp)
|
s390_short_displacement (rtx disp)
|
{
|
{
|
/* No displacement is OK. */
|
/* No displacement is OK. */
|
if (!disp)
|
if (!disp)
|
return true;
|
return true;
|
|
|
/* Integer displacement in range. */
|
/* Integer displacement in range. */
|
if (GET_CODE (disp) == CONST_INT)
|
if (GET_CODE (disp) == CONST_INT)
|
return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
|
return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
|
|
|
/* GOT offset is not OK, the GOT can be large. */
|
/* GOT offset is not OK, the GOT can be large. */
|
if (GET_CODE (disp) == CONST
|
if (GET_CODE (disp) == CONST
|
&& GET_CODE (XEXP (disp, 0)) == UNSPEC
|
&& GET_CODE (XEXP (disp, 0)) == UNSPEC
|
&& (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
|
&& (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
|
|| XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
|
|| XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
|
return false;
|
return false;
|
|
|
/* All other symbolic constants are literal pool references,
|
/* All other symbolic constants are literal pool references,
|
which are OK as the literal pool must be small. */
|
which are OK as the literal pool must be small. */
|
if (GET_CODE (disp) == CONST)
|
if (GET_CODE (disp) == CONST)
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Decompose a RTL expression ADDR for a memory address into
|
/* Decompose a RTL expression ADDR for a memory address into
|
its components, returned in OUT.
|
its components, returned in OUT.
|
|
|
Returns false if ADDR is not a valid memory address, true
|
Returns false if ADDR is not a valid memory address, true
|
otherwise. If OUT is NULL, don't return the components,
|
otherwise. If OUT is NULL, don't return the components,
|
but check for validity only.
|
but check for validity only.
|
|
|
Note: Only addresses in canonical form are recognized.
|
Note: Only addresses in canonical form are recognized.
|
LEGITIMIZE_ADDRESS should convert non-canonical forms to the
|
LEGITIMIZE_ADDRESS should convert non-canonical forms to the
|
canonical form so that they will be recognized. */
|
canonical form so that they will be recognized. */
|
|
|
static int
|
static int
|
s390_decompose_address (rtx addr, struct s390_address *out)
|
s390_decompose_address (rtx addr, struct s390_address *out)
|
{
|
{
|
HOST_WIDE_INT offset = 0;
|
HOST_WIDE_INT offset = 0;
|
rtx base = NULL_RTX;
|
rtx base = NULL_RTX;
|
rtx indx = NULL_RTX;
|
rtx indx = NULL_RTX;
|
rtx disp = NULL_RTX;
|
rtx disp = NULL_RTX;
|
rtx orig_disp;
|
rtx orig_disp;
|
bool pointer = false;
|
bool pointer = false;
|
bool base_ptr = false;
|
bool base_ptr = false;
|
bool indx_ptr = false;
|
bool indx_ptr = false;
|
bool literal_pool = false;
|
bool literal_pool = false;
|
|
|
/* We may need to substitute the literal pool base register into the address
|
/* We may need to substitute the literal pool base register into the address
|
below. However, at this point we do not know which register is going to
|
below. However, at this point we do not know which register is going to
|
be used as base, so we substitute the arg pointer register. This is going
|
be used as base, so we substitute the arg pointer register. This is going
|
to be treated as holding a pointer below -- it shouldn't be used for any
|
to be treated as holding a pointer below -- it shouldn't be used for any
|
other purpose. */
|
other purpose. */
|
rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM);
|
rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM);
|
|
|
/* Decompose address into base + index + displacement. */
|
/* Decompose address into base + index + displacement. */
|
|
|
if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
|
if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
|
base = addr;
|
base = addr;
|
|
|
else if (GET_CODE (addr) == PLUS)
|
else if (GET_CODE (addr) == PLUS)
|
{
|
{
|
rtx op0 = XEXP (addr, 0);
|
rtx op0 = XEXP (addr, 0);
|
rtx op1 = XEXP (addr, 1);
|
rtx op1 = XEXP (addr, 1);
|
enum rtx_code code0 = GET_CODE (op0);
|
enum rtx_code code0 = GET_CODE (op0);
|
enum rtx_code code1 = GET_CODE (op1);
|
enum rtx_code code1 = GET_CODE (op1);
|
|
|
if (code0 == REG || code0 == UNSPEC)
|
if (code0 == REG || code0 == UNSPEC)
|
{
|
{
|
if (code1 == REG || code1 == UNSPEC)
|
if (code1 == REG || code1 == UNSPEC)
|
{
|
{
|
indx = op0; /* index + base */
|
indx = op0; /* index + base */
|
base = op1;
|
base = op1;
|
}
|
}
|
|
|
else
|
else
|
{
|
{
|
base = op0; /* base + displacement */
|
base = op0; /* base + displacement */
|
disp = op1;
|
disp = op1;
|
}
|
}
|
}
|
}
|
|
|
else if (code0 == PLUS)
|
else if (code0 == PLUS)
|
{
|
{
|
indx = XEXP (op0, 0); /* index + base + disp */
|
indx = XEXP (op0, 0); /* index + base + disp */
|
base = XEXP (op0, 1);
|
base = XEXP (op0, 1);
|
disp = op1;
|
disp = op1;
|
}
|
}
|
|
|
else
|
else
|
{
|
{
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
else
|
else
|
disp = addr; /* displacement */
|
disp = addr; /* displacement */
|
|
|
/* Extract integer part of displacement. */
|
/* Extract integer part of displacement. */
|
orig_disp = disp;
|
orig_disp = disp;
|
if (disp)
|
if (disp)
|
{
|
{
|
if (GET_CODE (disp) == CONST_INT)
|
if (GET_CODE (disp) == CONST_INT)
|
{
|
{
|
offset = INTVAL (disp);
|
offset = INTVAL (disp);
|
disp = NULL_RTX;
|
disp = NULL_RTX;
|
}
|
}
|
else if (GET_CODE (disp) == CONST
|
else if (GET_CODE (disp) == CONST
|
&& GET_CODE (XEXP (disp, 0)) == PLUS
|
&& GET_CODE (XEXP (disp, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
|
&& GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
|
{
|
{
|
offset = INTVAL (XEXP (XEXP (disp, 0), 1));
|
offset = INTVAL (XEXP (XEXP (disp, 0), 1));
|
disp = XEXP (XEXP (disp, 0), 0);
|
disp = XEXP (XEXP (disp, 0), 0);
|
}
|
}
|
}
|
}
|
|
|
/* Strip off CONST here to avoid special case tests later. */
|
/* Strip off CONST here to avoid special case tests later. */
|
if (disp && GET_CODE (disp) == CONST)
|
if (disp && GET_CODE (disp) == CONST)
|
disp = XEXP (disp, 0);
|
disp = XEXP (disp, 0);
|
|
|
/* We can convert literal pool addresses to
|
/* We can convert literal pool addresses to
|
displacements by basing them off the base register. */
|
displacements by basing them off the base register. */
|
if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
|
if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
|
{
|
{
|
/* Either base or index must be free to hold the base register. */
|
/* Either base or index must be free to hold the base register. */
|
if (!base)
|
if (!base)
|
base = fake_pool_base, literal_pool = true;
|
base = fake_pool_base, literal_pool = true;
|
else if (!indx)
|
else if (!indx)
|
indx = fake_pool_base, literal_pool = true;
|
indx = fake_pool_base, literal_pool = true;
|
else
|
else
|
return false;
|
return false;
|
|
|
/* Mark up the displacement. */
|
/* Mark up the displacement. */
|
disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
|
disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
|
UNSPEC_LTREL_OFFSET);
|
UNSPEC_LTREL_OFFSET);
|
}
|
}
|
|
|
/* Validate base register. */
|
/* Validate base register. */
|
if (base)
|
if (base)
|
{
|
{
|
if (GET_CODE (base) == UNSPEC)
|
if (GET_CODE (base) == UNSPEC)
|
switch (XINT (base, 1))
|
switch (XINT (base, 1))
|
{
|
{
|
case UNSPEC_LTREF:
|
case UNSPEC_LTREF:
|
if (!disp)
|
if (!disp)
|
disp = gen_rtx_UNSPEC (Pmode,
|
disp = gen_rtx_UNSPEC (Pmode,
|
gen_rtvec (1, XVECEXP (base, 0, 0)),
|
gen_rtvec (1, XVECEXP (base, 0, 0)),
|
UNSPEC_LTREL_OFFSET);
|
UNSPEC_LTREL_OFFSET);
|
else
|
else
|
return false;
|
return false;
|
|
|
base = XVECEXP (base, 0, 1);
|
base = XVECEXP (base, 0, 1);
|
break;
|
break;
|
|
|
case UNSPEC_LTREL_BASE:
|
case UNSPEC_LTREL_BASE:
|
if (XVECLEN (base, 0) == 1)
|
if (XVECLEN (base, 0) == 1)
|
base = fake_pool_base, literal_pool = true;
|
base = fake_pool_base, literal_pool = true;
|
else
|
else
|
base = XVECEXP (base, 0, 1);
|
base = XVECEXP (base, 0, 1);
|
break;
|
break;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
|
|
if (!REG_P (base)
|
if (!REG_P (base)
|
|| (GET_MODE (base) != SImode
|
|| (GET_MODE (base) != SImode
|
&& GET_MODE (base) != Pmode))
|
&& GET_MODE (base) != Pmode))
|
return false;
|
return false;
|
|
|
if (REGNO (base) == STACK_POINTER_REGNUM
|
if (REGNO (base) == STACK_POINTER_REGNUM
|
|| REGNO (base) == FRAME_POINTER_REGNUM
|
|| REGNO (base) == FRAME_POINTER_REGNUM
|
|| ((reload_completed || reload_in_progress)
|
|| ((reload_completed || reload_in_progress)
|
&& frame_pointer_needed
|
&& frame_pointer_needed
|
&& REGNO (base) == HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (base) == HARD_FRAME_POINTER_REGNUM)
|
|| REGNO (base) == ARG_POINTER_REGNUM
|
|| REGNO (base) == ARG_POINTER_REGNUM
|
|| (flag_pic
|
|| (flag_pic
|
&& REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
|
&& REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
|
pointer = base_ptr = true;
|
pointer = base_ptr = true;
|
|
|
if ((reload_completed || reload_in_progress)
|
if ((reload_completed || reload_in_progress)
|
&& base == cfun->machine->base_reg)
|
&& base == cfun->machine->base_reg)
|
pointer = base_ptr = literal_pool = true;
|
pointer = base_ptr = literal_pool = true;
|
}
|
}
|
|
|
/* Validate index register. */
|
/* Validate index register. */
|
if (indx)
|
if (indx)
|
{
|
{
|
if (GET_CODE (indx) == UNSPEC)
|
if (GET_CODE (indx) == UNSPEC)
|
switch (XINT (indx, 1))
|
switch (XINT (indx, 1))
|
{
|
{
|
case UNSPEC_LTREF:
|
case UNSPEC_LTREF:
|
if (!disp)
|
if (!disp)
|
disp = gen_rtx_UNSPEC (Pmode,
|
disp = gen_rtx_UNSPEC (Pmode,
|
gen_rtvec (1, XVECEXP (indx, 0, 0)),
|
gen_rtvec (1, XVECEXP (indx, 0, 0)),
|
UNSPEC_LTREL_OFFSET);
|
UNSPEC_LTREL_OFFSET);
|
else
|
else
|
return false;
|
return false;
|
|
|
indx = XVECEXP (indx, 0, 1);
|
indx = XVECEXP (indx, 0, 1);
|
break;
|
break;
|
|
|
case UNSPEC_LTREL_BASE:
|
case UNSPEC_LTREL_BASE:
|
if (XVECLEN (indx, 0) == 1)
|
if (XVECLEN (indx, 0) == 1)
|
indx = fake_pool_base, literal_pool = true;
|
indx = fake_pool_base, literal_pool = true;
|
else
|
else
|
indx = XVECEXP (indx, 0, 1);
|
indx = XVECEXP (indx, 0, 1);
|
break;
|
break;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
|
|
if (!REG_P (indx)
|
if (!REG_P (indx)
|
|| (GET_MODE (indx) != SImode
|
|| (GET_MODE (indx) != SImode
|
&& GET_MODE (indx) != Pmode))
|
&& GET_MODE (indx) != Pmode))
|
return false;
|
return false;
|
|
|
if (REGNO (indx) == STACK_POINTER_REGNUM
|
if (REGNO (indx) == STACK_POINTER_REGNUM
|
|| REGNO (indx) == FRAME_POINTER_REGNUM
|
|| REGNO (indx) == FRAME_POINTER_REGNUM
|
|| ((reload_completed || reload_in_progress)
|
|| ((reload_completed || reload_in_progress)
|
&& frame_pointer_needed
|
&& frame_pointer_needed
|
&& REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
|
|| REGNO (indx) == ARG_POINTER_REGNUM
|
|| REGNO (indx) == ARG_POINTER_REGNUM
|
|| (flag_pic
|
|| (flag_pic
|
&& REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
|
&& REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
|
pointer = indx_ptr = true;
|
pointer = indx_ptr = true;
|
|
|
if ((reload_completed || reload_in_progress)
|
if ((reload_completed || reload_in_progress)
|
&& indx == cfun->machine->base_reg)
|
&& indx == cfun->machine->base_reg)
|
pointer = indx_ptr = literal_pool = true;
|
pointer = indx_ptr = literal_pool = true;
|
}
|
}
|
|
|
/* Prefer to use pointer as base, not index. */
|
/* Prefer to use pointer as base, not index. */
|
if (base && indx && !base_ptr
|
if (base && indx && !base_ptr
|
&& (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
|
&& (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
|
{
|
{
|
rtx tmp = base;
|
rtx tmp = base;
|
base = indx;
|
base = indx;
|
indx = tmp;
|
indx = tmp;
|
}
|
}
|
|
|
/* Validate displacement. */
|
/* Validate displacement. */
|
if (!disp)
|
if (!disp)
|
{
|
{
|
/* If virtual registers are involved, the displacement will change later
|
/* If virtual registers are involved, the displacement will change later
|
anyway as the virtual registers get eliminated. This could make a
|
anyway as the virtual registers get eliminated. This could make a
|
valid displacement invalid, but it is more likely to make an invalid
|
valid displacement invalid, but it is more likely to make an invalid
|
displacement valid, because we sometimes access the register save area
|
displacement valid, because we sometimes access the register save area
|
via negative offsets to one of those registers.
|
via negative offsets to one of those registers.
|
Thus we don't check the displacement for validity here. If after
|
Thus we don't check the displacement for validity here. If after
|
elimination the displacement turns out to be invalid after all,
|
elimination the displacement turns out to be invalid after all,
|
this is fixed up by reload in any case. */
|
this is fixed up by reload in any case. */
|
if (base != arg_pointer_rtx
|
if (base != arg_pointer_rtx
|
&& indx != arg_pointer_rtx
|
&& indx != arg_pointer_rtx
|
&& base != return_address_pointer_rtx
|
&& base != return_address_pointer_rtx
|
&& indx != return_address_pointer_rtx
|
&& indx != return_address_pointer_rtx
|
&& base != frame_pointer_rtx
|
&& base != frame_pointer_rtx
|
&& indx != frame_pointer_rtx
|
&& indx != frame_pointer_rtx
|
&& base != virtual_stack_vars_rtx
|
&& base != virtual_stack_vars_rtx
|
&& indx != virtual_stack_vars_rtx)
|
&& indx != virtual_stack_vars_rtx)
|
if (!DISP_IN_RANGE (offset))
|
if (!DISP_IN_RANGE (offset))
|
return false;
|
return false;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* All the special cases are pointers. */
|
/* All the special cases are pointers. */
|
pointer = true;
|
pointer = true;
|
|
|
/* In the small-PIC case, the linker converts @GOT
|
/* In the small-PIC case, the linker converts @GOT
|
and @GOTNTPOFF offsets to possible displacements. */
|
and @GOTNTPOFF offsets to possible displacements. */
|
if (GET_CODE (disp) == UNSPEC
|
if (GET_CODE (disp) == UNSPEC
|
&& (XINT (disp, 1) == UNSPEC_GOT
|
&& (XINT (disp, 1) == UNSPEC_GOT
|
|| XINT (disp, 1) == UNSPEC_GOTNTPOFF)
|
|| XINT (disp, 1) == UNSPEC_GOTNTPOFF)
|
&& flag_pic == 1)
|
&& flag_pic == 1)
|
{
|
{
|
;
|
;
|
}
|
}
|
|
|
/* Accept chunkified literal pool symbol references. */
|
/* Accept chunkified literal pool symbol references. */
|
else if (cfun && cfun->machine
|
else if (cfun && cfun->machine
|
&& cfun->machine->decomposed_literal_pool_addresses_ok_p
|
&& cfun->machine->decomposed_literal_pool_addresses_ok_p
|
&& GET_CODE (disp) == MINUS
|
&& GET_CODE (disp) == MINUS
|
&& GET_CODE (XEXP (disp, 0)) == LABEL_REF
|
&& GET_CODE (XEXP (disp, 0)) == LABEL_REF
|
&& GET_CODE (XEXP (disp, 1)) == LABEL_REF)
|
&& GET_CODE (XEXP (disp, 1)) == LABEL_REF)
|
{
|
{
|
;
|
;
|
}
|
}
|
|
|
/* Accept literal pool references. */
|
/* Accept literal pool references. */
|
else if (GET_CODE (disp) == UNSPEC
|
else if (GET_CODE (disp) == UNSPEC
|
&& XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
|
&& XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
|
{
|
{
|
orig_disp = gen_rtx_CONST (Pmode, disp);
|
orig_disp = gen_rtx_CONST (Pmode, disp);
|
if (offset)
|
if (offset)
|
{
|
{
|
/* If we have an offset, make sure it does not
|
/* If we have an offset, make sure it does not
|
exceed the size of the constant pool entry. */
|
exceed the size of the constant pool entry. */
|
rtx sym = XVECEXP (disp, 0, 0);
|
rtx sym = XVECEXP (disp, 0, 0);
|
if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
|
if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
|
return false;
|
return false;
|
|
|
orig_disp = plus_constant (orig_disp, offset);
|
orig_disp = plus_constant (orig_disp, offset);
|
}
|
}
|
}
|
}
|
|
|
else
|
else
|
return false;
|
return false;
|
}
|
}
|
|
|
if (!base && !indx)
|
if (!base && !indx)
|
pointer = true;
|
pointer = true;
|
|
|
if (out)
|
if (out)
|
{
|
{
|
out->base = base;
|
out->base = base;
|
out->indx = indx;
|
out->indx = indx;
|
out->disp = orig_disp;
|
out->disp = orig_disp;
|
out->pointer = pointer;
|
out->pointer = pointer;
|
out->literal_pool = literal_pool;
|
out->literal_pool = literal_pool;
|
}
|
}
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Decompose a RTL expression OP for a shift count into its components,
|
/* Decompose a RTL expression OP for a shift count into its components,
|
and return the base register in BASE and the offset in OFFSET.
|
and return the base register in BASE and the offset in OFFSET.
|
|
|
Return true if OP is a valid shift count, false if not. */
|
Return true if OP is a valid shift count, false if not. */
|
|
|
bool
|
bool
|
s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset)
|
s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset)
|
{
|
{
|
HOST_WIDE_INT off = 0;
|
HOST_WIDE_INT off = 0;
|
|
|
/* We can have an integer constant, an address register,
|
/* We can have an integer constant, an address register,
|
or a sum of the two. */
|
or a sum of the two. */
|
if (GET_CODE (op) == CONST_INT)
|
if (GET_CODE (op) == CONST_INT)
|
{
|
{
|
off = INTVAL (op);
|
off = INTVAL (op);
|
op = NULL_RTX;
|
op = NULL_RTX;
|
}
|
}
|
if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
|
if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
|
{
|
{
|
off = INTVAL (XEXP (op, 1));
|
off = INTVAL (XEXP (op, 1));
|
op = XEXP (op, 0);
|
op = XEXP (op, 0);
|
}
|
}
|
while (op && GET_CODE (op) == SUBREG)
|
while (op && GET_CODE (op) == SUBREG)
|
op = SUBREG_REG (op);
|
op = SUBREG_REG (op);
|
|
|
if (op && GET_CODE (op) != REG)
|
if (op && GET_CODE (op) != REG)
|
return false;
|
return false;
|
|
|
if (offset)
|
if (offset)
|
*offset = off;
|
*offset = off;
|
if (base)
|
if (base)
|
*base = op;
|
*base = op;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
|
|
/* Return true if CODE is a valid address without index. */
|
/* Return true if CODE is a valid address without index. */
|
|
|
bool
|
bool
|
s390_legitimate_address_without_index_p (rtx op)
|
s390_legitimate_address_without_index_p (rtx op)
|
{
|
{
|
struct s390_address addr;
|
struct s390_address addr;
|
|
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
return false;
|
return false;
|
if (addr.indx)
|
if (addr.indx)
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
|
|
/* Evaluates constraint strings described by the regular expression
|
/* Evaluates constraint strings described by the regular expression
|
([A|B](Q|R|S|T))|U|W and returns 1 if OP is a valid operand for the
|
([A|B](Q|R|S|T))|U|W and returns 1 if OP is a valid operand for the
|
constraint given in STR, or 0 else. */
|
constraint given in STR, or 0 else. */
|
|
|
int
|
int
|
s390_mem_constraint (const char *str, rtx op)
|
s390_mem_constraint (const char *str, rtx op)
|
{
|
{
|
struct s390_address addr;
|
struct s390_address addr;
|
char c = str[0];
|
char c = str[0];
|
|
|
/* Check for offsettable variants of memory constraints. */
|
/* Check for offsettable variants of memory constraints. */
|
if (c == 'A')
|
if (c == 'A')
|
{
|
{
|
/* Only accept non-volatile MEMs. */
|
/* Only accept non-volatile MEMs. */
|
if (!MEM_P (op) || MEM_VOLATILE_P (op))
|
if (!MEM_P (op) || MEM_VOLATILE_P (op))
|
return 0;
|
return 0;
|
|
|
if ((reload_completed || reload_in_progress)
|
if ((reload_completed || reload_in_progress)
|
? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
|
? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
|
return 0;
|
return 0;
|
|
|
c = str[1];
|
c = str[1];
|
}
|
}
|
|
|
/* Check for non-literal-pool variants of memory constraints. */
|
/* Check for non-literal-pool variants of memory constraints. */
|
else if (c == 'B')
|
else if (c == 'B')
|
{
|
{
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return 0;
|
return 0;
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
return 0;
|
return 0;
|
if (addr.literal_pool)
|
if (addr.literal_pool)
|
return 0;
|
return 0;
|
|
|
c = str[1];
|
c = str[1];
|
}
|
}
|
|
|
switch (c)
|
switch (c)
|
{
|
{
|
case 'Q':
|
case 'Q':
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return 0;
|
return 0;
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
return 0;
|
return 0;
|
if (addr.indx)
|
if (addr.indx)
|
return 0;
|
return 0;
|
|
|
if (TARGET_LONG_DISPLACEMENT)
|
if (TARGET_LONG_DISPLACEMENT)
|
{
|
{
|
if (!s390_short_displacement (addr.disp))
|
if (!s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
}
|
}
|
break;
|
break;
|
|
|
case 'R':
|
case 'R':
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return 0;
|
return 0;
|
|
|
if (TARGET_LONG_DISPLACEMENT)
|
if (TARGET_LONG_DISPLACEMENT)
|
{
|
{
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
return 0;
|
return 0;
|
if (!s390_short_displacement (addr.disp))
|
if (!s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
}
|
}
|
break;
|
break;
|
|
|
case 'S':
|
case 'S':
|
if (!TARGET_LONG_DISPLACEMENT)
|
if (!TARGET_LONG_DISPLACEMENT)
|
return 0;
|
return 0;
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return 0;
|
return 0;
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
if (!s390_decompose_address (XEXP (op, 0), &addr))
|
return 0;
|
return 0;
|
if (addr.indx)
|
if (addr.indx)
|
return 0;
|
return 0;
|
if (s390_short_displacement (addr.disp))
|
if (s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
case 'T':
|
case 'T':
|
if (!TARGET_LONG_DISPLACEMENT)
|
if (!TARGET_LONG_DISPLACEMENT)
|
return 0;
|
return 0;
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return 0;
|
return 0;
|
/* Any invalid address here will be fixed up by reload,
|
/* Any invalid address here will be fixed up by reload,
|
so accept it for the most generic constraint. */
|
so accept it for the most generic constraint. */
|
if (s390_decompose_address (XEXP (op, 0), &addr)
|
if (s390_decompose_address (XEXP (op, 0), &addr)
|
&& s390_short_displacement (addr.disp))
|
&& s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
case 'U':
|
case 'U':
|
if (TARGET_LONG_DISPLACEMENT)
|
if (TARGET_LONG_DISPLACEMENT)
|
{
|
{
|
if (!s390_decompose_address (op, &addr))
|
if (!s390_decompose_address (op, &addr))
|
return 0;
|
return 0;
|
if (!s390_short_displacement (addr.disp))
|
if (!s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
}
|
}
|
break;
|
break;
|
|
|
case 'W':
|
case 'W':
|
if (!TARGET_LONG_DISPLACEMENT)
|
if (!TARGET_LONG_DISPLACEMENT)
|
return 0;
|
return 0;
|
/* Any invalid address here will be fixed up by reload,
|
/* Any invalid address here will be fixed up by reload,
|
so accept it for the most generic constraint. */
|
so accept it for the most generic constraint. */
|
if (s390_decompose_address (op, &addr)
|
if (s390_decompose_address (op, &addr)
|
&& s390_short_displacement (addr.disp))
|
&& s390_short_displacement (addr.disp))
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
case 'Y':
|
case 'Y':
|
/* Simply check for the basic form of a shift count. Reload will
|
/* Simply check for the basic form of a shift count. Reload will
|
take care of making sure we have a proper base register. */
|
take care of making sure we have a proper base register. */
|
if (!s390_decompose_shift_count (op, NULL, NULL))
|
if (!s390_decompose_shift_count (op, NULL, NULL))
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
default:
|
default:
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
|
|
|
/* Evaluates constraint strings starting with letter O. Input
|
/* Evaluates constraint strings starting with letter O. Input
|
parameter C is the second letter following the "O" in the constraint
|
parameter C is the second letter following the "O" in the constraint
|
string. Returns 1 if VALUE meets the respective constraint and 0
|
string. Returns 1 if VALUE meets the respective constraint and 0
|
otherwise. */
|
otherwise. */
|
|
|
int
|
int
|
s390_O_constraint_str (const char c, HOST_WIDE_INT value)
|
s390_O_constraint_str (const char c, HOST_WIDE_INT value)
|
{
|
{
|
if (!TARGET_EXTIMM)
|
if (!TARGET_EXTIMM)
|
return 0;
|
return 0;
|
|
|
switch (c)
|
switch (c)
|
{
|
{
|
case 's':
|
case 's':
|
return trunc_int_for_mode (value, SImode) == value;
|
return trunc_int_for_mode (value, SImode) == value;
|
|
|
case 'p':
|
case 'p':
|
return value == 0
|
return value == 0
|
|| s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
|
|| s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
|
|
|
case 'n':
|
case 'n':
|
return value == -1
|
return value == -1
|
|| s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1;
|
|| s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Evaluates constraint strings starting with letter N. Parameter STR
|
/* Evaluates constraint strings starting with letter N. Parameter STR
|
contains the letters following letter "N" in the constraint string.
|
contains the letters following letter "N" in the constraint string.
|
Returns true if VALUE matches the constraint. */
|
Returns true if VALUE matches the constraint. */
|
|
|
int
|
int
|
s390_N_constraint_str (const char *str, HOST_WIDE_INT value)
|
s390_N_constraint_str (const char *str, HOST_WIDE_INT value)
|
{
|
{
|
enum machine_mode mode, part_mode;
|
enum machine_mode mode, part_mode;
|
int def;
|
int def;
|
int part, part_goal;
|
int part, part_goal;
|
|
|
|
|
if (str[0] == 'x')
|
if (str[0] == 'x')
|
part_goal = -1;
|
part_goal = -1;
|
else
|
else
|
part_goal = str[0] - '0';
|
part_goal = str[0] - '0';
|
|
|
switch (str[1])
|
switch (str[1])
|
{
|
{
|
case 'Q':
|
case 'Q':
|
part_mode = QImode;
|
part_mode = QImode;
|
break;
|
break;
|
case 'H':
|
case 'H':
|
part_mode = HImode;
|
part_mode = HImode;
|
break;
|
break;
|
case 'S':
|
case 'S':
|
part_mode = SImode;
|
part_mode = SImode;
|
break;
|
break;
|
default:
|
default:
|
return 0;
|
return 0;
|
}
|
}
|
|
|
switch (str[2])
|
switch (str[2])
|
{
|
{
|
case 'H':
|
case 'H':
|
mode = HImode;
|
mode = HImode;
|
break;
|
break;
|
case 'S':
|
case 'S':
|
mode = SImode;
|
mode = SImode;
|
break;
|
break;
|
case 'D':
|
case 'D':
|
mode = DImode;
|
mode = DImode;
|
break;
|
break;
|
default:
|
default:
|
return 0;
|
return 0;
|
}
|
}
|
|
|
switch (str[3])
|
switch (str[3])
|
{
|
{
|
case '0':
|
case '0':
|
def = 0;
|
def = 0;
|
break;
|
break;
|
case 'F':
|
case 'F':
|
def = -1;
|
def = -1;
|
break;
|
break;
|
default:
|
default:
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
|
if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
|
return 0;
|
return 0;
|
|
|
part = s390_single_part (GEN_INT (value), mode, part_mode, def);
|
part = s390_single_part (GEN_INT (value), mode, part_mode, def);
|
if (part < 0)
|
if (part < 0)
|
return 0;
|
return 0;
|
if (part_goal != -1 && part_goal != part)
|
if (part_goal != -1 && part_goal != part)
|
return 0;
|
return 0;
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
|
/* Returns true if the input parameter VALUE is a float zero. */
|
/* Returns true if the input parameter VALUE is a float zero. */
|
|
|
int
|
int
|
s390_float_const_zero_p (rtx value)
|
s390_float_const_zero_p (rtx value)
|
{
|
{
|
return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
|
return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
|
&& value == CONST0_RTX (GET_MODE (value)));
|
&& value == CONST0_RTX (GET_MODE (value)));
|
}
|
}
|
|
|
|
|
/* Compute a (partial) cost for rtx X. Return true if the complete
|
/* Compute a (partial) cost for rtx X. Return true if the complete
|
cost has been computed, and false if subexpressions should be
|
cost has been computed, and false if subexpressions should be
|
scanned. In either case, *TOTAL contains the cost result.
|
scanned. In either case, *TOTAL contains the cost result.
|
CODE contains GET_CODE (x), OUTER_CODE contains the code
|
CODE contains GET_CODE (x), OUTER_CODE contains the code
|
of the superexpression of x. */
|
of the superexpression of x. */
|
|
|
static bool
|
static bool
|
s390_rtx_costs (rtx x, int code, int outer_code, int *total)
|
s390_rtx_costs (rtx x, int code, int outer_code, int *total)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case CONST:
|
case CONST:
|
case CONST_INT:
|
case CONST_INT:
|
case LABEL_REF:
|
case LABEL_REF:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
case MEM:
|
case MEM:
|
*total = 0;
|
*total = 0;
|
return true;
|
return true;
|
|
|
case ASHIFT:
|
case ASHIFT:
|
case ASHIFTRT:
|
case ASHIFTRT:
|
case LSHIFTRT:
|
case LSHIFTRT:
|
case ROTATE:
|
case ROTATE:
|
case ROTATERT:
|
case ROTATERT:
|
case AND:
|
case AND:
|
case IOR:
|
case IOR:
|
case XOR:
|
case XOR:
|
case NEG:
|
case NEG:
|
case NOT:
|
case NOT:
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return false;
|
return false;
|
|
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
/* Check for multiply and add. */
|
/* Check for multiply and add. */
|
if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode)
|
if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode)
|
&& GET_CODE (XEXP (x, 0)) == MULT
|
&& GET_CODE (XEXP (x, 0)) == MULT
|
&& TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD)
|
&& TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT && TARGET_FUSED_MADD)
|
{
|
{
|
/* This is the multiply and add case. */
|
/* This is the multiply and add case. */
|
if (GET_MODE (x) == DFmode)
|
if (GET_MODE (x) == DFmode)
|
*total = s390_cost->madbr;
|
*total = s390_cost->madbr;
|
else
|
else
|
*total = s390_cost->maebr;
|
*total = s390_cost->maebr;
|
*total += rtx_cost (XEXP (XEXP (x, 0), 0), MULT)
|
*total += rtx_cost (XEXP (XEXP (x, 0), 0), MULT)
|
+ rtx_cost (XEXP (XEXP (x, 0), 1), MULT)
|
+ rtx_cost (XEXP (XEXP (x, 0), 1), MULT)
|
+ rtx_cost (XEXP (x, 1), code);
|
+ rtx_cost (XEXP (x, 1), code);
|
return true; /* Do not do an additional recursive descent. */
|
return true; /* Do not do an additional recursive descent. */
|
}
|
}
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return false;
|
return false;
|
|
|
case MULT:
|
case MULT:
|
switch (GET_MODE (x))
|
switch (GET_MODE (x))
|
{
|
{
|
case SImode:
|
case SImode:
|
{
|
{
|
rtx left = XEXP (x, 0);
|
rtx left = XEXP (x, 0);
|
rtx right = XEXP (x, 1);
|
rtx right = XEXP (x, 1);
|
if (GET_CODE (right) == CONST_INT
|
if (GET_CODE (right) == CONST_INT
|
&& CONST_OK_FOR_K (INTVAL (right)))
|
&& CONST_OK_FOR_K (INTVAL (right)))
|
*total = s390_cost->mhi;
|
*total = s390_cost->mhi;
|
else if (GET_CODE (left) == SIGN_EXTEND)
|
else if (GET_CODE (left) == SIGN_EXTEND)
|
*total = s390_cost->mh;
|
*total = s390_cost->mh;
|
else
|
else
|
*total = s390_cost->ms; /* msr, ms, msy */
|
*total = s390_cost->ms; /* msr, ms, msy */
|
break;
|
break;
|
}
|
}
|
case DImode:
|
case DImode:
|
{
|
{
|
rtx left = XEXP (x, 0);
|
rtx left = XEXP (x, 0);
|
rtx right = XEXP (x, 1);
|
rtx right = XEXP (x, 1);
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
if (GET_CODE (right) == CONST_INT
|
if (GET_CODE (right) == CONST_INT
|
&& CONST_OK_FOR_K (INTVAL (right)))
|
&& CONST_OK_FOR_K (INTVAL (right)))
|
*total = s390_cost->mghi;
|
*total = s390_cost->mghi;
|
else if (GET_CODE (left) == SIGN_EXTEND)
|
else if (GET_CODE (left) == SIGN_EXTEND)
|
*total = s390_cost->msgf;
|
*total = s390_cost->msgf;
|
else
|
else
|
*total = s390_cost->msg; /* msgr, msg */
|
*total = s390_cost->msg; /* msgr, msg */
|
}
|
}
|
else /* TARGET_31BIT */
|
else /* TARGET_31BIT */
|
{
|
{
|
if (GET_CODE (left) == SIGN_EXTEND
|
if (GET_CODE (left) == SIGN_EXTEND
|
&& GET_CODE (right) == SIGN_EXTEND)
|
&& GET_CODE (right) == SIGN_EXTEND)
|
/* mulsidi case: mr, m */
|
/* mulsidi case: mr, m */
|
*total = s390_cost->m;
|
*total = s390_cost->m;
|
else if (GET_CODE (left) == ZERO_EXTEND
|
else if (GET_CODE (left) == ZERO_EXTEND
|
&& GET_CODE (right) == ZERO_EXTEND
|
&& GET_CODE (right) == ZERO_EXTEND
|
&& TARGET_CPU_ZARCH)
|
&& TARGET_CPU_ZARCH)
|
/* umulsidi case: ml, mlr */
|
/* umulsidi case: ml, mlr */
|
*total = s390_cost->ml;
|
*total = s390_cost->ml;
|
else
|
else
|
/* Complex calculation is required. */
|
/* Complex calculation is required. */
|
*total = COSTS_N_INSNS (40);
|
*total = COSTS_N_INSNS (40);
|
}
|
}
|
break;
|
break;
|
}
|
}
|
case SFmode:
|
case SFmode:
|
case DFmode:
|
case DFmode:
|
*total = s390_cost->mult_df;
|
*total = s390_cost->mult_df;
|
break;
|
break;
|
case TFmode:
|
case TFmode:
|
*total = s390_cost->mxbr;
|
*total = s390_cost->mxbr;
|
break;
|
break;
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
return false;
|
return false;
|
|
|
case UDIV:
|
case UDIV:
|
case UMOD:
|
case UMOD:
|
if (GET_MODE (x) == TImode) /* 128 bit division */
|
if (GET_MODE (x) == TImode) /* 128 bit division */
|
*total = s390_cost->dlgr;
|
*total = s390_cost->dlgr;
|
else if (GET_MODE (x) == DImode)
|
else if (GET_MODE (x) == DImode)
|
{
|
{
|
rtx right = XEXP (x, 1);
|
rtx right = XEXP (x, 1);
|
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
|
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
|
*total = s390_cost->dlr;
|
*total = s390_cost->dlr;
|
else /* 64 by 64 bit division */
|
else /* 64 by 64 bit division */
|
*total = s390_cost->dlgr;
|
*total = s390_cost->dlgr;
|
}
|
}
|
else if (GET_MODE (x) == SImode) /* 32 bit division */
|
else if (GET_MODE (x) == SImode) /* 32 bit division */
|
*total = s390_cost->dlr;
|
*total = s390_cost->dlr;
|
return false;
|
return false;
|
|
|
case DIV:
|
case DIV:
|
case MOD:
|
case MOD:
|
if (GET_MODE (x) == DImode)
|
if (GET_MODE (x) == DImode)
|
{
|
{
|
rtx right = XEXP (x, 1);
|
rtx right = XEXP (x, 1);
|
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
|
if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
*total = s390_cost->dsgfr;
|
*total = s390_cost->dsgfr;
|
else
|
else
|
*total = s390_cost->dr;
|
*total = s390_cost->dr;
|
else /* 64 by 64 bit division */
|
else /* 64 by 64 bit division */
|
*total = s390_cost->dsgr;
|
*total = s390_cost->dsgr;
|
}
|
}
|
else if (GET_MODE (x) == SImode) /* 32 bit division */
|
else if (GET_MODE (x) == SImode) /* 32 bit division */
|
*total = s390_cost->dlr;
|
*total = s390_cost->dlr;
|
else if (GET_MODE (x) == SFmode)
|
else if (GET_MODE (x) == SFmode)
|
{
|
{
|
if (TARGET_IEEE_FLOAT)
|
if (TARGET_IEEE_FLOAT)
|
*total = s390_cost->debr;
|
*total = s390_cost->debr;
|
else /* TARGET_IBM_FLOAT */
|
else /* TARGET_IBM_FLOAT */
|
*total = s390_cost->der;
|
*total = s390_cost->der;
|
}
|
}
|
else if (GET_MODE (x) == DFmode)
|
else if (GET_MODE (x) == DFmode)
|
{
|
{
|
if (TARGET_IEEE_FLOAT)
|
if (TARGET_IEEE_FLOAT)
|
*total = s390_cost->ddbr;
|
*total = s390_cost->ddbr;
|
else /* TARGET_IBM_FLOAT */
|
else /* TARGET_IBM_FLOAT */
|
*total = s390_cost->ddr;
|
*total = s390_cost->ddr;
|
}
|
}
|
else if (GET_MODE (x) == TFmode)
|
else if (GET_MODE (x) == TFmode)
|
{
|
{
|
if (TARGET_IEEE_FLOAT)
|
if (TARGET_IEEE_FLOAT)
|
*total = s390_cost->dxbr;
|
*total = s390_cost->dxbr;
|
else /* TARGET_IBM_FLOAT */
|
else /* TARGET_IBM_FLOAT */
|
*total = s390_cost->dxr;
|
*total = s390_cost->dxr;
|
}
|
}
|
return false;
|
return false;
|
|
|
case SQRT:
|
case SQRT:
|
if (GET_MODE (x) == SFmode)
|
if (GET_MODE (x) == SFmode)
|
*total = s390_cost->sqebr;
|
*total = s390_cost->sqebr;
|
else if (GET_MODE (x) == DFmode)
|
else if (GET_MODE (x) == DFmode)
|
*total = s390_cost->sqdbr;
|
*total = s390_cost->sqdbr;
|
else /* TFmode */
|
else /* TFmode */
|
*total = s390_cost->sqxbr;
|
*total = s390_cost->sqxbr;
|
return false;
|
return false;
|
|
|
case SIGN_EXTEND:
|
case SIGN_EXTEND:
|
case ZERO_EXTEND:
|
case ZERO_EXTEND:
|
if (outer_code == MULT || outer_code == DIV || outer_code == MOD
|
if (outer_code == MULT || outer_code == DIV || outer_code == MOD
|
|| outer_code == PLUS || outer_code == MINUS
|
|| outer_code == PLUS || outer_code == MINUS
|
|| outer_code == COMPARE)
|
|| outer_code == COMPARE)
|
*total = 0;
|
*total = 0;
|
return false;
|
return false;
|
|
|
case COMPARE:
|
case COMPARE:
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
if (GET_CODE (XEXP (x, 0)) == AND
|
if (GET_CODE (XEXP (x, 0)) == AND
|
&& GET_CODE (XEXP (x, 1)) == CONST_INT
|
&& GET_CODE (XEXP (x, 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
|
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
|
{
|
{
|
rtx op0 = XEXP (XEXP (x, 0), 0);
|
rtx op0 = XEXP (XEXP (x, 0), 0);
|
rtx op1 = XEXP (XEXP (x, 0), 1);
|
rtx op1 = XEXP (XEXP (x, 0), 1);
|
rtx op2 = XEXP (x, 1);
|
rtx op2 = XEXP (x, 1);
|
|
|
if (memory_operand (op0, GET_MODE (op0))
|
if (memory_operand (op0, GET_MODE (op0))
|
&& s390_tm_ccmode (op1, op2, 0) != VOIDmode)
|
&& s390_tm_ccmode (op1, op2, 0) != VOIDmode)
|
return true;
|
return true;
|
if (register_operand (op0, GET_MODE (op0))
|
if (register_operand (op0, GET_MODE (op0))
|
&& s390_tm_ccmode (op1, op2, 1) != VOIDmode)
|
&& s390_tm_ccmode (op1, op2, 1) != VOIDmode)
|
return true;
|
return true;
|
}
|
}
|
return false;
|
return false;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
/* Return the cost of an address rtx ADDR. */
|
/* Return the cost of an address rtx ADDR. */
|
|
|
static int
|
static int
|
s390_address_cost (rtx addr)
|
s390_address_cost (rtx addr)
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
if (!s390_decompose_address (addr, &ad))
|
if (!s390_decompose_address (addr, &ad))
|
return 1000;
|
return 1000;
|
|
|
return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
|
return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
|
}
|
}
|
|
|
/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode,
|
/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode,
|
otherwise return 0. */
|
otherwise return 0. */
|
|
|
int
|
int
|
tls_symbolic_operand (rtx op)
|
tls_symbolic_operand (rtx op)
|
{
|
{
|
if (GET_CODE (op) != SYMBOL_REF)
|
if (GET_CODE (op) != SYMBOL_REF)
|
return 0;
|
return 0;
|
return SYMBOL_REF_TLS_MODEL (op);
|
return SYMBOL_REF_TLS_MODEL (op);
|
}
|
}
|
|
|
/* Split DImode access register reference REG (on 64-bit) into its constituent
|
/* Split DImode access register reference REG (on 64-bit) into its constituent
|
low and high parts, and store them into LO and HI. Note that gen_lowpart/
|
low and high parts, and store them into LO and HI. Note that gen_lowpart/
|
gen_highpart cannot be used as they assume all registers are word-sized,
|
gen_highpart cannot be used as they assume all registers are word-sized,
|
while our access registers have only half that size. */
|
while our access registers have only half that size. */
|
|
|
void
|
void
|
s390_split_access_reg (rtx reg, rtx *lo, rtx *hi)
|
s390_split_access_reg (rtx reg, rtx *lo, rtx *hi)
|
{
|
{
|
gcc_assert (TARGET_64BIT);
|
gcc_assert (TARGET_64BIT);
|
gcc_assert (ACCESS_REG_P (reg));
|
gcc_assert (ACCESS_REG_P (reg));
|
gcc_assert (GET_MODE (reg) == DImode);
|
gcc_assert (GET_MODE (reg) == DImode);
|
gcc_assert (!(REGNO (reg) & 1));
|
gcc_assert (!(REGNO (reg) & 1));
|
|
|
*lo = gen_rtx_REG (SImode, REGNO (reg) + 1);
|
*lo = gen_rtx_REG (SImode, REGNO (reg) + 1);
|
*hi = gen_rtx_REG (SImode, REGNO (reg));
|
*hi = gen_rtx_REG (SImode, REGNO (reg));
|
}
|
}
|
|
|
/* Return true if OP contains a symbol reference */
|
/* Return true if OP contains a symbol reference */
|
|
|
bool
|
bool
|
symbolic_reference_mentioned_p (rtx op)
|
symbolic_reference_mentioned_p (rtx op)
|
{
|
{
|
const char *fmt;
|
const char *fmt;
|
int i;
|
int i;
|
|
|
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
|
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
|
return 1;
|
return 1;
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (op));
|
fmt = GET_RTX_FORMAT (GET_CODE (op));
|
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'E')
|
if (fmt[i] == 'E')
|
{
|
{
|
int j;
|
int j;
|
|
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
|
if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
|
if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
|
else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return true if OP contains a reference to a thread-local symbol. */
|
/* Return true if OP contains a reference to a thread-local symbol. */
|
|
|
bool
|
bool
|
tls_symbolic_reference_mentioned_p (rtx op)
|
tls_symbolic_reference_mentioned_p (rtx op)
|
{
|
{
|
const char *fmt;
|
const char *fmt;
|
int i;
|
int i;
|
|
|
if (GET_CODE (op) == SYMBOL_REF)
|
if (GET_CODE (op) == SYMBOL_REF)
|
return tls_symbolic_operand (op);
|
return tls_symbolic_operand (op);
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (op));
|
fmt = GET_RTX_FORMAT (GET_CODE (op));
|
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'E')
|
if (fmt[i] == 'E')
|
{
|
{
|
int j;
|
int j;
|
|
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
|
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
|
if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
|
if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
|
return true;
|
return true;
|
}
|
}
|
|
|
else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i)))
|
else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i)))
|
return true;
|
return true;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
|
|
/* Return true if OP is a legitimate general operand when
|
/* Return true if OP is a legitimate general operand when
|
generating PIC code. It is given that flag_pic is on
|
generating PIC code. It is given that flag_pic is on
|
and that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
and that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
|
|
int
|
int
|
legitimate_pic_operand_p (rtx op)
|
legitimate_pic_operand_p (rtx op)
|
{
|
{
|
/* Accept all non-symbolic constants. */
|
/* Accept all non-symbolic constants. */
|
if (!SYMBOLIC_CONST (op))
|
if (!SYMBOLIC_CONST (op))
|
return 1;
|
return 1;
|
|
|
/* Reject everything else; must be handled
|
/* Reject everything else; must be handled
|
via emit_symbolic_move. */
|
via emit_symbolic_move. */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Returns true if the constant value OP is a legitimate general operand.
|
/* Returns true if the constant value OP is a legitimate general operand.
|
It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
|
|
int
|
int
|
legitimate_constant_p (rtx op)
|
legitimate_constant_p (rtx op)
|
{
|
{
|
/* Accept all non-symbolic constants. */
|
/* Accept all non-symbolic constants. */
|
if (!SYMBOLIC_CONST (op))
|
if (!SYMBOLIC_CONST (op))
|
return 1;
|
return 1;
|
|
|
/* Accept immediate LARL operands. */
|
/* Accept immediate LARL operands. */
|
if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode))
|
if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode))
|
return 1;
|
return 1;
|
|
|
/* Thread-local symbols are never legal constants. This is
|
/* Thread-local symbols are never legal constants. This is
|
so that emit_call knows that computing such addresses
|
so that emit_call knows that computing such addresses
|
might require a function call. */
|
might require a function call. */
|
if (TLS_SYMBOLIC_CONST (op))
|
if (TLS_SYMBOLIC_CONST (op))
|
return 0;
|
return 0;
|
|
|
/* In the PIC case, symbolic constants must *not* be
|
/* In the PIC case, symbolic constants must *not* be
|
forced into the literal pool. We accept them here,
|
forced into the literal pool. We accept them here,
|
so that they will be handled by emit_symbolic_move. */
|
so that they will be handled by emit_symbolic_move. */
|
if (flag_pic)
|
if (flag_pic)
|
return 1;
|
return 1;
|
|
|
/* All remaining non-PIC symbolic constants are
|
/* All remaining non-PIC symbolic constants are
|
forced into the literal pool. */
|
forced into the literal pool. */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Determine if it's legal to put X into the constant pool. This
|
/* Determine if it's legal to put X into the constant pool. This
|
is not possible if X contains the address of a symbol that is
|
is not possible if X contains the address of a symbol that is
|
not constant (TLS) or not known at final link time (PIC). */
|
not constant (TLS) or not known at final link time (PIC). */
|
|
|
static bool
|
static bool
|
s390_cannot_force_const_mem (rtx x)
|
s390_cannot_force_const_mem (rtx x)
|
{
|
{
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
/* Accept all non-symbolic constants. */
|
/* Accept all non-symbolic constants. */
|
return false;
|
return false;
|
|
|
case LABEL_REF:
|
case LABEL_REF:
|
/* Labels are OK iff we are non-PIC. */
|
/* Labels are OK iff we are non-PIC. */
|
return flag_pic != 0;
|
return flag_pic != 0;
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
/* 'Naked' TLS symbol references are never OK,
|
/* 'Naked' TLS symbol references are never OK,
|
non-TLS symbols are OK iff we are non-PIC. */
|
non-TLS symbols are OK iff we are non-PIC. */
|
if (tls_symbolic_operand (x))
|
if (tls_symbolic_operand (x))
|
return true;
|
return true;
|
else
|
else
|
return flag_pic != 0;
|
return flag_pic != 0;
|
|
|
case CONST:
|
case CONST:
|
return s390_cannot_force_const_mem (XEXP (x, 0));
|
return s390_cannot_force_const_mem (XEXP (x, 0));
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
return s390_cannot_force_const_mem (XEXP (x, 0))
|
return s390_cannot_force_const_mem (XEXP (x, 0))
|
|| s390_cannot_force_const_mem (XEXP (x, 1));
|
|| s390_cannot_force_const_mem (XEXP (x, 1));
|
|
|
case UNSPEC:
|
case UNSPEC:
|
switch (XINT (x, 1))
|
switch (XINT (x, 1))
|
{
|
{
|
/* Only lt-relative or GOT-relative UNSPECs are OK. */
|
/* Only lt-relative or GOT-relative UNSPECs are OK. */
|
case UNSPEC_LTREL_OFFSET:
|
case UNSPEC_LTREL_OFFSET:
|
case UNSPEC_GOT:
|
case UNSPEC_GOT:
|
case UNSPEC_GOTOFF:
|
case UNSPEC_GOTOFF:
|
case UNSPEC_PLTOFF:
|
case UNSPEC_PLTOFF:
|
case UNSPEC_TLSGD:
|
case UNSPEC_TLSGD:
|
case UNSPEC_TLSLDM:
|
case UNSPEC_TLSLDM:
|
case UNSPEC_NTPOFF:
|
case UNSPEC_NTPOFF:
|
case UNSPEC_DTPOFF:
|
case UNSPEC_DTPOFF:
|
case UNSPEC_GOTNTPOFF:
|
case UNSPEC_GOTNTPOFF:
|
case UNSPEC_INDNTPOFF:
|
case UNSPEC_INDNTPOFF:
|
return false;
|
return false;
|
|
|
/* If the literal pool shares the code section, be put
|
/* If the literal pool shares the code section, be put
|
execute template placeholders into the pool as well. */
|
execute template placeholders into the pool as well. */
|
case UNSPEC_INSN:
|
case UNSPEC_INSN:
|
return TARGET_CPU_ZARCH;
|
return TARGET_CPU_ZARCH;
|
|
|
default:
|
default:
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
/* Returns true if the constant value OP is a legitimate general
|
/* Returns true if the constant value OP is a legitimate general
|
operand during and after reload. The difference to
|
operand during and after reload. The difference to
|
legitimate_constant_p is that this function will not accept
|
legitimate_constant_p is that this function will not accept
|
a constant that would need to be forced to the literal pool
|
a constant that would need to be forced to the literal pool
|
before it can be used as operand. */
|
before it can be used as operand. */
|
|
|
bool
|
bool
|
legitimate_reload_constant_p (rtx op)
|
legitimate_reload_constant_p (rtx op)
|
{
|
{
|
/* Accept la(y) operands. */
|
/* Accept la(y) operands. */
|
if (GET_CODE (op) == CONST_INT
|
if (GET_CODE (op) == CONST_INT
|
&& DISP_IN_RANGE (INTVAL (op)))
|
&& DISP_IN_RANGE (INTVAL (op)))
|
return true;
|
return true;
|
|
|
/* Accept l(g)hi/l(g)fi operands. */
|
/* Accept l(g)hi/l(g)fi operands. */
|
if (GET_CODE (op) == CONST_INT
|
if (GET_CODE (op) == CONST_INT
|
&& (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op))))
|
&& (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op))))
|
return true;
|
return true;
|
|
|
/* Accept lliXX operands. */
|
/* Accept lliXX operands. */
|
if (TARGET_ZARCH
|
if (TARGET_ZARCH
|
&& GET_CODE (op) == CONST_INT
|
&& GET_CODE (op) == CONST_INT
|
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
|
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
|
&& s390_single_part (op, word_mode, HImode, 0) >= 0)
|
&& s390_single_part (op, word_mode, HImode, 0) >= 0)
|
return true;
|
return true;
|
|
|
if (TARGET_EXTIMM
|
if (TARGET_EXTIMM
|
&& GET_CODE (op) == CONST_INT
|
&& GET_CODE (op) == CONST_INT
|
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
|
&& trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
|
&& s390_single_part (op, word_mode, SImode, 0) >= 0)
|
&& s390_single_part (op, word_mode, SImode, 0) >= 0)
|
return true;
|
return true;
|
|
|
/* Accept larl operands. */
|
/* Accept larl operands. */
|
if (TARGET_CPU_ZARCH
|
if (TARGET_CPU_ZARCH
|
&& larl_operand (op, VOIDmode))
|
&& larl_operand (op, VOIDmode))
|
return true;
|
return true;
|
|
|
/* Accept lzXX operands. */
|
/* Accept lzXX operands. */
|
if (GET_CODE (op) == CONST_DOUBLE
|
if (GET_CODE (op) == CONST_DOUBLE
|
&& CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
|
&& CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
|
return true;
|
return true;
|
|
|
/* Accept double-word operands that can be split. */
|
/* Accept double-word operands that can be split. */
|
if (GET_CODE (op) == CONST_INT
|
if (GET_CODE (op) == CONST_INT
|
&& trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op))
|
&& trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op))
|
{
|
{
|
enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode;
|
enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode;
|
rtx hi = operand_subword (op, 0, 0, dword_mode);
|
rtx hi = operand_subword (op, 0, 0, dword_mode);
|
rtx lo = operand_subword (op, 1, 0, dword_mode);
|
rtx lo = operand_subword (op, 1, 0, dword_mode);
|
return legitimate_reload_constant_p (hi)
|
return legitimate_reload_constant_p (hi)
|
&& legitimate_reload_constant_p (lo);
|
&& legitimate_reload_constant_p (lo);
|
}
|
}
|
|
|
/* Everything else cannot be handled without reload. */
|
/* Everything else cannot be handled without reload. */
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
|
/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
|
return the class of reg to actually use. */
|
return the class of reg to actually use. */
|
|
|
enum reg_class
|
enum reg_class
|
s390_preferred_reload_class (rtx op, enum reg_class class)
|
s390_preferred_reload_class (rtx op, enum reg_class class)
|
{
|
{
|
switch (GET_CODE (op))
|
switch (GET_CODE (op))
|
{
|
{
|
/* Constants we cannot reload must be forced into the
|
/* Constants we cannot reload must be forced into the
|
literal pool. */
|
literal pool. */
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
case CONST_INT:
|
case CONST_INT:
|
if (legitimate_reload_constant_p (op))
|
if (legitimate_reload_constant_p (op))
|
return class;
|
return class;
|
else
|
else
|
return NO_REGS;
|
return NO_REGS;
|
|
|
/* If a symbolic constant or a PLUS is reloaded,
|
/* If a symbolic constant or a PLUS is reloaded,
|
it is most likely being used as an address, so
|
it is most likely being used as an address, so
|
prefer ADDR_REGS. If 'class' is not a superset
|
prefer ADDR_REGS. If 'class' is not a superset
|
of ADDR_REGS, e.g. FP_REGS, reject this reload. */
|
of ADDR_REGS, e.g. FP_REGS, reject this reload. */
|
case PLUS:
|
case PLUS:
|
case LABEL_REF:
|
case LABEL_REF:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case CONST:
|
case CONST:
|
if (reg_class_subset_p (ADDR_REGS, class))
|
if (reg_class_subset_p (ADDR_REGS, class))
|
return ADDR_REGS;
|
return ADDR_REGS;
|
else
|
else
|
return NO_REGS;
|
return NO_REGS;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return class;
|
return class;
|
}
|
}
|
|
|
/* Return the register class of a scratch register needed to
|
/* Return the register class of a scratch register needed to
|
load IN into a register of class CLASS in MODE.
|
load IN into a register of class CLASS in MODE.
|
|
|
We need a temporary when loading a PLUS expression which
|
We need a temporary when loading a PLUS expression which
|
is not a legitimate operand of the LOAD ADDRESS instruction. */
|
is not a legitimate operand of the LOAD ADDRESS instruction. */
|
|
|
enum reg_class
|
enum reg_class
|
s390_secondary_input_reload_class (enum reg_class class,
|
s390_secondary_input_reload_class (enum reg_class class,
|
enum machine_mode mode, rtx in)
|
enum machine_mode mode, rtx in)
|
{
|
{
|
if (s390_plus_operand (in, mode))
|
if (s390_plus_operand (in, mode))
|
return ADDR_REGS;
|
return ADDR_REGS;
|
|
|
if (reg_classes_intersect_p (FP_REGS, class)
|
if (reg_classes_intersect_p (FP_REGS, class)
|
&& mode == TFmode
|
&& mode == TFmode
|
&& GET_CODE (in) == MEM
|
&& GET_CODE (in) == MEM
|
&& GET_CODE (XEXP (in, 0)) == PLUS
|
&& GET_CODE (XEXP (in, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (in, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (in, 0), 1)) == CONST_INT
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (in, 0), 1))
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (in, 0), 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
return ADDR_REGS;
|
return ADDR_REGS;
|
|
|
if (reg_classes_intersect_p (CC_REGS, class))
|
if (reg_classes_intersect_p (CC_REGS, class))
|
return GENERAL_REGS;
|
return GENERAL_REGS;
|
|
|
return NO_REGS;
|
return NO_REGS;
|
}
|
}
|
|
|
/* Return the register class of a scratch register needed to
|
/* Return the register class of a scratch register needed to
|
store a register of class CLASS in MODE into OUT:
|
store a register of class CLASS in MODE into OUT:
|
|
|
We need a temporary when storing a double-word to a
|
We need a temporary when storing a double-word to a
|
non-offsettable memory address. */
|
non-offsettable memory address. */
|
|
|
enum reg_class
|
enum reg_class
|
s390_secondary_output_reload_class (enum reg_class class,
|
s390_secondary_output_reload_class (enum reg_class class,
|
enum machine_mode mode, rtx out)
|
enum machine_mode mode, rtx out)
|
{
|
{
|
if ((TARGET_64BIT ? (mode == TImode || mode == TFmode)
|
if ((TARGET_64BIT ? (mode == TImode || mode == TFmode)
|
: (mode == DImode || mode == DFmode))
|
: (mode == DImode || mode == DFmode))
|
&& reg_classes_intersect_p (GENERAL_REGS, class)
|
&& reg_classes_intersect_p (GENERAL_REGS, class)
|
&& GET_CODE (out) == MEM
|
&& GET_CODE (out) == MEM
|
&& GET_CODE (XEXP (out, 0)) == PLUS
|
&& GET_CODE (XEXP (out, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
return ADDR_REGS;
|
return ADDR_REGS;
|
|
|
if (reg_classes_intersect_p (FP_REGS, class)
|
if (reg_classes_intersect_p (FP_REGS, class)
|
&& mode == TFmode
|
&& mode == TFmode
|
&& GET_CODE (out) == MEM
|
&& GET_CODE (out) == MEM
|
&& GET_CODE (XEXP (out, 0)) == PLUS
|
&& GET_CODE (XEXP (out, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
|
&& !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
+ GET_MODE_SIZE (mode) - 1))
|
return ADDR_REGS;
|
return ADDR_REGS;
|
|
|
if (reg_classes_intersect_p (CC_REGS, class))
|
if (reg_classes_intersect_p (CC_REGS, class))
|
return GENERAL_REGS;
|
return GENERAL_REGS;
|
|
|
return NO_REGS;
|
return NO_REGS;
|
}
|
}
|
|
|
/* Generate code to load SRC, which is PLUS that is not a
|
/* Generate code to load SRC, which is PLUS that is not a
|
legitimate operand for the LA instruction, into TARGET.
|
legitimate operand for the LA instruction, into TARGET.
|
SCRATCH may be used as scratch register. */
|
SCRATCH may be used as scratch register. */
|
|
|
void
|
void
|
s390_expand_plus_operand (rtx target, rtx src,
|
s390_expand_plus_operand (rtx target, rtx src,
|
rtx scratch)
|
rtx scratch)
|
{
|
{
|
rtx sum1, sum2;
|
rtx sum1, sum2;
|
struct s390_address ad;
|
struct s390_address ad;
|
|
|
/* src must be a PLUS; get its two operands. */
|
/* src must be a PLUS; get its two operands. */
|
gcc_assert (GET_CODE (src) == PLUS);
|
gcc_assert (GET_CODE (src) == PLUS);
|
gcc_assert (GET_MODE (src) == Pmode);
|
gcc_assert (GET_MODE (src) == Pmode);
|
|
|
/* Check if any of the two operands is already scheduled
|
/* Check if any of the two operands is already scheduled
|
for replacement by reload. This can happen e.g. when
|
for replacement by reload. This can happen e.g. when
|
float registers occur in an address. */
|
float registers occur in an address. */
|
sum1 = find_replacement (&XEXP (src, 0));
|
sum1 = find_replacement (&XEXP (src, 0));
|
sum2 = find_replacement (&XEXP (src, 1));
|
sum2 = find_replacement (&XEXP (src, 1));
|
src = gen_rtx_PLUS (Pmode, sum1, sum2);
|
src = gen_rtx_PLUS (Pmode, sum1, sum2);
|
|
|
/* If the address is already strictly valid, there's nothing to do. */
|
/* If the address is already strictly valid, there's nothing to do. */
|
if (!s390_decompose_address (src, &ad)
|
if (!s390_decompose_address (src, &ad)
|
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
|
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
|
{
|
{
|
/* Otherwise, one of the operands cannot be an address register;
|
/* Otherwise, one of the operands cannot be an address register;
|
we reload its value into the scratch register. */
|
we reload its value into the scratch register. */
|
if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
|
if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
|
{
|
{
|
emit_move_insn (scratch, sum1);
|
emit_move_insn (scratch, sum1);
|
sum1 = scratch;
|
sum1 = scratch;
|
}
|
}
|
if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
|
if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
|
{
|
{
|
emit_move_insn (scratch, sum2);
|
emit_move_insn (scratch, sum2);
|
sum2 = scratch;
|
sum2 = scratch;
|
}
|
}
|
|
|
/* According to the way these invalid addresses are generated
|
/* According to the way these invalid addresses are generated
|
in reload.c, it should never happen (at least on s390) that
|
in reload.c, it should never happen (at least on s390) that
|
*neither* of the PLUS components, after find_replacements
|
*neither* of the PLUS components, after find_replacements
|
was applied, is an address register. */
|
was applied, is an address register. */
|
if (sum1 == scratch && sum2 == scratch)
|
if (sum1 == scratch && sum2 == scratch)
|
{
|
{
|
debug_rtx (src);
|
debug_rtx (src);
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
src = gen_rtx_PLUS (Pmode, sum1, sum2);
|
src = gen_rtx_PLUS (Pmode, sum1, sum2);
|
}
|
}
|
|
|
/* Emit the LOAD ADDRESS pattern. Note that reload of PLUS
|
/* Emit the LOAD ADDRESS pattern. Note that reload of PLUS
|
is only ever performed on addresses, so we can mark the
|
is only ever performed on addresses, so we can mark the
|
sum as legitimate for LA in any case. */
|
sum as legitimate for LA in any case. */
|
s390_load_address (target, src);
|
s390_load_address (target, src);
|
}
|
}
|
|
|
|
|
/* Return true if ADDR is a valid memory address.
|
/* Return true if ADDR is a valid memory address.
|
STRICT specifies whether strict register checking applies. */
|
STRICT specifies whether strict register checking applies. */
|
|
|
bool
|
bool
|
legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
|
legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
|
rtx addr, int strict)
|
rtx addr, int strict)
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
if (!s390_decompose_address (addr, &ad))
|
if (!s390_decompose_address (addr, &ad))
|
return false;
|
return false;
|
|
|
if (strict)
|
if (strict)
|
{
|
{
|
if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
return false;
|
return false;
|
|
|
if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))
|
if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))
|
return false;
|
return false;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (ad.base
|
if (ad.base
|
&& !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER
|
&& !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER
|
|| REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS))
|
|| REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS))
|
return false;
|
return false;
|
|
|
if (ad.indx
|
if (ad.indx
|
&& !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER
|
&& !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER
|
|| REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS))
|
|| REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS))
|
return false;
|
return false;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Return true if OP is a valid operand for the LA instruction.
|
/* Return true if OP is a valid operand for the LA instruction.
|
In 31-bit, we need to prove that the result is used as an
|
In 31-bit, we need to prove that the result is used as an
|
address, as LA performs only a 31-bit addition. */
|
address, as LA performs only a 31-bit addition. */
|
|
|
bool
|
bool
|
legitimate_la_operand_p (rtx op)
|
legitimate_la_operand_p (rtx op)
|
{
|
{
|
struct s390_address addr;
|
struct s390_address addr;
|
if (!s390_decompose_address (op, &addr))
|
if (!s390_decompose_address (op, &addr))
|
return false;
|
return false;
|
|
|
return (TARGET_64BIT || addr.pointer);
|
return (TARGET_64BIT || addr.pointer);
|
}
|
}
|
|
|
/* Return true if it is valid *and* preferable to use LA to
|
/* Return true if it is valid *and* preferable to use LA to
|
compute the sum of OP1 and OP2. */
|
compute the sum of OP1 and OP2. */
|
|
|
bool
|
bool
|
preferred_la_operand_p (rtx op1, rtx op2)
|
preferred_la_operand_p (rtx op1, rtx op2)
|
{
|
{
|
struct s390_address addr;
|
struct s390_address addr;
|
|
|
if (op2 != const0_rtx)
|
if (op2 != const0_rtx)
|
op1 = gen_rtx_PLUS (Pmode, op1, op2);
|
op1 = gen_rtx_PLUS (Pmode, op1, op2);
|
|
|
if (!s390_decompose_address (op1, &addr))
|
if (!s390_decompose_address (op1, &addr))
|
return false;
|
return false;
|
if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base)))
|
if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base)))
|
return false;
|
return false;
|
if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx)))
|
if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx)))
|
return false;
|
return false;
|
|
|
if (!TARGET_64BIT && !addr.pointer)
|
if (!TARGET_64BIT && !addr.pointer)
|
return false;
|
return false;
|
|
|
if (addr.pointer)
|
if (addr.pointer)
|
return true;
|
return true;
|
|
|
if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
|
if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
|
|| (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
|
|| (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Emit a forced load-address operation to load SRC into DST.
|
/* Emit a forced load-address operation to load SRC into DST.
|
This will use the LOAD ADDRESS instruction even in situations
|
This will use the LOAD ADDRESS instruction even in situations
|
where legitimate_la_operand_p (SRC) returns false. */
|
where legitimate_la_operand_p (SRC) returns false. */
|
|
|
void
|
void
|
s390_load_address (rtx dst, rtx src)
|
s390_load_address (rtx dst, rtx src)
|
{
|
{
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
emit_move_insn (dst, src);
|
emit_move_insn (dst, src);
|
else
|
else
|
emit_insn (gen_force_la_31 (dst, src));
|
emit_insn (gen_force_la_31 (dst, src));
|
}
|
}
|
|
|
/* Return a legitimate reference for ORIG (an address) using the
|
/* Return a legitimate reference for ORIG (an address) using the
|
register REG. If REG is 0, a new pseudo is generated.
|
register REG. If REG is 0, a new pseudo is generated.
|
|
|
There are two types of references that must be handled:
|
There are two types of references that must be handled:
|
|
|
1. Global data references must load the address from the GOT, via
|
1. Global data references must load the address from the GOT, via
|
the PIC reg. An insn is emitted to do this load, and the reg is
|
the PIC reg. An insn is emitted to do this load, and the reg is
|
returned.
|
returned.
|
|
|
2. Static data references, constant pool addresses, and code labels
|
2. Static data references, constant pool addresses, and code labels
|
compute the address as an offset from the GOT, whose base is in
|
compute the address as an offset from the GOT, whose base is in
|
the PIC reg. Static data objects have SYMBOL_FLAG_LOCAL set to
|
the PIC reg. Static data objects have SYMBOL_FLAG_LOCAL set to
|
differentiate them from global data objects. The returned
|
differentiate them from global data objects. The returned
|
address is the PIC reg + an unspec constant.
|
address is the PIC reg + an unspec constant.
|
|
|
GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
|
GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
|
reg also appears in the address. */
|
reg also appears in the address. */
|
|
|
rtx
|
rtx
|
legitimize_pic_address (rtx orig, rtx reg)
|
legitimize_pic_address (rtx orig, rtx reg)
|
{
|
{
|
rtx addr = orig;
|
rtx addr = orig;
|
rtx new = orig;
|
rtx new = orig;
|
rtx base;
|
rtx base;
|
|
|
gcc_assert (!TLS_SYMBOLIC_CONST (addr));
|
gcc_assert (!TLS_SYMBOLIC_CONST (addr));
|
|
|
if (GET_CODE (addr) == LABEL_REF
|
if (GET_CODE (addr) == LABEL_REF
|
|| (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
|
|| (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
|
{
|
{
|
/* This is a local symbol. */
|
/* This is a local symbol. */
|
if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
|
if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
|
{
|
{
|
/* Access local symbols PC-relative via LARL.
|
/* Access local symbols PC-relative via LARL.
|
This is the same as in the non-PIC case, so it is
|
This is the same as in the non-PIC case, so it is
|
handled automatically ... */
|
handled automatically ... */
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Access local symbols relative to the GOT. */
|
/* Access local symbols relative to the GOT. */
|
|
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
emit_move_insn (temp, addr);
|
emit_move_insn (temp, addr);
|
|
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
}
|
}
|
}
|
}
|
else if (GET_CODE (addr) == SYMBOL_REF)
|
else if (GET_CODE (addr) == SYMBOL_REF)
|
{
|
{
|
if (reg == 0)
|
if (reg == 0)
|
reg = gen_reg_rtx (Pmode);
|
reg = gen_reg_rtx (Pmode);
|
|
|
if (flag_pic == 1)
|
if (flag_pic == 1)
|
{
|
{
|
/* Assume GOT offset < 4k. This is handled the same way
|
/* Assume GOT offset < 4k. This is handled the same way
|
in both 31- and 64-bit code (@GOT). */
|
in both 31- and 64-bit code (@GOT). */
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
|
new = gen_const_mem (Pmode, new);
|
new = gen_const_mem (Pmode, new);
|
emit_move_insn (reg, new);
|
emit_move_insn (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
else if (TARGET_CPU_ZARCH)
|
else if (TARGET_CPU_ZARCH)
|
{
|
{
|
/* If the GOT offset might be >= 4k, we determine the position
|
/* If the GOT offset might be >= 4k, we determine the position
|
of the GOT entry via a PC-relative LARL (@GOTENT). */
|
of the GOT entry via a PC-relative LARL (@GOTENT). */
|
|
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx temp = gen_reg_rtx (Pmode);
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = gen_const_mem (Pmode, temp);
|
new = gen_const_mem (Pmode, temp);
|
emit_move_insn (reg, new);
|
emit_move_insn (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* If the GOT offset might be >= 4k, we have to load it
|
/* If the GOT offset might be >= 4k, we have to load it
|
from the literal pool (@GOT). */
|
from the literal pool (@GOT). */
|
|
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx temp = gen_reg_rtx (Pmode);
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
emit_move_insn (temp, addr);
|
emit_move_insn (temp, addr);
|
|
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_const_mem (Pmode, new);
|
new = gen_const_mem (Pmode, new);
|
emit_move_insn (reg, new);
|
emit_move_insn (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
if (GET_CODE (addr) == CONST)
|
if (GET_CODE (addr) == CONST)
|
{
|
{
|
addr = XEXP (addr, 0);
|
addr = XEXP (addr, 0);
|
if (GET_CODE (addr) == UNSPEC)
|
if (GET_CODE (addr) == UNSPEC)
|
{
|
{
|
gcc_assert (XVECLEN (addr, 0) == 1);
|
gcc_assert (XVECLEN (addr, 0) == 1);
|
switch (XINT (addr, 1))
|
switch (XINT (addr, 1))
|
{
|
{
|
/* If someone moved a GOT-relative UNSPEC
|
/* If someone moved a GOT-relative UNSPEC
|
out of the literal pool, force them back in. */
|
out of the literal pool, force them back in. */
|
case UNSPEC_GOTOFF:
|
case UNSPEC_GOTOFF:
|
case UNSPEC_PLTOFF:
|
case UNSPEC_PLTOFF:
|
new = force_const_mem (Pmode, orig);
|
new = force_const_mem (Pmode, orig);
|
break;
|
break;
|
|
|
/* @GOT is OK as is if small. */
|
/* @GOT is OK as is if small. */
|
case UNSPEC_GOT:
|
case UNSPEC_GOT:
|
if (flag_pic == 2)
|
if (flag_pic == 2)
|
new = force_const_mem (Pmode, orig);
|
new = force_const_mem (Pmode, orig);
|
break;
|
break;
|
|
|
/* @GOTENT is OK as is. */
|
/* @GOTENT is OK as is. */
|
case UNSPEC_GOTENT:
|
case UNSPEC_GOTENT:
|
break;
|
break;
|
|
|
/* @PLT is OK as is on 64-bit, must be converted to
|
/* @PLT is OK as is on 64-bit, must be converted to
|
GOT-relative @PLTOFF on 31-bit. */
|
GOT-relative @PLTOFF on 31-bit. */
|
case UNSPEC_PLT:
|
case UNSPEC_PLT:
|
if (!TARGET_CPU_ZARCH)
|
if (!TARGET_CPU_ZARCH)
|
{
|
{
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
addr = XVECEXP (addr, 0, 0);
|
addr = XVECEXP (addr, 0, 0);
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
|
UNSPEC_PLTOFF);
|
UNSPEC_PLTOFF);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
emit_move_insn (temp, addr);
|
emit_move_insn (temp, addr);
|
|
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
}
|
}
|
break;
|
break;
|
|
|
/* Everything else cannot happen. */
|
/* Everything else cannot happen. */
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
else
|
else
|
gcc_assert (GET_CODE (addr) == PLUS);
|
gcc_assert (GET_CODE (addr) == PLUS);
|
}
|
}
|
if (GET_CODE (addr) == PLUS)
|
if (GET_CODE (addr) == PLUS)
|
{
|
{
|
rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
|
rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
|
|
|
gcc_assert (!TLS_SYMBOLIC_CONST (op0));
|
gcc_assert (!TLS_SYMBOLIC_CONST (op0));
|
gcc_assert (!TLS_SYMBOLIC_CONST (op1));
|
gcc_assert (!TLS_SYMBOLIC_CONST (op1));
|
|
|
/* Check first to see if this is a constant offset
|
/* Check first to see if this is a constant offset
|
from a local symbol reference. */
|
from a local symbol reference. */
|
if ((GET_CODE (op0) == LABEL_REF
|
if ((GET_CODE (op0) == LABEL_REF
|
|| (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0)))
|
|| (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0)))
|
&& GET_CODE (op1) == CONST_INT)
|
&& GET_CODE (op1) == CONST_INT)
|
{
|
{
|
if (TARGET_CPU_ZARCH
|
if (TARGET_CPU_ZARCH
|
&& larl_operand (op0, VOIDmode)
|
&& larl_operand (op0, VOIDmode)
|
&& INTVAL (op1) < (HOST_WIDE_INT)1 << 31
|
&& INTVAL (op1) < (HOST_WIDE_INT)1 << 31
|
&& INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31))
|
&& INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31))
|
{
|
{
|
if (INTVAL (op1) & 1)
|
if (INTVAL (op1) & 1)
|
{
|
{
|
/* LARL can't handle odd offsets, so emit a
|
/* LARL can't handle odd offsets, so emit a
|
pair of LARL and LA. */
|
pair of LARL and LA. */
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
|
|
if (!DISP_IN_RANGE (INTVAL (op1)))
|
if (!DISP_IN_RANGE (INTVAL (op1)))
|
{
|
{
|
HOST_WIDE_INT even = INTVAL (op1) - 1;
|
HOST_WIDE_INT even = INTVAL (op1) - 1;
|
op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
|
op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
|
op0 = gen_rtx_CONST (Pmode, op0);
|
op0 = gen_rtx_CONST (Pmode, op0);
|
op1 = const1_rtx;
|
op1 = const1_rtx;
|
}
|
}
|
|
|
emit_move_insn (temp, op0);
|
emit_move_insn (temp, op0);
|
new = gen_rtx_PLUS (Pmode, temp, op1);
|
new = gen_rtx_PLUS (Pmode, temp, op1);
|
|
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* If the offset is even, we can just use LARL.
|
/* If the offset is even, we can just use LARL.
|
This will happen automatically. */
|
This will happen automatically. */
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Access local symbols relative to the GOT. */
|
/* Access local symbols relative to the GOT. */
|
|
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
rtx temp = reg? reg : gen_reg_rtx (Pmode);
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0),
|
addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0),
|
UNSPEC_GOTOFF);
|
UNSPEC_GOTOFF);
|
addr = gen_rtx_PLUS (Pmode, addr, op1);
|
addr = gen_rtx_PLUS (Pmode, addr, op1);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = gen_rtx_CONST (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
addr = force_const_mem (Pmode, addr);
|
emit_move_insn (temp, addr);
|
emit_move_insn (temp, addr);
|
|
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Now, check whether it is a GOT relative symbol plus offset
|
/* Now, check whether it is a GOT relative symbol plus offset
|
that was pulled out of the literal pool. Force it back in. */
|
that was pulled out of the literal pool. Force it back in. */
|
|
|
else if (GET_CODE (op0) == UNSPEC
|
else if (GET_CODE (op0) == UNSPEC
|
&& GET_CODE (op1) == CONST_INT
|
&& GET_CODE (op1) == CONST_INT
|
&& XINT (op0, 1) == UNSPEC_GOTOFF)
|
&& XINT (op0, 1) == UNSPEC_GOTOFF)
|
{
|
{
|
gcc_assert (XVECLEN (op0, 0) == 1);
|
gcc_assert (XVECLEN (op0, 0) == 1);
|
|
|
new = force_const_mem (Pmode, orig);
|
new = force_const_mem (Pmode, orig);
|
}
|
}
|
|
|
/* Otherwise, compute the sum. */
|
/* Otherwise, compute the sum. */
|
else
|
else
|
{
|
{
|
base = legitimize_pic_address (XEXP (addr, 0), reg);
|
base = legitimize_pic_address (XEXP (addr, 0), reg);
|
new = legitimize_pic_address (XEXP (addr, 1),
|
new = legitimize_pic_address (XEXP (addr, 1),
|
base == reg ? NULL_RTX : reg);
|
base == reg ? NULL_RTX : reg);
|
if (GET_CODE (new) == CONST_INT)
|
if (GET_CODE (new) == CONST_INT)
|
new = plus_constant (base, INTVAL (new));
|
new = plus_constant (base, INTVAL (new));
|
else
|
else
|
{
|
{
|
if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
|
if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
|
{
|
{
|
base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
|
base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
|
new = XEXP (new, 1);
|
new = XEXP (new, 1);
|
}
|
}
|
new = gen_rtx_PLUS (Pmode, base, new);
|
new = gen_rtx_PLUS (Pmode, base, new);
|
}
|
}
|
|
|
if (GET_CODE (new) == CONST)
|
if (GET_CODE (new) == CONST)
|
new = XEXP (new, 0);
|
new = XEXP (new, 0);
|
new = force_operand (new, 0);
|
new = force_operand (new, 0);
|
}
|
}
|
}
|
}
|
}
|
}
|
return new;
|
return new;
|
}
|
}
|
|
|
/* Load the thread pointer into a register. */
|
/* Load the thread pointer into a register. */
|
|
|
rtx
|
rtx
|
s390_get_thread_pointer (void)
|
s390_get_thread_pointer (void)
|
{
|
{
|
rtx tp = gen_reg_rtx (Pmode);
|
rtx tp = gen_reg_rtx (Pmode);
|
|
|
emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM));
|
emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM));
|
mark_reg_pointer (tp, BITS_PER_WORD);
|
mark_reg_pointer (tp, BITS_PER_WORD);
|
|
|
return tp;
|
return tp;
|
}
|
}
|
|
|
/* Emit a tls call insn. The call target is the SYMBOL_REF stored
|
/* Emit a tls call insn. The call target is the SYMBOL_REF stored
|
in s390_tls_symbol which always refers to __tls_get_offset.
|
in s390_tls_symbol which always refers to __tls_get_offset.
|
The returned offset is written to RESULT_REG and an USE rtx is
|
The returned offset is written to RESULT_REG and an USE rtx is
|
generated for TLS_CALL. */
|
generated for TLS_CALL. */
|
|
|
static GTY(()) rtx s390_tls_symbol;
|
static GTY(()) rtx s390_tls_symbol;
|
|
|
static void
|
static void
|
s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
|
s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
gcc_assert (flag_pic);
|
gcc_assert (flag_pic);
|
|
|
if (!s390_tls_symbol)
|
if (!s390_tls_symbol)
|
s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
|
s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
|
|
|
insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg,
|
insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg,
|
gen_rtx_REG (Pmode, RETURN_REGNUM));
|
gen_rtx_REG (Pmode, RETURN_REGNUM));
|
|
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg);
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg);
|
CONST_OR_PURE_CALL_P (insn) = 1;
|
CONST_OR_PURE_CALL_P (insn) = 1;
|
}
|
}
|
|
|
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
|
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
|
this (thread-local) address. REG may be used as temporary. */
|
this (thread-local) address. REG may be used as temporary. */
|
|
|
static rtx
|
static rtx
|
legitimize_tls_address (rtx addr, rtx reg)
|
legitimize_tls_address (rtx addr, rtx reg)
|
{
|
{
|
rtx new, tls_call, temp, base, r2, insn;
|
rtx new, tls_call, temp, base, r2, insn;
|
|
|
if (GET_CODE (addr) == SYMBOL_REF)
|
if (GET_CODE (addr) == SYMBOL_REF)
|
switch (tls_symbolic_operand (addr))
|
switch (tls_symbolic_operand (addr))
|
{
|
{
|
case TLS_MODEL_GLOBAL_DYNAMIC:
|
case TLS_MODEL_GLOBAL_DYNAMIC:
|
start_sequence ();
|
start_sequence ();
|
r2 = gen_rtx_REG (Pmode, 2);
|
r2 = gen_rtx_REG (Pmode, 2);
|
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD);
|
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD);
|
new = gen_rtx_CONST (Pmode, tls_call);
|
new = gen_rtx_CONST (Pmode, tls_call);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
emit_move_insn (r2, new);
|
emit_move_insn (r2, new);
|
s390_emit_tls_call_insn (r2, tls_call);
|
s390_emit_tls_call_insn (r2, tls_call);
|
insn = get_insns ();
|
insn = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_libcall_block (insn, temp, r2, new);
|
emit_libcall_block (insn, temp, r2, new);
|
|
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
break;
|
break;
|
|
|
case TLS_MODEL_LOCAL_DYNAMIC:
|
case TLS_MODEL_LOCAL_DYNAMIC:
|
start_sequence ();
|
start_sequence ();
|
r2 = gen_rtx_REG (Pmode, 2);
|
r2 = gen_rtx_REG (Pmode, 2);
|
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM);
|
tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM);
|
new = gen_rtx_CONST (Pmode, tls_call);
|
new = gen_rtx_CONST (Pmode, tls_call);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
emit_move_insn (r2, new);
|
emit_move_insn (r2, new);
|
s390_emit_tls_call_insn (r2, tls_call);
|
s390_emit_tls_call_insn (r2, tls_call);
|
insn = get_insns ();
|
insn = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_libcall_block (insn, temp, r2, new);
|
emit_libcall_block (insn, temp, r2, new);
|
|
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
base = gen_reg_rtx (Pmode);
|
base = gen_reg_rtx (Pmode);
|
s390_load_address (base, new);
|
s390_load_address (base, new);
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = gen_rtx_PLUS (Pmode, base, temp);
|
new = gen_rtx_PLUS (Pmode, base, temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
break;
|
break;
|
|
|
case TLS_MODEL_INITIAL_EXEC:
|
case TLS_MODEL_INITIAL_EXEC:
|
if (flag_pic == 1)
|
if (flag_pic == 1)
|
{
|
{
|
/* Assume GOT offset < 4k. This is handled the same way
|
/* Assume GOT offset < 4k. This is handled the same way
|
in both 31- and 64-bit code. */
|
in both 31- and 64-bit code. */
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
|
new = gen_const_mem (Pmode, new);
|
new = gen_const_mem (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
}
|
}
|
else if (TARGET_CPU_ZARCH)
|
else if (TARGET_CPU_ZARCH)
|
{
|
{
|
/* If the GOT offset might be >= 4k, we determine the position
|
/* If the GOT offset might be >= 4k, we determine the position
|
of the GOT entry via a PC-relative LARL. */
|
of the GOT entry via a PC-relative LARL. */
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = gen_const_mem (Pmode, temp);
|
new = gen_const_mem (Pmode, temp);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
}
|
}
|
else if (flag_pic)
|
else if (flag_pic)
|
{
|
{
|
/* If the GOT offset might be >= 4k, we have to load it
|
/* If the GOT offset might be >= 4k, we have to load it
|
from the literal pool. */
|
from the literal pool. */
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
|
new = gen_const_mem (Pmode, new);
|
new = gen_const_mem (Pmode, new);
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_insn (gen_rtx_SET (Pmode, temp, new));
|
emit_insn (gen_rtx_SET (Pmode, temp, new));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* In position-dependent code, load the absolute address of
|
/* In position-dependent code, load the absolute address of
|
the GOT entry from the literal pool. */
|
the GOT entry from the literal pool. */
|
|
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = temp;
|
new = temp;
|
new = gen_const_mem (Pmode, new);
|
new = gen_const_mem (Pmode, new);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_insn (gen_rtx_SET (Pmode, temp, new));
|
emit_insn (gen_rtx_SET (Pmode, temp, new));
|
}
|
}
|
|
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
break;
|
break;
|
|
|
case TLS_MODEL_LOCAL_EXEC:
|
case TLS_MODEL_LOCAL_EXEC:
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
|
new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
new = force_const_mem (Pmode, new);
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, new);
|
emit_move_insn (temp, new);
|
|
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
|
if (reg != 0)
|
if (reg != 0)
|
{
|
{
|
s390_load_address (reg, new);
|
s390_load_address (reg, new);
|
new = reg;
|
new = reg;
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC)
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC)
|
{
|
{
|
switch (XINT (XEXP (addr, 0), 1))
|
switch (XINT (XEXP (addr, 0), 1))
|
{
|
{
|
case UNSPEC_INDNTPOFF:
|
case UNSPEC_INDNTPOFF:
|
gcc_assert (TARGET_CPU_ZARCH);
|
gcc_assert (TARGET_CPU_ZARCH);
|
new = addr;
|
new = addr;
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
|
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
|
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
|
{
|
{
|
new = XEXP (XEXP (addr, 0), 0);
|
new = XEXP (XEXP (addr, 0), 0);
|
if (GET_CODE (new) != SYMBOL_REF)
|
if (GET_CODE (new) != SYMBOL_REF)
|
new = gen_rtx_CONST (Pmode, new);
|
new = gen_rtx_CONST (Pmode, new);
|
|
|
new = legitimize_tls_address (new, reg);
|
new = legitimize_tls_address (new, reg);
|
new = plus_constant (new, INTVAL (XEXP (XEXP (addr, 0), 1)));
|
new = plus_constant (new, INTVAL (XEXP (XEXP (addr, 0), 1)));
|
new = force_operand (new, 0);
|
new = force_operand (new, 0);
|
}
|
}
|
|
|
else
|
else
|
gcc_unreachable (); /* for now ... */
|
gcc_unreachable (); /* for now ... */
|
|
|
return new;
|
return new;
|
}
|
}
|
|
|
/* Emit insns to move operands[1] into operands[0]. */
|
/* Emit insns to move operands[1] into operands[0]. */
|
|
|
void
|
void
|
emit_symbolic_move (rtx *operands)
|
emit_symbolic_move (rtx *operands)
|
{
|
{
|
rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
|
rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
|
|
|
if (GET_CODE (operands[0]) == MEM)
|
if (GET_CODE (operands[0]) == MEM)
|
operands[1] = force_reg (Pmode, operands[1]);
|
operands[1] = force_reg (Pmode, operands[1]);
|
else if (TLS_SYMBOLIC_CONST (operands[1]))
|
else if (TLS_SYMBOLIC_CONST (operands[1]))
|
operands[1] = legitimize_tls_address (operands[1], temp);
|
operands[1] = legitimize_tls_address (operands[1], temp);
|
else if (flag_pic)
|
else if (flag_pic)
|
operands[1] = legitimize_pic_address (operands[1], temp);
|
operands[1] = legitimize_pic_address (operands[1], temp);
|
}
|
}
|
|
|
/* Try machine-dependent ways of modifying an illegitimate address X
|
/* Try machine-dependent ways of modifying an illegitimate address X
|
to be legitimate. If we find one, return the new, valid address.
|
to be legitimate. If we find one, return the new, valid address.
|
|
|
OLDX is the address as it was before break_out_memory_refs was called.
|
OLDX is the address as it was before break_out_memory_refs was called.
|
In some cases it is useful to look at this to decide what needs to be done.
|
In some cases it is useful to look at this to decide what needs to be done.
|
|
|
MODE is the mode of the operand pointed to by X.
|
MODE is the mode of the operand pointed to by X.
|
|
|
When -fpic is used, special handling is needed for symbolic references.
|
When -fpic is used, special handling is needed for symbolic references.
|
See comments by legitimize_pic_address for details. */
|
See comments by legitimize_pic_address for details. */
|
|
|
rtx
|
rtx
|
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
|
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
|
enum machine_mode mode ATTRIBUTE_UNUSED)
|
enum machine_mode mode ATTRIBUTE_UNUSED)
|
{
|
{
|
rtx constant_term = const0_rtx;
|
rtx constant_term = const0_rtx;
|
|
|
if (TLS_SYMBOLIC_CONST (x))
|
if (TLS_SYMBOLIC_CONST (x))
|
{
|
{
|
x = legitimize_tls_address (x, 0);
|
x = legitimize_tls_address (x, 0);
|
|
|
if (legitimate_address_p (mode, x, FALSE))
|
if (legitimate_address_p (mode, x, FALSE))
|
return x;
|
return x;
|
}
|
}
|
else if (GET_CODE (x) == PLUS
|
else if (GET_CODE (x) == PLUS
|
&& (TLS_SYMBOLIC_CONST (XEXP (x, 0))
|
&& (TLS_SYMBOLIC_CONST (XEXP (x, 0))
|
|| TLS_SYMBOLIC_CONST (XEXP (x, 1))))
|
|| TLS_SYMBOLIC_CONST (XEXP (x, 1))))
|
{
|
{
|
return x;
|
return x;
|
}
|
}
|
else if (flag_pic)
|
else if (flag_pic)
|
{
|
{
|
if (SYMBOLIC_CONST (x)
|
if (SYMBOLIC_CONST (x)
|
|| (GET_CODE (x) == PLUS
|
|| (GET_CODE (x) == PLUS
|
&& (SYMBOLIC_CONST (XEXP (x, 0))
|
&& (SYMBOLIC_CONST (XEXP (x, 0))
|
|| SYMBOLIC_CONST (XEXP (x, 1)))))
|
|| SYMBOLIC_CONST (XEXP (x, 1)))))
|
x = legitimize_pic_address (x, 0);
|
x = legitimize_pic_address (x, 0);
|
|
|
if (legitimate_address_p (mode, x, FALSE))
|
if (legitimate_address_p (mode, x, FALSE))
|
return x;
|
return x;
|
}
|
}
|
|
|
x = eliminate_constant_term (x, &constant_term);
|
x = eliminate_constant_term (x, &constant_term);
|
|
|
/* Optimize loading of large displacements by splitting them
|
/* Optimize loading of large displacements by splitting them
|
into the multiple of 4K and the rest; this allows the
|
into the multiple of 4K and the rest; this allows the
|
former to be CSE'd if possible.
|
former to be CSE'd if possible.
|
|
|
Don't do this if the displacement is added to a register
|
Don't do this if the displacement is added to a register
|
pointing into the stack frame, as the offsets will
|
pointing into the stack frame, as the offsets will
|
change later anyway. */
|
change later anyway. */
|
|
|
if (GET_CODE (constant_term) == CONST_INT
|
if (GET_CODE (constant_term) == CONST_INT
|
&& !TARGET_LONG_DISPLACEMENT
|
&& !TARGET_LONG_DISPLACEMENT
|
&& !DISP_IN_RANGE (INTVAL (constant_term))
|
&& !DISP_IN_RANGE (INTVAL (constant_term))
|
&& !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x))))
|
&& !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x))))
|
{
|
{
|
HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff;
|
HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff;
|
HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower;
|
HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower;
|
|
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx val = force_operand (GEN_INT (upper), temp);
|
rtx val = force_operand (GEN_INT (upper), temp);
|
if (val != temp)
|
if (val != temp)
|
emit_move_insn (temp, val);
|
emit_move_insn (temp, val);
|
|
|
x = gen_rtx_PLUS (Pmode, x, temp);
|
x = gen_rtx_PLUS (Pmode, x, temp);
|
constant_term = GEN_INT (lower);
|
constant_term = GEN_INT (lower);
|
}
|
}
|
|
|
if (GET_CODE (x) == PLUS)
|
if (GET_CODE (x) == PLUS)
|
{
|
{
|
if (GET_CODE (XEXP (x, 0)) == REG)
|
if (GET_CODE (XEXP (x, 0)) == REG)
|
{
|
{
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx val = force_operand (XEXP (x, 1), temp);
|
rtx val = force_operand (XEXP (x, 1), temp);
|
if (val != temp)
|
if (val != temp)
|
emit_move_insn (temp, val);
|
emit_move_insn (temp, val);
|
|
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp);
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp);
|
}
|
}
|
|
|
else if (GET_CODE (XEXP (x, 1)) == REG)
|
else if (GET_CODE (XEXP (x, 1)) == REG)
|
{
|
{
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx temp = gen_reg_rtx (Pmode);
|
rtx val = force_operand (XEXP (x, 0), temp);
|
rtx val = force_operand (XEXP (x, 0), temp);
|
if (val != temp)
|
if (val != temp)
|
emit_move_insn (temp, val);
|
emit_move_insn (temp, val);
|
|
|
x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1));
|
x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1));
|
}
|
}
|
}
|
}
|
|
|
if (constant_term != const0_rtx)
|
if (constant_term != const0_rtx)
|
x = gen_rtx_PLUS (Pmode, x, constant_term);
|
x = gen_rtx_PLUS (Pmode, x, constant_term);
|
|
|
return x;
|
return x;
|
}
|
}
|
|
|
/* Try a machine-dependent way of reloading an illegitimate address AD
|
/* Try a machine-dependent way of reloading an illegitimate address AD
|
operand. If we find one, push the reload and and return the new address.
|
operand. If we find one, push the reload and and return the new address.
|
|
|
MODE is the mode of the enclosing MEM. OPNUM is the operand number
|
MODE is the mode of the enclosing MEM. OPNUM is the operand number
|
and TYPE is the reload type of the current reload. */
|
and TYPE is the reload type of the current reload. */
|
|
|
rtx
|
rtx
|
legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
|
legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
|
int opnum, int type)
|
int opnum, int type)
|
{
|
{
|
if (!optimize || TARGET_LONG_DISPLACEMENT)
|
if (!optimize || TARGET_LONG_DISPLACEMENT)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
if (GET_CODE (ad) == PLUS)
|
if (GET_CODE (ad) == PLUS)
|
{
|
{
|
rtx tem = simplify_binary_operation (PLUS, Pmode,
|
rtx tem = simplify_binary_operation (PLUS, Pmode,
|
XEXP (ad, 0), XEXP (ad, 1));
|
XEXP (ad, 0), XEXP (ad, 1));
|
if (tem)
|
if (tem)
|
ad = tem;
|
ad = tem;
|
}
|
}
|
|
|
if (GET_CODE (ad) == PLUS
|
if (GET_CODE (ad) == PLUS
|
&& GET_CODE (XEXP (ad, 0)) == REG
|
&& GET_CODE (XEXP (ad, 0)) == REG
|
&& GET_CODE (XEXP (ad, 1)) == CONST_INT
|
&& GET_CODE (XEXP (ad, 1)) == CONST_INT
|
&& !DISP_IN_RANGE (INTVAL (XEXP (ad, 1))))
|
&& !DISP_IN_RANGE (INTVAL (XEXP (ad, 1))))
|
{
|
{
|
HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff;
|
HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff;
|
HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower;
|
HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower;
|
rtx cst, tem, new;
|
rtx cst, tem, new;
|
|
|
cst = GEN_INT (upper);
|
cst = GEN_INT (upper);
|
if (!legitimate_reload_constant_p (cst))
|
if (!legitimate_reload_constant_p (cst))
|
cst = force_const_mem (Pmode, cst);
|
cst = force_const_mem (Pmode, cst);
|
|
|
tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst);
|
tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst);
|
new = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower));
|
new = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower));
|
|
|
push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0,
|
push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0,
|
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
|
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
|
opnum, (enum reload_type) type);
|
opnum, (enum reload_type) type);
|
return new;
|
return new;
|
}
|
}
|
|
|
return NULL_RTX;
|
return NULL_RTX;
|
}
|
}
|
|
|
/* Emit code to move LEN bytes from DST to SRC. */
|
/* Emit code to move LEN bytes from DST to SRC. */
|
|
|
void
|
void
|
s390_expand_movmem (rtx dst, rtx src, rtx len)
|
s390_expand_movmem (rtx dst, rtx src, rtx len)
|
{
|
{
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
|
{
|
{
|
if (INTVAL (len) > 0)
|
if (INTVAL (len) > 0)
|
emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1)));
|
emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1)));
|
}
|
}
|
|
|
else if (TARGET_MVCLE)
|
else if (TARGET_MVCLE)
|
{
|
{
|
emit_insn (gen_movmem_long (dst, src, convert_to_mode (Pmode, len, 1)));
|
emit_insn (gen_movmem_long (dst, src, convert_to_mode (Pmode, len, 1)));
|
}
|
}
|
|
|
else
|
else
|
{
|
{
|
rtx dst_addr, src_addr, count, blocks, temp;
|
rtx dst_addr, src_addr, count, blocks, temp;
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
enum machine_mode mode;
|
enum machine_mode mode;
|
|
|
mode = GET_MODE (len);
|
mode = GET_MODE (len);
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
mode = Pmode;
|
mode = Pmode;
|
|
|
dst_addr = gen_reg_rtx (Pmode);
|
dst_addr = gen_reg_rtx (Pmode);
|
src_addr = gen_reg_rtx (Pmode);
|
src_addr = gen_reg_rtx (Pmode);
|
count = gen_reg_rtx (mode);
|
count = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
|
|
convert_move (count, len, 1);
|
convert_move (count, len, 1);
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
EQ, NULL_RTX, mode, 1, end_label);
|
EQ, NULL_RTX, mode, 1, end_label);
|
|
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
|
emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX));
|
emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX));
|
dst = change_address (dst, VOIDmode, dst_addr);
|
dst = change_address (dst, VOIDmode, dst_addr);
|
src = change_address (src, VOIDmode, src_addr);
|
src = change_address (src, VOIDmode, src_addr);
|
|
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
if (temp != count)
|
if (temp != count)
|
emit_move_insn (count, temp);
|
emit_move_insn (count, temp);
|
|
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_label (loop_start_label);
|
emit_label (loop_start_label);
|
|
|
emit_insn (gen_movmem_short (dst, src, GEN_INT (255)));
|
emit_insn (gen_movmem_short (dst, src, GEN_INT (255)));
|
s390_load_address (dst_addr,
|
s390_load_address (dst_addr,
|
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
|
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
|
s390_load_address (src_addr,
|
s390_load_address (src_addr,
|
gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256)));
|
gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256)));
|
|
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_jump (loop_start_label);
|
emit_jump (loop_start_label);
|
emit_label (loop_end_label);
|
emit_label (loop_end_label);
|
|
|
emit_insn (gen_movmem_short (dst, src,
|
emit_insn (gen_movmem_short (dst, src,
|
convert_to_mode (Pmode, count, 1)));
|
convert_to_mode (Pmode, count, 1)));
|
emit_label (end_label);
|
emit_label (end_label);
|
}
|
}
|
}
|
}
|
|
|
/* Emit code to set LEN bytes at DST to VAL.
|
/* Emit code to set LEN bytes at DST to VAL.
|
Make use of clrmem if VAL is zero. */
|
Make use of clrmem if VAL is zero. */
|
|
|
void
|
void
|
s390_expand_setmem (rtx dst, rtx len, rtx val)
|
s390_expand_setmem (rtx dst, rtx len, rtx val)
|
{
|
{
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0)
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0)
|
return;
|
return;
|
|
|
gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
|
gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
|
|
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257)
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257)
|
{
|
{
|
if (val == const0_rtx && INTVAL (len) <= 256)
|
if (val == const0_rtx && INTVAL (len) <= 256)
|
emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
|
emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
|
else
|
else
|
{
|
{
|
/* Initialize memory by storing the first byte. */
|
/* Initialize memory by storing the first byte. */
|
emit_move_insn (adjust_address (dst, QImode, 0), val);
|
emit_move_insn (adjust_address (dst, QImode, 0), val);
|
|
|
if (INTVAL (len) > 1)
|
if (INTVAL (len) > 1)
|
{
|
{
|
/* Initiate 1 byte overlap move.
|
/* Initiate 1 byte overlap move.
|
The first byte of DST is propagated through DSTP1.
|
The first byte of DST is propagated through DSTP1.
|
Prepare a movmem for: DST+1 = DST (length = LEN - 1).
|
Prepare a movmem for: DST+1 = DST (length = LEN - 1).
|
DST is set to size 1 so the rest of the memory location
|
DST is set to size 1 so the rest of the memory location
|
does not count as source operand. */
|
does not count as source operand. */
|
rtx dstp1 = adjust_address (dst, VOIDmode, 1);
|
rtx dstp1 = adjust_address (dst, VOIDmode, 1);
|
set_mem_size (dst, const1_rtx);
|
set_mem_size (dst, const1_rtx);
|
|
|
emit_insn (gen_movmem_short (dstp1, dst,
|
emit_insn (gen_movmem_short (dstp1, dst,
|
GEN_INT (INTVAL (len) - 2)));
|
GEN_INT (INTVAL (len) - 2)));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
else if (TARGET_MVCLE)
|
else if (TARGET_MVCLE)
|
{
|
{
|
val = force_not_mem (convert_modes (Pmode, QImode, val, 1));
|
val = force_not_mem (convert_modes (Pmode, QImode, val, 1));
|
emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val));
|
emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val));
|
}
|
}
|
|
|
else
|
else
|
{
|
{
|
rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX;
|
rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX;
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
enum machine_mode mode;
|
enum machine_mode mode;
|
|
|
mode = GET_MODE (len);
|
mode = GET_MODE (len);
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
mode = Pmode;
|
mode = Pmode;
|
|
|
dst_addr = gen_reg_rtx (Pmode);
|
dst_addr = gen_reg_rtx (Pmode);
|
src_addr = gen_reg_rtx (Pmode);
|
src_addr = gen_reg_rtx (Pmode);
|
count = gen_reg_rtx (mode);
|
count = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
|
|
convert_move (count, len, 1);
|
convert_move (count, len, 1);
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
EQ, NULL_RTX, mode, 1, end_label);
|
EQ, NULL_RTX, mode, 1, end_label);
|
|
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
|
emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
|
dst = change_address (dst, VOIDmode, dst_addr);
|
dst = change_address (dst, VOIDmode, dst_addr);
|
|
|
if (val == const0_rtx)
|
if (val == const0_rtx)
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
else
|
else
|
{
|
{
|
dstp1 = adjust_address (dst, VOIDmode, 1);
|
dstp1 = adjust_address (dst, VOIDmode, 1);
|
set_mem_size (dst, const1_rtx);
|
set_mem_size (dst, const1_rtx);
|
|
|
/* Initialize memory by storing the first byte. */
|
/* Initialize memory by storing the first byte. */
|
emit_move_insn (adjust_address (dst, QImode, 0), val);
|
emit_move_insn (adjust_address (dst, QImode, 0), val);
|
|
|
/* If count is 1 we are done. */
|
/* If count is 1 we are done. */
|
emit_cmp_and_jump_insns (count, const1_rtx,
|
emit_cmp_and_jump_insns (count, const1_rtx,
|
EQ, NULL_RTX, mode, 1, end_label);
|
EQ, NULL_RTX, mode, 1, end_label);
|
|
|
temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, 0);
|
temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, 0);
|
}
|
}
|
if (temp != count)
|
if (temp != count)
|
emit_move_insn (count, temp);
|
emit_move_insn (count, temp);
|
|
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_label (loop_start_label);
|
emit_label (loop_start_label);
|
|
|
if (val == const0_rtx)
|
if (val == const0_rtx)
|
emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
|
emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
|
else
|
else
|
emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255)));
|
emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255)));
|
s390_load_address (dst_addr,
|
s390_load_address (dst_addr,
|
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
|
gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
|
|
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_jump (loop_start_label);
|
emit_jump (loop_start_label);
|
emit_label (loop_end_label);
|
emit_label (loop_end_label);
|
|
|
if (val == const0_rtx)
|
if (val == const0_rtx)
|
emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
|
emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
|
else
|
else
|
emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1)));
|
emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1)));
|
emit_label (end_label);
|
emit_label (end_label);
|
}
|
}
|
}
|
}
|
|
|
/* Emit code to compare LEN bytes at OP0 with those at OP1,
|
/* Emit code to compare LEN bytes at OP0 with those at OP1,
|
and return the result in TARGET. */
|
and return the result in TARGET. */
|
|
|
void
|
void
|
s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
|
s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
|
{
|
{
|
rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM);
|
rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM);
|
rtx tmp;
|
rtx tmp;
|
|
|
/* As the result of CMPINT is inverted compared to what we need,
|
/* As the result of CMPINT is inverted compared to what we need,
|
we have to swap the operands. */
|
we have to swap the operands. */
|
tmp = op0; op0 = op1; op1 = tmp;
|
tmp = op0; op0 = op1; op1 = tmp;
|
|
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
|
if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
|
{
|
{
|
if (INTVAL (len) > 0)
|
if (INTVAL (len) > 0)
|
{
|
{
|
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1)));
|
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1)));
|
emit_insn (gen_cmpint (target, ccreg));
|
emit_insn (gen_cmpint (target, ccreg));
|
}
|
}
|
else
|
else
|
emit_move_insn (target, const0_rtx);
|
emit_move_insn (target, const0_rtx);
|
}
|
}
|
else if (TARGET_MVCLE)
|
else if (TARGET_MVCLE)
|
{
|
{
|
emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1)));
|
emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1)));
|
emit_insn (gen_cmpint (target, ccreg));
|
emit_insn (gen_cmpint (target, ccreg));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx addr0, addr1, count, blocks, temp;
|
rtx addr0, addr1, count, blocks, temp;
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_start_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx loop_end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
rtx end_label = gen_label_rtx ();
|
enum machine_mode mode;
|
enum machine_mode mode;
|
|
|
mode = GET_MODE (len);
|
mode = GET_MODE (len);
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
mode = Pmode;
|
mode = Pmode;
|
|
|
addr0 = gen_reg_rtx (Pmode);
|
addr0 = gen_reg_rtx (Pmode);
|
addr1 = gen_reg_rtx (Pmode);
|
addr1 = gen_reg_rtx (Pmode);
|
count = gen_reg_rtx (mode);
|
count = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
blocks = gen_reg_rtx (mode);
|
|
|
convert_move (count, len, 1);
|
convert_move (count, len, 1);
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
emit_cmp_and_jump_insns (count, const0_rtx,
|
EQ, NULL_RTX, mode, 1, end_label);
|
EQ, NULL_RTX, mode, 1, end_label);
|
|
|
emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX));
|
emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX));
|
emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX));
|
emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX));
|
op0 = change_address (op0, VOIDmode, addr0);
|
op0 = change_address (op0, VOIDmode, addr0);
|
op1 = change_address (op1, VOIDmode, addr1);
|
op1 = change_address (op1, VOIDmode, addr1);
|
|
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
|
if (temp != count)
|
if (temp != count)
|
emit_move_insn (count, temp);
|
emit_move_insn (count, temp);
|
|
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_label (loop_start_label);
|
emit_label (loop_start_label);
|
|
|
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
|
emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
|
temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
|
temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
|
temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
|
temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
|
gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx);
|
gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx);
|
temp = gen_rtx_SET (VOIDmode, pc_rtx, temp);
|
temp = gen_rtx_SET (VOIDmode, pc_rtx, temp);
|
emit_jump_insn (temp);
|
emit_jump_insn (temp);
|
|
|
s390_load_address (addr0,
|
s390_load_address (addr0,
|
gen_rtx_PLUS (Pmode, addr0, GEN_INT (256)));
|
gen_rtx_PLUS (Pmode, addr0, GEN_INT (256)));
|
s390_load_address (addr1,
|
s390_load_address (addr1,
|
gen_rtx_PLUS (Pmode, addr1, GEN_INT (256)));
|
gen_rtx_PLUS (Pmode, addr1, GEN_INT (256)));
|
|
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
|
if (temp != blocks)
|
if (temp != blocks)
|
emit_move_insn (blocks, temp);
|
emit_move_insn (blocks, temp);
|
|
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
emit_cmp_and_jump_insns (blocks, const0_rtx,
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
EQ, NULL_RTX, mode, 1, loop_end_label);
|
|
|
emit_jump (loop_start_label);
|
emit_jump (loop_start_label);
|
emit_label (loop_end_label);
|
emit_label (loop_end_label);
|
|
|
emit_insn (gen_cmpmem_short (op0, op1,
|
emit_insn (gen_cmpmem_short (op0, op1,
|
convert_to_mode (Pmode, count, 1)));
|
convert_to_mode (Pmode, count, 1)));
|
emit_label (end_label);
|
emit_label (end_label);
|
|
|
emit_insn (gen_cmpint (target, ccreg));
|
emit_insn (gen_cmpint (target, ccreg));
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Expand conditional increment or decrement using alc/slb instructions.
|
/* Expand conditional increment or decrement using alc/slb instructions.
|
Should generate code setting DST to either SRC or SRC + INCREMENT,
|
Should generate code setting DST to either SRC or SRC + INCREMENT,
|
depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1.
|
depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1.
|
Returns true if successful, false otherwise.
|
Returns true if successful, false otherwise.
|
|
|
That makes it possible to implement some if-constructs without jumps e.g.:
|
That makes it possible to implement some if-constructs without jumps e.g.:
|
(borrow = CC0 | CC1 and carry = CC2 | CC3)
|
(borrow = CC0 | CC1 and carry = CC2 | CC3)
|
unsigned int a, b, c;
|
unsigned int a, b, c;
|
if (a < b) c++; -> CCU b > a -> CC2; c += carry;
|
if (a < b) c++; -> CCU b > a -> CC2; c += carry;
|
if (a < b) c--; -> CCL3 a - b -> borrow; c -= borrow;
|
if (a < b) c--; -> CCL3 a - b -> borrow; c -= borrow;
|
if (a <= b) c++; -> CCL3 b - a -> borrow; c += carry;
|
if (a <= b) c++; -> CCL3 b - a -> borrow; c += carry;
|
if (a <= b) c--; -> CCU a <= b -> borrow; c -= borrow;
|
if (a <= b) c--; -> CCU a <= b -> borrow; c -= borrow;
|
|
|
Checks for EQ and NE with a nonzero value need an additional xor e.g.:
|
Checks for EQ and NE with a nonzero value need an additional xor e.g.:
|
if (a == b) c++; -> CCL3 a ^= b; 0 - a -> borrow; c += carry;
|
if (a == b) c++; -> CCL3 a ^= b; 0 - a -> borrow; c += carry;
|
if (a == b) c--; -> CCU a ^= b; a <= 0 -> CC0 | CC1; c -= borrow;
|
if (a == b) c--; -> CCU a ^= b; a <= 0 -> CC0 | CC1; c -= borrow;
|
if (a != b) c++; -> CCU a ^= b; a > 0 -> CC2; c += carry;
|
if (a != b) c++; -> CCU a ^= b; a > 0 -> CC2; c += carry;
|
if (a != b) c--; -> CCL3 a ^= b; 0 - a -> borrow; c -= borrow; */
|
if (a != b) c--; -> CCL3 a ^= b; 0 - a -> borrow; c -= borrow; */
|
|
|
bool
|
bool
|
s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
|
s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
|
rtx dst, rtx src, rtx increment)
|
rtx dst, rtx src, rtx increment)
|
{
|
{
|
enum machine_mode cmp_mode;
|
enum machine_mode cmp_mode;
|
enum machine_mode cc_mode;
|
enum machine_mode cc_mode;
|
rtx op_res;
|
rtx op_res;
|
rtx insn;
|
rtx insn;
|
rtvec p;
|
rtvec p;
|
int ret;
|
int ret;
|
|
|
if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode)
|
if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode)
|
&& (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode))
|
&& (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode))
|
cmp_mode = SImode;
|
cmp_mode = SImode;
|
else if ((GET_MODE (cmp_op0) == DImode || GET_MODE (cmp_op0) == VOIDmode)
|
else if ((GET_MODE (cmp_op0) == DImode || GET_MODE (cmp_op0) == VOIDmode)
|
&& (GET_MODE (cmp_op1) == DImode || GET_MODE (cmp_op1) == VOIDmode))
|
&& (GET_MODE (cmp_op1) == DImode || GET_MODE (cmp_op1) == VOIDmode))
|
cmp_mode = DImode;
|
cmp_mode = DImode;
|
else
|
else
|
return false;
|
return false;
|
|
|
/* Try ADD LOGICAL WITH CARRY. */
|
/* Try ADD LOGICAL WITH CARRY. */
|
if (increment == const1_rtx)
|
if (increment == const1_rtx)
|
{
|
{
|
/* Determine CC mode to use. */
|
/* Determine CC mode to use. */
|
if (cmp_code == EQ || cmp_code == NE)
|
if (cmp_code == EQ || cmp_code == NE)
|
{
|
{
|
if (cmp_op1 != const0_rtx)
|
if (cmp_op1 != const0_rtx)
|
{
|
{
|
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
|
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
|
NULL_RTX, 0, OPTAB_WIDEN);
|
NULL_RTX, 0, OPTAB_WIDEN);
|
cmp_op1 = const0_rtx;
|
cmp_op1 = const0_rtx;
|
}
|
}
|
|
|
cmp_code = cmp_code == EQ ? LEU : GTU;
|
cmp_code = cmp_code == EQ ? LEU : GTU;
|
}
|
}
|
|
|
if (cmp_code == LTU || cmp_code == LEU)
|
if (cmp_code == LTU || cmp_code == LEU)
|
{
|
{
|
rtx tem = cmp_op0;
|
rtx tem = cmp_op0;
|
cmp_op0 = cmp_op1;
|
cmp_op0 = cmp_op1;
|
cmp_op1 = tem;
|
cmp_op1 = tem;
|
cmp_code = swap_condition (cmp_code);
|
cmp_code = swap_condition (cmp_code);
|
}
|
}
|
|
|
switch (cmp_code)
|
switch (cmp_code)
|
{
|
{
|
case GTU:
|
case GTU:
|
cc_mode = CCUmode;
|
cc_mode = CCUmode;
|
break;
|
break;
|
|
|
case GEU:
|
case GEU:
|
cc_mode = CCL3mode;
|
cc_mode = CCL3mode;
|
break;
|
break;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Emit comparison instruction pattern. */
|
/* Emit comparison instruction pattern. */
|
if (!register_operand (cmp_op0, cmp_mode))
|
if (!register_operand (cmp_op0, cmp_mode))
|
cmp_op0 = force_reg (cmp_mode, cmp_op0);
|
cmp_op0 = force_reg (cmp_mode, cmp_op0);
|
|
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
|
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
|
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
|
/* We use insn_invalid_p here to add clobbers if required. */
|
/* We use insn_invalid_p here to add clobbers if required. */
|
ret = insn_invalid_p (emit_insn (insn));
|
ret = insn_invalid_p (emit_insn (insn));
|
gcc_assert (!ret);
|
gcc_assert (!ret);
|
|
|
/* Emit ALC instruction pattern. */
|
/* Emit ALC instruction pattern. */
|
op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
|
op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
|
gen_rtx_REG (cc_mode, CC_REGNUM),
|
gen_rtx_REG (cc_mode, CC_REGNUM),
|
const0_rtx);
|
const0_rtx);
|
|
|
if (src != const0_rtx)
|
if (src != const0_rtx)
|
{
|
{
|
if (!register_operand (src, GET_MODE (dst)))
|
if (!register_operand (src, GET_MODE (dst)))
|
src = force_reg (GET_MODE (dst), src);
|
src = force_reg (GET_MODE (dst), src);
|
|
|
src = gen_rtx_PLUS (GET_MODE (dst), src, const0_rtx);
|
src = gen_rtx_PLUS (GET_MODE (dst), src, const0_rtx);
|
op_res = gen_rtx_PLUS (GET_MODE (dst), src, op_res);
|
op_res = gen_rtx_PLUS (GET_MODE (dst), src, op_res);
|
}
|
}
|
|
|
p = rtvec_alloc (2);
|
p = rtvec_alloc (2);
|
RTVEC_ELT (p, 0) =
|
RTVEC_ELT (p, 0) =
|
gen_rtx_SET (VOIDmode, dst, op_res);
|
gen_rtx_SET (VOIDmode, dst, op_res);
|
RTVEC_ELT (p, 1) =
|
RTVEC_ELT (p, 1) =
|
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Try SUBTRACT LOGICAL WITH BORROW. */
|
/* Try SUBTRACT LOGICAL WITH BORROW. */
|
if (increment == constm1_rtx)
|
if (increment == constm1_rtx)
|
{
|
{
|
/* Determine CC mode to use. */
|
/* Determine CC mode to use. */
|
if (cmp_code == EQ || cmp_code == NE)
|
if (cmp_code == EQ || cmp_code == NE)
|
{
|
{
|
if (cmp_op1 != const0_rtx)
|
if (cmp_op1 != const0_rtx)
|
{
|
{
|
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
|
cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
|
NULL_RTX, 0, OPTAB_WIDEN);
|
NULL_RTX, 0, OPTAB_WIDEN);
|
cmp_op1 = const0_rtx;
|
cmp_op1 = const0_rtx;
|
}
|
}
|
|
|
cmp_code = cmp_code == EQ ? LEU : GTU;
|
cmp_code = cmp_code == EQ ? LEU : GTU;
|
}
|
}
|
|
|
if (cmp_code == GTU || cmp_code == GEU)
|
if (cmp_code == GTU || cmp_code == GEU)
|
{
|
{
|
rtx tem = cmp_op0;
|
rtx tem = cmp_op0;
|
cmp_op0 = cmp_op1;
|
cmp_op0 = cmp_op1;
|
cmp_op1 = tem;
|
cmp_op1 = tem;
|
cmp_code = swap_condition (cmp_code);
|
cmp_code = swap_condition (cmp_code);
|
}
|
}
|
|
|
switch (cmp_code)
|
switch (cmp_code)
|
{
|
{
|
case LEU:
|
case LEU:
|
cc_mode = CCUmode;
|
cc_mode = CCUmode;
|
break;
|
break;
|
|
|
case LTU:
|
case LTU:
|
cc_mode = CCL3mode;
|
cc_mode = CCL3mode;
|
break;
|
break;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Emit comparison instruction pattern. */
|
/* Emit comparison instruction pattern. */
|
if (!register_operand (cmp_op0, cmp_mode))
|
if (!register_operand (cmp_op0, cmp_mode))
|
cmp_op0 = force_reg (cmp_mode, cmp_op0);
|
cmp_op0 = force_reg (cmp_mode, cmp_op0);
|
|
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
|
insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
|
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
|
gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
|
/* We use insn_invalid_p here to add clobbers if required. */
|
/* We use insn_invalid_p here to add clobbers if required. */
|
ret = insn_invalid_p (emit_insn (insn));
|
ret = insn_invalid_p (emit_insn (insn));
|
gcc_assert (!ret);
|
gcc_assert (!ret);
|
|
|
/* Emit SLB instruction pattern. */
|
/* Emit SLB instruction pattern. */
|
if (!register_operand (src, GET_MODE (dst)))
|
if (!register_operand (src, GET_MODE (dst)))
|
src = force_reg (GET_MODE (dst), src);
|
src = force_reg (GET_MODE (dst), src);
|
|
|
op_res = gen_rtx_MINUS (GET_MODE (dst),
|
op_res = gen_rtx_MINUS (GET_MODE (dst),
|
gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx),
|
gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx),
|
gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
|
gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
|
gen_rtx_REG (cc_mode, CC_REGNUM),
|
gen_rtx_REG (cc_mode, CC_REGNUM),
|
const0_rtx));
|
const0_rtx));
|
p = rtvec_alloc (2);
|
p = rtvec_alloc (2);
|
RTVEC_ELT (p, 0) =
|
RTVEC_ELT (p, 0) =
|
gen_rtx_SET (VOIDmode, dst, op_res);
|
gen_rtx_SET (VOIDmode, dst, op_res);
|
RTVEC_ELT (p, 1) =
|
RTVEC_ELT (p, 1) =
|
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Expand code for the insv template. Return true if successful, false else. */
|
/* Expand code for the insv template. Return true if successful, false else. */
|
|
|
bool
|
bool
|
s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
|
s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
|
{
|
{
|
int bitsize = INTVAL (op1);
|
int bitsize = INTVAL (op1);
|
int bitpos = INTVAL (op2);
|
int bitpos = INTVAL (op2);
|
|
|
/* We need byte alignment. */
|
/* We need byte alignment. */
|
if (bitsize % BITS_PER_UNIT)
|
if (bitsize % BITS_PER_UNIT)
|
return false;
|
return false;
|
|
|
if (bitpos == 0
|
if (bitpos == 0
|
&& memory_operand (dest, VOIDmode)
|
&& memory_operand (dest, VOIDmode)
|
&& (register_operand (src, word_mode)
|
&& (register_operand (src, word_mode)
|
|| const_int_operand (src, VOIDmode)))
|
|| const_int_operand (src, VOIDmode)))
|
{
|
{
|
/* Emit standard pattern if possible. */
|
/* Emit standard pattern if possible. */
|
enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT);
|
enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT);
|
if (GET_MODE_BITSIZE (mode) == bitsize)
|
if (GET_MODE_BITSIZE (mode) == bitsize)
|
emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src));
|
emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src));
|
|
|
/* (set (ze (mem)) (const_int)). */
|
/* (set (ze (mem)) (const_int)). */
|
else if (const_int_operand (src, VOIDmode))
|
else if (const_int_operand (src, VOIDmode))
|
{
|
{
|
int size = bitsize / BITS_PER_UNIT;
|
int size = bitsize / BITS_PER_UNIT;
|
rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode,
|
rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode,
|
GET_MODE_SIZE (word_mode) - size);
|
GET_MODE_SIZE (word_mode) - size);
|
|
|
dest = adjust_address (dest, BLKmode, 0);
|
dest = adjust_address (dest, BLKmode, 0);
|
set_mem_size (dest, GEN_INT (size));
|
set_mem_size (dest, GEN_INT (size));
|
s390_expand_movmem (dest, src_mem, GEN_INT (size));
|
s390_expand_movmem (dest, src_mem, GEN_INT (size));
|
}
|
}
|
|
|
/* (set (ze (mem)) (reg)). */
|
/* (set (ze (mem)) (reg)). */
|
else if (register_operand (src, word_mode))
|
else if (register_operand (src, word_mode))
|
{
|
{
|
if (bitsize <= GET_MODE_BITSIZE (SImode))
|
if (bitsize <= GET_MODE_BITSIZE (SImode))
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1,
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1,
|
const0_rtx), src);
|
const0_rtx), src);
|
else
|
else
|
{
|
{
|
/* Emit st,stcmh sequence. */
|
/* Emit st,stcmh sequence. */
|
int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode);
|
int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode);
|
int size = stcmh_width / BITS_PER_UNIT;
|
int size = stcmh_width / BITS_PER_UNIT;
|
|
|
emit_move_insn (adjust_address (dest, SImode, size),
|
emit_move_insn (adjust_address (dest, SImode, size),
|
gen_lowpart (SImode, src));
|
gen_lowpart (SImode, src));
|
set_mem_size (dest, GEN_INT (size));
|
set_mem_size (dest, GEN_INT (size));
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT
|
(stcmh_width), const0_rtx),
|
(stcmh_width), const0_rtx),
|
gen_rtx_LSHIFTRT (word_mode, src, GEN_INT
|
gen_rtx_LSHIFTRT (word_mode, src, GEN_INT
|
(GET_MODE_BITSIZE (SImode))));
|
(GET_MODE_BITSIZE (SImode))));
|
}
|
}
|
}
|
}
|
else
|
else
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* (set (ze (reg)) (const_int)). */
|
/* (set (ze (reg)) (const_int)). */
|
if (TARGET_ZARCH
|
if (TARGET_ZARCH
|
&& register_operand (dest, word_mode)
|
&& register_operand (dest, word_mode)
|
&& (bitpos % 16) == 0
|
&& (bitpos % 16) == 0
|
&& (bitsize % 16) == 0
|
&& (bitsize % 16) == 0
|
&& const_int_operand (src, VOIDmode))
|
&& const_int_operand (src, VOIDmode))
|
{
|
{
|
HOST_WIDE_INT val = INTVAL (src);
|
HOST_WIDE_INT val = INTVAL (src);
|
int regpos = bitpos + bitsize;
|
int regpos = bitpos + bitsize;
|
|
|
while (regpos > bitpos)
|
while (regpos > bitpos)
|
{
|
{
|
enum machine_mode putmode;
|
enum machine_mode putmode;
|
int putsize;
|
int putsize;
|
|
|
if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32))
|
if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32))
|
putmode = SImode;
|
putmode = SImode;
|
else
|
else
|
putmode = HImode;
|
putmode = HImode;
|
|
|
putsize = GET_MODE_BITSIZE (putmode);
|
putsize = GET_MODE_BITSIZE (putmode);
|
regpos -= putsize;
|
regpos -= putsize;
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest,
|
emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest,
|
GEN_INT (putsize),
|
GEN_INT (putsize),
|
GEN_INT (regpos)),
|
GEN_INT (regpos)),
|
gen_int_mode (val, putmode));
|
gen_int_mode (val, putmode));
|
val >>= putsize;
|
val >>= putsize;
|
}
|
}
|
gcc_assert (regpos == bitpos);
|
gcc_assert (regpos == bitpos);
|
return true;
|
return true;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a
|
register that holds VAL of mode MODE shifted by COUNT bits. */
|
register that holds VAL of mode MODE shifted by COUNT bits. */
|
|
|
static inline rtx
|
static inline rtx
|
s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
|
s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
|
{
|
{
|
val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
|
val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
return expand_simple_binop (SImode, ASHIFT, val, count,
|
return expand_simple_binop (SImode, ASHIFT, val, count,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
}
|
}
|
|
|
/* Structure to hold the initial parameters for a compare_and_swap operation
|
/* Structure to hold the initial parameters for a compare_and_swap operation
|
in HImode and QImode. */
|
in HImode and QImode. */
|
|
|
struct alignment_context
|
struct alignment_context
|
{
|
{
|
rtx memsi; /* SI aligned memory location. */
|
rtx memsi; /* SI aligned memory location. */
|
rtx shift; /* Bit offset with regard to lsb. */
|
rtx shift; /* Bit offset with regard to lsb. */
|
rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */
|
rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */
|
rtx modemaski; /* ~modemask */
|
rtx modemaski; /* ~modemask */
|
bool aligned; /* True if memory is aligned, false else. */
|
bool aligned; /* True if memory is aligned, false else. */
|
};
|
};
|
|
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize
|
/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize
|
structure AC for transparent simplifying, if the memory alignment is known
|
structure AC for transparent simplifying, if the memory alignment is known
|
to be at least 32bit. MEM is the memory location for the actual operation
|
to be at least 32bit. MEM is the memory location for the actual operation
|
and MODE its mode. */
|
and MODE its mode. */
|
|
|
static void
|
static void
|
init_alignment_context (struct alignment_context *ac, rtx mem,
|
init_alignment_context (struct alignment_context *ac, rtx mem,
|
enum machine_mode mode)
|
enum machine_mode mode)
|
{
|
{
|
ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
|
ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
|
ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
|
ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
|
|
|
if (ac->aligned)
|
if (ac->aligned)
|
ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */
|
ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */
|
else
|
else
|
{
|
{
|
/* Alignment is unknown. */
|
/* Alignment is unknown. */
|
rtx byteoffset, addr, align;
|
rtx byteoffset, addr, align;
|
|
|
/* Force the address into a register. */
|
/* Force the address into a register. */
|
addr = force_reg (Pmode, XEXP (mem, 0));
|
addr = force_reg (Pmode, XEXP (mem, 0));
|
|
|
/* Align it to SImode. */
|
/* Align it to SImode. */
|
align = expand_simple_binop (Pmode, AND, addr,
|
align = expand_simple_binop (Pmode, AND, addr,
|
GEN_INT (-GET_MODE_SIZE (SImode)),
|
GEN_INT (-GET_MODE_SIZE (SImode)),
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
/* Generate MEM. */
|
/* Generate MEM. */
|
ac->memsi = gen_rtx_MEM (SImode, align);
|
ac->memsi = gen_rtx_MEM (SImode, align);
|
MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
|
MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
|
set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
|
set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
|
set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
|
set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
|
|
|
/* Calculate shiftcount. */
|
/* Calculate shiftcount. */
|
byteoffset = expand_simple_binop (Pmode, AND, addr,
|
byteoffset = expand_simple_binop (Pmode, AND, addr,
|
GEN_INT (GET_MODE_SIZE (SImode) - 1),
|
GEN_INT (GET_MODE_SIZE (SImode) - 1),
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
/* As we already have some offset, evaluate the remaining distance. */
|
/* As we already have some offset, evaluate the remaining distance. */
|
ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
|
ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
|
|
}
|
}
|
/* Shift is the byte count, but we need the bitcount. */
|
/* Shift is the byte count, but we need the bitcount. */
|
ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT),
|
ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT),
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
/* Calculate masks. */
|
/* Calculate masks. */
|
ac->modemask = expand_simple_binop (SImode, ASHIFT,
|
ac->modemask = expand_simple_binop (SImode, ASHIFT,
|
GEN_INT (GET_MODE_MASK (mode)), ac->shift,
|
GEN_INT (GET_MODE_MASK (mode)), ac->shift,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
|
ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
|
}
|
}
|
|
|
/* Expand an atomic compare and swap operation for HImode and QImode. MEM is
|
/* Expand an atomic compare and swap operation for HImode and QImode. MEM is
|
the memory location, CMP the old value to compare MEM with and NEW the value
|
the memory location, CMP the old value to compare MEM with and NEW the value
|
to set if CMP == MEM.
|
to set if CMP == MEM.
|
CMP is never in memory for compare_and_swap_cc because
|
CMP is never in memory for compare_and_swap_cc because
|
expand_bool_compare_and_swap puts it into a register for later compare. */
|
expand_bool_compare_and_swap puts it into a register for later compare. */
|
|
|
void
|
void
|
s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new)
|
s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new)
|
{
|
{
|
struct alignment_context ac;
|
struct alignment_context ac;
|
rtx cmpv, newv, val, resv, cc;
|
rtx cmpv, newv, val, resv, cc;
|
rtx res = gen_reg_rtx (SImode);
|
rtx res = gen_reg_rtx (SImode);
|
rtx csloop = gen_label_rtx ();
|
rtx csloop = gen_label_rtx ();
|
rtx csend = gen_label_rtx ();
|
rtx csend = gen_label_rtx ();
|
|
|
gcc_assert (register_operand (target, VOIDmode));
|
gcc_assert (register_operand (target, VOIDmode));
|
gcc_assert (MEM_P (mem));
|
gcc_assert (MEM_P (mem));
|
|
|
init_alignment_context (&ac, mem, mode);
|
init_alignment_context (&ac, mem, mode);
|
|
|
/* Shift the values to the correct bit positions. */
|
/* Shift the values to the correct bit positions. */
|
if (!(ac.aligned && MEM_P (cmp)))
|
if (!(ac.aligned && MEM_P (cmp)))
|
cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift);
|
cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift);
|
if (!(ac.aligned && MEM_P (new)))
|
if (!(ac.aligned && MEM_P (new)))
|
new = s390_expand_mask_and_shift (new, mode, ac.shift);
|
new = s390_expand_mask_and_shift (new, mode, ac.shift);
|
|
|
/* Load full word. Subsequent loads are performed by CS. */
|
/* Load full word. Subsequent loads are performed by CS. */
|
val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski,
|
val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
|
|
/* Start CS loop. */
|
/* Start CS loop. */
|
emit_label (csloop);
|
emit_label (csloop);
|
/* val = "<mem>00..0<mem>"
|
/* val = "<mem>00..0<mem>"
|
* cmp = "00..0<cmp>00..0"
|
* cmp = "00..0<cmp>00..0"
|
* new = "00..0<new>00..0"
|
* new = "00..0<new>00..0"
|
*/
|
*/
|
|
|
/* Patch cmp and new with val at correct position. */
|
/* Patch cmp and new with val at correct position. */
|
if (ac.aligned && MEM_P (cmp))
|
if (ac.aligned && MEM_P (cmp))
|
{
|
{
|
cmpv = force_reg (SImode, val);
|
cmpv = force_reg (SImode, val);
|
store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp);
|
store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp);
|
}
|
}
|
else
|
else
|
cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
|
cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
if (ac.aligned && MEM_P (new))
|
if (ac.aligned && MEM_P (new))
|
{
|
{
|
newv = force_reg (SImode, val);
|
newv = force_reg (SImode, val);
|
store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new);
|
store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new);
|
}
|
}
|
else
|
else
|
newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val,
|
newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val,
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
|
|
/* Jump to end if we're done (likely?). */
|
/* Jump to end if we're done (likely?). */
|
s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi,
|
s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi,
|
cmpv, newv));
|
cmpv, newv));
|
|
|
/* Check for changes outside mode. */
|
/* Check for changes outside mode. */
|
resv = expand_simple_binop (SImode, AND, res, ac.modemaski,
|
resv = expand_simple_binop (SImode, AND, res, ac.modemaski,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
cc = s390_emit_compare (NE, resv, val);
|
cc = s390_emit_compare (NE, resv, val);
|
emit_move_insn (val, resv);
|
emit_move_insn (val, resv);
|
/* Loop internal if so. */
|
/* Loop internal if so. */
|
s390_emit_jump (csloop, cc);
|
s390_emit_jump (csloop, cc);
|
|
|
emit_label (csend);
|
emit_label (csend);
|
|
|
/* Return the correct part of the bitfield. */
|
/* Return the correct part of the bitfield. */
|
convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift,
|
convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift,
|
NULL_RTX, 1, OPTAB_DIRECT), 1);
|
NULL_RTX, 1, OPTAB_DIRECT), 1);
|
}
|
}
|
|
|
/* Expand an atomic operation CODE of mode MODE. MEM is the memory location
|
/* Expand an atomic operation CODE of mode MODE. MEM is the memory location
|
and VAL the value to play with. If AFTER is true then store the the value
|
and VAL the value to play with. If AFTER is true then store the the value
|
MEM holds after the operation, if AFTER is false then store the value MEM
|
MEM holds after the operation, if AFTER is false then store the value MEM
|
holds before the operation. If TARGET is zero then discard that value, else
|
holds before the operation. If TARGET is zero then discard that value, else
|
store it to TARGET. */
|
store it to TARGET. */
|
|
|
void
|
void
|
s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
|
s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
|
rtx target, rtx mem, rtx val, bool after)
|
rtx target, rtx mem, rtx val, bool after)
|
{
|
{
|
struct alignment_context ac;
|
struct alignment_context ac;
|
rtx cmp;
|
rtx cmp;
|
rtx new = gen_reg_rtx (SImode);
|
rtx new = gen_reg_rtx (SImode);
|
rtx orig = gen_reg_rtx (SImode);
|
rtx orig = gen_reg_rtx (SImode);
|
rtx csloop = gen_label_rtx ();
|
rtx csloop = gen_label_rtx ();
|
|
|
gcc_assert (!target || register_operand (target, VOIDmode));
|
gcc_assert (!target || register_operand (target, VOIDmode));
|
gcc_assert (MEM_P (mem));
|
gcc_assert (MEM_P (mem));
|
|
|
init_alignment_context (&ac, mem, mode);
|
init_alignment_context (&ac, mem, mode);
|
|
|
/* Shift val to the correct bit positions.
|
/* Shift val to the correct bit positions.
|
Preserve "icm", but prevent "ex icm". */
|
Preserve "icm", but prevent "ex icm". */
|
if (!(ac.aligned && code == SET && MEM_P (val)))
|
if (!(ac.aligned && code == SET && MEM_P (val)))
|
val = s390_expand_mask_and_shift (val, mode, ac.shift);
|
val = s390_expand_mask_and_shift (val, mode, ac.shift);
|
|
|
/* Further preparation insns. */
|
/* Further preparation insns. */
|
if (code == PLUS || code == MINUS)
|
if (code == PLUS || code == MINUS)
|
emit_move_insn (orig, val);
|
emit_move_insn (orig, val);
|
else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */
|
else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */
|
val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
|
val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
|
|
/* Load full word. Subsequent loads are performed by CS. */
|
/* Load full word. Subsequent loads are performed by CS. */
|
cmp = force_reg (SImode, ac.memsi);
|
cmp = force_reg (SImode, ac.memsi);
|
|
|
/* Start CS loop. */
|
/* Start CS loop. */
|
emit_label (csloop);
|
emit_label (csloop);
|
emit_move_insn (new, cmp);
|
emit_move_insn (new, cmp);
|
|
|
/* Patch new with val at correct position. */
|
/* Patch new with val at correct position. */
|
switch (code)
|
switch (code)
|
{
|
{
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
val = expand_simple_binop (SImode, code, new, orig,
|
val = expand_simple_binop (SImode, code, new, orig,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
val = expand_simple_binop (SImode, AND, val, ac.modemask,
|
val = expand_simple_binop (SImode, AND, val, ac.modemask,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
/* FALLTHRU */
|
/* FALLTHRU */
|
case SET:
|
case SET:
|
if (ac.aligned && MEM_P (val))
|
if (ac.aligned && MEM_P (val))
|
store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val);
|
store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val);
|
else
|
else
|
{
|
{
|
new = expand_simple_binop (SImode, AND, new, ac.modemaski,
|
new = expand_simple_binop (SImode, AND, new, ac.modemaski,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
new = expand_simple_binop (SImode, IOR, new, val,
|
new = expand_simple_binop (SImode, IOR, new, val,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
}
|
}
|
break;
|
break;
|
case AND:
|
case AND:
|
case IOR:
|
case IOR:
|
case XOR:
|
case XOR:
|
new = expand_simple_binop (SImode, code, new, val,
|
new = expand_simple_binop (SImode, code, new, val,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
break;
|
break;
|
case MULT: /* NAND */
|
case MULT: /* NAND */
|
new = expand_simple_binop (SImode, XOR, new, ac.modemask,
|
new = expand_simple_binop (SImode, XOR, new, ac.modemask,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
new = expand_simple_binop (SImode, AND, new, val,
|
new = expand_simple_binop (SImode, AND, new, val,
|
NULL_RTX, 1, OPTAB_DIRECT);
|
NULL_RTX, 1, OPTAB_DIRECT);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
|
s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
|
ac.memsi, cmp, new));
|
ac.memsi, cmp, new));
|
|
|
/* Return the correct part of the bitfield. */
|
/* Return the correct part of the bitfield. */
|
if (target)
|
if (target)
|
convert_move (target, expand_simple_binop (SImode, LSHIFTRT,
|
convert_move (target, expand_simple_binop (SImode, LSHIFTRT,
|
after ? new : cmp, ac.shift,
|
after ? new : cmp, ac.shift,
|
NULL_RTX, 1, OPTAB_DIRECT), 1);
|
NULL_RTX, 1, OPTAB_DIRECT), 1);
|
}
|
}
|
|
|
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
|
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
|
We need to emit DTP-relative relocations. */
|
We need to emit DTP-relative relocations. */
|
|
|
static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
|
static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
|
|
|
static void
|
static void
|
s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
|
s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
|
{
|
{
|
switch (size)
|
switch (size)
|
{
|
{
|
case 4:
|
case 4:
|
fputs ("\t.long\t", file);
|
fputs ("\t.long\t", file);
|
break;
|
break;
|
case 8:
|
case 8:
|
fputs ("\t.quad\t", file);
|
fputs ("\t.quad\t", file);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
fputs ("@DTPOFF", file);
|
fputs ("@DTPOFF", file);
|
}
|
}
|
|
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
/* Implement TARGET_MANGLE_FUNDAMENTAL_TYPE. */
|
/* Implement TARGET_MANGLE_FUNDAMENTAL_TYPE. */
|
|
|
static const char *
|
static const char *
|
s390_mangle_fundamental_type (tree type)
|
s390_mangle_fundamental_type (tree type)
|
{
|
{
|
if (TYPE_MAIN_VARIANT (type) == long_double_type_node
|
if (TYPE_MAIN_VARIANT (type) == long_double_type_node
|
&& TARGET_LONG_DOUBLE_128)
|
&& TARGET_LONG_DOUBLE_128)
|
return "g";
|
return "g";
|
|
|
/* For all other types, use normal C++ mangling. */
|
/* For all other types, use normal C++ mangling. */
|
return NULL;
|
return NULL;
|
}
|
}
|
#endif
|
#endif
|
|
|
/* In the name of slightly smaller debug output, and to cater to
|
/* In the name of slightly smaller debug output, and to cater to
|
general assembler lossage, recognize various UNSPEC sequences
|
general assembler lossage, recognize various UNSPEC sequences
|
and turn them back into a direct symbol reference. */
|
and turn them back into a direct symbol reference. */
|
|
|
static rtx
|
static rtx
|
s390_delegitimize_address (rtx orig_x)
|
s390_delegitimize_address (rtx orig_x)
|
{
|
{
|
rtx x = orig_x, y;
|
rtx x = orig_x, y;
|
|
|
if (GET_CODE (x) != MEM)
|
if (GET_CODE (x) != MEM)
|
return orig_x;
|
return orig_x;
|
|
|
x = XEXP (x, 0);
|
x = XEXP (x, 0);
|
if (GET_CODE (x) == PLUS
|
if (GET_CODE (x) == PLUS
|
&& GET_CODE (XEXP (x, 1)) == CONST
|
&& GET_CODE (XEXP (x, 1)) == CONST
|
&& GET_CODE (XEXP (x, 0)) == REG
|
&& GET_CODE (XEXP (x, 0)) == REG
|
&& REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM)
|
&& REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM)
|
{
|
{
|
y = XEXP (XEXP (x, 1), 0);
|
y = XEXP (XEXP (x, 1), 0);
|
if (GET_CODE (y) == UNSPEC
|
if (GET_CODE (y) == UNSPEC
|
&& XINT (y, 1) == UNSPEC_GOT)
|
&& XINT (y, 1) == UNSPEC_GOT)
|
return XVECEXP (y, 0, 0);
|
return XVECEXP (y, 0, 0);
|
return orig_x;
|
return orig_x;
|
}
|
}
|
|
|
if (GET_CODE (x) == CONST)
|
if (GET_CODE (x) == CONST)
|
{
|
{
|
y = XEXP (x, 0);
|
y = XEXP (x, 0);
|
if (GET_CODE (y) == UNSPEC
|
if (GET_CODE (y) == UNSPEC
|
&& XINT (y, 1) == UNSPEC_GOTENT)
|
&& XINT (y, 1) == UNSPEC_GOTENT)
|
return XVECEXP (y, 0, 0);
|
return XVECEXP (y, 0, 0);
|
return orig_x;
|
return orig_x;
|
}
|
}
|
|
|
return orig_x;
|
return orig_x;
|
}
|
}
|
|
|
/* Output operand OP to stdio stream FILE.
|
/* Output operand OP to stdio stream FILE.
|
OP is an address (register + offset) which is not used to address data;
|
OP is an address (register + offset) which is not used to address data;
|
instead the rightmost bits are interpreted as the value. */
|
instead the rightmost bits are interpreted as the value. */
|
|
|
static void
|
static void
|
print_shift_count_operand (FILE *file, rtx op)
|
print_shift_count_operand (FILE *file, rtx op)
|
{
|
{
|
HOST_WIDE_INT offset;
|
HOST_WIDE_INT offset;
|
rtx base;
|
rtx base;
|
|
|
/* Extract base register and offset. */
|
/* Extract base register and offset. */
|
if (!s390_decompose_shift_count (op, &base, &offset))
|
if (!s390_decompose_shift_count (op, &base, &offset))
|
gcc_unreachable ();
|
gcc_unreachable ();
|
|
|
/* Sanity check. */
|
/* Sanity check. */
|
if (base)
|
if (base)
|
{
|
{
|
gcc_assert (GET_CODE (base) == REG);
|
gcc_assert (GET_CODE (base) == REG);
|
gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER);
|
gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER);
|
gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS);
|
gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS);
|
}
|
}
|
|
|
/* Offsets are constricted to twelve bits. */
|
/* Offsets are constricted to twelve bits. */
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1));
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1));
|
if (base)
|
if (base)
|
fprintf (file, "(%s)", reg_names[REGNO (base)]);
|
fprintf (file, "(%s)", reg_names[REGNO (base)]);
|
}
|
}
|
|
|
/* See 'get_some_local_dynamic_name'. */
|
/* See 'get_some_local_dynamic_name'. */
|
|
|
static int
|
static int
|
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
|
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
|
{
|
{
|
rtx x = *px;
|
rtx x = *px;
|
|
|
if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
|
if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
|
{
|
{
|
x = get_pool_constant (x);
|
x = get_pool_constant (x);
|
return for_each_rtx (&x, get_some_local_dynamic_name_1, 0);
|
return for_each_rtx (&x, get_some_local_dynamic_name_1, 0);
|
}
|
}
|
|
|
if (GET_CODE (x) == SYMBOL_REF
|
if (GET_CODE (x) == SYMBOL_REF
|
&& tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC)
|
&& tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC)
|
{
|
{
|
cfun->machine->some_ld_name = XSTR (x, 0);
|
cfun->machine->some_ld_name = XSTR (x, 0);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Locate some local-dynamic symbol still in use by this function
|
/* Locate some local-dynamic symbol still in use by this function
|
so that we can print its name in local-dynamic base patterns. */
|
so that we can print its name in local-dynamic base patterns. */
|
|
|
static const char *
|
static const char *
|
get_some_local_dynamic_name (void)
|
get_some_local_dynamic_name (void)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
if (cfun->machine->some_ld_name)
|
if (cfun->machine->some_ld_name)
|
return cfun->machine->some_ld_name;
|
return cfun->machine->some_ld_name;
|
|
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
if (INSN_P (insn)
|
if (INSN_P (insn)
|
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
|
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
|
return cfun->machine->some_ld_name;
|
return cfun->machine->some_ld_name;
|
|
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* Output machine-dependent UNSPECs occurring in address constant X
|
/* Output machine-dependent UNSPECs occurring in address constant X
|
in assembler syntax to stdio stream FILE. Returns true if the
|
in assembler syntax to stdio stream FILE. Returns true if the
|
constant X could be recognized, false otherwise. */
|
constant X could be recognized, false otherwise. */
|
|
|
bool
|
bool
|
s390_output_addr_const_extra (FILE *file, rtx x)
|
s390_output_addr_const_extra (FILE *file, rtx x)
|
{
|
{
|
if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
|
if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
|
switch (XINT (x, 1))
|
switch (XINT (x, 1))
|
{
|
{
|
case UNSPEC_GOTENT:
|
case UNSPEC_GOTENT:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@GOTENT");
|
fprintf (file, "@GOTENT");
|
return true;
|
return true;
|
case UNSPEC_GOT:
|
case UNSPEC_GOT:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@GOT");
|
fprintf (file, "@GOT");
|
return true;
|
return true;
|
case UNSPEC_GOTOFF:
|
case UNSPEC_GOTOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@GOTOFF");
|
fprintf (file, "@GOTOFF");
|
return true;
|
return true;
|
case UNSPEC_PLT:
|
case UNSPEC_PLT:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@PLT");
|
fprintf (file, "@PLT");
|
return true;
|
return true;
|
case UNSPEC_PLTOFF:
|
case UNSPEC_PLTOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@PLTOFF");
|
fprintf (file, "@PLTOFF");
|
return true;
|
return true;
|
case UNSPEC_TLSGD:
|
case UNSPEC_TLSGD:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@TLSGD");
|
fprintf (file, "@TLSGD");
|
return true;
|
return true;
|
case UNSPEC_TLSLDM:
|
case UNSPEC_TLSLDM:
|
assemble_name (file, get_some_local_dynamic_name ());
|
assemble_name (file, get_some_local_dynamic_name ());
|
fprintf (file, "@TLSLDM");
|
fprintf (file, "@TLSLDM");
|
return true;
|
return true;
|
case UNSPEC_DTPOFF:
|
case UNSPEC_DTPOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@DTPOFF");
|
fprintf (file, "@DTPOFF");
|
return true;
|
return true;
|
case UNSPEC_NTPOFF:
|
case UNSPEC_NTPOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@NTPOFF");
|
fprintf (file, "@NTPOFF");
|
return true;
|
return true;
|
case UNSPEC_GOTNTPOFF:
|
case UNSPEC_GOTNTPOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@GOTNTPOFF");
|
fprintf (file, "@GOTNTPOFF");
|
return true;
|
return true;
|
case UNSPEC_INDNTPOFF:
|
case UNSPEC_INDNTPOFF:
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
fprintf (file, "@INDNTPOFF");
|
fprintf (file, "@INDNTPOFF");
|
return true;
|
return true;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Output address operand ADDR in assembler syntax to
|
/* Output address operand ADDR in assembler syntax to
|
stdio stream FILE. */
|
stdio stream FILE. */
|
|
|
void
|
void
|
print_operand_address (FILE *file, rtx addr)
|
print_operand_address (FILE *file, rtx addr)
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
|
|
if (!s390_decompose_address (addr, &ad)
|
if (!s390_decompose_address (addr, &ad)
|
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
|| (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
|
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
|
|| (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
|
output_operand_lossage ("cannot decompose address");
|
output_operand_lossage ("cannot decompose address");
|
|
|
if (ad.disp)
|
if (ad.disp)
|
output_addr_const (file, ad.disp);
|
output_addr_const (file, ad.disp);
|
else
|
else
|
fprintf (file, "0");
|
fprintf (file, "0");
|
|
|
if (ad.base && ad.indx)
|
if (ad.base && ad.indx)
|
fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)],
|
fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)],
|
reg_names[REGNO (ad.base)]);
|
reg_names[REGNO (ad.base)]);
|
else if (ad.base)
|
else if (ad.base)
|
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
|
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
|
}
|
}
|
|
|
/* Output operand X in assembler syntax to stdio stream FILE.
|
/* Output operand X in assembler syntax to stdio stream FILE.
|
CODE specified the format flag. The following format flags
|
CODE specified the format flag. The following format flags
|
are recognized:
|
are recognized:
|
|
|
'C': print opcode suffix for branch condition.
|
'C': print opcode suffix for branch condition.
|
'D': print opcode suffix for inverse branch condition.
|
'D': print opcode suffix for inverse branch condition.
|
'J': print tls_load/tls_gdcall/tls_ldcall suffix
|
'J': print tls_load/tls_gdcall/tls_ldcall suffix
|
'G': print the size of the operand in bytes.
|
'G': print the size of the operand in bytes.
|
'O': print only the displacement of a memory reference.
|
'O': print only the displacement of a memory reference.
|
'R': print only the base register of a memory reference.
|
'R': print only the base register of a memory reference.
|
'S': print S-type memory reference (base+displacement).
|
'S': print S-type memory reference (base+displacement).
|
'N': print the second word of a DImode operand.
|
'N': print the second word of a DImode operand.
|
'M': print the second word of a TImode operand.
|
'M': print the second word of a TImode operand.
|
'Y': print shift count operand.
|
'Y': print shift count operand.
|
|
|
'b': print integer X as if it's an unsigned byte.
|
'b': print integer X as if it's an unsigned byte.
|
'x': print integer X as if it's an unsigned halfword.
|
'x': print integer X as if it's an unsigned halfword.
|
'h': print integer X as if it's a signed halfword.
|
'h': print integer X as if it's a signed halfword.
|
'i': print the first nonzero HImode part of X.
|
'i': print the first nonzero HImode part of X.
|
'j': print the first HImode part unequal to -1 of X.
|
'j': print the first HImode part unequal to -1 of X.
|
'k': print the first nonzero SImode part of X.
|
'k': print the first nonzero SImode part of X.
|
'm': print the first SImode part unequal to -1 of X.
|
'm': print the first SImode part unequal to -1 of X.
|
'o': print integer X as if it's an unsigned 32bit word. */
|
'o': print integer X as if it's an unsigned 32bit word. */
|
|
|
void
|
void
|
print_operand (FILE *file, rtx x, int code)
|
print_operand (FILE *file, rtx x, int code)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case 'C':
|
case 'C':
|
fprintf (file, s390_branch_condition_mnemonic (x, FALSE));
|
fprintf (file, s390_branch_condition_mnemonic (x, FALSE));
|
return;
|
return;
|
|
|
case 'D':
|
case 'D':
|
fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
|
fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
|
return;
|
return;
|
|
|
case 'J':
|
case 'J':
|
if (GET_CODE (x) == SYMBOL_REF)
|
if (GET_CODE (x) == SYMBOL_REF)
|
{
|
{
|
fprintf (file, "%s", ":tls_load:");
|
fprintf (file, "%s", ":tls_load:");
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
}
|
}
|
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
|
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
|
{
|
{
|
fprintf (file, "%s", ":tls_gdcall:");
|
fprintf (file, "%s", ":tls_gdcall:");
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
output_addr_const (file, XVECEXP (x, 0, 0));
|
}
|
}
|
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM)
|
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM)
|
{
|
{
|
fprintf (file, "%s", ":tls_ldcall:");
|
fprintf (file, "%s", ":tls_ldcall:");
|
assemble_name (file, get_some_local_dynamic_name ());
|
assemble_name (file, get_some_local_dynamic_name ());
|
}
|
}
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
return;
|
return;
|
|
|
case 'G':
|
case 'G':
|
fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x)));
|
fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x)));
|
return;
|
return;
|
|
|
case 'O':
|
case 'O':
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
int ret;
|
int ret;
|
|
|
gcc_assert (GET_CODE (x) == MEM);
|
gcc_assert (GET_CODE (x) == MEM);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
gcc_assert (ret);
|
gcc_assert (ret);
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.indx);
|
gcc_assert (!ad.indx);
|
|
|
if (ad.disp)
|
if (ad.disp)
|
output_addr_const (file, ad.disp);
|
output_addr_const (file, ad.disp);
|
else
|
else
|
fprintf (file, "0");
|
fprintf (file, "0");
|
}
|
}
|
return;
|
return;
|
|
|
case 'R':
|
case 'R':
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
int ret;
|
int ret;
|
|
|
gcc_assert (GET_CODE (x) == MEM);
|
gcc_assert (GET_CODE (x) == MEM);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
gcc_assert (ret);
|
gcc_assert (ret);
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.indx);
|
gcc_assert (!ad.indx);
|
|
|
if (ad.base)
|
if (ad.base)
|
fprintf (file, "%s", reg_names[REGNO (ad.base)]);
|
fprintf (file, "%s", reg_names[REGNO (ad.base)]);
|
else
|
else
|
fprintf (file, "0");
|
fprintf (file, "0");
|
}
|
}
|
return;
|
return;
|
|
|
case 'S':
|
case 'S':
|
{
|
{
|
struct s390_address ad;
|
struct s390_address ad;
|
int ret;
|
int ret;
|
|
|
gcc_assert (GET_CODE (x) == MEM);
|
gcc_assert (GET_CODE (x) == MEM);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
ret = s390_decompose_address (XEXP (x, 0), &ad);
|
gcc_assert (ret);
|
gcc_assert (ret);
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
|
gcc_assert (!ad.indx);
|
gcc_assert (!ad.indx);
|
|
|
if (ad.disp)
|
if (ad.disp)
|
output_addr_const (file, ad.disp);
|
output_addr_const (file, ad.disp);
|
else
|
else
|
fprintf (file, "0");
|
fprintf (file, "0");
|
|
|
if (ad.base)
|
if (ad.base)
|
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
|
fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
|
}
|
}
|
return;
|
return;
|
|
|
case 'N':
|
case 'N':
|
if (GET_CODE (x) == REG)
|
if (GET_CODE (x) == REG)
|
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
|
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
|
else if (GET_CODE (x) == MEM)
|
else if (GET_CODE (x) == MEM)
|
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
|
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
break;
|
break;
|
|
|
case 'M':
|
case 'M':
|
if (GET_CODE (x) == REG)
|
if (GET_CODE (x) == REG)
|
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
|
x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
|
else if (GET_CODE (x) == MEM)
|
else if (GET_CODE (x) == MEM)
|
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
|
x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
break;
|
break;
|
|
|
case 'Y':
|
case 'Y':
|
print_shift_count_operand (file, x);
|
print_shift_count_operand (file, x);
|
return;
|
return;
|
}
|
}
|
|
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case REG:
|
case REG:
|
fprintf (file, "%s", reg_names[REGNO (x)]);
|
fprintf (file, "%s", reg_names[REGNO (x)]);
|
break;
|
break;
|
|
|
case MEM:
|
case MEM:
|
output_address (XEXP (x, 0));
|
output_address (XEXP (x, 0));
|
break;
|
break;
|
|
|
case CONST:
|
case CONST:
|
case CODE_LABEL:
|
case CODE_LABEL:
|
case LABEL_REF:
|
case LABEL_REF:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
break;
|
break;
|
|
|
case CONST_INT:
|
case CONST_INT:
|
if (code == 'b')
|
if (code == 'b')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
|
else if (code == 'x')
|
else if (code == 'x')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
|
else if (code == 'h')
|
else if (code == 'h')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
|
else if (code == 'i')
|
else if (code == 'i')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
s390_extract_part (x, HImode, 0));
|
s390_extract_part (x, HImode, 0));
|
else if (code == 'j')
|
else if (code == 'j')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
s390_extract_part (x, HImode, -1));
|
s390_extract_part (x, HImode, -1));
|
else if (code == 'k')
|
else if (code == 'k')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
s390_extract_part (x, SImode, 0));
|
s390_extract_part (x, SImode, 0));
|
else if (code == 'm')
|
else if (code == 'm')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
|
s390_extract_part (x, SImode, -1));
|
s390_extract_part (x, SImode, -1));
|
else if (code == 'o')
|
else if (code == 'o')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff);
|
else
|
else
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
|
break;
|
break;
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
gcc_assert (GET_MODE (x) == VOIDmode);
|
gcc_assert (GET_MODE (x) == VOIDmode);
|
if (code == 'b')
|
if (code == 'b')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
|
else if (code == 'x')
|
else if (code == 'x')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
|
else if (code == 'h')
|
else if (code == 'h')
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
break;
|
break;
|
|
|
default:
|
default:
|
fatal_insn ("UNKNOWN in print_operand !?", x);
|
fatal_insn ("UNKNOWN in print_operand !?", x);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* Target hook for assembling integer objects. We need to define it
|
/* Target hook for assembling integer objects. We need to define it
|
here to work a round a bug in some versions of GAS, which couldn't
|
here to work a round a bug in some versions of GAS, which couldn't
|
handle values smaller than INT_MIN when printed in decimal. */
|
handle values smaller than INT_MIN when printed in decimal. */
|
|
|
static bool
|
static bool
|
s390_assemble_integer (rtx x, unsigned int size, int aligned_p)
|
s390_assemble_integer (rtx x, unsigned int size, int aligned_p)
|
{
|
{
|
if (size == 8 && aligned_p
|
if (size == 8 && aligned_p
|
&& GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
|
&& GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
|
{
|
{
|
fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n",
|
fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n",
|
INTVAL (x));
|
INTVAL (x));
|
return true;
|
return true;
|
}
|
}
|
return default_assemble_integer (x, size, aligned_p);
|
return default_assemble_integer (x, size, aligned_p);
|
}
|
}
|
|
|
/* Returns true if register REGNO is used for forming
|
/* Returns true if register REGNO is used for forming
|
a memory address in expression X. */
|
a memory address in expression X. */
|
|
|
static bool
|
static bool
|
reg_used_in_mem_p (int regno, rtx x)
|
reg_used_in_mem_p (int regno, rtx x)
|
{
|
{
|
enum rtx_code code = GET_CODE (x);
|
enum rtx_code code = GET_CODE (x);
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
if (code == MEM)
|
if (code == MEM)
|
{
|
{
|
if (refers_to_regno_p (regno, regno+1,
|
if (refers_to_regno_p (regno, regno+1,
|
XEXP (x, 0), 0))
|
XEXP (x, 0), 0))
|
return true;
|
return true;
|
}
|
}
|
else if (code == SET
|
else if (code == SET
|
&& GET_CODE (SET_DEST (x)) == PC)
|
&& GET_CODE (SET_DEST (x)) == PC)
|
{
|
{
|
if (refers_to_regno_p (regno, regno+1,
|
if (refers_to_regno_p (regno, regno+1,
|
SET_SRC (x), 0))
|
SET_SRC (x), 0))
|
return true;
|
return true;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (code);
|
fmt = GET_RTX_FORMAT (code);
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e'
|
if (fmt[i] == 'e'
|
&& reg_used_in_mem_p (regno, XEXP (x, i)))
|
&& reg_used_in_mem_p (regno, XEXP (x, i)))
|
return true;
|
return true;
|
|
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
for (j = 0; j < XVECLEN (x, i); j++)
|
for (j = 0; j < XVECLEN (x, i); j++)
|
if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
|
if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
|
return true;
|
return true;
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Returns true if expression DEP_RTX sets an address register
|
/* Returns true if expression DEP_RTX sets an address register
|
used by instruction INSN to address memory. */
|
used by instruction INSN to address memory. */
|
|
|
static bool
|
static bool
|
addr_generation_dependency_p (rtx dep_rtx, rtx insn)
|
addr_generation_dependency_p (rtx dep_rtx, rtx insn)
|
{
|
{
|
rtx target, pat;
|
rtx target, pat;
|
|
|
if (GET_CODE (dep_rtx) == INSN)
|
if (GET_CODE (dep_rtx) == INSN)
|
dep_rtx = PATTERN (dep_rtx);
|
dep_rtx = PATTERN (dep_rtx);
|
|
|
if (GET_CODE (dep_rtx) == SET)
|
if (GET_CODE (dep_rtx) == SET)
|
{
|
{
|
target = SET_DEST (dep_rtx);
|
target = SET_DEST (dep_rtx);
|
if (GET_CODE (target) == STRICT_LOW_PART)
|
if (GET_CODE (target) == STRICT_LOW_PART)
|
target = XEXP (target, 0);
|
target = XEXP (target, 0);
|
while (GET_CODE (target) == SUBREG)
|
while (GET_CODE (target) == SUBREG)
|
target = SUBREG_REG (target);
|
target = SUBREG_REG (target);
|
|
|
if (GET_CODE (target) == REG)
|
if (GET_CODE (target) == REG)
|
{
|
{
|
int regno = REGNO (target);
|
int regno = REGNO (target);
|
|
|
if (s390_safe_attr_type (insn) == TYPE_LA)
|
if (s390_safe_attr_type (insn) == TYPE_LA)
|
{
|
{
|
pat = PATTERN (insn);
|
pat = PATTERN (insn);
|
if (GET_CODE (pat) == PARALLEL)
|
if (GET_CODE (pat) == PARALLEL)
|
{
|
{
|
gcc_assert (XVECLEN (pat, 0) == 2);
|
gcc_assert (XVECLEN (pat, 0) == 2);
|
pat = XVECEXP (pat, 0, 0);
|
pat = XVECEXP (pat, 0, 0);
|
}
|
}
|
gcc_assert (GET_CODE (pat) == SET);
|
gcc_assert (GET_CODE (pat) == SET);
|
return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
|
return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
|
}
|
}
|
else if (get_attr_atype (insn) == ATYPE_AGEN)
|
else if (get_attr_atype (insn) == ATYPE_AGEN)
|
return reg_used_in_mem_p (regno, PATTERN (insn));
|
return reg_used_in_mem_p (regno, PATTERN (insn));
|
}
|
}
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Return 1, if dep_insn sets register used in insn in the agen unit. */
|
/* Return 1, if dep_insn sets register used in insn in the agen unit. */
|
|
|
int
|
int
|
s390_agen_dep_p (rtx dep_insn, rtx insn)
|
s390_agen_dep_p (rtx dep_insn, rtx insn)
|
{
|
{
|
rtx dep_rtx = PATTERN (dep_insn);
|
rtx dep_rtx = PATTERN (dep_insn);
|
int i;
|
int i;
|
|
|
if (GET_CODE (dep_rtx) == SET
|
if (GET_CODE (dep_rtx) == SET
|
&& addr_generation_dependency_p (dep_rtx, insn))
|
&& addr_generation_dependency_p (dep_rtx, insn))
|
return 1;
|
return 1;
|
else if (GET_CODE (dep_rtx) == PARALLEL)
|
else if (GET_CODE (dep_rtx) == PARALLEL)
|
{
|
{
|
for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
|
for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
|
{
|
{
|
if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn))
|
if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn))
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* A C statement (sans semicolon) to update the integer scheduling priority
|
/* A C statement (sans semicolon) to update the integer scheduling priority
|
INSN_PRIORITY (INSN). Increase the priority to execute the INSN earlier,
|
INSN_PRIORITY (INSN). Increase the priority to execute the INSN earlier,
|
reduce the priority to execute INSN later. Do not define this macro if
|
reduce the priority to execute INSN later. Do not define this macro if
|
you do not need to adjust the scheduling priorities of insns.
|
you do not need to adjust the scheduling priorities of insns.
|
|
|
A STD instruction should be scheduled earlier,
|
A STD instruction should be scheduled earlier,
|
in order to use the bypass. */
|
in order to use the bypass. */
|
|
|
static int
|
static int
|
s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
|
s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
|
{
|
{
|
if (! INSN_P (insn))
|
if (! INSN_P (insn))
|
return priority;
|
return priority;
|
|
|
if (s390_tune != PROCESSOR_2084_Z990
|
if (s390_tune != PROCESSOR_2084_Z990
|
&& s390_tune != PROCESSOR_2094_Z9_109)
|
&& s390_tune != PROCESSOR_2094_Z9_109)
|
return priority;
|
return priority;
|
|
|
switch (s390_safe_attr_type (insn))
|
switch (s390_safe_attr_type (insn))
|
{
|
{
|
case TYPE_FSTOREDF:
|
case TYPE_FSTOREDF:
|
case TYPE_FSTORESF:
|
case TYPE_FSTORESF:
|
priority = priority << 3;
|
priority = priority << 3;
|
break;
|
break;
|
case TYPE_STORE:
|
case TYPE_STORE:
|
case TYPE_STM:
|
case TYPE_STM:
|
priority = priority << 1;
|
priority = priority << 1;
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
return priority;
|
return priority;
|
}
|
}
|
|
|
/* The number of instructions that can be issued per cycle. */
|
/* The number of instructions that can be issued per cycle. */
|
|
|
static int
|
static int
|
s390_issue_rate (void)
|
s390_issue_rate (void)
|
{
|
{
|
if (s390_tune == PROCESSOR_2084_Z990
|
if (s390_tune == PROCESSOR_2084_Z990
|
|| s390_tune == PROCESSOR_2094_Z9_109)
|
|| s390_tune == PROCESSOR_2094_Z9_109)
|
return 3;
|
return 3;
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static int
|
static int
|
s390_first_cycle_multipass_dfa_lookahead (void)
|
s390_first_cycle_multipass_dfa_lookahead (void)
|
{
|
{
|
return 4;
|
return 4;
|
}
|
}
|
|
|
|
|
/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
|
/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
|
Fix up MEMs as required. */
|
Fix up MEMs as required. */
|
|
|
static void
|
static void
|
annotate_constant_pool_refs (rtx *x)
|
annotate_constant_pool_refs (rtx *x)
|
{
|
{
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
gcc_assert (GET_CODE (*x) != SYMBOL_REF
|
gcc_assert (GET_CODE (*x) != SYMBOL_REF
|
|| !CONSTANT_POOL_ADDRESS_P (*x));
|
|| !CONSTANT_POOL_ADDRESS_P (*x));
|
|
|
/* Literal pool references can only occur inside a MEM ... */
|
/* Literal pool references can only occur inside a MEM ... */
|
if (GET_CODE (*x) == MEM)
|
if (GET_CODE (*x) == MEM)
|
{
|
{
|
rtx memref = XEXP (*x, 0);
|
rtx memref = XEXP (*x, 0);
|
|
|
if (GET_CODE (memref) == SYMBOL_REF
|
if (GET_CODE (memref) == SYMBOL_REF
|
&& CONSTANT_POOL_ADDRESS_P (memref))
|
&& CONSTANT_POOL_ADDRESS_P (memref))
|
{
|
{
|
rtx base = cfun->machine->base_reg;
|
rtx base = cfun->machine->base_reg;
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base),
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base),
|
UNSPEC_LTREF);
|
UNSPEC_LTREF);
|
|
|
*x = replace_equiv_address (*x, addr);
|
*x = replace_equiv_address (*x, addr);
|
return;
|
return;
|
}
|
}
|
|
|
if (GET_CODE (memref) == CONST
|
if (GET_CODE (memref) == CONST
|
&& GET_CODE (XEXP (memref, 0)) == PLUS
|
&& GET_CODE (XEXP (memref, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (memref, 0), 0)) == SYMBOL_REF
|
&& GET_CODE (XEXP (XEXP (memref, 0), 0)) == SYMBOL_REF
|
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (memref, 0), 0)))
|
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (memref, 0), 0)))
|
{
|
{
|
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1));
|
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1));
|
rtx sym = XEXP (XEXP (memref, 0), 0);
|
rtx sym = XEXP (XEXP (memref, 0), 0);
|
rtx base = cfun->machine->base_reg;
|
rtx base = cfun->machine->base_reg;
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
|
UNSPEC_LTREF);
|
UNSPEC_LTREF);
|
|
|
*x = replace_equiv_address (*x, plus_constant (addr, off));
|
*x = replace_equiv_address (*x, plus_constant (addr, off));
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
/* ... or a load-address type pattern. */
|
/* ... or a load-address type pattern. */
|
if (GET_CODE (*x) == SET)
|
if (GET_CODE (*x) == SET)
|
{
|
{
|
rtx addrref = SET_SRC (*x);
|
rtx addrref = SET_SRC (*x);
|
|
|
if (GET_CODE (addrref) == SYMBOL_REF
|
if (GET_CODE (addrref) == SYMBOL_REF
|
&& CONSTANT_POOL_ADDRESS_P (addrref))
|
&& CONSTANT_POOL_ADDRESS_P (addrref))
|
{
|
{
|
rtx base = cfun->machine->base_reg;
|
rtx base = cfun->machine->base_reg;
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addrref, base),
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addrref, base),
|
UNSPEC_LTREF);
|
UNSPEC_LTREF);
|
|
|
SET_SRC (*x) = addr;
|
SET_SRC (*x) = addr;
|
return;
|
return;
|
}
|
}
|
|
|
if (GET_CODE (addrref) == CONST
|
if (GET_CODE (addrref) == CONST
|
&& GET_CODE (XEXP (addrref, 0)) == PLUS
|
&& GET_CODE (XEXP (addrref, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (addrref, 0), 0)) == SYMBOL_REF
|
&& GET_CODE (XEXP (XEXP (addrref, 0), 0)) == SYMBOL_REF
|
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (addrref, 0), 0)))
|
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (addrref, 0), 0)))
|
{
|
{
|
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1));
|
HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1));
|
rtx sym = XEXP (XEXP (addrref, 0), 0);
|
rtx sym = XEXP (XEXP (addrref, 0), 0);
|
rtx base = cfun->machine->base_reg;
|
rtx base = cfun->machine->base_reg;
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
|
rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
|
UNSPEC_LTREF);
|
UNSPEC_LTREF);
|
|
|
SET_SRC (*x) = plus_constant (addr, off);
|
SET_SRC (*x) = plus_constant (addr, off);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
/* Annotate LTREL_BASE as well. */
|
/* Annotate LTREL_BASE as well. */
|
if (GET_CODE (*x) == UNSPEC
|
if (GET_CODE (*x) == UNSPEC
|
&& XINT (*x, 1) == UNSPEC_LTREL_BASE)
|
&& XINT (*x, 1) == UNSPEC_LTREL_BASE)
|
{
|
{
|
rtx base = cfun->machine->base_reg;
|
rtx base = cfun->machine->base_reg;
|
*x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XVECEXP (*x, 0, 0), base),
|
*x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XVECEXP (*x, 0, 0), base),
|
UNSPEC_LTREL_BASE);
|
UNSPEC_LTREL_BASE);
|
return;
|
return;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e')
|
if (fmt[i] == 'e')
|
{
|
{
|
annotate_constant_pool_refs (&XEXP (*x, i));
|
annotate_constant_pool_refs (&XEXP (*x, i));
|
}
|
}
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
{
|
{
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
annotate_constant_pool_refs (&XVECEXP (*x, i, j));
|
annotate_constant_pool_refs (&XVECEXP (*x, i, j));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Split all branches that exceed the maximum distance.
|
/* Split all branches that exceed the maximum distance.
|
Returns true if this created a new literal pool entry. */
|
Returns true if this created a new literal pool entry. */
|
|
|
static int
|
static int
|
s390_split_branches (void)
|
s390_split_branches (void)
|
{
|
{
|
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
int new_literal = 0, ret;
|
int new_literal = 0, ret;
|
rtx insn, pat, tmp, target;
|
rtx insn, pat, tmp, target;
|
rtx *label;
|
rtx *label;
|
|
|
/* We need correct insn addresses. */
|
/* We need correct insn addresses. */
|
|
|
shorten_branches (get_insns ());
|
shorten_branches (get_insns ());
|
|
|
/* Find all branches that exceed 64KB, and split them. */
|
/* Find all branches that exceed 64KB, and split them. */
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
if (GET_CODE (insn) != JUMP_INSN)
|
if (GET_CODE (insn) != JUMP_INSN)
|
continue;
|
continue;
|
|
|
pat = PATTERN (insn);
|
pat = PATTERN (insn);
|
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
|
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
|
pat = XVECEXP (pat, 0, 0);
|
pat = XVECEXP (pat, 0, 0);
|
if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
|
if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
|
continue;
|
continue;
|
|
|
if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
|
if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
|
{
|
{
|
label = &SET_SRC (pat);
|
label = &SET_SRC (pat);
|
}
|
}
|
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
|
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
|
{
|
{
|
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
|
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
|
label = &XEXP (SET_SRC (pat), 1);
|
label = &XEXP (SET_SRC (pat), 1);
|
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
|
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
|
label = &XEXP (SET_SRC (pat), 2);
|
label = &XEXP (SET_SRC (pat), 2);
|
else
|
else
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
continue;
|
continue;
|
|
|
if (get_attr_length (insn) <= 4)
|
if (get_attr_length (insn) <= 4)
|
continue;
|
continue;
|
|
|
/* We are going to use the return register as scratch register,
|
/* We are going to use the return register as scratch register,
|
make sure it will be saved/restored by the prologue/epilogue. */
|
make sure it will be saved/restored by the prologue/epilogue. */
|
cfun_frame_layout.save_return_addr_p = 1;
|
cfun_frame_layout.save_return_addr_p = 1;
|
|
|
if (!flag_pic)
|
if (!flag_pic)
|
{
|
{
|
new_literal = 1;
|
new_literal = 1;
|
tmp = force_const_mem (Pmode, *label);
|
tmp = force_const_mem (Pmode, *label);
|
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
|
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
|
INSN_ADDRESSES_NEW (tmp, -1);
|
INSN_ADDRESSES_NEW (tmp, -1);
|
annotate_constant_pool_refs (&PATTERN (tmp));
|
annotate_constant_pool_refs (&PATTERN (tmp));
|
|
|
target = temp_reg;
|
target = temp_reg;
|
}
|
}
|
else
|
else
|
{
|
{
|
new_literal = 1;
|
new_literal = 1;
|
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label),
|
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label),
|
UNSPEC_LTREL_OFFSET);
|
UNSPEC_LTREL_OFFSET);
|
target = gen_rtx_CONST (Pmode, target);
|
target = gen_rtx_CONST (Pmode, target);
|
target = force_const_mem (Pmode, target);
|
target = force_const_mem (Pmode, target);
|
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
|
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
|
INSN_ADDRESSES_NEW (tmp, -1);
|
INSN_ADDRESSES_NEW (tmp, -1);
|
annotate_constant_pool_refs (&PATTERN (tmp));
|
annotate_constant_pool_refs (&PATTERN (tmp));
|
|
|
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0),
|
target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0),
|
cfun->machine->base_reg),
|
cfun->machine->base_reg),
|
UNSPEC_LTREL_BASE);
|
UNSPEC_LTREL_BASE);
|
target = gen_rtx_PLUS (Pmode, temp_reg, target);
|
target = gen_rtx_PLUS (Pmode, temp_reg, target);
|
}
|
}
|
|
|
ret = validate_change (insn, label, target, 0);
|
ret = validate_change (insn, label, target, 0);
|
gcc_assert (ret);
|
gcc_assert (ret);
|
}
|
}
|
|
|
return new_literal;
|
return new_literal;
|
}
|
}
|
|
|
|
|
/* Find an annotated literal pool symbol referenced in RTX X,
|
/* Find an annotated literal pool symbol referenced in RTX X,
|
and store it at REF. Will abort if X contains references to
|
and store it at REF. Will abort if X contains references to
|
more than one such pool symbol; multiple references to the same
|
more than one such pool symbol; multiple references to the same
|
symbol are allowed, however.
|
symbol are allowed, however.
|
|
|
The rtx pointed to by REF must be initialized to NULL_RTX
|
The rtx pointed to by REF must be initialized to NULL_RTX
|
by the caller before calling this routine. */
|
by the caller before calling this routine. */
|
|
|
static void
|
static void
|
find_constant_pool_ref (rtx x, rtx *ref)
|
find_constant_pool_ref (rtx x, rtx *ref)
|
{
|
{
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
/* Ignore LTREL_BASE references. */
|
/* Ignore LTREL_BASE references. */
|
if (GET_CODE (x) == UNSPEC
|
if (GET_CODE (x) == UNSPEC
|
&& XINT (x, 1) == UNSPEC_LTREL_BASE)
|
&& XINT (x, 1) == UNSPEC_LTREL_BASE)
|
return;
|
return;
|
/* Likewise POOL_ENTRY insns. */
|
/* Likewise POOL_ENTRY insns. */
|
if (GET_CODE (x) == UNSPEC_VOLATILE
|
if (GET_CODE (x) == UNSPEC_VOLATILE
|
&& XINT (x, 1) == UNSPECV_POOL_ENTRY)
|
&& XINT (x, 1) == UNSPECV_POOL_ENTRY)
|
return;
|
return;
|
|
|
gcc_assert (GET_CODE (x) != SYMBOL_REF
|
gcc_assert (GET_CODE (x) != SYMBOL_REF
|
|| !CONSTANT_POOL_ADDRESS_P (x));
|
|| !CONSTANT_POOL_ADDRESS_P (x));
|
|
|
if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF)
|
if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF)
|
{
|
{
|
rtx sym = XVECEXP (x, 0, 0);
|
rtx sym = XVECEXP (x, 0, 0);
|
gcc_assert (GET_CODE (sym) == SYMBOL_REF
|
gcc_assert (GET_CODE (sym) == SYMBOL_REF
|
&& CONSTANT_POOL_ADDRESS_P (sym));
|
&& CONSTANT_POOL_ADDRESS_P (sym));
|
|
|
if (*ref == NULL_RTX)
|
if (*ref == NULL_RTX)
|
*ref = sym;
|
*ref = sym;
|
else
|
else
|
gcc_assert (*ref == sym);
|
gcc_assert (*ref == sym);
|
|
|
return;
|
return;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e')
|
if (fmt[i] == 'e')
|
{
|
{
|
find_constant_pool_ref (XEXP (x, i), ref);
|
find_constant_pool_ref (XEXP (x, i), ref);
|
}
|
}
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
{
|
{
|
for (j = 0; j < XVECLEN (x, i); j++)
|
for (j = 0; j < XVECLEN (x, i); j++)
|
find_constant_pool_ref (XVECEXP (x, i, j), ref);
|
find_constant_pool_ref (XVECEXP (x, i, j), ref);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Replace every reference to the annotated literal pool
|
/* Replace every reference to the annotated literal pool
|
symbol REF in X by its base plus OFFSET. */
|
symbol REF in X by its base plus OFFSET. */
|
|
|
static void
|
static void
|
replace_constant_pool_ref (rtx *x, rtx ref, rtx offset)
|
replace_constant_pool_ref (rtx *x, rtx ref, rtx offset)
|
{
|
{
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
gcc_assert (*x != ref);
|
gcc_assert (*x != ref);
|
|
|
if (GET_CODE (*x) == UNSPEC
|
if (GET_CODE (*x) == UNSPEC
|
&& XINT (*x, 1) == UNSPEC_LTREF
|
&& XINT (*x, 1) == UNSPEC_LTREF
|
&& XVECEXP (*x, 0, 0) == ref)
|
&& XVECEXP (*x, 0, 0) == ref)
|
{
|
{
|
*x = gen_rtx_PLUS (Pmode, XVECEXP (*x, 0, 1), offset);
|
*x = gen_rtx_PLUS (Pmode, XVECEXP (*x, 0, 1), offset);
|
return;
|
return;
|
}
|
}
|
|
|
if (GET_CODE (*x) == PLUS
|
if (GET_CODE (*x) == PLUS
|
&& GET_CODE (XEXP (*x, 1)) == CONST_INT
|
&& GET_CODE (XEXP (*x, 1)) == CONST_INT
|
&& GET_CODE (XEXP (*x, 0)) == UNSPEC
|
&& GET_CODE (XEXP (*x, 0)) == UNSPEC
|
&& XINT (XEXP (*x, 0), 1) == UNSPEC_LTREF
|
&& XINT (XEXP (*x, 0), 1) == UNSPEC_LTREF
|
&& XVECEXP (XEXP (*x, 0), 0, 0) == ref)
|
&& XVECEXP (XEXP (*x, 0), 0, 0) == ref)
|
{
|
{
|
rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset);
|
rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset);
|
*x = plus_constant (addr, INTVAL (XEXP (*x, 1)));
|
*x = plus_constant (addr, INTVAL (XEXP (*x, 1)));
|
return;
|
return;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e')
|
if (fmt[i] == 'e')
|
{
|
{
|
replace_constant_pool_ref (&XEXP (*x, i), ref, offset);
|
replace_constant_pool_ref (&XEXP (*x, i), ref, offset);
|
}
|
}
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
{
|
{
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, offset);
|
replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, offset);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Check whether X contains an UNSPEC_LTREL_BASE.
|
/* Check whether X contains an UNSPEC_LTREL_BASE.
|
Return its constant pool symbol if found, NULL_RTX otherwise. */
|
Return its constant pool symbol if found, NULL_RTX otherwise. */
|
|
|
static rtx
|
static rtx
|
find_ltrel_base (rtx x)
|
find_ltrel_base (rtx x)
|
{
|
{
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
if (GET_CODE (x) == UNSPEC
|
if (GET_CODE (x) == UNSPEC
|
&& XINT (x, 1) == UNSPEC_LTREL_BASE)
|
&& XINT (x, 1) == UNSPEC_LTREL_BASE)
|
return XVECEXP (x, 0, 0);
|
return XVECEXP (x, 0, 0);
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e')
|
if (fmt[i] == 'e')
|
{
|
{
|
rtx fnd = find_ltrel_base (XEXP (x, i));
|
rtx fnd = find_ltrel_base (XEXP (x, i));
|
if (fnd)
|
if (fnd)
|
return fnd;
|
return fnd;
|
}
|
}
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
{
|
{
|
for (j = 0; j < XVECLEN (x, i); j++)
|
for (j = 0; j < XVECLEN (x, i); j++)
|
{
|
{
|
rtx fnd = find_ltrel_base (XVECEXP (x, i, j));
|
rtx fnd = find_ltrel_base (XVECEXP (x, i, j));
|
if (fnd)
|
if (fnd)
|
return fnd;
|
return fnd;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
return NULL_RTX;
|
return NULL_RTX;
|
}
|
}
|
|
|
/* Replace any occurrence of UNSPEC_LTREL_BASE in X with its base. */
|
/* Replace any occurrence of UNSPEC_LTREL_BASE in X with its base. */
|
|
|
static void
|
static void
|
replace_ltrel_base (rtx *x)
|
replace_ltrel_base (rtx *x)
|
{
|
{
|
int i, j;
|
int i, j;
|
const char *fmt;
|
const char *fmt;
|
|
|
if (GET_CODE (*x) == UNSPEC
|
if (GET_CODE (*x) == UNSPEC
|
&& XINT (*x, 1) == UNSPEC_LTREL_BASE)
|
&& XINT (*x, 1) == UNSPEC_LTREL_BASE)
|
{
|
{
|
*x = XVECEXP (*x, 0, 1);
|
*x = XVECEXP (*x, 0, 1);
|
return;
|
return;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
fmt = GET_RTX_FORMAT (GET_CODE (*x));
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'e')
|
if (fmt[i] == 'e')
|
{
|
{
|
replace_ltrel_base (&XEXP (*x, i));
|
replace_ltrel_base (&XEXP (*x, i));
|
}
|
}
|
else if (fmt[i] == 'E')
|
else if (fmt[i] == 'E')
|
{
|
{
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
for (j = 0; j < XVECLEN (*x, i); j++)
|
replace_ltrel_base (&XVECEXP (*x, i, j));
|
replace_ltrel_base (&XVECEXP (*x, i, j));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
|
/* We keep a list of constants which we have to add to internal
|
/* We keep a list of constants which we have to add to internal
|
constant tables in the middle of large functions. */
|
constant tables in the middle of large functions. */
|
|
|
#define NR_C_MODES 11
|
#define NR_C_MODES 11
|
enum machine_mode constant_modes[NR_C_MODES] =
|
enum machine_mode constant_modes[NR_C_MODES] =
|
{
|
{
|
TFmode, TImode, TDmode,
|
TFmode, TImode, TDmode,
|
DFmode, DImode, DDmode,
|
DFmode, DImode, DDmode,
|
SFmode, SImode, SDmode,
|
SFmode, SImode, SDmode,
|
HImode,
|
HImode,
|
QImode
|
QImode
|
};
|
};
|
|
|
struct constant
|
struct constant
|
{
|
{
|
struct constant *next;
|
struct constant *next;
|
rtx value;
|
rtx value;
|
rtx label;
|
rtx label;
|
};
|
};
|
|
|
struct constant_pool
|
struct constant_pool
|
{
|
{
|
struct constant_pool *next;
|
struct constant_pool *next;
|
rtx first_insn;
|
rtx first_insn;
|
rtx pool_insn;
|
rtx pool_insn;
|
bitmap insns;
|
bitmap insns;
|
|
|
struct constant *constants[NR_C_MODES];
|
struct constant *constants[NR_C_MODES];
|
struct constant *execute;
|
struct constant *execute;
|
rtx label;
|
rtx label;
|
int size;
|
int size;
|
};
|
};
|
|
|
/* Allocate new constant_pool structure. */
|
/* Allocate new constant_pool structure. */
|
|
|
static struct constant_pool *
|
static struct constant_pool *
|
s390_alloc_pool (void)
|
s390_alloc_pool (void)
|
{
|
{
|
struct constant_pool *pool;
|
struct constant_pool *pool;
|
int i;
|
int i;
|
|
|
pool = (struct constant_pool *) xmalloc (sizeof *pool);
|
pool = (struct constant_pool *) xmalloc (sizeof *pool);
|
pool->next = NULL;
|
pool->next = NULL;
|
for (i = 0; i < NR_C_MODES; i++)
|
for (i = 0; i < NR_C_MODES; i++)
|
pool->constants[i] = NULL;
|
pool->constants[i] = NULL;
|
|
|
pool->execute = NULL;
|
pool->execute = NULL;
|
pool->label = gen_label_rtx ();
|
pool->label = gen_label_rtx ();
|
pool->first_insn = NULL_RTX;
|
pool->first_insn = NULL_RTX;
|
pool->pool_insn = NULL_RTX;
|
pool->pool_insn = NULL_RTX;
|
pool->insns = BITMAP_ALLOC (NULL);
|
pool->insns = BITMAP_ALLOC (NULL);
|
pool->size = 0;
|
pool->size = 0;
|
|
|
return pool;
|
return pool;
|
}
|
}
|
|
|
/* Create new constant pool covering instructions starting at INSN
|
/* Create new constant pool covering instructions starting at INSN
|
and chain it to the end of POOL_LIST. */
|
and chain it to the end of POOL_LIST. */
|
|
|
static struct constant_pool *
|
static struct constant_pool *
|
s390_start_pool (struct constant_pool **pool_list, rtx insn)
|
s390_start_pool (struct constant_pool **pool_list, rtx insn)
|
{
|
{
|
struct constant_pool *pool, **prev;
|
struct constant_pool *pool, **prev;
|
|
|
pool = s390_alloc_pool ();
|
pool = s390_alloc_pool ();
|
pool->first_insn = insn;
|
pool->first_insn = insn;
|
|
|
for (prev = pool_list; *prev; prev = &(*prev)->next)
|
for (prev = pool_list; *prev; prev = &(*prev)->next)
|
;
|
;
|
*prev = pool;
|
*prev = pool;
|
|
|
return pool;
|
return pool;
|
}
|
}
|
|
|
/* End range of instructions covered by POOL at INSN and emit
|
/* End range of instructions covered by POOL at INSN and emit
|
placeholder insn representing the pool. */
|
placeholder insn representing the pool. */
|
|
|
static void
|
static void
|
s390_end_pool (struct constant_pool *pool, rtx insn)
|
s390_end_pool (struct constant_pool *pool, rtx insn)
|
{
|
{
|
rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
|
rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
|
|
|
if (!insn)
|
if (!insn)
|
insn = get_last_insn ();
|
insn = get_last_insn ();
|
|
|
pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn);
|
pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
}
|
}
|
|
|
/* Add INSN to the list of insns covered by POOL. */
|
/* Add INSN to the list of insns covered by POOL. */
|
|
|
static void
|
static void
|
s390_add_pool_insn (struct constant_pool *pool, rtx insn)
|
s390_add_pool_insn (struct constant_pool *pool, rtx insn)
|
{
|
{
|
bitmap_set_bit (pool->insns, INSN_UID (insn));
|
bitmap_set_bit (pool->insns, INSN_UID (insn));
|
}
|
}
|
|
|
/* Return pool out of POOL_LIST that covers INSN. */
|
/* Return pool out of POOL_LIST that covers INSN. */
|
|
|
static struct constant_pool *
|
static struct constant_pool *
|
s390_find_pool (struct constant_pool *pool_list, rtx insn)
|
s390_find_pool (struct constant_pool *pool_list, rtx insn)
|
{
|
{
|
struct constant_pool *pool;
|
struct constant_pool *pool;
|
|
|
for (pool = pool_list; pool; pool = pool->next)
|
for (pool = pool_list; pool; pool = pool->next)
|
if (bitmap_bit_p (pool->insns, INSN_UID (insn)))
|
if (bitmap_bit_p (pool->insns, INSN_UID (insn)))
|
break;
|
break;
|
|
|
return pool;
|
return pool;
|
}
|
}
|
|
|
/* Add constant VAL of mode MODE to the constant pool POOL. */
|
/* Add constant VAL of mode MODE to the constant pool POOL. */
|
|
|
static void
|
static void
|
s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
|
s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
|
{
|
{
|
struct constant *c;
|
struct constant *c;
|
int i;
|
int i;
|
|
|
for (i = 0; i < NR_C_MODES; i++)
|
for (i = 0; i < NR_C_MODES; i++)
|
if (constant_modes[i] == mode)
|
if (constant_modes[i] == mode)
|
break;
|
break;
|
gcc_assert (i != NR_C_MODES);
|
gcc_assert (i != NR_C_MODES);
|
|
|
for (c = pool->constants[i]; c != NULL; c = c->next)
|
for (c = pool->constants[i]; c != NULL; c = c->next)
|
if (rtx_equal_p (val, c->value))
|
if (rtx_equal_p (val, c->value))
|
break;
|
break;
|
|
|
if (c == NULL)
|
if (c == NULL)
|
{
|
{
|
c = (struct constant *) xmalloc (sizeof *c);
|
c = (struct constant *) xmalloc (sizeof *c);
|
c->value = val;
|
c->value = val;
|
c->label = gen_label_rtx ();
|
c->label = gen_label_rtx ();
|
c->next = pool->constants[i];
|
c->next = pool->constants[i];
|
pool->constants[i] = c;
|
pool->constants[i] = c;
|
pool->size += GET_MODE_SIZE (mode);
|
pool->size += GET_MODE_SIZE (mode);
|
}
|
}
|
}
|
}
|
|
|
/* Find constant VAL of mode MODE in the constant pool POOL.
|
/* Find constant VAL of mode MODE in the constant pool POOL.
|
Return an RTX describing the distance from the start of
|
Return an RTX describing the distance from the start of
|
the pool to the location of the new constant. */
|
the pool to the location of the new constant. */
|
|
|
static rtx
|
static rtx
|
s390_find_constant (struct constant_pool *pool, rtx val,
|
s390_find_constant (struct constant_pool *pool, rtx val,
|
enum machine_mode mode)
|
enum machine_mode mode)
|
{
|
{
|
struct constant *c;
|
struct constant *c;
|
rtx offset;
|
rtx offset;
|
int i;
|
int i;
|
|
|
for (i = 0; i < NR_C_MODES; i++)
|
for (i = 0; i < NR_C_MODES; i++)
|
if (constant_modes[i] == mode)
|
if (constant_modes[i] == mode)
|
break;
|
break;
|
gcc_assert (i != NR_C_MODES);
|
gcc_assert (i != NR_C_MODES);
|
|
|
for (c = pool->constants[i]; c != NULL; c = c->next)
|
for (c = pool->constants[i]; c != NULL; c = c->next)
|
if (rtx_equal_p (val, c->value))
|
if (rtx_equal_p (val, c->value))
|
break;
|
break;
|
|
|
gcc_assert (c);
|
gcc_assert (c);
|
|
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
|
gen_rtx_LABEL_REF (Pmode, pool->label));
|
gen_rtx_LABEL_REF (Pmode, pool->label));
|
offset = gen_rtx_CONST (Pmode, offset);
|
offset = gen_rtx_CONST (Pmode, offset);
|
return offset;
|
return offset;
|
}
|
}
|
|
|
/* Check whether INSN is an execute. Return the label_ref to its
|
/* Check whether INSN is an execute. Return the label_ref to its
|
execute target template if so, NULL_RTX otherwise. */
|
execute target template if so, NULL_RTX otherwise. */
|
|
|
static rtx
|
static rtx
|
s390_execute_label (rtx insn)
|
s390_execute_label (rtx insn)
|
{
|
{
|
if (GET_CODE (insn) == INSN
|
if (GET_CODE (insn) == INSN
|
&& GET_CODE (PATTERN (insn)) == PARALLEL
|
&& GET_CODE (PATTERN (insn)) == PARALLEL
|
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
|
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
|
&& XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
|
&& XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
|
return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
|
return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
|
|
|
return NULL_RTX;
|
return NULL_RTX;
|
}
|
}
|
|
|
/* Add execute target for INSN to the constant pool POOL. */
|
/* Add execute target for INSN to the constant pool POOL. */
|
|
|
static void
|
static void
|
s390_add_execute (struct constant_pool *pool, rtx insn)
|
s390_add_execute (struct constant_pool *pool, rtx insn)
|
{
|
{
|
struct constant *c;
|
struct constant *c;
|
|
|
for (c = pool->execute; c != NULL; c = c->next)
|
for (c = pool->execute; c != NULL; c = c->next)
|
if (INSN_UID (insn) == INSN_UID (c->value))
|
if (INSN_UID (insn) == INSN_UID (c->value))
|
break;
|
break;
|
|
|
if (c == NULL)
|
if (c == NULL)
|
{
|
{
|
c = (struct constant *) xmalloc (sizeof *c);
|
c = (struct constant *) xmalloc (sizeof *c);
|
c->value = insn;
|
c->value = insn;
|
c->label = gen_label_rtx ();
|
c->label = gen_label_rtx ();
|
c->next = pool->execute;
|
c->next = pool->execute;
|
pool->execute = c;
|
pool->execute = c;
|
pool->size += 6;
|
pool->size += 6;
|
}
|
}
|
}
|
}
|
|
|
/* Find execute target for INSN in the constant pool POOL.
|
/* Find execute target for INSN in the constant pool POOL.
|
Return an RTX describing the distance from the start of
|
Return an RTX describing the distance from the start of
|
the pool to the location of the execute target. */
|
the pool to the location of the execute target. */
|
|
|
static rtx
|
static rtx
|
s390_find_execute (struct constant_pool *pool, rtx insn)
|
s390_find_execute (struct constant_pool *pool, rtx insn)
|
{
|
{
|
struct constant *c;
|
struct constant *c;
|
rtx offset;
|
rtx offset;
|
|
|
for (c = pool->execute; c != NULL; c = c->next)
|
for (c = pool->execute; c != NULL; c = c->next)
|
if (INSN_UID (insn) == INSN_UID (c->value))
|
if (INSN_UID (insn) == INSN_UID (c->value))
|
break;
|
break;
|
|
|
gcc_assert (c);
|
gcc_assert (c);
|
|
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
|
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
|
gen_rtx_LABEL_REF (Pmode, pool->label));
|
gen_rtx_LABEL_REF (Pmode, pool->label));
|
offset = gen_rtx_CONST (Pmode, offset);
|
offset = gen_rtx_CONST (Pmode, offset);
|
return offset;
|
return offset;
|
}
|
}
|
|
|
/* For an execute INSN, extract the execute target template. */
|
/* For an execute INSN, extract the execute target template. */
|
|
|
static rtx
|
static rtx
|
s390_execute_target (rtx insn)
|
s390_execute_target (rtx insn)
|
{
|
{
|
rtx pattern = PATTERN (insn);
|
rtx pattern = PATTERN (insn);
|
gcc_assert (s390_execute_label (insn));
|
gcc_assert (s390_execute_label (insn));
|
|
|
if (XVECLEN (pattern, 0) == 2)
|
if (XVECLEN (pattern, 0) == 2)
|
{
|
{
|
pattern = copy_rtx (XVECEXP (pattern, 0, 1));
|
pattern = copy_rtx (XVECEXP (pattern, 0, 1));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1);
|
rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1);
|
int i;
|
int i;
|
|
|
for (i = 0; i < XVECLEN (pattern, 0) - 1; i++)
|
for (i = 0; i < XVECLEN (pattern, 0) - 1; i++)
|
RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1));
|
RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1));
|
|
|
pattern = gen_rtx_PARALLEL (VOIDmode, vec);
|
pattern = gen_rtx_PARALLEL (VOIDmode, vec);
|
}
|
}
|
|
|
return pattern;
|
return pattern;
|
}
|
}
|
|
|
/* Indicate that INSN cannot be duplicated. This is the case for
|
/* Indicate that INSN cannot be duplicated. This is the case for
|
execute insns that carry a unique label. */
|
execute insns that carry a unique label. */
|
|
|
static bool
|
static bool
|
s390_cannot_copy_insn_p (rtx insn)
|
s390_cannot_copy_insn_p (rtx insn)
|
{
|
{
|
rtx label = s390_execute_label (insn);
|
rtx label = s390_execute_label (insn);
|
return label && label != const0_rtx;
|
return label && label != const0_rtx;
|
}
|
}
|
|
|
/* Dump out the constants in POOL. If REMOTE_LABEL is true,
|
/* Dump out the constants in POOL. If REMOTE_LABEL is true,
|
do not emit the pool base label. */
|
do not emit the pool base label. */
|
|
|
static void
|
static void
|
s390_dump_pool (struct constant_pool *pool, bool remote_label)
|
s390_dump_pool (struct constant_pool *pool, bool remote_label)
|
{
|
{
|
struct constant *c;
|
struct constant *c;
|
rtx insn = pool->pool_insn;
|
rtx insn = pool->pool_insn;
|
int i;
|
int i;
|
|
|
/* Switch to rodata section. */
|
/* Switch to rodata section. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
insn = emit_insn_after (gen_pool_section_start (), insn);
|
insn = emit_insn_after (gen_pool_section_start (), insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
}
|
}
|
|
|
/* Ensure minimum pool alignment. */
|
/* Ensure minimum pool alignment. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn);
|
insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn);
|
else
|
else
|
insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn);
|
insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
/* Emit pool base label. */
|
/* Emit pool base label. */
|
if (!remote_label)
|
if (!remote_label)
|
{
|
{
|
insn = emit_label_after (pool->label, insn);
|
insn = emit_label_after (pool->label, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
}
|
}
|
|
|
/* Dump constants in descending alignment requirement order,
|
/* Dump constants in descending alignment requirement order,
|
ensuring proper alignment for every constant. */
|
ensuring proper alignment for every constant. */
|
for (i = 0; i < NR_C_MODES; i++)
|
for (i = 0; i < NR_C_MODES; i++)
|
for (c = pool->constants[i]; c; c = c->next)
|
for (c = pool->constants[i]; c; c = c->next)
|
{
|
{
|
/* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references. */
|
/* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references. */
|
rtx value = c->value;
|
rtx value = c->value;
|
if (GET_CODE (value) == CONST
|
if (GET_CODE (value) == CONST
|
&& GET_CODE (XEXP (value, 0)) == UNSPEC
|
&& GET_CODE (XEXP (value, 0)) == UNSPEC
|
&& XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET
|
&& XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET
|
&& XVECLEN (XEXP (value, 0), 0) == 1)
|
&& XVECLEN (XEXP (value, 0), 0) == 1)
|
{
|
{
|
value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
|
value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
|
gen_rtx_LABEL_REF (VOIDmode, pool->label));
|
gen_rtx_LABEL_REF (VOIDmode, pool->label));
|
value = gen_rtx_CONST (VOIDmode, value);
|
value = gen_rtx_CONST (VOIDmode, value);
|
}
|
}
|
|
|
insn = emit_label_after (c->label, insn);
|
insn = emit_label_after (c->label, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i],
|
value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i],
|
gen_rtvec (1, value),
|
gen_rtvec (1, value),
|
UNSPECV_POOL_ENTRY);
|
UNSPECV_POOL_ENTRY);
|
insn = emit_insn_after (value, insn);
|
insn = emit_insn_after (value, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
}
|
}
|
|
|
/* Ensure minimum alignment for instructions. */
|
/* Ensure minimum alignment for instructions. */
|
insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn);
|
insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
/* Output in-pool execute template insns. */
|
/* Output in-pool execute template insns. */
|
for (c = pool->execute; c; c = c->next)
|
for (c = pool->execute; c; c = c->next)
|
{
|
{
|
insn = emit_label_after (c->label, insn);
|
insn = emit_label_after (c->label, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
insn = emit_insn_after (s390_execute_target (c->value), insn);
|
insn = emit_insn_after (s390_execute_target (c->value), insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
}
|
}
|
|
|
/* Switch back to previous section. */
|
/* Switch back to previous section. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
insn = emit_insn_after (gen_pool_section_end (), insn);
|
insn = emit_insn_after (gen_pool_section_end (), insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
}
|
}
|
|
|
insn = emit_barrier_after (insn);
|
insn = emit_barrier_after (insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
/* Remove placeholder insn. */
|
/* Remove placeholder insn. */
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
}
|
}
|
|
|
/* Free all memory used by POOL. */
|
/* Free all memory used by POOL. */
|
|
|
static void
|
static void
|
s390_free_pool (struct constant_pool *pool)
|
s390_free_pool (struct constant_pool *pool)
|
{
|
{
|
struct constant *c, *next;
|
struct constant *c, *next;
|
int i;
|
int i;
|
|
|
for (i = 0; i < NR_C_MODES; i++)
|
for (i = 0; i < NR_C_MODES; i++)
|
for (c = pool->constants[i]; c; c = next)
|
for (c = pool->constants[i]; c; c = next)
|
{
|
{
|
next = c->next;
|
next = c->next;
|
free (c);
|
free (c);
|
}
|
}
|
|
|
for (c = pool->execute; c; c = next)
|
for (c = pool->execute; c; c = next)
|
{
|
{
|
next = c->next;
|
next = c->next;
|
free (c);
|
free (c);
|
}
|
}
|
|
|
BITMAP_FREE (pool->insns);
|
BITMAP_FREE (pool->insns);
|
free (pool);
|
free (pool);
|
}
|
}
|
|
|
|
|
/* Collect main literal pool. Return NULL on overflow. */
|
/* Collect main literal pool. Return NULL on overflow. */
|
|
|
static struct constant_pool *
|
static struct constant_pool *
|
s390_mainpool_start (void)
|
s390_mainpool_start (void)
|
{
|
{
|
struct constant_pool *pool;
|
struct constant_pool *pool;
|
rtx insn;
|
rtx insn;
|
|
|
pool = s390_alloc_pool ();
|
pool = s390_alloc_pool ();
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
if (GET_CODE (insn) == INSN
|
if (GET_CODE (insn) == INSN
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE
|
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL)
|
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL)
|
{
|
{
|
gcc_assert (!pool->pool_insn);
|
gcc_assert (!pool->pool_insn);
|
pool->pool_insn = insn;
|
pool->pool_insn = insn;
|
}
|
}
|
|
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
|
{
|
{
|
s390_add_execute (pool, insn);
|
s390_add_execute (pool, insn);
|
}
|
}
|
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
{
|
{
|
rtx pool_ref = NULL_RTX;
|
rtx pool_ref = NULL_RTX;
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
if (pool_ref)
|
if (pool_ref)
|
{
|
{
|
rtx constant = get_pool_constant (pool_ref);
|
rtx constant = get_pool_constant (pool_ref);
|
enum machine_mode mode = get_pool_mode (pool_ref);
|
enum machine_mode mode = get_pool_mode (pool_ref);
|
s390_add_constant (pool, constant, mode);
|
s390_add_constant (pool, constant, mode);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
gcc_assert (pool->pool_insn || pool->size == 0);
|
gcc_assert (pool->pool_insn || pool->size == 0);
|
|
|
if (pool->size >= 4096)
|
if (pool->size >= 4096)
|
{
|
{
|
/* We're going to chunkify the pool, so remove the main
|
/* We're going to chunkify the pool, so remove the main
|
pool placeholder insn. */
|
pool placeholder insn. */
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
|
|
s390_free_pool (pool);
|
s390_free_pool (pool);
|
pool = NULL;
|
pool = NULL;
|
}
|
}
|
|
|
return pool;
|
return pool;
|
}
|
}
|
|
|
/* POOL holds the main literal pool as collected by s390_mainpool_start.
|
/* POOL holds the main literal pool as collected by s390_mainpool_start.
|
Modify the current function to output the pool constants as well as
|
Modify the current function to output the pool constants as well as
|
the pool register setup instruction. */
|
the pool register setup instruction. */
|
|
|
static void
|
static void
|
s390_mainpool_finish (struct constant_pool *pool)
|
s390_mainpool_finish (struct constant_pool *pool)
|
{
|
{
|
rtx base_reg = cfun->machine->base_reg;
|
rtx base_reg = cfun->machine->base_reg;
|
rtx insn;
|
rtx insn;
|
|
|
/* If the pool is empty, we're done. */
|
/* If the pool is empty, we're done. */
|
if (pool->size == 0)
|
if (pool->size == 0)
|
{
|
{
|
/* We don't actually need a base register after all. */
|
/* We don't actually need a base register after all. */
|
cfun->machine->base_reg = NULL_RTX;
|
cfun->machine->base_reg = NULL_RTX;
|
|
|
if (pool->pool_insn)
|
if (pool->pool_insn)
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
s390_free_pool (pool);
|
s390_free_pool (pool);
|
return;
|
return;
|
}
|
}
|
|
|
/* We need correct insn addresses. */
|
/* We need correct insn addresses. */
|
shorten_branches (get_insns ());
|
shorten_branches (get_insns ());
|
|
|
/* On zSeries, we use a LARL to load the pool register. The pool is
|
/* On zSeries, we use a LARL to load the pool register. The pool is
|
located in the .rodata section, so we emit it after the function. */
|
located in the .rodata section, so we emit it after the function. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
insn = gen_main_base_64 (base_reg, pool->label);
|
insn = gen_main_base_64 (base_reg, pool->label);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
|
|
insn = get_last_insn ();
|
insn = get_last_insn ();
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
|
|
s390_dump_pool (pool, 0);
|
s390_dump_pool (pool, 0);
|
}
|
}
|
|
|
/* On S/390, if the total size of the function's code plus literal pool
|
/* On S/390, if the total size of the function's code plus literal pool
|
does not exceed 4096 bytes, we use BASR to set up a function base
|
does not exceed 4096 bytes, we use BASR to set up a function base
|
pointer, and emit the literal pool at the end of the function. */
|
pointer, and emit the literal pool at the end of the function. */
|
else if (INSN_ADDRESSES (INSN_UID (get_last_insn ()))
|
else if (INSN_ADDRESSES (INSN_UID (get_last_insn ()))
|
+ pool->size + 8 /* alignment slop */ < 4096)
|
+ pool->size + 8 /* alignment slop */ < 4096)
|
{
|
{
|
insn = gen_main_base_31_small (base_reg, pool->label);
|
insn = gen_main_base_31_small (base_reg, pool->label);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
|
|
insn = emit_label_after (pool->label, insn);
|
insn = emit_label_after (pool->label, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
insn = get_last_insn ();
|
insn = get_last_insn ();
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
|
|
s390_dump_pool (pool, 1);
|
s390_dump_pool (pool, 1);
|
}
|
}
|
|
|
/* Otherwise, we emit an inline literal pool and use BASR to branch
|
/* Otherwise, we emit an inline literal pool and use BASR to branch
|
over it, setting up the pool register at the same time. */
|
over it, setting up the pool register at the same time. */
|
else
|
else
|
{
|
{
|
rtx pool_end = gen_label_rtx ();
|
rtx pool_end = gen_label_rtx ();
|
|
|
insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
|
insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
insn = emit_insn_after (insn, pool->pool_insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
remove_insn (pool->pool_insn);
|
remove_insn (pool->pool_insn);
|
|
|
insn = emit_label_after (pool->label, insn);
|
insn = emit_label_after (pool->label, insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
|
|
|
insn = emit_label_after (pool_end, pool->pool_insn);
|
insn = emit_label_after (pool_end, pool->pool_insn);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
s390_dump_pool (pool, 1);
|
s390_dump_pool (pool, 1);
|
}
|
}
|
|
|
|
|
/* Replace all literal pool references. */
|
/* Replace all literal pool references. */
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
if (INSN_P (insn))
|
if (INSN_P (insn))
|
replace_ltrel_base (&PATTERN (insn));
|
replace_ltrel_base (&PATTERN (insn));
|
|
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
{
|
{
|
rtx addr, pool_ref = NULL_RTX;
|
rtx addr, pool_ref = NULL_RTX;
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
if (pool_ref)
|
if (pool_ref)
|
{
|
{
|
if (s390_execute_label (insn))
|
if (s390_execute_label (insn))
|
addr = s390_find_execute (pool, insn);
|
addr = s390_find_execute (pool, insn);
|
else
|
else
|
addr = s390_find_constant (pool, get_pool_constant (pool_ref),
|
addr = s390_find_constant (pool, get_pool_constant (pool_ref),
|
get_pool_mode (pool_ref));
|
get_pool_mode (pool_ref));
|
|
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
|
INSN_CODE (insn) = -1;
|
INSN_CODE (insn) = -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Free the pool. */
|
/* Free the pool. */
|
s390_free_pool (pool);
|
s390_free_pool (pool);
|
}
|
}
|
|
|
/* POOL holds the main literal pool as collected by s390_mainpool_start.
|
/* POOL holds the main literal pool as collected by s390_mainpool_start.
|
We have decided we cannot use this pool, so revert all changes
|
We have decided we cannot use this pool, so revert all changes
|
to the current function that were done by s390_mainpool_start. */
|
to the current function that were done by s390_mainpool_start. */
|
static void
|
static void
|
s390_mainpool_cancel (struct constant_pool *pool)
|
s390_mainpool_cancel (struct constant_pool *pool)
|
{
|
{
|
/* We didn't actually change the instruction stream, so simply
|
/* We didn't actually change the instruction stream, so simply
|
free the pool memory. */
|
free the pool memory. */
|
s390_free_pool (pool);
|
s390_free_pool (pool);
|
}
|
}
|
|
|
|
|
/* Chunkify the literal pool. */
|
/* Chunkify the literal pool. */
|
|
|
#define S390_POOL_CHUNK_MIN 0xc00
|
#define S390_POOL_CHUNK_MIN 0xc00
|
#define S390_POOL_CHUNK_MAX 0xe00
|
#define S390_POOL_CHUNK_MAX 0xe00
|
|
|
static struct constant_pool *
|
static struct constant_pool *
|
s390_chunkify_start (void)
|
s390_chunkify_start (void)
|
{
|
{
|
struct constant_pool *curr_pool = NULL, *pool_list = NULL;
|
struct constant_pool *curr_pool = NULL, *pool_list = NULL;
|
int extra_size = 0;
|
int extra_size = 0;
|
bitmap far_labels;
|
bitmap far_labels;
|
rtx pending_ltrel = NULL_RTX;
|
rtx pending_ltrel = NULL_RTX;
|
rtx insn;
|
rtx insn;
|
|
|
rtx (*gen_reload_base) (rtx, rtx) =
|
rtx (*gen_reload_base) (rtx, rtx) =
|
TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31;
|
TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31;
|
|
|
|
|
/* We need correct insn addresses. */
|
/* We need correct insn addresses. */
|
|
|
shorten_branches (get_insns ());
|
shorten_branches (get_insns ());
|
|
|
/* Scan all insns and move literals to pool chunks. */
|
/* Scan all insns and move literals to pool chunks. */
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
/* Check for pending LTREL_BASE. */
|
/* Check for pending LTREL_BASE. */
|
if (INSN_P (insn))
|
if (INSN_P (insn))
|
{
|
{
|
rtx ltrel_base = find_ltrel_base (PATTERN (insn));
|
rtx ltrel_base = find_ltrel_base (PATTERN (insn));
|
if (ltrel_base)
|
if (ltrel_base)
|
{
|
{
|
gcc_assert (ltrel_base == pending_ltrel);
|
gcc_assert (ltrel_base == pending_ltrel);
|
pending_ltrel = NULL_RTX;
|
pending_ltrel = NULL_RTX;
|
}
|
}
|
}
|
}
|
|
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
|
if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
|
{
|
{
|
if (!curr_pool)
|
if (!curr_pool)
|
curr_pool = s390_start_pool (&pool_list, insn);
|
curr_pool = s390_start_pool (&pool_list, insn);
|
|
|
s390_add_execute (curr_pool, insn);
|
s390_add_execute (curr_pool, insn);
|
s390_add_pool_insn (curr_pool, insn);
|
s390_add_pool_insn (curr_pool, insn);
|
}
|
}
|
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
{
|
{
|
rtx pool_ref = NULL_RTX;
|
rtx pool_ref = NULL_RTX;
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
if (pool_ref)
|
if (pool_ref)
|
{
|
{
|
rtx constant = get_pool_constant (pool_ref);
|
rtx constant = get_pool_constant (pool_ref);
|
enum machine_mode mode = get_pool_mode (pool_ref);
|
enum machine_mode mode = get_pool_mode (pool_ref);
|
|
|
if (!curr_pool)
|
if (!curr_pool)
|
curr_pool = s390_start_pool (&pool_list, insn);
|
curr_pool = s390_start_pool (&pool_list, insn);
|
|
|
s390_add_constant (curr_pool, constant, mode);
|
s390_add_constant (curr_pool, constant, mode);
|
s390_add_pool_insn (curr_pool, insn);
|
s390_add_pool_insn (curr_pool, insn);
|
|
|
/* Don't split the pool chunk between a LTREL_OFFSET load
|
/* Don't split the pool chunk between a LTREL_OFFSET load
|
and the corresponding LTREL_BASE. */
|
and the corresponding LTREL_BASE. */
|
if (GET_CODE (constant) == CONST
|
if (GET_CODE (constant) == CONST
|
&& GET_CODE (XEXP (constant, 0)) == UNSPEC
|
&& GET_CODE (XEXP (constant, 0)) == UNSPEC
|
&& XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET)
|
&& XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET)
|
{
|
{
|
gcc_assert (!pending_ltrel);
|
gcc_assert (!pending_ltrel);
|
pending_ltrel = pool_ref;
|
pending_ltrel = pool_ref;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
|
if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
|
{
|
{
|
if (curr_pool)
|
if (curr_pool)
|
s390_add_pool_insn (curr_pool, insn);
|
s390_add_pool_insn (curr_pool, insn);
|
/* An LTREL_BASE must follow within the same basic block. */
|
/* An LTREL_BASE must follow within the same basic block. */
|
gcc_assert (!pending_ltrel);
|
gcc_assert (!pending_ltrel);
|
}
|
}
|
|
|
if (!curr_pool
|
if (!curr_pool
|
|| INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
|
|| INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
|
|| INSN_ADDRESSES (INSN_UID (insn)) == -1)
|
|| INSN_ADDRESSES (INSN_UID (insn)) == -1)
|
continue;
|
continue;
|
|
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
if (curr_pool->size < S390_POOL_CHUNK_MAX)
|
if (curr_pool->size < S390_POOL_CHUNK_MAX)
|
continue;
|
continue;
|
|
|
s390_end_pool (curr_pool, NULL_RTX);
|
s390_end_pool (curr_pool, NULL_RTX);
|
curr_pool = NULL;
|
curr_pool = NULL;
|
}
|
}
|
else
|
else
|
{
|
{
|
int chunk_size = INSN_ADDRESSES (INSN_UID (insn))
|
int chunk_size = INSN_ADDRESSES (INSN_UID (insn))
|
- INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
|
- INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
|
+ extra_size;
|
+ extra_size;
|
|
|
/* We will later have to insert base register reload insns.
|
/* We will later have to insert base register reload insns.
|
Those will have an effect on code size, which we need to
|
Those will have an effect on code size, which we need to
|
consider here. This calculation makes rather pessimistic
|
consider here. This calculation makes rather pessimistic
|
worst-case assumptions. */
|
worst-case assumptions. */
|
if (GET_CODE (insn) == CODE_LABEL)
|
if (GET_CODE (insn) == CODE_LABEL)
|
extra_size += 6;
|
extra_size += 6;
|
|
|
if (chunk_size < S390_POOL_CHUNK_MIN
|
if (chunk_size < S390_POOL_CHUNK_MIN
|
&& curr_pool->size < S390_POOL_CHUNK_MIN)
|
&& curr_pool->size < S390_POOL_CHUNK_MIN)
|
continue;
|
continue;
|
|
|
/* Pool chunks can only be inserted after BARRIERs ... */
|
/* Pool chunks can only be inserted after BARRIERs ... */
|
if (GET_CODE (insn) == BARRIER)
|
if (GET_CODE (insn) == BARRIER)
|
{
|
{
|
s390_end_pool (curr_pool, insn);
|
s390_end_pool (curr_pool, insn);
|
curr_pool = NULL;
|
curr_pool = NULL;
|
extra_size = 0;
|
extra_size = 0;
|
}
|
}
|
|
|
/* ... so if we don't find one in time, create one. */
|
/* ... so if we don't find one in time, create one. */
|
else if ((chunk_size > S390_POOL_CHUNK_MAX
|
else if ((chunk_size > S390_POOL_CHUNK_MAX
|
|| curr_pool->size > S390_POOL_CHUNK_MAX))
|
|| curr_pool->size > S390_POOL_CHUNK_MAX))
|
{
|
{
|
rtx label, jump, barrier;
|
rtx label, jump, barrier;
|
|
|
/* We can insert the barrier only after a 'real' insn. */
|
/* We can insert the barrier only after a 'real' insn. */
|
if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
|
if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
|
continue;
|
continue;
|
if (get_attr_length (insn) == 0)
|
if (get_attr_length (insn) == 0)
|
continue;
|
continue;
|
|
|
/* Don't separate LTREL_BASE from the corresponding
|
/* Don't separate LTREL_BASE from the corresponding
|
LTREL_OFFSET load. */
|
LTREL_OFFSET load. */
|
if (pending_ltrel)
|
if (pending_ltrel)
|
continue;
|
continue;
|
|
|
label = gen_label_rtx ();
|
label = gen_label_rtx ();
|
jump = emit_jump_insn_after (gen_jump (label), insn);
|
jump = emit_jump_insn_after (gen_jump (label), insn);
|
barrier = emit_barrier_after (jump);
|
barrier = emit_barrier_after (jump);
|
insn = emit_label_after (label, barrier);
|
insn = emit_label_after (label, barrier);
|
JUMP_LABEL (jump) = label;
|
JUMP_LABEL (jump) = label;
|
LABEL_NUSES (label) = 1;
|
LABEL_NUSES (label) = 1;
|
|
|
INSN_ADDRESSES_NEW (jump, -1);
|
INSN_ADDRESSES_NEW (jump, -1);
|
INSN_ADDRESSES_NEW (barrier, -1);
|
INSN_ADDRESSES_NEW (barrier, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
INSN_ADDRESSES_NEW (insn, -1);
|
|
|
s390_end_pool (curr_pool, barrier);
|
s390_end_pool (curr_pool, barrier);
|
curr_pool = NULL;
|
curr_pool = NULL;
|
extra_size = 0;
|
extra_size = 0;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (curr_pool)
|
if (curr_pool)
|
s390_end_pool (curr_pool, NULL_RTX);
|
s390_end_pool (curr_pool, NULL_RTX);
|
gcc_assert (!pending_ltrel);
|
gcc_assert (!pending_ltrel);
|
|
|
/* Find all labels that are branched into
|
/* Find all labels that are branched into
|
from an insn belonging to a different chunk. */
|
from an insn belonging to a different chunk. */
|
|
|
far_labels = BITMAP_ALLOC (NULL);
|
far_labels = BITMAP_ALLOC (NULL);
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
/* Labels marked with LABEL_PRESERVE_P can be target
|
/* Labels marked with LABEL_PRESERVE_P can be target
|
of non-local jumps, so we have to mark them.
|
of non-local jumps, so we have to mark them.
|
The same holds for named labels.
|
The same holds for named labels.
|
|
|
Don't do that, however, if it is the label before
|
Don't do that, however, if it is the label before
|
a jump table. */
|
a jump table. */
|
|
|
if (GET_CODE (insn) == CODE_LABEL
|
if (GET_CODE (insn) == CODE_LABEL
|
&& (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
|
&& (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
|
{
|
{
|
rtx vec_insn = next_real_insn (insn);
|
rtx vec_insn = next_real_insn (insn);
|
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
|
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
|
PATTERN (vec_insn) : NULL_RTX;
|
PATTERN (vec_insn) : NULL_RTX;
|
if (!vec_pat
|
if (!vec_pat
|
|| !(GET_CODE (vec_pat) == ADDR_VEC
|
|| !(GET_CODE (vec_pat) == ADDR_VEC
|
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC))
|
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC))
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn));
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn));
|
}
|
}
|
|
|
/* If we have a direct jump (conditional or unconditional)
|
/* If we have a direct jump (conditional or unconditional)
|
or a casesi jump, check all potential targets. */
|
or a casesi jump, check all potential targets. */
|
else if (GET_CODE (insn) == JUMP_INSN)
|
else if (GET_CODE (insn) == JUMP_INSN)
|
{
|
{
|
rtx pat = PATTERN (insn);
|
rtx pat = PATTERN (insn);
|
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
|
if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
|
pat = XVECEXP (pat, 0, 0);
|
pat = XVECEXP (pat, 0, 0);
|
|
|
if (GET_CODE (pat) == SET)
|
if (GET_CODE (pat) == SET)
|
{
|
{
|
rtx label = JUMP_LABEL (insn);
|
rtx label = JUMP_LABEL (insn);
|
if (label)
|
if (label)
|
{
|
{
|
if (s390_find_pool (pool_list, label)
|
if (s390_find_pool (pool_list, label)
|
!= s390_find_pool (pool_list, insn))
|
!= s390_find_pool (pool_list, insn))
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
|
}
|
}
|
}
|
}
|
else if (GET_CODE (pat) == PARALLEL
|
else if (GET_CODE (pat) == PARALLEL
|
&& XVECLEN (pat, 0) == 2
|
&& XVECLEN (pat, 0) == 2
|
&& GET_CODE (XVECEXP (pat, 0, 0)) == SET
|
&& GET_CODE (XVECEXP (pat, 0, 0)) == SET
|
&& GET_CODE (XVECEXP (pat, 0, 1)) == USE
|
&& GET_CODE (XVECEXP (pat, 0, 1)) == USE
|
&& GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF)
|
&& GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF)
|
{
|
{
|
/* Find the jump table used by this casesi jump. */
|
/* Find the jump table used by this casesi jump. */
|
rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
|
rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
|
rtx vec_insn = next_real_insn (vec_label);
|
rtx vec_insn = next_real_insn (vec_label);
|
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
|
rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
|
PATTERN (vec_insn) : NULL_RTX;
|
PATTERN (vec_insn) : NULL_RTX;
|
if (vec_pat
|
if (vec_pat
|
&& (GET_CODE (vec_pat) == ADDR_VEC
|
&& (GET_CODE (vec_pat) == ADDR_VEC
|
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC))
|
|| GET_CODE (vec_pat) == ADDR_DIFF_VEC))
|
{
|
{
|
int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
|
int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
|
|
|
for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
|
for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
|
{
|
{
|
rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
|
rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
|
|
|
if (s390_find_pool (pool_list, label)
|
if (s390_find_pool (pool_list, label)
|
!= s390_find_pool (pool_list, insn))
|
!= s390_find_pool (pool_list, insn))
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
|
bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Insert base register reload insns before every pool. */
|
/* Insert base register reload insns before every pool. */
|
|
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
{
|
{
|
rtx new_insn = gen_reload_base (cfun->machine->base_reg,
|
rtx new_insn = gen_reload_base (cfun->machine->base_reg,
|
curr_pool->label);
|
curr_pool->label);
|
rtx insn = curr_pool->first_insn;
|
rtx insn = curr_pool->first_insn;
|
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
|
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
|
}
|
}
|
|
|
/* Insert base register reload insns at every far label. */
|
/* Insert base register reload insns at every far label. */
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
if (GET_CODE (insn) == CODE_LABEL
|
if (GET_CODE (insn) == CODE_LABEL
|
&& bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
|
&& bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
|
{
|
{
|
struct constant_pool *pool = s390_find_pool (pool_list, insn);
|
struct constant_pool *pool = s390_find_pool (pool_list, insn);
|
if (pool)
|
if (pool)
|
{
|
{
|
rtx new_insn = gen_reload_base (cfun->machine->base_reg,
|
rtx new_insn = gen_reload_base (cfun->machine->base_reg,
|
pool->label);
|
pool->label);
|
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
|
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
|
}
|
}
|
}
|
}
|
|
|
|
|
BITMAP_FREE (far_labels);
|
BITMAP_FREE (far_labels);
|
|
|
|
|
/* Recompute insn addresses. */
|
/* Recompute insn addresses. */
|
|
|
init_insn_lengths ();
|
init_insn_lengths ();
|
shorten_branches (get_insns ());
|
shorten_branches (get_insns ());
|
|
|
return pool_list;
|
return pool_list;
|
}
|
}
|
|
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
|
After we have decided to use this list, finish implementing
|
After we have decided to use this list, finish implementing
|
all changes to the current function as required. */
|
all changes to the current function as required. */
|
|
|
static void
|
static void
|
s390_chunkify_finish (struct constant_pool *pool_list)
|
s390_chunkify_finish (struct constant_pool *pool_list)
|
{
|
{
|
struct constant_pool *curr_pool = NULL;
|
struct constant_pool *curr_pool = NULL;
|
rtx insn;
|
rtx insn;
|
|
|
|
|
/* Replace all literal pool references. */
|
/* Replace all literal pool references. */
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
if (INSN_P (insn))
|
if (INSN_P (insn))
|
replace_ltrel_base (&PATTERN (insn));
|
replace_ltrel_base (&PATTERN (insn));
|
|
|
curr_pool = s390_find_pool (pool_list, insn);
|
curr_pool = s390_find_pool (pool_list, insn);
|
if (!curr_pool)
|
if (!curr_pool)
|
continue;
|
continue;
|
|
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
|
{
|
{
|
rtx addr, pool_ref = NULL_RTX;
|
rtx addr, pool_ref = NULL_RTX;
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
find_constant_pool_ref (PATTERN (insn), &pool_ref);
|
if (pool_ref)
|
if (pool_ref)
|
{
|
{
|
if (s390_execute_label (insn))
|
if (s390_execute_label (insn))
|
addr = s390_find_execute (curr_pool, insn);
|
addr = s390_find_execute (curr_pool, insn);
|
else
|
else
|
addr = s390_find_constant (curr_pool,
|
addr = s390_find_constant (curr_pool,
|
get_pool_constant (pool_ref),
|
get_pool_constant (pool_ref),
|
get_pool_mode (pool_ref));
|
get_pool_mode (pool_ref));
|
|
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
|
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
|
INSN_CODE (insn) = -1;
|
INSN_CODE (insn) = -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Dump out all literal pools. */
|
/* Dump out all literal pools. */
|
|
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
s390_dump_pool (curr_pool, 0);
|
s390_dump_pool (curr_pool, 0);
|
|
|
/* Free pool list. */
|
/* Free pool list. */
|
|
|
while (pool_list)
|
while (pool_list)
|
{
|
{
|
struct constant_pool *next = pool_list->next;
|
struct constant_pool *next = pool_list->next;
|
s390_free_pool (pool_list);
|
s390_free_pool (pool_list);
|
pool_list = next;
|
pool_list = next;
|
}
|
}
|
}
|
}
|
|
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
|
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
|
We have decided we cannot use this list, so revert all changes
|
We have decided we cannot use this list, so revert all changes
|
to the current function that were done by s390_chunkify_start. */
|
to the current function that were done by s390_chunkify_start. */
|
|
|
static void
|
static void
|
s390_chunkify_cancel (struct constant_pool *pool_list)
|
s390_chunkify_cancel (struct constant_pool *pool_list)
|
{
|
{
|
struct constant_pool *curr_pool = NULL;
|
struct constant_pool *curr_pool = NULL;
|
rtx insn;
|
rtx insn;
|
|
|
/* Remove all pool placeholder insns. */
|
/* Remove all pool placeholder insns. */
|
|
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
|
{
|
{
|
/* Did we insert an extra barrier? Remove it. */
|
/* Did we insert an extra barrier? Remove it. */
|
rtx barrier = PREV_INSN (curr_pool->pool_insn);
|
rtx barrier = PREV_INSN (curr_pool->pool_insn);
|
rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
|
rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
|
rtx label = NEXT_INSN (curr_pool->pool_insn);
|
rtx label = NEXT_INSN (curr_pool->pool_insn);
|
|
|
if (jump && GET_CODE (jump) == JUMP_INSN
|
if (jump && GET_CODE (jump) == JUMP_INSN
|
&& barrier && GET_CODE (barrier) == BARRIER
|
&& barrier && GET_CODE (barrier) == BARRIER
|
&& label && GET_CODE (label) == CODE_LABEL
|
&& label && GET_CODE (label) == CODE_LABEL
|
&& GET_CODE (PATTERN (jump)) == SET
|
&& GET_CODE (PATTERN (jump)) == SET
|
&& SET_DEST (PATTERN (jump)) == pc_rtx
|
&& SET_DEST (PATTERN (jump)) == pc_rtx
|
&& GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
|
&& GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
|
&& XEXP (SET_SRC (PATTERN (jump)), 0) == label)
|
&& XEXP (SET_SRC (PATTERN (jump)), 0) == label)
|
{
|
{
|
remove_insn (jump);
|
remove_insn (jump);
|
remove_insn (barrier);
|
remove_insn (barrier);
|
remove_insn (label);
|
remove_insn (label);
|
}
|
}
|
|
|
remove_insn (curr_pool->pool_insn);
|
remove_insn (curr_pool->pool_insn);
|
}
|
}
|
|
|
/* Remove all base register reload insns. */
|
/* Remove all base register reload insns. */
|
|
|
for (insn = get_insns (); insn; )
|
for (insn = get_insns (); insn; )
|
{
|
{
|
rtx next_insn = NEXT_INSN (insn);
|
rtx next_insn = NEXT_INSN (insn);
|
|
|
if (GET_CODE (insn) == INSN
|
if (GET_CODE (insn) == INSN
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
|
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
|
&& XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
|
remove_insn (insn);
|
remove_insn (insn);
|
|
|
insn = next_insn;
|
insn = next_insn;
|
}
|
}
|
|
|
/* Free pool list. */
|
/* Free pool list. */
|
|
|
while (pool_list)
|
while (pool_list)
|
{
|
{
|
struct constant_pool *next = pool_list->next;
|
struct constant_pool *next = pool_list->next;
|
s390_free_pool (pool_list);
|
s390_free_pool (pool_list);
|
pool_list = next;
|
pool_list = next;
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Output the constant pool entry EXP in mode MODE with alignment ALIGN. */
|
/* Output the constant pool entry EXP in mode MODE with alignment ALIGN. */
|
|
|
void
|
void
|
s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
|
s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
|
{
|
{
|
REAL_VALUE_TYPE r;
|
REAL_VALUE_TYPE r;
|
|
|
switch (GET_MODE_CLASS (mode))
|
switch (GET_MODE_CLASS (mode))
|
{
|
{
|
case MODE_FLOAT:
|
case MODE_FLOAT:
|
case MODE_DECIMAL_FLOAT:
|
case MODE_DECIMAL_FLOAT:
|
gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
|
gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
|
|
|
REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
|
REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
|
assemble_real (r, mode, align);
|
assemble_real (r, mode, align);
|
break;
|
break;
|
|
|
case MODE_INT:
|
case MODE_INT:
|
assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
|
assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Return an RTL expression representing the value of the return address
|
/* Return an RTL expression representing the value of the return address
|
for the frame COUNT steps up from the current frame. FRAME is the
|
for the frame COUNT steps up from the current frame. FRAME is the
|
frame pointer of that frame. */
|
frame pointer of that frame. */
|
|
|
rtx
|
rtx
|
s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
|
s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
|
{
|
{
|
int offset;
|
int offset;
|
rtx addr;
|
rtx addr;
|
|
|
/* Without backchain, we fail for all but the current frame. */
|
/* Without backchain, we fail for all but the current frame. */
|
|
|
if (!TARGET_BACKCHAIN && count > 0)
|
if (!TARGET_BACKCHAIN && count > 0)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
/* For the current frame, we need to make sure the initial
|
/* For the current frame, we need to make sure the initial
|
value of RETURN_REGNUM is actually saved. */
|
value of RETURN_REGNUM is actually saved. */
|
|
|
if (count == 0)
|
if (count == 0)
|
{
|
{
|
/* On non-z architectures branch splitting could overwrite r14. */
|
/* On non-z architectures branch splitting could overwrite r14. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
|
return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
|
else
|
else
|
{
|
{
|
cfun_frame_layout.save_return_addr_p = true;
|
cfun_frame_layout.save_return_addr_p = true;
|
return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
|
return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
|
}
|
}
|
}
|
}
|
|
|
if (TARGET_PACKED_STACK)
|
if (TARGET_PACKED_STACK)
|
offset = -2 * UNITS_PER_WORD;
|
offset = -2 * UNITS_PER_WORD;
|
else
|
else
|
offset = RETURN_REGNUM * UNITS_PER_WORD;
|
offset = RETURN_REGNUM * UNITS_PER_WORD;
|
|
|
addr = plus_constant (frame, offset);
|
addr = plus_constant (frame, offset);
|
addr = memory_address (Pmode, addr);
|
addr = memory_address (Pmode, addr);
|
return gen_rtx_MEM (Pmode, addr);
|
return gen_rtx_MEM (Pmode, addr);
|
}
|
}
|
|
|
/* Return an RTL expression representing the back chain stored in
|
/* Return an RTL expression representing the back chain stored in
|
the current stack frame. */
|
the current stack frame. */
|
|
|
rtx
|
rtx
|
s390_back_chain_rtx (void)
|
s390_back_chain_rtx (void)
|
{
|
{
|
rtx chain;
|
rtx chain;
|
|
|
gcc_assert (TARGET_BACKCHAIN);
|
gcc_assert (TARGET_BACKCHAIN);
|
|
|
if (TARGET_PACKED_STACK)
|
if (TARGET_PACKED_STACK)
|
chain = plus_constant (stack_pointer_rtx,
|
chain = plus_constant (stack_pointer_rtx,
|
STACK_POINTER_OFFSET - UNITS_PER_WORD);
|
STACK_POINTER_OFFSET - UNITS_PER_WORD);
|
else
|
else
|
chain = stack_pointer_rtx;
|
chain = stack_pointer_rtx;
|
|
|
chain = gen_rtx_MEM (Pmode, chain);
|
chain = gen_rtx_MEM (Pmode, chain);
|
return chain;
|
return chain;
|
}
|
}
|
|
|
/* Find first call clobbered register unused in a function.
|
/* Find first call clobbered register unused in a function.
|
This could be used as base register in a leaf function
|
This could be used as base register in a leaf function
|
or for holding the return address before epilogue. */
|
or for holding the return address before epilogue. */
|
|
|
static int
|
static int
|
find_unused_clobbered_reg (void)
|
find_unused_clobbered_reg (void)
|
{
|
{
|
int i;
|
int i;
|
for (i = 0; i < 6; i++)
|
for (i = 0; i < 6; i++)
|
if (!regs_ever_live[i])
|
if (!regs_ever_live[i])
|
return i;
|
return i;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/* Helper function for s390_regs_ever_clobbered. Sets the fields in DATA for all
|
/* Helper function for s390_regs_ever_clobbered. Sets the fields in DATA for all
|
clobbered hard regs in SETREG. */
|
clobbered hard regs in SETREG. */
|
|
|
static void
|
static void
|
s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data)
|
s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data)
|
{
|
{
|
int *regs_ever_clobbered = (int *)data;
|
int *regs_ever_clobbered = (int *)data;
|
unsigned int i, regno;
|
unsigned int i, regno;
|
enum machine_mode mode = GET_MODE (setreg);
|
enum machine_mode mode = GET_MODE (setreg);
|
|
|
if (GET_CODE (setreg) == SUBREG)
|
if (GET_CODE (setreg) == SUBREG)
|
{
|
{
|
rtx inner = SUBREG_REG (setreg);
|
rtx inner = SUBREG_REG (setreg);
|
if (!GENERAL_REG_P (inner))
|
if (!GENERAL_REG_P (inner))
|
return;
|
return;
|
regno = subreg_regno (setreg);
|
regno = subreg_regno (setreg);
|
}
|
}
|
else if (GENERAL_REG_P (setreg))
|
else if (GENERAL_REG_P (setreg))
|
regno = REGNO (setreg);
|
regno = REGNO (setreg);
|
else
|
else
|
return;
|
return;
|
|
|
for (i = regno;
|
for (i = regno;
|
i < regno + HARD_REGNO_NREGS (regno, mode);
|
i < regno + HARD_REGNO_NREGS (regno, mode);
|
i++)
|
i++)
|
regs_ever_clobbered[i] = 1;
|
regs_ever_clobbered[i] = 1;
|
}
|
}
|
|
|
/* Walks through all basic blocks of the current function looking
|
/* Walks through all basic blocks of the current function looking
|
for clobbered hard regs using s390_reg_clobbered_rtx. The fields
|
for clobbered hard regs using s390_reg_clobbered_rtx. The fields
|
of the passed integer array REGS_EVER_CLOBBERED are set to one for
|
of the passed integer array REGS_EVER_CLOBBERED are set to one for
|
each of those regs. */
|
each of those regs. */
|
|
|
static void
|
static void
|
s390_regs_ever_clobbered (int *regs_ever_clobbered)
|
s390_regs_ever_clobbered (int *regs_ever_clobbered)
|
{
|
{
|
basic_block cur_bb;
|
basic_block cur_bb;
|
rtx cur_insn;
|
rtx cur_insn;
|
unsigned int i;
|
unsigned int i;
|
|
|
memset (regs_ever_clobbered, 0, 16 * sizeof (int));
|
memset (regs_ever_clobbered, 0, 16 * sizeof (int));
|
|
|
/* For non-leaf functions we have to consider all call clobbered regs to be
|
/* For non-leaf functions we have to consider all call clobbered regs to be
|
clobbered. */
|
clobbered. */
|
if (!current_function_is_leaf)
|
if (!current_function_is_leaf)
|
{
|
{
|
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
regs_ever_clobbered[i] = call_really_used_regs[i];
|
regs_ever_clobbered[i] = call_really_used_regs[i];
|
}
|
}
|
|
|
/* Make the "magic" eh_return registers live if necessary. For regs_ever_live
|
/* Make the "magic" eh_return registers live if necessary. For regs_ever_live
|
this work is done by liveness analysis (mark_regs_live_at_end).
|
this work is done by liveness analysis (mark_regs_live_at_end).
|
Special care is needed for functions containing landing pads. Landing pads
|
Special care is needed for functions containing landing pads. Landing pads
|
may use the eh registers, but the code which sets these registers is not
|
may use the eh registers, but the code which sets these registers is not
|
contained in that function. Hence s390_regs_ever_clobbered is not able to
|
contained in that function. Hence s390_regs_ever_clobbered is not able to
|
deal with this automatically. */
|
deal with this automatically. */
|
if (current_function_calls_eh_return || cfun->machine->has_landing_pad_p)
|
if (current_function_calls_eh_return || cfun->machine->has_landing_pad_p)
|
for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++)
|
for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++)
|
if (current_function_calls_eh_return
|
if (current_function_calls_eh_return
|
|| (cfun->machine->has_landing_pad_p
|
|| (cfun->machine->has_landing_pad_p
|
&& regs_ever_live [EH_RETURN_DATA_REGNO (i)]))
|
&& regs_ever_live [EH_RETURN_DATA_REGNO (i)]))
|
regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1;
|
regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1;
|
|
|
/* For nonlocal gotos all call-saved registers have to be saved.
|
/* For nonlocal gotos all call-saved registers have to be saved.
|
This flag is also set for the unwinding code in libgcc.
|
This flag is also set for the unwinding code in libgcc.
|
See expand_builtin_unwind_init. For regs_ever_live this is done by
|
See expand_builtin_unwind_init. For regs_ever_live this is done by
|
reload. */
|
reload. */
|
if (current_function_has_nonlocal_label)
|
if (current_function_has_nonlocal_label)
|
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
if (!call_really_used_regs[i])
|
if (!call_really_used_regs[i])
|
regs_ever_clobbered[i] = 1;
|
regs_ever_clobbered[i] = 1;
|
|
|
FOR_EACH_BB (cur_bb)
|
FOR_EACH_BB (cur_bb)
|
{
|
{
|
FOR_BB_INSNS (cur_bb, cur_insn)
|
FOR_BB_INSNS (cur_bb, cur_insn)
|
{
|
{
|
if (INSN_P (cur_insn))
|
if (INSN_P (cur_insn))
|
note_stores (PATTERN (cur_insn),
|
note_stores (PATTERN (cur_insn),
|
s390_reg_clobbered_rtx,
|
s390_reg_clobbered_rtx,
|
regs_ever_clobbered);
|
regs_ever_clobbered);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Determine the frame area which actually has to be accessed
|
/* Determine the frame area which actually has to be accessed
|
in the function epilogue. The values are stored at the
|
in the function epilogue. The values are stored at the
|
given pointers AREA_BOTTOM (address of the lowest used stack
|
given pointers AREA_BOTTOM (address of the lowest used stack
|
address) and AREA_TOP (address of the first item which does
|
address) and AREA_TOP (address of the first item which does
|
not belong to the stack frame). */
|
not belong to the stack frame). */
|
|
|
static void
|
static void
|
s390_frame_area (int *area_bottom, int *area_top)
|
s390_frame_area (int *area_bottom, int *area_top)
|
{
|
{
|
int b, t;
|
int b, t;
|
int i;
|
int i;
|
|
|
b = INT_MAX;
|
b = INT_MAX;
|
t = INT_MIN;
|
t = INT_MIN;
|
|
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
{
|
{
|
b = (cfun_frame_layout.gprs_offset
|
b = (cfun_frame_layout.gprs_offset
|
+ cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
|
+ cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
|
t = b + (cfun_frame_layout.last_restore_gpr
|
t = b + (cfun_frame_layout.last_restore_gpr
|
- cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
|
- cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
|
}
|
}
|
|
|
if (TARGET_64BIT && cfun_save_high_fprs_p)
|
if (TARGET_64BIT && cfun_save_high_fprs_p)
|
{
|
{
|
b = MIN (b, cfun_frame_layout.f8_offset);
|
b = MIN (b, cfun_frame_layout.f8_offset);
|
t = MAX (t, (cfun_frame_layout.f8_offset
|
t = MAX (t, (cfun_frame_layout.f8_offset
|
+ cfun_frame_layout.high_fprs * 8));
|
+ cfun_frame_layout.high_fprs * 8));
|
}
|
}
|
|
|
if (!TARGET_64BIT)
|
if (!TARGET_64BIT)
|
for (i = 2; i < 4; i++)
|
for (i = 2; i < 4; i++)
|
if (cfun_fpr_bit_p (i))
|
if (cfun_fpr_bit_p (i))
|
{
|
{
|
b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
|
b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
|
t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
|
t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
|
}
|
}
|
|
|
*area_bottom = b;
|
*area_bottom = b;
|
*area_top = t;
|
*area_top = t;
|
}
|
}
|
|
|
/* Fill cfun->machine with info about register usage of current function.
|
/* Fill cfun->machine with info about register usage of current function.
|
Return in CLOBBERED_REGS which GPRs are currently considered set. */
|
Return in CLOBBERED_REGS which GPRs are currently considered set. */
|
|
|
static void
|
static void
|
s390_register_info (int clobbered_regs[])
|
s390_register_info (int clobbered_regs[])
|
{
|
{
|
int i, j;
|
int i, j;
|
|
|
/* fprs 8 - 15 are call saved for 64 Bit ABI. */
|
/* fprs 8 - 15 are call saved for 64 Bit ABI. */
|
cfun_frame_layout.fpr_bitmap = 0;
|
cfun_frame_layout.fpr_bitmap = 0;
|
cfun_frame_layout.high_fprs = 0;
|
cfun_frame_layout.high_fprs = 0;
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
for (i = 24; i < 32; i++)
|
for (i = 24; i < 32; i++)
|
if (regs_ever_live[i] && !global_regs[i])
|
if (regs_ever_live[i] && !global_regs[i])
|
{
|
{
|
cfun_set_fpr_bit (i - 16);
|
cfun_set_fpr_bit (i - 16);
|
cfun_frame_layout.high_fprs++;
|
cfun_frame_layout.high_fprs++;
|
}
|
}
|
|
|
/* Find first and last gpr to be saved. We trust regs_ever_live
|
/* Find first and last gpr to be saved. We trust regs_ever_live
|
data, except that we don't save and restore global registers.
|
data, except that we don't save and restore global registers.
|
|
|
Also, all registers with special meaning to the compiler need
|
Also, all registers with special meaning to the compiler need
|
to be handled extra. */
|
to be handled extra. */
|
|
|
s390_regs_ever_clobbered (clobbered_regs);
|
s390_regs_ever_clobbered (clobbered_regs);
|
|
|
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
|
clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
|
|
|
if (frame_pointer_needed)
|
if (frame_pointer_needed)
|
clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
|
clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
|
|
|
if (flag_pic)
|
if (flag_pic)
|
clobbered_regs[PIC_OFFSET_TABLE_REGNUM]
|
clobbered_regs[PIC_OFFSET_TABLE_REGNUM]
|
|= regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
|
|= regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
|
|
|
clobbered_regs[BASE_REGNUM]
|
clobbered_regs[BASE_REGNUM]
|
|= (cfun->machine->base_reg
|
|= (cfun->machine->base_reg
|
&& REGNO (cfun->machine->base_reg) == BASE_REGNUM);
|
&& REGNO (cfun->machine->base_reg) == BASE_REGNUM);
|
|
|
clobbered_regs[RETURN_REGNUM]
|
clobbered_regs[RETURN_REGNUM]
|
|= (!current_function_is_leaf
|
|= (!current_function_is_leaf
|
|| TARGET_TPF_PROFILING
|
|| TARGET_TPF_PROFILING
|
|| cfun->machine->split_branches_pending_p
|
|| cfun->machine->split_branches_pending_p
|
|| cfun_frame_layout.save_return_addr_p
|
|| cfun_frame_layout.save_return_addr_p
|
|| current_function_calls_eh_return
|
|| current_function_calls_eh_return
|
|| current_function_stdarg);
|
|| current_function_stdarg);
|
|
|
clobbered_regs[STACK_POINTER_REGNUM]
|
clobbered_regs[STACK_POINTER_REGNUM]
|
|= (!current_function_is_leaf
|
|= (!current_function_is_leaf
|
|| TARGET_TPF_PROFILING
|
|| TARGET_TPF_PROFILING
|
|| cfun_save_high_fprs_p
|
|| cfun_save_high_fprs_p
|
|| get_frame_size () > 0
|
|| get_frame_size () > 0
|
|| current_function_calls_alloca
|
|| current_function_calls_alloca
|
|| current_function_stdarg);
|
|| current_function_stdarg);
|
|
|
for (i = 6; i < 16; i++)
|
for (i = 6; i < 16; i++)
|
if (regs_ever_live[i] || clobbered_regs[i])
|
if (regs_ever_live[i] || clobbered_regs[i])
|
break;
|
break;
|
for (j = 15; j > i; j--)
|
for (j = 15; j > i; j--)
|
if (regs_ever_live[j] || clobbered_regs[j])
|
if (regs_ever_live[j] || clobbered_regs[j])
|
break;
|
break;
|
|
|
if (i == 16)
|
if (i == 16)
|
{
|
{
|
/* Nothing to save/restore. */
|
/* Nothing to save/restore. */
|
cfun_frame_layout.first_save_gpr_slot = -1;
|
cfun_frame_layout.first_save_gpr_slot = -1;
|
cfun_frame_layout.last_save_gpr_slot = -1;
|
cfun_frame_layout.last_save_gpr_slot = -1;
|
cfun_frame_layout.first_save_gpr = -1;
|
cfun_frame_layout.first_save_gpr = -1;
|
cfun_frame_layout.first_restore_gpr = -1;
|
cfun_frame_layout.first_restore_gpr = -1;
|
cfun_frame_layout.last_save_gpr = -1;
|
cfun_frame_layout.last_save_gpr = -1;
|
cfun_frame_layout.last_restore_gpr = -1;
|
cfun_frame_layout.last_restore_gpr = -1;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Save slots for gprs from i to j. */
|
/* Save slots for gprs from i to j. */
|
cfun_frame_layout.first_save_gpr_slot = i;
|
cfun_frame_layout.first_save_gpr_slot = i;
|
cfun_frame_layout.last_save_gpr_slot = j;
|
cfun_frame_layout.last_save_gpr_slot = j;
|
|
|
for (i = cfun_frame_layout.first_save_gpr_slot;
|
for (i = cfun_frame_layout.first_save_gpr_slot;
|
i < cfun_frame_layout.last_save_gpr_slot + 1;
|
i < cfun_frame_layout.last_save_gpr_slot + 1;
|
i++)
|
i++)
|
if (clobbered_regs[i])
|
if (clobbered_regs[i])
|
break;
|
break;
|
|
|
for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--)
|
for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--)
|
if (clobbered_regs[j])
|
if (clobbered_regs[j])
|
break;
|
break;
|
|
|
if (i == cfun_frame_layout.last_save_gpr_slot + 1)
|
if (i == cfun_frame_layout.last_save_gpr_slot + 1)
|
{
|
{
|
/* Nothing to save/restore. */
|
/* Nothing to save/restore. */
|
cfun_frame_layout.first_save_gpr = -1;
|
cfun_frame_layout.first_save_gpr = -1;
|
cfun_frame_layout.first_restore_gpr = -1;
|
cfun_frame_layout.first_restore_gpr = -1;
|
cfun_frame_layout.last_save_gpr = -1;
|
cfun_frame_layout.last_save_gpr = -1;
|
cfun_frame_layout.last_restore_gpr = -1;
|
cfun_frame_layout.last_restore_gpr = -1;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Save / Restore from gpr i to j. */
|
/* Save / Restore from gpr i to j. */
|
cfun_frame_layout.first_save_gpr = i;
|
cfun_frame_layout.first_save_gpr = i;
|
cfun_frame_layout.first_restore_gpr = i;
|
cfun_frame_layout.first_restore_gpr = i;
|
cfun_frame_layout.last_save_gpr = j;
|
cfun_frame_layout.last_save_gpr = j;
|
cfun_frame_layout.last_restore_gpr = j;
|
cfun_frame_layout.last_restore_gpr = j;
|
}
|
}
|
}
|
}
|
|
|
if (current_function_stdarg)
|
if (current_function_stdarg)
|
{
|
{
|
/* Varargs functions need to save gprs 2 to 6. */
|
/* Varargs functions need to save gprs 2 to 6. */
|
if (cfun->va_list_gpr_size
|
if (cfun->va_list_gpr_size
|
&& current_function_args_info.gprs < GP_ARG_NUM_REG)
|
&& current_function_args_info.gprs < GP_ARG_NUM_REG)
|
{
|
{
|
int min_gpr = current_function_args_info.gprs;
|
int min_gpr = current_function_args_info.gprs;
|
int max_gpr = min_gpr + cfun->va_list_gpr_size;
|
int max_gpr = min_gpr + cfun->va_list_gpr_size;
|
if (max_gpr > GP_ARG_NUM_REG)
|
if (max_gpr > GP_ARG_NUM_REG)
|
max_gpr = GP_ARG_NUM_REG;
|
max_gpr = GP_ARG_NUM_REG;
|
|
|
if (cfun_frame_layout.first_save_gpr == -1
|
if (cfun_frame_layout.first_save_gpr == -1
|
|| cfun_frame_layout.first_save_gpr > 2 + min_gpr)
|
|| cfun_frame_layout.first_save_gpr > 2 + min_gpr)
|
{
|
{
|
cfun_frame_layout.first_save_gpr = 2 + min_gpr;
|
cfun_frame_layout.first_save_gpr = 2 + min_gpr;
|
cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr;
|
cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr;
|
}
|
}
|
|
|
if (cfun_frame_layout.last_save_gpr == -1
|
if (cfun_frame_layout.last_save_gpr == -1
|
|| cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1)
|
|| cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1)
|
{
|
{
|
cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1;
|
cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1;
|
cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1;
|
cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1;
|
}
|
}
|
}
|
}
|
|
|
/* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved. */
|
/* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved. */
|
if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size
|
if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size
|
&& current_function_args_info.fprs < FP_ARG_NUM_REG)
|
&& current_function_args_info.fprs < FP_ARG_NUM_REG)
|
{
|
{
|
int min_fpr = current_function_args_info.fprs;
|
int min_fpr = current_function_args_info.fprs;
|
int max_fpr = min_fpr + cfun->va_list_fpr_size;
|
int max_fpr = min_fpr + cfun->va_list_fpr_size;
|
if (max_fpr > FP_ARG_NUM_REG)
|
if (max_fpr > FP_ARG_NUM_REG)
|
max_fpr = FP_ARG_NUM_REG;
|
max_fpr = FP_ARG_NUM_REG;
|
|
|
/* ??? This is currently required to ensure proper location
|
/* ??? This is currently required to ensure proper location
|
of the fpr save slots within the va_list save area. */
|
of the fpr save slots within the va_list save area. */
|
if (TARGET_PACKED_STACK)
|
if (TARGET_PACKED_STACK)
|
min_fpr = 0;
|
min_fpr = 0;
|
|
|
for (i = min_fpr; i < max_fpr; i++)
|
for (i = min_fpr; i < max_fpr; i++)
|
cfun_set_fpr_bit (i);
|
cfun_set_fpr_bit (i);
|
}
|
}
|
}
|
}
|
|
|
if (!TARGET_64BIT)
|
if (!TARGET_64BIT)
|
for (i = 2; i < 4; i++)
|
for (i = 2; i < 4; i++)
|
if (regs_ever_live[i + 16] && !global_regs[i + 16])
|
if (regs_ever_live[i + 16] && !global_regs[i + 16])
|
cfun_set_fpr_bit (i);
|
cfun_set_fpr_bit (i);
|
}
|
}
|
|
|
/* Fill cfun->machine with info about frame of current function. */
|
/* Fill cfun->machine with info about frame of current function. */
|
|
|
static void
|
static void
|
s390_frame_info (void)
|
s390_frame_info (void)
|
{
|
{
|
int i;
|
int i;
|
|
|
cfun_frame_layout.frame_size = get_frame_size ();
|
cfun_frame_layout.frame_size = get_frame_size ();
|
if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
|
if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
|
fatal_error ("total size of local variables exceeds architecture limit");
|
fatal_error ("total size of local variables exceeds architecture limit");
|
|
|
if (!TARGET_PACKED_STACK)
|
if (!TARGET_PACKED_STACK)
|
{
|
{
|
cfun_frame_layout.backchain_offset = 0;
|
cfun_frame_layout.backchain_offset = 0;
|
cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
|
cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
|
cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
|
cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
|
cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
|
cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
|
cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot
|
cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot
|
* UNITS_PER_WORD);
|
* UNITS_PER_WORD);
|
}
|
}
|
else if (TARGET_BACKCHAIN) /* kernel stack layout */
|
else if (TARGET_BACKCHAIN) /* kernel stack layout */
|
{
|
{
|
cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
|
cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
|
- UNITS_PER_WORD);
|
- UNITS_PER_WORD);
|
cfun_frame_layout.gprs_offset
|
cfun_frame_layout.gprs_offset
|
= (cfun_frame_layout.backchain_offset
|
= (cfun_frame_layout.backchain_offset
|
- (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1)
|
- (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1)
|
* UNITS_PER_WORD);
|
* UNITS_PER_WORD);
|
|
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
cfun_frame_layout.f4_offset
|
cfun_frame_layout.f4_offset
|
= (cfun_frame_layout.gprs_offset
|
= (cfun_frame_layout.gprs_offset
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
|
|
cfun_frame_layout.f0_offset
|
cfun_frame_layout.f0_offset
|
= (cfun_frame_layout.f4_offset
|
= (cfun_frame_layout.f4_offset
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* On 31 bit we have to care about alignment of the
|
/* On 31 bit we have to care about alignment of the
|
floating point regs to provide fastest access. */
|
floating point regs to provide fastest access. */
|
cfun_frame_layout.f0_offset
|
cfun_frame_layout.f0_offset
|
= ((cfun_frame_layout.gprs_offset
|
= ((cfun_frame_layout.gprs_offset
|
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1))
|
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1))
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
|
|
cfun_frame_layout.f4_offset
|
cfun_frame_layout.f4_offset
|
= (cfun_frame_layout.f0_offset
|
= (cfun_frame_layout.f0_offset
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
}
|
}
|
}
|
}
|
else /* no backchain */
|
else /* no backchain */
|
{
|
{
|
cfun_frame_layout.f4_offset
|
cfun_frame_layout.f4_offset
|
= (STACK_POINTER_OFFSET
|
= (STACK_POINTER_OFFSET
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
- 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
|
|
|
cfun_frame_layout.f0_offset
|
cfun_frame_layout.f0_offset
|
= (cfun_frame_layout.f4_offset
|
= (cfun_frame_layout.f4_offset
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
- 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
|
|
|
cfun_frame_layout.gprs_offset
|
cfun_frame_layout.gprs_offset
|
= cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
|
= cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
|
}
|
}
|
|
|
if (current_function_is_leaf
|
if (current_function_is_leaf
|
&& !TARGET_TPF_PROFILING
|
&& !TARGET_TPF_PROFILING
|
&& cfun_frame_layout.frame_size == 0
|
&& cfun_frame_layout.frame_size == 0
|
&& !cfun_save_high_fprs_p
|
&& !cfun_save_high_fprs_p
|
&& !current_function_calls_alloca
|
&& !current_function_calls_alloca
|
&& !current_function_stdarg)
|
&& !current_function_stdarg)
|
return;
|
return;
|
|
|
if (!TARGET_PACKED_STACK)
|
if (!TARGET_PACKED_STACK)
|
cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET
|
cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET
|
+ current_function_outgoing_args_size
|
+ current_function_outgoing_args_size
|
+ cfun_frame_layout.high_fprs * 8);
|
+ cfun_frame_layout.high_fprs * 8);
|
else
|
else
|
{
|
{
|
if (TARGET_BACKCHAIN)
|
if (TARGET_BACKCHAIN)
|
cfun_frame_layout.frame_size += UNITS_PER_WORD;
|
cfun_frame_layout.frame_size += UNITS_PER_WORD;
|
|
|
/* No alignment trouble here because f8-f15 are only saved under
|
/* No alignment trouble here because f8-f15 are only saved under
|
64 bit. */
|
64 bit. */
|
cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
|
cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
|
cfun_frame_layout.f4_offset),
|
cfun_frame_layout.f4_offset),
|
cfun_frame_layout.gprs_offset)
|
cfun_frame_layout.gprs_offset)
|
- cfun_frame_layout.high_fprs * 8);
|
- cfun_frame_layout.high_fprs * 8);
|
|
|
cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
|
cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
|
|
|
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
if (cfun_fpr_bit_p (i))
|
if (cfun_fpr_bit_p (i))
|
cfun_frame_layout.frame_size += 8;
|
cfun_frame_layout.frame_size += 8;
|
|
|
cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
|
cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
|
|
|
/* If under 31 bit an odd number of gprs has to be saved we have to adjust
|
/* If under 31 bit an odd number of gprs has to be saved we have to adjust
|
the frame size to sustain 8 byte alignment of stack frames. */
|
the frame size to sustain 8 byte alignment of stack frames. */
|
cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
|
cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
|
STACK_BOUNDARY / BITS_PER_UNIT - 1)
|
STACK_BOUNDARY / BITS_PER_UNIT - 1)
|
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
|
& ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
|
|
|
cfun_frame_layout.frame_size += current_function_outgoing_args_size;
|
cfun_frame_layout.frame_size += current_function_outgoing_args_size;
|
}
|
}
|
}
|
}
|
|
|
/* Generate frame layout. Fills in register and frame data for the current
|
/* Generate frame layout. Fills in register and frame data for the current
|
function in cfun->machine. This routine can be called multiple times;
|
function in cfun->machine. This routine can be called multiple times;
|
it will re-do the complete frame layout every time. */
|
it will re-do the complete frame layout every time. */
|
|
|
static void
|
static void
|
s390_init_frame_layout (void)
|
s390_init_frame_layout (void)
|
{
|
{
|
HOST_WIDE_INT frame_size;
|
HOST_WIDE_INT frame_size;
|
int base_used;
|
int base_used;
|
int clobbered_regs[16];
|
int clobbered_regs[16];
|
|
|
/* On S/390 machines, we may need to perform branch splitting, which
|
/* On S/390 machines, we may need to perform branch splitting, which
|
will require both base and return address register. We have no
|
will require both base and return address register. We have no
|
choice but to assume we're going to need them until right at the
|
choice but to assume we're going to need them until right at the
|
end of the machine dependent reorg phase. */
|
end of the machine dependent reorg phase. */
|
if (!TARGET_CPU_ZARCH)
|
if (!TARGET_CPU_ZARCH)
|
cfun->machine->split_branches_pending_p = true;
|
cfun->machine->split_branches_pending_p = true;
|
|
|
do
|
do
|
{
|
{
|
frame_size = cfun_frame_layout.frame_size;
|
frame_size = cfun_frame_layout.frame_size;
|
|
|
/* Try to predict whether we'll need the base register. */
|
/* Try to predict whether we'll need the base register. */
|
base_used = cfun->machine->split_branches_pending_p
|
base_used = cfun->machine->split_branches_pending_p
|
|| current_function_uses_const_pool
|
|| current_function_uses_const_pool
|
|| (!DISP_IN_RANGE (frame_size)
|
|| (!DISP_IN_RANGE (frame_size)
|
&& !CONST_OK_FOR_K (frame_size));
|
&& !CONST_OK_FOR_K (frame_size));
|
|
|
/* Decide which register to use as literal pool base. In small
|
/* Decide which register to use as literal pool base. In small
|
leaf functions, try to use an unused call-clobbered register
|
leaf functions, try to use an unused call-clobbered register
|
as base register to avoid save/restore overhead. */
|
as base register to avoid save/restore overhead. */
|
if (!base_used)
|
if (!base_used)
|
cfun->machine->base_reg = NULL_RTX;
|
cfun->machine->base_reg = NULL_RTX;
|
else if (current_function_is_leaf && !regs_ever_live[5])
|
else if (current_function_is_leaf && !regs_ever_live[5])
|
cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
|
cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
|
else
|
else
|
cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
|
cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
|
|
|
s390_register_info (clobbered_regs);
|
s390_register_info (clobbered_regs);
|
s390_frame_info ();
|
s390_frame_info ();
|
}
|
}
|
while (frame_size != cfun_frame_layout.frame_size);
|
while (frame_size != cfun_frame_layout.frame_size);
|
}
|
}
|
|
|
/* Update frame layout. Recompute actual register save data based on
|
/* Update frame layout. Recompute actual register save data based on
|
current info and update regs_ever_live for the special registers.
|
current info and update regs_ever_live for the special registers.
|
May be called multiple times, but may never cause *more* registers
|
May be called multiple times, but may never cause *more* registers
|
to be saved than s390_init_frame_layout allocated room for. */
|
to be saved than s390_init_frame_layout allocated room for. */
|
|
|
static void
|
static void
|
s390_update_frame_layout (void)
|
s390_update_frame_layout (void)
|
{
|
{
|
int clobbered_regs[16];
|
int clobbered_regs[16];
|
|
|
s390_register_info (clobbered_regs);
|
s390_register_info (clobbered_regs);
|
|
|
regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM];
|
regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM];
|
regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM];
|
regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM];
|
regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM];
|
regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM];
|
|
|
if (cfun->machine->base_reg)
|
if (cfun->machine->base_reg)
|
regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
|
regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
|
}
|
}
|
|
|
/* Return true if it is legal to put a value with MODE into REGNO. */
|
/* Return true if it is legal to put a value with MODE into REGNO. */
|
|
|
bool
|
bool
|
s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
|
s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
|
{
|
{
|
switch (REGNO_REG_CLASS (regno))
|
switch (REGNO_REG_CLASS (regno))
|
{
|
{
|
case FP_REGS:
|
case FP_REGS:
|
if (REGNO_PAIR_OK (regno, mode))
|
if (REGNO_PAIR_OK (regno, mode))
|
{
|
{
|
if (mode == SImode || mode == DImode)
|
if (mode == SImode || mode == DImode)
|
return true;
|
return true;
|
|
|
if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
|
if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case ADDR_REGS:
|
case ADDR_REGS:
|
if (FRAME_REGNO_P (regno) && mode == Pmode)
|
if (FRAME_REGNO_P (regno) && mode == Pmode)
|
return true;
|
return true;
|
|
|
/* fallthrough */
|
/* fallthrough */
|
case GENERAL_REGS:
|
case GENERAL_REGS:
|
if (REGNO_PAIR_OK (regno, mode))
|
if (REGNO_PAIR_OK (regno, mode))
|
{
|
{
|
if (TARGET_64BIT
|
if (TARGET_64BIT
|
|| (mode != TFmode && mode != TCmode && mode != TDmode))
|
|| (mode != TFmode && mode != TCmode && mode != TDmode))
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case CC_REGS:
|
case CC_REGS:
|
if (GET_MODE_CLASS (mode) == MODE_CC)
|
if (GET_MODE_CLASS (mode) == MODE_CC)
|
return true;
|
return true;
|
break;
|
break;
|
case ACCESS_REGS:
|
case ACCESS_REGS:
|
if (REGNO_PAIR_OK (regno, mode))
|
if (REGNO_PAIR_OK (regno, mode))
|
{
|
{
|
if (mode == SImode || mode == Pmode)
|
if (mode == SImode || mode == Pmode)
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */
|
/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */
|
|
|
bool
|
bool
|
s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg)
|
s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg)
|
{
|
{
|
/* Once we've decided upon a register to use as base register, it must
|
/* Once we've decided upon a register to use as base register, it must
|
no longer be used for any other purpose. */
|
no longer be used for any other purpose. */
|
if (cfun->machine->base_reg)
|
if (cfun->machine->base_reg)
|
if (REGNO (cfun->machine->base_reg) == old_reg
|
if (REGNO (cfun->machine->base_reg) == old_reg
|
|| REGNO (cfun->machine->base_reg) == new_reg)
|
|| REGNO (cfun->machine->base_reg) == new_reg)
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Maximum number of registers to represent a value of mode MODE
|
/* Maximum number of registers to represent a value of mode MODE
|
in a register of class CLASS. */
|
in a register of class CLASS. */
|
|
|
bool
|
bool
|
s390_class_max_nregs (enum reg_class class, enum machine_mode mode)
|
s390_class_max_nregs (enum reg_class class, enum machine_mode mode)
|
{
|
{
|
switch (class)
|
switch (class)
|
{
|
{
|
case FP_REGS:
|
case FP_REGS:
|
if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
|
if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
|
return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8);
|
return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8);
|
else
|
else
|
return (GET_MODE_SIZE (mode) + 8 - 1) / 8;
|
return (GET_MODE_SIZE (mode) + 8 - 1) / 8;
|
case ACCESS_REGS:
|
case ACCESS_REGS:
|
return (GET_MODE_SIZE (mode) + 4 - 1) / 4;
|
return (GET_MODE_SIZE (mode) + 4 - 1) / 4;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
}
|
}
|
|
|
/* Return true if register FROM can be eliminated via register TO. */
|
/* Return true if register FROM can be eliminated via register TO. */
|
|
|
bool
|
bool
|
s390_can_eliminate (int from, int to)
|
s390_can_eliminate (int from, int to)
|
{
|
{
|
/* On zSeries machines, we have not marked the base register as fixed.
|
/* On zSeries machines, we have not marked the base register as fixed.
|
Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
|
Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
|
If a function requires the base register, we say here that this
|
If a function requires the base register, we say here that this
|
elimination cannot be performed. This will cause reload to free
|
elimination cannot be performed. This will cause reload to free
|
up the base register (as if it were fixed). On the other hand,
|
up the base register (as if it were fixed). On the other hand,
|
if the current function does *not* require the base register, we
|
if the current function does *not* require the base register, we
|
say here the elimination succeeds, which in turn allows reload
|
say here the elimination succeeds, which in turn allows reload
|
to allocate the base register for any other purpose. */
|
to allocate the base register for any other purpose. */
|
if (from == BASE_REGNUM && to == BASE_REGNUM)
|
if (from == BASE_REGNUM && to == BASE_REGNUM)
|
{
|
{
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
s390_init_frame_layout ();
|
s390_init_frame_layout ();
|
return cfun->machine->base_reg == NULL_RTX;
|
return cfun->machine->base_reg == NULL_RTX;
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Everything else must point into the stack frame. */
|
/* Everything else must point into the stack frame. */
|
gcc_assert (to == STACK_POINTER_REGNUM
|
gcc_assert (to == STACK_POINTER_REGNUM
|
|| to == HARD_FRAME_POINTER_REGNUM);
|
|| to == HARD_FRAME_POINTER_REGNUM);
|
|
|
gcc_assert (from == FRAME_POINTER_REGNUM
|
gcc_assert (from == FRAME_POINTER_REGNUM
|
|| from == ARG_POINTER_REGNUM
|
|| from == ARG_POINTER_REGNUM
|
|| from == RETURN_ADDRESS_POINTER_REGNUM);
|
|| from == RETURN_ADDRESS_POINTER_REGNUM);
|
|
|
/* Make sure we actually saved the return address. */
|
/* Make sure we actually saved the return address. */
|
if (from == RETURN_ADDRESS_POINTER_REGNUM)
|
if (from == RETURN_ADDRESS_POINTER_REGNUM)
|
if (!current_function_calls_eh_return
|
if (!current_function_calls_eh_return
|
&& !current_function_stdarg
|
&& !current_function_stdarg
|
&& !cfun_frame_layout.save_return_addr_p)
|
&& !cfun_frame_layout.save_return_addr_p)
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Return offset between register FROM and TO initially after prolog. */
|
/* Return offset between register FROM and TO initially after prolog. */
|
|
|
HOST_WIDE_INT
|
HOST_WIDE_INT
|
s390_initial_elimination_offset (int from, int to)
|
s390_initial_elimination_offset (int from, int to)
|
{
|
{
|
HOST_WIDE_INT offset;
|
HOST_WIDE_INT offset;
|
int index;
|
int index;
|
|
|
/* ??? Why are we called for non-eliminable pairs? */
|
/* ??? Why are we called for non-eliminable pairs? */
|
if (!s390_can_eliminate (from, to))
|
if (!s390_can_eliminate (from, to))
|
return 0;
|
return 0;
|
|
|
switch (from)
|
switch (from)
|
{
|
{
|
case FRAME_POINTER_REGNUM:
|
case FRAME_POINTER_REGNUM:
|
offset = (get_frame_size()
|
offset = (get_frame_size()
|
+ STACK_POINTER_OFFSET
|
+ STACK_POINTER_OFFSET
|
+ current_function_outgoing_args_size);
|
+ current_function_outgoing_args_size);
|
break;
|
break;
|
|
|
case ARG_POINTER_REGNUM:
|
case ARG_POINTER_REGNUM:
|
s390_init_frame_layout ();
|
s390_init_frame_layout ();
|
offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
|
offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
|
break;
|
break;
|
|
|
case RETURN_ADDRESS_POINTER_REGNUM:
|
case RETURN_ADDRESS_POINTER_REGNUM:
|
s390_init_frame_layout ();
|
s390_init_frame_layout ();
|
index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot;
|
index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot;
|
gcc_assert (index >= 0);
|
gcc_assert (index >= 0);
|
offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset;
|
offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset;
|
offset += index * UNITS_PER_WORD;
|
offset += index * UNITS_PER_WORD;
|
break;
|
break;
|
|
|
case BASE_REGNUM:
|
case BASE_REGNUM:
|
offset = 0;
|
offset = 0;
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
return offset;
|
return offset;
|
}
|
}
|
|
|
/* Emit insn to save fpr REGNUM at offset OFFSET relative
|
/* Emit insn to save fpr REGNUM at offset OFFSET relative
|
to register BASE. Return generated insn. */
|
to register BASE. Return generated insn. */
|
|
|
static rtx
|
static rtx
|
save_fpr (rtx base, int offset, int regnum)
|
save_fpr (rtx base, int offset, int regnum)
|
{
|
{
|
rtx addr;
|
rtx addr;
|
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
|
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
|
|
|
if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
|
if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
|
set_mem_alias_set (addr, get_varargs_alias_set ());
|
set_mem_alias_set (addr, get_varargs_alias_set ());
|
else
|
else
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
|
|
return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
|
return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
|
}
|
}
|
|
|
/* Emit insn to restore fpr REGNUM from offset OFFSET relative
|
/* Emit insn to restore fpr REGNUM from offset OFFSET relative
|
to register BASE. Return generated insn. */
|
to register BASE. Return generated insn. */
|
|
|
static rtx
|
static rtx
|
restore_fpr (rtx base, int offset, int regnum)
|
restore_fpr (rtx base, int offset, int regnum)
|
{
|
{
|
rtx addr;
|
rtx addr;
|
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
|
addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
|
|
return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
|
return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
|
}
|
}
|
|
|
/* Generate insn to save registers FIRST to LAST into
|
/* Generate insn to save registers FIRST to LAST into
|
the register save area located at offset OFFSET
|
the register save area located at offset OFFSET
|
relative to register BASE. */
|
relative to register BASE. */
|
|
|
static rtx
|
static rtx
|
save_gprs (rtx base, int offset, int first, int last)
|
save_gprs (rtx base, int offset, int first, int last)
|
{
|
{
|
rtx addr, insn, note;
|
rtx addr, insn, note;
|
int i;
|
int i;
|
|
|
addr = plus_constant (base, offset);
|
addr = plus_constant (base, offset);
|
addr = gen_rtx_MEM (Pmode, addr);
|
addr = gen_rtx_MEM (Pmode, addr);
|
|
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
|
|
/* Special-case single register. */
|
/* Special-case single register. */
|
if (first == last)
|
if (first == last)
|
{
|
{
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
insn = gen_movdi (addr, gen_rtx_REG (Pmode, first));
|
insn = gen_movdi (addr, gen_rtx_REG (Pmode, first));
|
else
|
else
|
insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
|
insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
return insn;
|
return insn;
|
}
|
}
|
|
|
|
|
insn = gen_store_multiple (addr,
|
insn = gen_store_multiple (addr,
|
gen_rtx_REG (Pmode, first),
|
gen_rtx_REG (Pmode, first),
|
GEN_INT (last - first + 1));
|
GEN_INT (last - first + 1));
|
|
|
if (first <= 6 && current_function_stdarg)
|
if (first <= 6 && current_function_stdarg)
|
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
|
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
|
{
|
{
|
rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0);
|
rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0);
|
|
|
if (first + i <= 6)
|
if (first + i <= 6)
|
set_mem_alias_set (mem, get_varargs_alias_set ());
|
set_mem_alias_set (mem, get_varargs_alias_set ());
|
}
|
}
|
|
|
/* We need to set the FRAME_RELATED flag on all SETs
|
/* We need to set the FRAME_RELATED flag on all SETs
|
inside the store-multiple pattern.
|
inside the store-multiple pattern.
|
|
|
However, we must not emit DWARF records for registers 2..5
|
However, we must not emit DWARF records for registers 2..5
|
if they are stored for use by variable arguments ...
|
if they are stored for use by variable arguments ...
|
|
|
??? Unfortunately, it is not enough to simply not the
|
??? Unfortunately, it is not enough to simply not the
|
FRAME_RELATED flags for those SETs, because the first SET
|
FRAME_RELATED flags for those SETs, because the first SET
|
of the PARALLEL is always treated as if it had the flag
|
of the PARALLEL is always treated as if it had the flag
|
set, even if it does not. Therefore we emit a new pattern
|
set, even if it does not. Therefore we emit a new pattern
|
without those registers as REG_FRAME_RELATED_EXPR note. */
|
without those registers as REG_FRAME_RELATED_EXPR note. */
|
|
|
if (first >= 6)
|
if (first >= 6)
|
{
|
{
|
rtx pat = PATTERN (insn);
|
rtx pat = PATTERN (insn);
|
|
|
for (i = 0; i < XVECLEN (pat, 0); i++)
|
for (i = 0; i < XVECLEN (pat, 0); i++)
|
if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
|
if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
|
RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
|
RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else if (last >= 6)
|
else if (last >= 6)
|
{
|
{
|
addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
|
addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
|
note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
|
note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
|
gen_rtx_REG (Pmode, 6),
|
gen_rtx_REG (Pmode, 6),
|
GEN_INT (last - 6 + 1));
|
GEN_INT (last - 6 + 1));
|
note = PATTERN (note);
|
note = PATTERN (note);
|
|
|
REG_NOTES (insn) =
|
REG_NOTES (insn) =
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
note, REG_NOTES (insn));
|
note, REG_NOTES (insn));
|
|
|
for (i = 0; i < XVECLEN (note, 0); i++)
|
for (i = 0; i < XVECLEN (note, 0); i++)
|
if (GET_CODE (XVECEXP (note, 0, i)) == SET)
|
if (GET_CODE (XVECEXP (note, 0, i)) == SET)
|
RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
|
RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
|
|
return insn;
|
return insn;
|
}
|
}
|
|
|
/* Generate insn to restore registers FIRST to LAST from
|
/* Generate insn to restore registers FIRST to LAST from
|
the register save area located at offset OFFSET
|
the register save area located at offset OFFSET
|
relative to register BASE. */
|
relative to register BASE. */
|
|
|
static rtx
|
static rtx
|
restore_gprs (rtx base, int offset, int first, int last)
|
restore_gprs (rtx base, int offset, int first, int last)
|
{
|
{
|
rtx addr, insn;
|
rtx addr, insn;
|
|
|
addr = plus_constant (base, offset);
|
addr = plus_constant (base, offset);
|
addr = gen_rtx_MEM (Pmode, addr);
|
addr = gen_rtx_MEM (Pmode, addr);
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
|
|
/* Special-case single register. */
|
/* Special-case single register. */
|
if (first == last)
|
if (first == last)
|
{
|
{
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
insn = gen_movdi (gen_rtx_REG (Pmode, first), addr);
|
insn = gen_movdi (gen_rtx_REG (Pmode, first), addr);
|
else
|
else
|
insn = gen_movsi (gen_rtx_REG (Pmode, first), addr);
|
insn = gen_movsi (gen_rtx_REG (Pmode, first), addr);
|
|
|
return insn;
|
return insn;
|
}
|
}
|
|
|
insn = gen_load_multiple (gen_rtx_REG (Pmode, first),
|
insn = gen_load_multiple (gen_rtx_REG (Pmode, first),
|
addr,
|
addr,
|
GEN_INT (last - first + 1));
|
GEN_INT (last - first + 1));
|
return insn;
|
return insn;
|
}
|
}
|
|
|
/* Return insn sequence to load the GOT register. */
|
/* Return insn sequence to load the GOT register. */
|
|
|
static GTY(()) rtx got_symbol;
|
static GTY(()) rtx got_symbol;
|
rtx
|
rtx
|
s390_load_got (void)
|
s390_load_got (void)
|
{
|
{
|
rtx insns;
|
rtx insns;
|
|
|
if (!got_symbol)
|
if (!got_symbol)
|
{
|
{
|
got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
|
SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
|
}
|
}
|
|
|
start_sequence ();
|
start_sequence ();
|
|
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
emit_move_insn (pic_offset_table_rtx, got_symbol);
|
emit_move_insn (pic_offset_table_rtx, got_symbol);
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx offset;
|
rtx offset;
|
|
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol),
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol),
|
UNSPEC_LTREL_OFFSET);
|
UNSPEC_LTREL_OFFSET);
|
offset = gen_rtx_CONST (Pmode, offset);
|
offset = gen_rtx_CONST (Pmode, offset);
|
offset = force_const_mem (Pmode, offset);
|
offset = force_const_mem (Pmode, offset);
|
|
|
emit_move_insn (pic_offset_table_rtx, offset);
|
emit_move_insn (pic_offset_table_rtx, offset);
|
|
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
|
offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
|
UNSPEC_LTREL_BASE);
|
UNSPEC_LTREL_BASE);
|
offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
|
offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
|
|
|
emit_move_insn (pic_offset_table_rtx, offset);
|
emit_move_insn (pic_offset_table_rtx, offset);
|
}
|
}
|
|
|
insns = get_insns ();
|
insns = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
return insns;
|
return insns;
|
}
|
}
|
|
|
/* Expand the prologue into a bunch of separate insns. */
|
/* Expand the prologue into a bunch of separate insns. */
|
|
|
void
|
void
|
s390_emit_prologue (void)
|
s390_emit_prologue (void)
|
{
|
{
|
rtx insn, addr;
|
rtx insn, addr;
|
rtx temp_reg;
|
rtx temp_reg;
|
int i;
|
int i;
|
int offset;
|
int offset;
|
int next_fpr = 0;
|
int next_fpr = 0;
|
|
|
/* Complete frame layout. */
|
/* Complete frame layout. */
|
|
|
s390_update_frame_layout ();
|
s390_update_frame_layout ();
|
|
|
/* Annotate all constant pool references to let the scheduler know
|
/* Annotate all constant pool references to let the scheduler know
|
they implicitly use the base register. */
|
they implicitly use the base register. */
|
|
|
push_topmost_sequence ();
|
push_topmost_sequence ();
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
if (INSN_P (insn))
|
if (INSN_P (insn))
|
annotate_constant_pool_refs (&PATTERN (insn));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
|
|
pop_topmost_sequence ();
|
pop_topmost_sequence ();
|
|
|
/* Choose best register to use for temp use within prologue.
|
/* Choose best register to use for temp use within prologue.
|
See below for why TPF must use the register 1. */
|
See below for why TPF must use the register 1. */
|
|
|
if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
|
if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
|
&& !current_function_is_leaf
|
&& !current_function_is_leaf
|
&& !TARGET_TPF_PROFILING)
|
&& !TARGET_TPF_PROFILING)
|
temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
else
|
else
|
temp_reg = gen_rtx_REG (Pmode, 1);
|
temp_reg = gen_rtx_REG (Pmode, 1);
|
|
|
/* Save call saved gprs. */
|
/* Save call saved gprs. */
|
if (cfun_frame_layout.first_save_gpr != -1)
|
if (cfun_frame_layout.first_save_gpr != -1)
|
{
|
{
|
insn = save_gprs (stack_pointer_rtx,
|
insn = save_gprs (stack_pointer_rtx,
|
cfun_frame_layout.gprs_offset +
|
cfun_frame_layout.gprs_offset +
|
UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr
|
UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr
|
- cfun_frame_layout.first_save_gpr_slot),
|
- cfun_frame_layout.first_save_gpr_slot),
|
cfun_frame_layout.first_save_gpr,
|
cfun_frame_layout.first_save_gpr,
|
cfun_frame_layout.last_save_gpr);
|
cfun_frame_layout.last_save_gpr);
|
emit_insn (insn);
|
emit_insn (insn);
|
}
|
}
|
|
|
/* Dummy insn to mark literal pool slot. */
|
/* Dummy insn to mark literal pool slot. */
|
|
|
if (cfun->machine->base_reg)
|
if (cfun->machine->base_reg)
|
emit_insn (gen_main_pool (cfun->machine->base_reg));
|
emit_insn (gen_main_pool (cfun->machine->base_reg));
|
|
|
offset = cfun_frame_layout.f0_offset;
|
offset = cfun_frame_layout.f0_offset;
|
|
|
/* Save f0 and f2. */
|
/* Save f0 and f2. */
|
for (i = 0; i < 2; i++)
|
for (i = 0; i < 2; i++)
|
{
|
{
|
if (cfun_fpr_bit_p (i))
|
if (cfun_fpr_bit_p (i))
|
{
|
{
|
save_fpr (stack_pointer_rtx, offset, i + 16);
|
save_fpr (stack_pointer_rtx, offset, i + 16);
|
offset += 8;
|
offset += 8;
|
}
|
}
|
else if (!TARGET_PACKED_STACK)
|
else if (!TARGET_PACKED_STACK)
|
offset += 8;
|
offset += 8;
|
}
|
}
|
|
|
/* Save f4 and f6. */
|
/* Save f4 and f6. */
|
offset = cfun_frame_layout.f4_offset;
|
offset = cfun_frame_layout.f4_offset;
|
for (i = 2; i < 4; i++)
|
for (i = 2; i < 4; i++)
|
{
|
{
|
if (cfun_fpr_bit_p (i))
|
if (cfun_fpr_bit_p (i))
|
{
|
{
|
insn = save_fpr (stack_pointer_rtx, offset, i + 16);
|
insn = save_fpr (stack_pointer_rtx, offset, i + 16);
|
offset += 8;
|
offset += 8;
|
|
|
/* If f4 and f6 are call clobbered they are saved due to stdargs and
|
/* If f4 and f6 are call clobbered they are saved due to stdargs and
|
therefore are not frame related. */
|
therefore are not frame related. */
|
if (!call_really_used_regs[i + 16])
|
if (!call_really_used_regs[i + 16])
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else if (!TARGET_PACKED_STACK)
|
else if (!TARGET_PACKED_STACK)
|
offset += 8;
|
offset += 8;
|
}
|
}
|
|
|
if (TARGET_PACKED_STACK
|
if (TARGET_PACKED_STACK
|
&& cfun_save_high_fprs_p
|
&& cfun_save_high_fprs_p
|
&& cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
|
&& cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
|
{
|
{
|
offset = (cfun_frame_layout.f8_offset
|
offset = (cfun_frame_layout.f8_offset
|
+ (cfun_frame_layout.high_fprs - 1) * 8);
|
+ (cfun_frame_layout.high_fprs - 1) * 8);
|
|
|
for (i = 15; i > 7 && offset >= 0; i--)
|
for (i = 15; i > 7 && offset >= 0; i--)
|
if (cfun_fpr_bit_p (i))
|
if (cfun_fpr_bit_p (i))
|
{
|
{
|
insn = save_fpr (stack_pointer_rtx, offset, i + 16);
|
insn = save_fpr (stack_pointer_rtx, offset, i + 16);
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
offset -= 8;
|
offset -= 8;
|
}
|
}
|
if (offset >= cfun_frame_layout.f8_offset)
|
if (offset >= cfun_frame_layout.f8_offset)
|
next_fpr = i + 16;
|
next_fpr = i + 16;
|
}
|
}
|
|
|
if (!TARGET_PACKED_STACK)
|
if (!TARGET_PACKED_STACK)
|
next_fpr = cfun_save_high_fprs_p ? 31 : 0;
|
next_fpr = cfun_save_high_fprs_p ? 31 : 0;
|
|
|
/* Decrement stack pointer. */
|
/* Decrement stack pointer. */
|
|
|
if (cfun_frame_layout.frame_size > 0)
|
if (cfun_frame_layout.frame_size > 0)
|
{
|
{
|
rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
|
rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
|
|
|
if (s390_stack_size)
|
if (s390_stack_size)
|
{
|
{
|
HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
|
HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
|
& ~(s390_stack_guard - 1));
|
& ~(s390_stack_guard - 1));
|
rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
|
rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
|
GEN_INT (stack_check_mask));
|
GEN_INT (stack_check_mask));
|
|
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
gen_cmpdi (t, const0_rtx);
|
gen_cmpdi (t, const0_rtx);
|
else
|
else
|
gen_cmpsi (t, const0_rtx);
|
gen_cmpsi (t, const0_rtx);
|
|
|
emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode,
|
emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode,
|
gen_rtx_REG (CCmode,
|
gen_rtx_REG (CCmode,
|
CC_REGNUM),
|
CC_REGNUM),
|
const0_rtx),
|
const0_rtx),
|
const0_rtx));
|
const0_rtx));
|
}
|
}
|
|
|
if (s390_warn_framesize > 0
|
if (s390_warn_framesize > 0
|
&& cfun_frame_layout.frame_size >= s390_warn_framesize)
|
&& cfun_frame_layout.frame_size >= s390_warn_framesize)
|
warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes",
|
warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes",
|
current_function_name (), cfun_frame_layout.frame_size);
|
current_function_name (), cfun_frame_layout.frame_size);
|
|
|
if (s390_warn_dynamicstack_p && cfun->calls_alloca)
|
if (s390_warn_dynamicstack_p && cfun->calls_alloca)
|
warning (0, "%qs uses dynamic stack allocation", current_function_name ());
|
warning (0, "%qs uses dynamic stack allocation", current_function_name ());
|
|
|
/* Save incoming stack pointer into temp reg. */
|
/* Save incoming stack pointer into temp reg. */
|
if (TARGET_BACKCHAIN || next_fpr)
|
if (TARGET_BACKCHAIN || next_fpr)
|
insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
|
insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
|
|
|
/* Subtract frame size from stack pointer. */
|
/* Subtract frame size from stack pointer. */
|
|
|
if (DISP_IN_RANGE (INTVAL (frame_off)))
|
if (DISP_IN_RANGE (INTVAL (frame_off)))
|
{
|
{
|
insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
frame_off));
|
frame_off));
|
insn = emit_insn (insn);
|
insn = emit_insn (insn);
|
}
|
}
|
else
|
else
|
{
|
{
|
if (!CONST_OK_FOR_K (INTVAL (frame_off)))
|
if (!CONST_OK_FOR_K (INTVAL (frame_off)))
|
frame_off = force_const_mem (Pmode, frame_off);
|
frame_off = force_const_mem (Pmode, frame_off);
|
|
|
insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
|
insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
}
|
}
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
REG_NOTES (insn) =
|
REG_NOTES (insn) =
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
GEN_INT (-cfun_frame_layout.frame_size))),
|
GEN_INT (-cfun_frame_layout.frame_size))),
|
REG_NOTES (insn));
|
REG_NOTES (insn));
|
|
|
/* Set backchain. */
|
/* Set backchain. */
|
|
|
if (TARGET_BACKCHAIN)
|
if (TARGET_BACKCHAIN)
|
{
|
{
|
if (cfun_frame_layout.backchain_offset)
|
if (cfun_frame_layout.backchain_offset)
|
addr = gen_rtx_MEM (Pmode,
|
addr = gen_rtx_MEM (Pmode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
cfun_frame_layout.backchain_offset));
|
cfun_frame_layout.backchain_offset));
|
else
|
else
|
addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
|
addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
insn = emit_insn (gen_move_insn (addr, temp_reg));
|
insn = emit_insn (gen_move_insn (addr, temp_reg));
|
}
|
}
|
|
|
/* If we support asynchronous exceptions (e.g. for Java),
|
/* If we support asynchronous exceptions (e.g. for Java),
|
we need to make sure the backchain pointer is set up
|
we need to make sure the backchain pointer is set up
|
before any possibly trapping memory access. */
|
before any possibly trapping memory access. */
|
|
|
if (TARGET_BACKCHAIN && flag_non_call_exceptions)
|
if (TARGET_BACKCHAIN && flag_non_call_exceptions)
|
{
|
{
|
addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
|
addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
|
emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
|
}
|
}
|
}
|
}
|
|
|
/* Save fprs 8 - 15 (64 bit ABI). */
|
/* Save fprs 8 - 15 (64 bit ABI). */
|
|
|
if (cfun_save_high_fprs_p && next_fpr)
|
if (cfun_save_high_fprs_p && next_fpr)
|
{
|
{
|
insn = emit_insn (gen_add2_insn (temp_reg,
|
insn = emit_insn (gen_add2_insn (temp_reg,
|
GEN_INT (cfun_frame_layout.f8_offset)));
|
GEN_INT (cfun_frame_layout.f8_offset)));
|
|
|
offset = 0;
|
offset = 0;
|
|
|
for (i = 24; i <= next_fpr; i++)
|
for (i = 24; i <= next_fpr; i++)
|
if (cfun_fpr_bit_p (i - 16))
|
if (cfun_fpr_bit_p (i - 16))
|
{
|
{
|
rtx addr = plus_constant (stack_pointer_rtx,
|
rtx addr = plus_constant (stack_pointer_rtx,
|
cfun_frame_layout.frame_size
|
cfun_frame_layout.frame_size
|
+ cfun_frame_layout.f8_offset
|
+ cfun_frame_layout.f8_offset
|
+ offset);
|
+ offset);
|
|
|
insn = save_fpr (temp_reg, offset, i);
|
insn = save_fpr (temp_reg, offset, i);
|
offset += 8;
|
offset += 8;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
REG_NOTES (insn) =
|
REG_NOTES (insn) =
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
gen_rtx_SET (VOIDmode,
|
gen_rtx_SET (VOIDmode,
|
gen_rtx_MEM (DFmode, addr),
|
gen_rtx_MEM (DFmode, addr),
|
gen_rtx_REG (DFmode, i)),
|
gen_rtx_REG (DFmode, i)),
|
REG_NOTES (insn));
|
REG_NOTES (insn));
|
}
|
}
|
}
|
}
|
|
|
/* Set frame pointer, if needed. */
|
/* Set frame pointer, if needed. */
|
|
|
if (frame_pointer_needed)
|
if (frame_pointer_needed)
|
{
|
{
|
insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
|
|
/* Set up got pointer, if needed. */
|
/* Set up got pointer, if needed. */
|
|
|
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
|
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
|
{
|
{
|
rtx insns = s390_load_got ();
|
rtx insns = s390_load_got ();
|
|
|
for (insn = insns; insn; insn = NEXT_INSN (insn))
|
for (insn = insns; insn; insn = NEXT_INSN (insn))
|
{
|
{
|
annotate_constant_pool_refs (&PATTERN (insn));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
|
|
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
|
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
|
REG_NOTES (insn));
|
REG_NOTES (insn));
|
}
|
}
|
|
|
emit_insn (insns);
|
emit_insn (insns);
|
}
|
}
|
|
|
if (TARGET_TPF_PROFILING)
|
if (TARGET_TPF_PROFILING)
|
{
|
{
|
/* Generate a BAS instruction to serve as a function
|
/* Generate a BAS instruction to serve as a function
|
entry intercept to facilitate the use of tracing
|
entry intercept to facilitate the use of tracing
|
algorithms located at the branch target. */
|
algorithms located at the branch target. */
|
emit_insn (gen_prologue_tpf ());
|
emit_insn (gen_prologue_tpf ());
|
|
|
/* Emit a blockage here so that all code
|
/* Emit a blockage here so that all code
|
lies between the profiling mechanisms. */
|
lies between the profiling mechanisms. */
|
emit_insn (gen_blockage ());
|
emit_insn (gen_blockage ());
|
}
|
}
|
}
|
}
|
|
|
/* Expand the epilogue into a bunch of separate insns. */
|
/* Expand the epilogue into a bunch of separate insns. */
|
|
|
void
|
void
|
s390_emit_epilogue (bool sibcall)
|
s390_emit_epilogue (bool sibcall)
|
{
|
{
|
rtx frame_pointer, return_reg;
|
rtx frame_pointer, return_reg;
|
int area_bottom, area_top, offset = 0;
|
int area_bottom, area_top, offset = 0;
|
int next_offset;
|
int next_offset;
|
rtvec p;
|
rtvec p;
|
int i;
|
int i;
|
|
|
if (TARGET_TPF_PROFILING)
|
if (TARGET_TPF_PROFILING)
|
{
|
{
|
|
|
/* Generate a BAS instruction to serve as a function
|
/* Generate a BAS instruction to serve as a function
|
entry intercept to facilitate the use of tracing
|
entry intercept to facilitate the use of tracing
|
algorithms located at the branch target. */
|
algorithms located at the branch target. */
|
|
|
/* Emit a blockage here so that all code
|
/* Emit a blockage here so that all code
|
lies between the profiling mechanisms. */
|
lies between the profiling mechanisms. */
|
emit_insn (gen_blockage ());
|
emit_insn (gen_blockage ());
|
|
|
emit_insn (gen_epilogue_tpf ());
|
emit_insn (gen_epilogue_tpf ());
|
}
|
}
|
|
|
/* Check whether to use frame or stack pointer for restore. */
|
/* Check whether to use frame or stack pointer for restore. */
|
|
|
frame_pointer = (frame_pointer_needed
|
frame_pointer = (frame_pointer_needed
|
? hard_frame_pointer_rtx : stack_pointer_rtx);
|
? hard_frame_pointer_rtx : stack_pointer_rtx);
|
|
|
s390_frame_area (&area_bottom, &area_top);
|
s390_frame_area (&area_bottom, &area_top);
|
|
|
/* Check whether we can access the register save area.
|
/* Check whether we can access the register save area.
|
If not, increment the frame pointer as required. */
|
If not, increment the frame pointer as required. */
|
|
|
if (area_top <= area_bottom)
|
if (area_top <= area_bottom)
|
{
|
{
|
/* Nothing to restore. */
|
/* Nothing to restore. */
|
}
|
}
|
else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
|
else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
|
&& DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
|
&& DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
|
{
|
{
|
/* Area is in range. */
|
/* Area is in range. */
|
offset = cfun_frame_layout.frame_size;
|
offset = cfun_frame_layout.frame_size;
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx insn, frame_off;
|
rtx insn, frame_off;
|
|
|
offset = area_bottom < 0 ? -area_bottom : 0;
|
offset = area_bottom < 0 ? -area_bottom : 0;
|
frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
|
frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
|
|
|
if (DISP_IN_RANGE (INTVAL (frame_off)))
|
if (DISP_IN_RANGE (INTVAL (frame_off)))
|
{
|
{
|
insn = gen_rtx_SET (VOIDmode, frame_pointer,
|
insn = gen_rtx_SET (VOIDmode, frame_pointer,
|
gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
|
gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
|
insn = emit_insn (insn);
|
insn = emit_insn (insn);
|
}
|
}
|
else
|
else
|
{
|
{
|
if (!CONST_OK_FOR_K (INTVAL (frame_off)))
|
if (!CONST_OK_FOR_K (INTVAL (frame_off)))
|
frame_off = force_const_mem (Pmode, frame_off);
|
frame_off = force_const_mem (Pmode, frame_off);
|
|
|
insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
|
insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
annotate_constant_pool_refs (&PATTERN (insn));
|
}
|
}
|
}
|
}
|
|
|
/* Restore call saved fprs. */
|
/* Restore call saved fprs. */
|
|
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
if (cfun_save_high_fprs_p)
|
if (cfun_save_high_fprs_p)
|
{
|
{
|
next_offset = cfun_frame_layout.f8_offset;
|
next_offset = cfun_frame_layout.f8_offset;
|
for (i = 24; i < 32; i++)
|
for (i = 24; i < 32; i++)
|
{
|
{
|
if (cfun_fpr_bit_p (i - 16))
|
if (cfun_fpr_bit_p (i - 16))
|
{
|
{
|
restore_fpr (frame_pointer,
|
restore_fpr (frame_pointer,
|
offset + next_offset, i);
|
offset + next_offset, i);
|
next_offset += 8;
|
next_offset += 8;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
}
|
}
|
else
|
else
|
{
|
{
|
next_offset = cfun_frame_layout.f4_offset;
|
next_offset = cfun_frame_layout.f4_offset;
|
for (i = 18; i < 20; i++)
|
for (i = 18; i < 20; i++)
|
{
|
{
|
if (cfun_fpr_bit_p (i - 16))
|
if (cfun_fpr_bit_p (i - 16))
|
{
|
{
|
restore_fpr (frame_pointer,
|
restore_fpr (frame_pointer,
|
offset + next_offset, i);
|
offset + next_offset, i);
|
next_offset += 8;
|
next_offset += 8;
|
}
|
}
|
else if (!TARGET_PACKED_STACK)
|
else if (!TARGET_PACKED_STACK)
|
next_offset += 8;
|
next_offset += 8;
|
}
|
}
|
|
|
}
|
}
|
|
|
/* Return register. */
|
/* Return register. */
|
|
|
return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
|
|
/* Restore call saved gprs. */
|
/* Restore call saved gprs. */
|
|
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
{
|
{
|
rtx insn, addr;
|
rtx insn, addr;
|
int i;
|
int i;
|
|
|
/* Check for global register and save them
|
/* Check for global register and save them
|
to stack location from where they get restored. */
|
to stack location from where they get restored. */
|
|
|
for (i = cfun_frame_layout.first_restore_gpr;
|
for (i = cfun_frame_layout.first_restore_gpr;
|
i <= cfun_frame_layout.last_restore_gpr;
|
i <= cfun_frame_layout.last_restore_gpr;
|
i++)
|
i++)
|
{
|
{
|
/* These registers are special and need to be
|
/* These registers are special and need to be
|
restored in any case. */
|
restored in any case. */
|
if (i == STACK_POINTER_REGNUM
|
if (i == STACK_POINTER_REGNUM
|
|| i == RETURN_REGNUM
|
|| i == RETURN_REGNUM
|
|| i == BASE_REGNUM
|
|| i == BASE_REGNUM
|
|| (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
|
|| (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
|
continue;
|
continue;
|
|
|
if (global_regs[i])
|
if (global_regs[i])
|
{
|
{
|
addr = plus_constant (frame_pointer,
|
addr = plus_constant (frame_pointer,
|
offset + cfun_frame_layout.gprs_offset
|
offset + cfun_frame_layout.gprs_offset
|
+ (i - cfun_frame_layout.first_save_gpr_slot)
|
+ (i - cfun_frame_layout.first_save_gpr_slot)
|
* UNITS_PER_WORD);
|
* UNITS_PER_WORD);
|
addr = gen_rtx_MEM (Pmode, addr);
|
addr = gen_rtx_MEM (Pmode, addr);
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
emit_move_insn (addr, gen_rtx_REG (Pmode, i));
|
emit_move_insn (addr, gen_rtx_REG (Pmode, i));
|
}
|
}
|
}
|
}
|
|
|
if (! sibcall)
|
if (! sibcall)
|
{
|
{
|
/* Fetch return address from stack before load multiple,
|
/* Fetch return address from stack before load multiple,
|
this will do good for scheduling. */
|
this will do good for scheduling. */
|
|
|
if (cfun_frame_layout.save_return_addr_p
|
if (cfun_frame_layout.save_return_addr_p
|
|| (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
|
|| (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
|
&& cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
|
&& cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
|
{
|
{
|
int return_regnum = find_unused_clobbered_reg();
|
int return_regnum = find_unused_clobbered_reg();
|
if (!return_regnum)
|
if (!return_regnum)
|
return_regnum = 4;
|
return_regnum = 4;
|
return_reg = gen_rtx_REG (Pmode, return_regnum);
|
return_reg = gen_rtx_REG (Pmode, return_regnum);
|
|
|
addr = plus_constant (frame_pointer,
|
addr = plus_constant (frame_pointer,
|
offset + cfun_frame_layout.gprs_offset
|
offset + cfun_frame_layout.gprs_offset
|
+ (RETURN_REGNUM
|
+ (RETURN_REGNUM
|
- cfun_frame_layout.first_save_gpr_slot)
|
- cfun_frame_layout.first_save_gpr_slot)
|
* UNITS_PER_WORD);
|
* UNITS_PER_WORD);
|
addr = gen_rtx_MEM (Pmode, addr);
|
addr = gen_rtx_MEM (Pmode, addr);
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
set_mem_alias_set (addr, get_frame_alias_set ());
|
emit_move_insn (return_reg, addr);
|
emit_move_insn (return_reg, addr);
|
}
|
}
|
}
|
}
|
|
|
insn = restore_gprs (frame_pointer,
|
insn = restore_gprs (frame_pointer,
|
offset + cfun_frame_layout.gprs_offset
|
offset + cfun_frame_layout.gprs_offset
|
+ (cfun_frame_layout.first_restore_gpr
|
+ (cfun_frame_layout.first_restore_gpr
|
- cfun_frame_layout.first_save_gpr_slot)
|
- cfun_frame_layout.first_save_gpr_slot)
|
* UNITS_PER_WORD,
|
* UNITS_PER_WORD,
|
cfun_frame_layout.first_restore_gpr,
|
cfun_frame_layout.first_restore_gpr,
|
cfun_frame_layout.last_restore_gpr);
|
cfun_frame_layout.last_restore_gpr);
|
emit_insn (insn);
|
emit_insn (insn);
|
}
|
}
|
|
|
if (! sibcall)
|
if (! sibcall)
|
{
|
{
|
|
|
/* Return to caller. */
|
/* Return to caller. */
|
|
|
p = rtvec_alloc (2);
|
p = rtvec_alloc (2);
|
|
|
RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
|
RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
|
RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
|
RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
|
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Return the size in bytes of a function argument of
|
/* Return the size in bytes of a function argument of
|
type TYPE and/or mode MODE. At least one of TYPE or
|
type TYPE and/or mode MODE. At least one of TYPE or
|
MODE must be specified. */
|
MODE must be specified. */
|
|
|
static int
|
static int
|
s390_function_arg_size (enum machine_mode mode, tree type)
|
s390_function_arg_size (enum machine_mode mode, tree type)
|
{
|
{
|
if (type)
|
if (type)
|
return int_size_in_bytes (type);
|
return int_size_in_bytes (type);
|
|
|
/* No type info available for some library calls ... */
|
/* No type info available for some library calls ... */
|
if (mode != BLKmode)
|
if (mode != BLKmode)
|
return GET_MODE_SIZE (mode);
|
return GET_MODE_SIZE (mode);
|
|
|
/* If we have neither type nor mode, abort */
|
/* If we have neither type nor mode, abort */
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* Return true if a function argument of type TYPE and mode MODE
|
/* Return true if a function argument of type TYPE and mode MODE
|
is to be passed in a floating-point register, if available. */
|
is to be passed in a floating-point register, if available. */
|
|
|
static bool
|
static bool
|
s390_function_arg_float (enum machine_mode mode, tree type)
|
s390_function_arg_float (enum machine_mode mode, tree type)
|
{
|
{
|
int size = s390_function_arg_size (mode, type);
|
int size = s390_function_arg_size (mode, type);
|
if (size > 8)
|
if (size > 8)
|
return false;
|
return false;
|
|
|
/* Soft-float changes the ABI: no floating-point registers are used. */
|
/* Soft-float changes the ABI: no floating-point registers are used. */
|
if (TARGET_SOFT_FLOAT)
|
if (TARGET_SOFT_FLOAT)
|
return false;
|
return false;
|
|
|
/* No type info available for some library calls ... */
|
/* No type info available for some library calls ... */
|
if (!type)
|
if (!type)
|
return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode;
|
return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode;
|
|
|
/* The ABI says that record types with a single member are treated
|
/* The ABI says that record types with a single member are treated
|
just like that member would be. */
|
just like that member would be. */
|
while (TREE_CODE (type) == RECORD_TYPE)
|
while (TREE_CODE (type) == RECORD_TYPE)
|
{
|
{
|
tree field, single = NULL_TREE;
|
tree field, single = NULL_TREE;
|
|
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) != FIELD_DECL)
|
if (TREE_CODE (field) != FIELD_DECL)
|
continue;
|
continue;
|
|
|
if (single == NULL_TREE)
|
if (single == NULL_TREE)
|
single = TREE_TYPE (field);
|
single = TREE_TYPE (field);
|
else
|
else
|
return false;
|
return false;
|
}
|
}
|
|
|
if (single == NULL_TREE)
|
if (single == NULL_TREE)
|
return false;
|
return false;
|
else
|
else
|
type = single;
|
type = single;
|
}
|
}
|
|
|
return TREE_CODE (type) == REAL_TYPE;
|
return TREE_CODE (type) == REAL_TYPE;
|
}
|
}
|
|
|
/* Return true if a function argument of type TYPE and mode MODE
|
/* Return true if a function argument of type TYPE and mode MODE
|
is to be passed in an integer register, or a pair of integer
|
is to be passed in an integer register, or a pair of integer
|
registers, if available. */
|
registers, if available. */
|
|
|
static bool
|
static bool
|
s390_function_arg_integer (enum machine_mode mode, tree type)
|
s390_function_arg_integer (enum machine_mode mode, tree type)
|
{
|
{
|
int size = s390_function_arg_size (mode, type);
|
int size = s390_function_arg_size (mode, type);
|
if (size > 8)
|
if (size > 8)
|
return false;
|
return false;
|
|
|
/* No type info available for some library calls ... */
|
/* No type info available for some library calls ... */
|
if (!type)
|
if (!type)
|
return GET_MODE_CLASS (mode) == MODE_INT
|
return GET_MODE_CLASS (mode) == MODE_INT
|
|| (TARGET_SOFT_FLOAT && SCALAR_FLOAT_MODE_P (mode));
|
|| (TARGET_SOFT_FLOAT && SCALAR_FLOAT_MODE_P (mode));
|
|
|
/* We accept small integral (and similar) types. */
|
/* We accept small integral (and similar) types. */
|
if (INTEGRAL_TYPE_P (type)
|
if (INTEGRAL_TYPE_P (type)
|
|| POINTER_TYPE_P (type)
|
|| POINTER_TYPE_P (type)
|
|| TREE_CODE (type) == OFFSET_TYPE
|
|| TREE_CODE (type) == OFFSET_TYPE
|
|| (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE))
|
|| (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE))
|
return true;
|
return true;
|
|
|
/* We also accept structs of size 1, 2, 4, 8 that are not
|
/* We also accept structs of size 1, 2, 4, 8 that are not
|
passed in floating-point registers. */
|
passed in floating-point registers. */
|
if (AGGREGATE_TYPE_P (type)
|
if (AGGREGATE_TYPE_P (type)
|
&& exact_log2 (size) >= 0
|
&& exact_log2 (size) >= 0
|
&& !s390_function_arg_float (mode, type))
|
&& !s390_function_arg_float (mode, type))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Return 1 if a function argument of type TYPE and mode MODE
|
/* Return 1 if a function argument of type TYPE and mode MODE
|
is to be passed by reference. The ABI specifies that only
|
is to be passed by reference. The ABI specifies that only
|
structures of size 1, 2, 4, or 8 bytes are passed by value,
|
structures of size 1, 2, 4, or 8 bytes are passed by value,
|
all other structures (and complex numbers) are passed by
|
all other structures (and complex numbers) are passed by
|
reference. */
|
reference. */
|
|
|
static bool
|
static bool
|
s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
|
s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
|
enum machine_mode mode, tree type,
|
enum machine_mode mode, tree type,
|
bool named ATTRIBUTE_UNUSED)
|
bool named ATTRIBUTE_UNUSED)
|
{
|
{
|
int size = s390_function_arg_size (mode, type);
|
int size = s390_function_arg_size (mode, type);
|
if (size > 8)
|
if (size > 8)
|
return true;
|
return true;
|
|
|
if (type)
|
if (type)
|
{
|
{
|
if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
|
if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
|
return 1;
|
return 1;
|
|
|
if (TREE_CODE (type) == COMPLEX_TYPE
|
if (TREE_CODE (type) == COMPLEX_TYPE
|
|| TREE_CODE (type) == VECTOR_TYPE)
|
|| TREE_CODE (type) == VECTOR_TYPE)
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Update the data in CUM to advance over an argument of mode MODE and
|
/* Update the data in CUM to advance over an argument of mode MODE and
|
data type TYPE. (TYPE is null for libcalls where that information
|
data type TYPE. (TYPE is null for libcalls where that information
|
may not be available.). The boolean NAMED specifies whether the
|
may not be available.). The boolean NAMED specifies whether the
|
argument is a named argument (as opposed to an unnamed argument
|
argument is a named argument (as opposed to an unnamed argument
|
matching an ellipsis). */
|
matching an ellipsis). */
|
|
|
void
|
void
|
s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
|
s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
|
tree type, int named ATTRIBUTE_UNUSED)
|
tree type, int named ATTRIBUTE_UNUSED)
|
{
|
{
|
if (s390_function_arg_float (mode, type))
|
if (s390_function_arg_float (mode, type))
|
{
|
{
|
cum->fprs += 1;
|
cum->fprs += 1;
|
}
|
}
|
else if (s390_function_arg_integer (mode, type))
|
else if (s390_function_arg_integer (mode, type))
|
{
|
{
|
int size = s390_function_arg_size (mode, type);
|
int size = s390_function_arg_size (mode, type);
|
cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
|
cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
|
}
|
}
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* Define where to put the arguments to a function.
|
/* Define where to put the arguments to a function.
|
Value is zero to push the argument on the stack,
|
Value is zero to push the argument on the stack,
|
or a hard register in which to store the argument.
|
or a hard register in which to store the argument.
|
|
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
TYPE is the data type of the argument (as a tree).
|
TYPE is the data type of the argument (as a tree).
|
This is null for libcalls where that information may
|
This is null for libcalls where that information may
|
not be available.
|
not be available.
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
the preceding args and about the function being called.
|
the preceding args and about the function being called.
|
NAMED is nonzero if this argument is a named parameter
|
NAMED is nonzero if this argument is a named parameter
|
(otherwise it is an extra parameter matching an ellipsis).
|
(otherwise it is an extra parameter matching an ellipsis).
|
|
|
On S/390, we use general purpose registers 2 through 6 to
|
On S/390, we use general purpose registers 2 through 6 to
|
pass integer, pointer, and certain structure arguments, and
|
pass integer, pointer, and certain structure arguments, and
|
floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
|
floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
|
to pass floating point arguments. All remaining arguments
|
to pass floating point arguments. All remaining arguments
|
are pushed to the stack. */
|
are pushed to the stack. */
|
|
|
rtx
|
rtx
|
s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
|
s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
|
int named ATTRIBUTE_UNUSED)
|
int named ATTRIBUTE_UNUSED)
|
{
|
{
|
if (s390_function_arg_float (mode, type))
|
if (s390_function_arg_float (mode, type))
|
{
|
{
|
if (cum->fprs + 1 > FP_ARG_NUM_REG)
|
if (cum->fprs + 1 > FP_ARG_NUM_REG)
|
return 0;
|
return 0;
|
else
|
else
|
return gen_rtx_REG (mode, cum->fprs + 16);
|
return gen_rtx_REG (mode, cum->fprs + 16);
|
}
|
}
|
else if (s390_function_arg_integer (mode, type))
|
else if (s390_function_arg_integer (mode, type))
|
{
|
{
|
int size = s390_function_arg_size (mode, type);
|
int size = s390_function_arg_size (mode, type);
|
int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
|
int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
|
|
|
if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
|
if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
|
return 0;
|
return 0;
|
else
|
else
|
return gen_rtx_REG (mode, cum->gprs + 2);
|
return gen_rtx_REG (mode, cum->gprs + 2);
|
}
|
}
|
|
|
/* After the real arguments, expand_call calls us once again
|
/* After the real arguments, expand_call calls us once again
|
with a void_type_node type. Whatever we return here is
|
with a void_type_node type. Whatever we return here is
|
passed as operand 2 to the call expanders.
|
passed as operand 2 to the call expanders.
|
|
|
We don't need this feature ... */
|
We don't need this feature ... */
|
else if (type == void_type_node)
|
else if (type == void_type_node)
|
return const0_rtx;
|
return const0_rtx;
|
|
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* Return true if return values of type TYPE should be returned
|
/* Return true if return values of type TYPE should be returned
|
in a memory buffer whose address is passed by the caller as
|
in a memory buffer whose address is passed by the caller as
|
hidden first argument. */
|
hidden first argument. */
|
|
|
static bool
|
static bool
|
s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED)
|
s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED)
|
{
|
{
|
/* We accept small integral (and similar) types. */
|
/* We accept small integral (and similar) types. */
|
if (INTEGRAL_TYPE_P (type)
|
if (INTEGRAL_TYPE_P (type)
|
|| POINTER_TYPE_P (type)
|
|| POINTER_TYPE_P (type)
|
|| TREE_CODE (type) == OFFSET_TYPE
|
|| TREE_CODE (type) == OFFSET_TYPE
|
|| TREE_CODE (type) == REAL_TYPE)
|
|| TREE_CODE (type) == REAL_TYPE)
|
return int_size_in_bytes (type) > 8;
|
return int_size_in_bytes (type) > 8;
|
|
|
/* Aggregates and similar constructs are always returned
|
/* Aggregates and similar constructs are always returned
|
in memory. */
|
in memory. */
|
if (AGGREGATE_TYPE_P (type)
|
if (AGGREGATE_TYPE_P (type)
|
|| TREE_CODE (type) == COMPLEX_TYPE
|
|| TREE_CODE (type) == COMPLEX_TYPE
|
|| TREE_CODE (type) == VECTOR_TYPE)
|
|| TREE_CODE (type) == VECTOR_TYPE)
|
return true;
|
return true;
|
|
|
/* ??? We get called on all sorts of random stuff from
|
/* ??? We get called on all sorts of random stuff from
|
aggregate_value_p. We can't abort, but it's not clear
|
aggregate_value_p. We can't abort, but it's not clear
|
what's safe to return. Pretend it's a struct I guess. */
|
what's safe to return. Pretend it's a struct I guess. */
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Define where to return a (scalar) value of type TYPE.
|
/* Define where to return a (scalar) value of type TYPE.
|
If TYPE is null, define where to return a (scalar)
|
If TYPE is null, define where to return a (scalar)
|
value of mode MODE from a libcall. */
|
value of mode MODE from a libcall. */
|
|
|
rtx
|
rtx
|
s390_function_value (tree type, enum machine_mode mode)
|
s390_function_value (tree type, enum machine_mode mode)
|
{
|
{
|
if (type)
|
if (type)
|
{
|
{
|
int unsignedp = TYPE_UNSIGNED (type);
|
int unsignedp = TYPE_UNSIGNED (type);
|
mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
|
mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
|
}
|
}
|
|
|
gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode));
|
gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode));
|
gcc_assert (GET_MODE_SIZE (mode) <= 8);
|
gcc_assert (GET_MODE_SIZE (mode) <= 8);
|
|
|
if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode))
|
if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode))
|
return gen_rtx_REG (mode, 16);
|
return gen_rtx_REG (mode, 16);
|
else
|
else
|
return gen_rtx_REG (mode, 2);
|
return gen_rtx_REG (mode, 2);
|
}
|
}
|
|
|
|
|
/* Create and return the va_list datatype.
|
/* Create and return the va_list datatype.
|
|
|
On S/390, va_list is an array type equivalent to
|
On S/390, va_list is an array type equivalent to
|
|
|
typedef struct __va_list_tag
|
typedef struct __va_list_tag
|
{
|
{
|
long __gpr;
|
long __gpr;
|
long __fpr;
|
long __fpr;
|
void *__overflow_arg_area;
|
void *__overflow_arg_area;
|
void *__reg_save_area;
|
void *__reg_save_area;
|
} va_list[1];
|
} va_list[1];
|
|
|
where __gpr and __fpr hold the number of general purpose
|
where __gpr and __fpr hold the number of general purpose
|
or floating point arguments used up to now, respectively,
|
or floating point arguments used up to now, respectively,
|
__overflow_arg_area points to the stack location of the
|
__overflow_arg_area points to the stack location of the
|
next argument passed on the stack, and __reg_save_area
|
next argument passed on the stack, and __reg_save_area
|
always points to the start of the register area in the
|
always points to the start of the register area in the
|
call frame of the current function. The function prologue
|
call frame of the current function. The function prologue
|
saves all registers used for argument passing into this
|
saves all registers used for argument passing into this
|
area if the function uses variable arguments. */
|
area if the function uses variable arguments. */
|
|
|
static tree
|
static tree
|
s390_build_builtin_va_list (void)
|
s390_build_builtin_va_list (void)
|
{
|
{
|
tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
|
tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
|
|
|
record = lang_hooks.types.make_type (RECORD_TYPE);
|
record = lang_hooks.types.make_type (RECORD_TYPE);
|
|
|
type_decl =
|
type_decl =
|
build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
|
build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
|
|
|
f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
|
f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
|
long_integer_type_node);
|
long_integer_type_node);
|
f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
|
f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
|
long_integer_type_node);
|
long_integer_type_node);
|
f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
|
f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
|
ptr_type_node);
|
ptr_type_node);
|
f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
|
f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
|
ptr_type_node);
|
ptr_type_node);
|
|
|
va_list_gpr_counter_field = f_gpr;
|
va_list_gpr_counter_field = f_gpr;
|
va_list_fpr_counter_field = f_fpr;
|
va_list_fpr_counter_field = f_fpr;
|
|
|
DECL_FIELD_CONTEXT (f_gpr) = record;
|
DECL_FIELD_CONTEXT (f_gpr) = record;
|
DECL_FIELD_CONTEXT (f_fpr) = record;
|
DECL_FIELD_CONTEXT (f_fpr) = record;
|
DECL_FIELD_CONTEXT (f_ovf) = record;
|
DECL_FIELD_CONTEXT (f_ovf) = record;
|
DECL_FIELD_CONTEXT (f_sav) = record;
|
DECL_FIELD_CONTEXT (f_sav) = record;
|
|
|
TREE_CHAIN (record) = type_decl;
|
TREE_CHAIN (record) = type_decl;
|
TYPE_NAME (record) = type_decl;
|
TYPE_NAME (record) = type_decl;
|
TYPE_FIELDS (record) = f_gpr;
|
TYPE_FIELDS (record) = f_gpr;
|
TREE_CHAIN (f_gpr) = f_fpr;
|
TREE_CHAIN (f_gpr) = f_fpr;
|
TREE_CHAIN (f_fpr) = f_ovf;
|
TREE_CHAIN (f_fpr) = f_ovf;
|
TREE_CHAIN (f_ovf) = f_sav;
|
TREE_CHAIN (f_ovf) = f_sav;
|
|
|
layout_type (record);
|
layout_type (record);
|
|
|
/* The correct type is an array type of one element. */
|
/* The correct type is an array type of one element. */
|
return build_array_type (record, build_index_type (size_zero_node));
|
return build_array_type (record, build_index_type (size_zero_node));
|
}
|
}
|
|
|
/* Implement va_start by filling the va_list structure VALIST.
|
/* Implement va_start by filling the va_list structure VALIST.
|
STDARG_P is always true, and ignored.
|
STDARG_P is always true, and ignored.
|
NEXTARG points to the first anonymous stack argument.
|
NEXTARG points to the first anonymous stack argument.
|
|
|
The following global variables are used to initialize
|
The following global variables are used to initialize
|
the va_list structure:
|
the va_list structure:
|
|
|
current_function_args_info:
|
current_function_args_info:
|
holds number of gprs and fprs used for named arguments.
|
holds number of gprs and fprs used for named arguments.
|
current_function_arg_offset_rtx:
|
current_function_arg_offset_rtx:
|
holds the offset of the first anonymous stack argument
|
holds the offset of the first anonymous stack argument
|
(relative to the virtual arg pointer). */
|
(relative to the virtual arg pointer). */
|
|
|
void
|
void
|
s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
|
s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
|
{
|
{
|
HOST_WIDE_INT n_gpr, n_fpr;
|
HOST_WIDE_INT n_gpr, n_fpr;
|
int off;
|
int off;
|
tree f_gpr, f_fpr, f_ovf, f_sav;
|
tree f_gpr, f_fpr, f_ovf, f_sav;
|
tree gpr, fpr, ovf, sav, t;
|
tree gpr, fpr, ovf, sav, t;
|
|
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
|
f_fpr = TREE_CHAIN (f_gpr);
|
f_fpr = TREE_CHAIN (f_gpr);
|
f_ovf = TREE_CHAIN (f_fpr);
|
f_ovf = TREE_CHAIN (f_fpr);
|
f_sav = TREE_CHAIN (f_ovf);
|
f_sav = TREE_CHAIN (f_ovf);
|
|
|
valist = build_va_arg_indirect_ref (valist);
|
valist = build_va_arg_indirect_ref (valist);
|
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
|
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
|
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
|
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
|
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
|
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
|
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
|
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
|
|
|
/* Count number of gp and fp argument registers used. */
|
/* Count number of gp and fp argument registers used. */
|
|
|
n_gpr = current_function_args_info.gprs;
|
n_gpr = current_function_args_info.gprs;
|
n_fpr = current_function_args_info.fprs;
|
n_fpr = current_function_args_info.fprs;
|
|
|
if (cfun->va_list_gpr_size)
|
if (cfun->va_list_gpr_size)
|
{
|
{
|
t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
|
t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
|
build_int_cst (NULL_TREE, n_gpr));
|
build_int_cst (NULL_TREE, n_gpr));
|
TREE_SIDE_EFFECTS (t) = 1;
|
TREE_SIDE_EFFECTS (t) = 1;
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
}
|
}
|
|
|
if (cfun->va_list_fpr_size)
|
if (cfun->va_list_fpr_size)
|
{
|
{
|
t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
|
t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
|
build_int_cst (NULL_TREE, n_fpr));
|
build_int_cst (NULL_TREE, n_fpr));
|
TREE_SIDE_EFFECTS (t) = 1;
|
TREE_SIDE_EFFECTS (t) = 1;
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
}
|
}
|
|
|
/* Find the overflow area. */
|
/* Find the overflow area. */
|
if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG
|
if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG
|
|| n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG)
|
|| n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG)
|
{
|
{
|
t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
|
t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
|
|
|
off = INTVAL (current_function_arg_offset_rtx);
|
off = INTVAL (current_function_arg_offset_rtx);
|
off = off < 0 ? 0 : off;
|
off = off < 0 ? 0 : off;
|
if (TARGET_DEBUG_ARG)
|
if (TARGET_DEBUG_ARG)
|
fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
|
fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
|
(int)n_gpr, (int)n_fpr, off);
|
(int)n_gpr, (int)n_fpr, off);
|
|
|
t = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off));
|
t = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off));
|
|
|
t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
|
t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
|
TREE_SIDE_EFFECTS (t) = 1;
|
TREE_SIDE_EFFECTS (t) = 1;
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
}
|
}
|
|
|
/* Find the register save area. */
|
/* Find the register save area. */
|
if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG)
|
if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG)
|
|| (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
|
|| (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
|
{
|
{
|
t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
|
t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
|
t = build2 (PLUS_EXPR, TREE_TYPE (sav), t,
|
t = build2 (PLUS_EXPR, TREE_TYPE (sav), t,
|
build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD));
|
build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD));
|
|
|
t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
|
t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
|
TREE_SIDE_EFFECTS (t) = 1;
|
TREE_SIDE_EFFECTS (t) = 1;
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
|
}
|
}
|
}
|
}
|
|
|
/* Implement va_arg by updating the va_list structure
|
/* Implement va_arg by updating the va_list structure
|
VALIST as required to retrieve an argument of type
|
VALIST as required to retrieve an argument of type
|
TYPE, and returning that argument.
|
TYPE, and returning that argument.
|
|
|
Generates code equivalent to:
|
Generates code equivalent to:
|
|
|
if (integral value) {
|
if (integral value) {
|
if (size <= 4 && args.gpr < 5 ||
|
if (size <= 4 && args.gpr < 5 ||
|
size > 4 && args.gpr < 4 )
|
size > 4 && args.gpr < 4 )
|
ret = args.reg_save_area[args.gpr+8]
|
ret = args.reg_save_area[args.gpr+8]
|
else
|
else
|
ret = *args.overflow_arg_area++;
|
ret = *args.overflow_arg_area++;
|
} else if (float value) {
|
} else if (float value) {
|
if (args.fgpr < 2)
|
if (args.fgpr < 2)
|
ret = args.reg_save_area[args.fpr+64]
|
ret = args.reg_save_area[args.fpr+64]
|
else
|
else
|
ret = *args.overflow_arg_area++;
|
ret = *args.overflow_arg_area++;
|
} else if (aggregate value) {
|
} else if (aggregate value) {
|
if (args.gpr < 5)
|
if (args.gpr < 5)
|
ret = *args.reg_save_area[args.gpr]
|
ret = *args.reg_save_area[args.gpr]
|
else
|
else
|
ret = **args.overflow_arg_area++;
|
ret = **args.overflow_arg_area++;
|
} */
|
} */
|
|
|
static tree
|
static tree
|
s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
|
s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
|
tree *post_p ATTRIBUTE_UNUSED)
|
tree *post_p ATTRIBUTE_UNUSED)
|
{
|
{
|
tree f_gpr, f_fpr, f_ovf, f_sav;
|
tree f_gpr, f_fpr, f_ovf, f_sav;
|
tree gpr, fpr, ovf, sav, reg, t, u;
|
tree gpr, fpr, ovf, sav, reg, t, u;
|
int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
|
int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
|
tree lab_false, lab_over, addr;
|
tree lab_false, lab_over, addr;
|
|
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
|
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
|
f_fpr = TREE_CHAIN (f_gpr);
|
f_fpr = TREE_CHAIN (f_gpr);
|
f_ovf = TREE_CHAIN (f_fpr);
|
f_ovf = TREE_CHAIN (f_fpr);
|
f_sav = TREE_CHAIN (f_ovf);
|
f_sav = TREE_CHAIN (f_ovf);
|
|
|
valist = build_va_arg_indirect_ref (valist);
|
valist = build_va_arg_indirect_ref (valist);
|
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
|
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
|
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
|
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
|
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
|
ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
|
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
|
sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
|
|
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
|
|
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
|
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
|
{
|
{
|
if (TARGET_DEBUG_ARG)
|
if (TARGET_DEBUG_ARG)
|
{
|
{
|
fprintf (stderr, "va_arg: aggregate type");
|
fprintf (stderr, "va_arg: aggregate type");
|
debug_tree (type);
|
debug_tree (type);
|
}
|
}
|
|
|
/* Aggregates are passed by reference. */
|
/* Aggregates are passed by reference. */
|
indirect_p = 1;
|
indirect_p = 1;
|
reg = gpr;
|
reg = gpr;
|
n_reg = 1;
|
n_reg = 1;
|
|
|
/* kernel stack layout on 31 bit: It is assumed here that no padding
|
/* kernel stack layout on 31 bit: It is assumed here that no padding
|
will be added by s390_frame_info because for va_args always an even
|
will be added by s390_frame_info because for va_args always an even
|
number of gprs has to be saved r15-r2 = 14 regs. */
|
number of gprs has to be saved r15-r2 = 14 regs. */
|
sav_ofs = 2 * UNITS_PER_WORD;
|
sav_ofs = 2 * UNITS_PER_WORD;
|
sav_scale = UNITS_PER_WORD;
|
sav_scale = UNITS_PER_WORD;
|
size = UNITS_PER_WORD;
|
size = UNITS_PER_WORD;
|
max_reg = GP_ARG_NUM_REG - n_reg;
|
max_reg = GP_ARG_NUM_REG - n_reg;
|
}
|
}
|
else if (s390_function_arg_float (TYPE_MODE (type), type))
|
else if (s390_function_arg_float (TYPE_MODE (type), type))
|
{
|
{
|
if (TARGET_DEBUG_ARG)
|
if (TARGET_DEBUG_ARG)
|
{
|
{
|
fprintf (stderr, "va_arg: float type");
|
fprintf (stderr, "va_arg: float type");
|
debug_tree (type);
|
debug_tree (type);
|
}
|
}
|
|
|
/* FP args go in FP registers, if present. */
|
/* FP args go in FP registers, if present. */
|
indirect_p = 0;
|
indirect_p = 0;
|
reg = fpr;
|
reg = fpr;
|
n_reg = 1;
|
n_reg = 1;
|
sav_ofs = 16 * UNITS_PER_WORD;
|
sav_ofs = 16 * UNITS_PER_WORD;
|
sav_scale = 8;
|
sav_scale = 8;
|
max_reg = FP_ARG_NUM_REG - n_reg;
|
max_reg = FP_ARG_NUM_REG - n_reg;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (TARGET_DEBUG_ARG)
|
if (TARGET_DEBUG_ARG)
|
{
|
{
|
fprintf (stderr, "va_arg: other type");
|
fprintf (stderr, "va_arg: other type");
|
debug_tree (type);
|
debug_tree (type);
|
}
|
}
|
|
|
/* Otherwise into GP registers. */
|
/* Otherwise into GP registers. */
|
indirect_p = 0;
|
indirect_p = 0;
|
reg = gpr;
|
reg = gpr;
|
n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
|
|
/* kernel stack layout on 31 bit: It is assumed here that no padding
|
/* kernel stack layout on 31 bit: It is assumed here that no padding
|
will be added by s390_frame_info because for va_args always an even
|
will be added by s390_frame_info because for va_args always an even
|
number of gprs has to be saved r15-r2 = 14 regs. */
|
number of gprs has to be saved r15-r2 = 14 regs. */
|
sav_ofs = 2 * UNITS_PER_WORD;
|
sav_ofs = 2 * UNITS_PER_WORD;
|
|
|
if (size < UNITS_PER_WORD)
|
if (size < UNITS_PER_WORD)
|
sav_ofs += UNITS_PER_WORD - size;
|
sav_ofs += UNITS_PER_WORD - size;
|
|
|
sav_scale = UNITS_PER_WORD;
|
sav_scale = UNITS_PER_WORD;
|
max_reg = GP_ARG_NUM_REG - n_reg;
|
max_reg = GP_ARG_NUM_REG - n_reg;
|
}
|
}
|
|
|
/* Pull the value out of the saved registers ... */
|
/* Pull the value out of the saved registers ... */
|
|
|
lab_false = create_artificial_label ();
|
lab_false = create_artificial_label ();
|
lab_over = create_artificial_label ();
|
lab_over = create_artificial_label ();
|
addr = create_tmp_var (ptr_type_node, "addr");
|
addr = create_tmp_var (ptr_type_node, "addr");
|
DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set ();
|
DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set ();
|
|
|
t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
|
t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
|
t = build2 (GT_EXPR, boolean_type_node, reg, t);
|
t = build2 (GT_EXPR, boolean_type_node, reg, t);
|
u = build1 (GOTO_EXPR, void_type_node, lab_false);
|
u = build1 (GOTO_EXPR, void_type_node, lab_false);
|
t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
|
t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
|
gimplify_and_add (t, pre_p);
|
gimplify_and_add (t, pre_p);
|
|
|
t = build2 (PLUS_EXPR, ptr_type_node, sav,
|
t = build2 (PLUS_EXPR, ptr_type_node, sav,
|
fold_convert (ptr_type_node, size_int (sav_ofs)));
|
fold_convert (ptr_type_node, size_int (sav_ofs)));
|
u = build2 (MULT_EXPR, TREE_TYPE (reg), reg,
|
u = build2 (MULT_EXPR, TREE_TYPE (reg), reg,
|
fold_convert (TREE_TYPE (reg), size_int (sav_scale)));
|
fold_convert (TREE_TYPE (reg), size_int (sav_scale)));
|
t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u));
|
t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u));
|
|
|
t = build2 (MODIFY_EXPR, void_type_node, addr, t);
|
t = build2 (MODIFY_EXPR, void_type_node, addr, t);
|
gimplify_and_add (t, pre_p);
|
gimplify_and_add (t, pre_p);
|
|
|
t = build1 (GOTO_EXPR, void_type_node, lab_over);
|
t = build1 (GOTO_EXPR, void_type_node, lab_over);
|
gimplify_and_add (t, pre_p);
|
gimplify_and_add (t, pre_p);
|
|
|
t = build1 (LABEL_EXPR, void_type_node, lab_false);
|
t = build1 (LABEL_EXPR, void_type_node, lab_false);
|
append_to_statement_list (t, pre_p);
|
append_to_statement_list (t, pre_p);
|
|
|
|
|
/* ... Otherwise out of the overflow area. */
|
/* ... Otherwise out of the overflow area. */
|
|
|
t = ovf;
|
t = ovf;
|
if (size < UNITS_PER_WORD)
|
if (size < UNITS_PER_WORD)
|
t = build2 (PLUS_EXPR, ptr_type_node, t,
|
t = build2 (PLUS_EXPR, ptr_type_node, t,
|
fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size)));
|
fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size)));
|
|
|
gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
|
gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
|
|
|
u = build2 (MODIFY_EXPR, void_type_node, addr, t);
|
u = build2 (MODIFY_EXPR, void_type_node, addr, t);
|
gimplify_and_add (u, pre_p);
|
gimplify_and_add (u, pre_p);
|
|
|
t = build2 (PLUS_EXPR, ptr_type_node, t,
|
t = build2 (PLUS_EXPR, ptr_type_node, t,
|
fold_convert (ptr_type_node, size_int (size)));
|
fold_convert (ptr_type_node, size_int (size)));
|
t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t);
|
t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t);
|
gimplify_and_add (t, pre_p);
|
gimplify_and_add (t, pre_p);
|
|
|
t = build1 (LABEL_EXPR, void_type_node, lab_over);
|
t = build1 (LABEL_EXPR, void_type_node, lab_over);
|
append_to_statement_list (t, pre_p);
|
append_to_statement_list (t, pre_p);
|
|
|
|
|
/* Increment register save count. */
|
/* Increment register save count. */
|
|
|
u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
|
u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
|
fold_convert (TREE_TYPE (reg), size_int (n_reg)));
|
fold_convert (TREE_TYPE (reg), size_int (n_reg)));
|
gimplify_and_add (u, pre_p);
|
gimplify_and_add (u, pre_p);
|
|
|
if (indirect_p)
|
if (indirect_p)
|
{
|
{
|
t = build_pointer_type (build_pointer_type (type));
|
t = build_pointer_type (build_pointer_type (type));
|
addr = fold_convert (t, addr);
|
addr = fold_convert (t, addr);
|
addr = build_va_arg_indirect_ref (addr);
|
addr = build_va_arg_indirect_ref (addr);
|
}
|
}
|
else
|
else
|
{
|
{
|
t = build_pointer_type (type);
|
t = build_pointer_type (type);
|
addr = fold_convert (t, addr);
|
addr = fold_convert (t, addr);
|
}
|
}
|
|
|
return build_va_arg_indirect_ref (addr);
|
return build_va_arg_indirect_ref (addr);
|
}
|
}
|
|
|
|
|
/* Builtins. */
|
/* Builtins. */
|
|
|
enum s390_builtin
|
enum s390_builtin
|
{
|
{
|
S390_BUILTIN_THREAD_POINTER,
|
S390_BUILTIN_THREAD_POINTER,
|
S390_BUILTIN_SET_THREAD_POINTER,
|
S390_BUILTIN_SET_THREAD_POINTER,
|
|
|
S390_BUILTIN_max
|
S390_BUILTIN_max
|
};
|
};
|
|
|
static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
|
static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
|
CODE_FOR_get_tp_64,
|
CODE_FOR_get_tp_64,
|
CODE_FOR_set_tp_64
|
CODE_FOR_set_tp_64
|
};
|
};
|
|
|
static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
|
static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
|
CODE_FOR_get_tp_31,
|
CODE_FOR_get_tp_31,
|
CODE_FOR_set_tp_31
|
CODE_FOR_set_tp_31
|
};
|
};
|
|
|
static void
|
static void
|
s390_init_builtins (void)
|
s390_init_builtins (void)
|
{
|
{
|
tree ftype;
|
tree ftype;
|
|
|
ftype = build_function_type (ptr_type_node, void_list_node);
|
ftype = build_function_type (ptr_type_node, void_list_node);
|
lang_hooks.builtin_function ("__builtin_thread_pointer", ftype,
|
lang_hooks.builtin_function ("__builtin_thread_pointer", ftype,
|
S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
|
S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
|
NULL, NULL_TREE);
|
NULL, NULL_TREE);
|
|
|
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
|
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
|
lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype,
|
lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype,
|
S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
|
S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
|
NULL, NULL_TREE);
|
NULL, NULL_TREE);
|
}
|
}
|
|
|
/* Expand an expression EXP that calls a built-in function,
|
/* Expand an expression EXP that calls a built-in function,
|
with result going to TARGET if that's convenient
|
with result going to TARGET if that's convenient
|
(and in mode MODE if that's convenient).
|
(and in mode MODE if that's convenient).
|
SUBTARGET may be used as the target for computing one of EXP's operands.
|
SUBTARGET may be used as the target for computing one of EXP's operands.
|
IGNORE is nonzero if the value is to be ignored. */
|
IGNORE is nonzero if the value is to be ignored. */
|
|
|
static rtx
|
static rtx
|
s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
|
s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
|
enum machine_mode mode ATTRIBUTE_UNUSED,
|
enum machine_mode mode ATTRIBUTE_UNUSED,
|
int ignore ATTRIBUTE_UNUSED)
|
int ignore ATTRIBUTE_UNUSED)
|
{
|
{
|
#define MAX_ARGS 2
|
#define MAX_ARGS 2
|
|
|
unsigned int const *code_for_builtin =
|
unsigned int const *code_for_builtin =
|
TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
|
TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
|
|
|
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
|
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
|
unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
|
unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
|
tree arglist = TREE_OPERAND (exp, 1);
|
tree arglist = TREE_OPERAND (exp, 1);
|
enum insn_code icode;
|
enum insn_code icode;
|
rtx op[MAX_ARGS], pat;
|
rtx op[MAX_ARGS], pat;
|
int arity;
|
int arity;
|
bool nonvoid;
|
bool nonvoid;
|
|
|
if (fcode >= S390_BUILTIN_max)
|
if (fcode >= S390_BUILTIN_max)
|
internal_error ("bad builtin fcode");
|
internal_error ("bad builtin fcode");
|
icode = code_for_builtin[fcode];
|
icode = code_for_builtin[fcode];
|
if (icode == 0)
|
if (icode == 0)
|
internal_error ("bad builtin fcode");
|
internal_error ("bad builtin fcode");
|
|
|
nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
|
nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
|
|
|
for (arglist = TREE_OPERAND (exp, 1), arity = 0;
|
for (arglist = TREE_OPERAND (exp, 1), arity = 0;
|
arglist;
|
arglist;
|
arglist = TREE_CHAIN (arglist), arity++)
|
arglist = TREE_CHAIN (arglist), arity++)
|
{
|
{
|
const struct insn_operand_data *insn_op;
|
const struct insn_operand_data *insn_op;
|
|
|
tree arg = TREE_VALUE (arglist);
|
tree arg = TREE_VALUE (arglist);
|
if (arg == error_mark_node)
|
if (arg == error_mark_node)
|
return NULL_RTX;
|
return NULL_RTX;
|
if (arity > MAX_ARGS)
|
if (arity > MAX_ARGS)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
insn_op = &insn_data[icode].operand[arity + nonvoid];
|
insn_op = &insn_data[icode].operand[arity + nonvoid];
|
|
|
op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
|
op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
|
|
|
if (!(*insn_op->predicate) (op[arity], insn_op->mode))
|
if (!(*insn_op->predicate) (op[arity], insn_op->mode))
|
op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
|
op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
|
}
|
}
|
|
|
if (nonvoid)
|
if (nonvoid)
|
{
|
{
|
enum machine_mode tmode = insn_data[icode].operand[0].mode;
|
enum machine_mode tmode = insn_data[icode].operand[0].mode;
|
if (!target
|
if (!target
|
|| GET_MODE (target) != tmode
|
|| GET_MODE (target) != tmode
|
|| !(*insn_data[icode].operand[0].predicate) (target, tmode))
|
|| !(*insn_data[icode].operand[0].predicate) (target, tmode))
|
target = gen_reg_rtx (tmode);
|
target = gen_reg_rtx (tmode);
|
}
|
}
|
|
|
switch (arity)
|
switch (arity)
|
{
|
{
|
case 0:
|
case 0:
|
pat = GEN_FCN (icode) (target);
|
pat = GEN_FCN (icode) (target);
|
break;
|
break;
|
case 1:
|
case 1:
|
if (nonvoid)
|
if (nonvoid)
|
pat = GEN_FCN (icode) (target, op[0]);
|
pat = GEN_FCN (icode) (target, op[0]);
|
else
|
else
|
pat = GEN_FCN (icode) (op[0]);
|
pat = GEN_FCN (icode) (op[0]);
|
break;
|
break;
|
case 2:
|
case 2:
|
pat = GEN_FCN (icode) (target, op[0], op[1]);
|
pat = GEN_FCN (icode) (target, op[0], op[1]);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
if (!pat)
|
if (!pat)
|
return NULL_RTX;
|
return NULL_RTX;
|
emit_insn (pat);
|
emit_insn (pat);
|
|
|
if (nonvoid)
|
if (nonvoid)
|
return target;
|
return target;
|
else
|
else
|
return const0_rtx;
|
return const0_rtx;
|
}
|
}
|
|
|
|
|
/* Output assembly code for the trampoline template to
|
/* Output assembly code for the trampoline template to
|
stdio stream FILE.
|
stdio stream FILE.
|
|
|
On S/390, we use gpr 1 internally in the trampoline code;
|
On S/390, we use gpr 1 internally in the trampoline code;
|
gpr 0 is used to hold the static chain. */
|
gpr 0 is used to hold the static chain. */
|
|
|
void
|
void
|
s390_trampoline_template (FILE *file)
|
s390_trampoline_template (FILE *file)
|
{
|
{
|
rtx op[2];
|
rtx op[2];
|
op[0] = gen_rtx_REG (Pmode, 0);
|
op[0] = gen_rtx_REG (Pmode, 0);
|
op[1] = gen_rtx_REG (Pmode, 1);
|
op[1] = gen_rtx_REG (Pmode, 1);
|
|
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
output_asm_insn ("basr\t%1,0", op);
|
output_asm_insn ("basr\t%1,0", op);
|
output_asm_insn ("lmg\t%0,%1,14(%1)", op);
|
output_asm_insn ("lmg\t%0,%1,14(%1)", op);
|
output_asm_insn ("br\t%1", op);
|
output_asm_insn ("br\t%1", op);
|
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
|
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
|
}
|
}
|
else
|
else
|
{
|
{
|
output_asm_insn ("basr\t%1,0", op);
|
output_asm_insn ("basr\t%1,0", op);
|
output_asm_insn ("lm\t%0,%1,6(%1)", op);
|
output_asm_insn ("lm\t%0,%1,6(%1)", op);
|
output_asm_insn ("br\t%1", op);
|
output_asm_insn ("br\t%1", op);
|
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
|
ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
|
}
|
}
|
}
|
}
|
|
|
/* Emit RTL insns to initialize the variable parts of a trampoline.
|
/* Emit RTL insns to initialize the variable parts of a trampoline.
|
FNADDR is an RTX for the address of the function's pure code.
|
FNADDR is an RTX for the address of the function's pure code.
|
CXT is an RTX for the static chain value for the function. */
|
CXT is an RTX for the static chain value for the function. */
|
|
|
void
|
void
|
s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
|
s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
|
{
|
{
|
emit_move_insn (gen_rtx_MEM (Pmode,
|
emit_move_insn (gen_rtx_MEM (Pmode,
|
memory_address (Pmode,
|
memory_address (Pmode,
|
plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
|
plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
|
emit_move_insn (gen_rtx_MEM (Pmode,
|
emit_move_insn (gen_rtx_MEM (Pmode,
|
memory_address (Pmode,
|
memory_address (Pmode,
|
plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
|
plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
|
}
|
}
|
|
|
/* Return rtx for 64-bit constant formed from the 32-bit subwords
|
/* Return rtx for 64-bit constant formed from the 32-bit subwords
|
LOW and HIGH, independent of the host word size. */
|
LOW and HIGH, independent of the host word size. */
|
|
|
rtx
|
rtx
|
s390_gen_rtx_const_DI (int high, int low)
|
s390_gen_rtx_const_DI (int high, int low)
|
{
|
{
|
#if HOST_BITS_PER_WIDE_INT >= 64
|
#if HOST_BITS_PER_WIDE_INT >= 64
|
HOST_WIDE_INT val;
|
HOST_WIDE_INT val;
|
val = (HOST_WIDE_INT)high;
|
val = (HOST_WIDE_INT)high;
|
val <<= 32;
|
val <<= 32;
|
val |= (HOST_WIDE_INT)low;
|
val |= (HOST_WIDE_INT)low;
|
|
|
return GEN_INT (val);
|
return GEN_INT (val);
|
#else
|
#else
|
#if HOST_BITS_PER_WIDE_INT >= 32
|
#if HOST_BITS_PER_WIDE_INT >= 32
|
return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
|
return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
|
#else
|
#else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
#endif
|
#endif
|
#endif
|
#endif
|
}
|
}
|
|
|
/* Output assembler code to FILE to increment profiler label # LABELNO
|
/* Output assembler code to FILE to increment profiler label # LABELNO
|
for profiling a function entry. */
|
for profiling a function entry. */
|
|
|
void
|
void
|
s390_function_profiler (FILE *file, int labelno)
|
s390_function_profiler (FILE *file, int labelno)
|
{
|
{
|
rtx op[7];
|
rtx op[7];
|
|
|
char label[128];
|
char label[128];
|
ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
|
ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
|
|
|
fprintf (file, "# function profiler \n");
|
fprintf (file, "# function profiler \n");
|
|
|
op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
|
op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
|
op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
|
op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
|
op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
|
|
|
op[2] = gen_rtx_REG (Pmode, 1);
|
op[2] = gen_rtx_REG (Pmode, 1);
|
op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
|
op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
|
SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
|
SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
|
|
|
op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
|
op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
|
if (flag_pic)
|
if (flag_pic)
|
{
|
{
|
op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
|
op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
|
op[4] = gen_rtx_CONST (Pmode, op[4]);
|
op[4] = gen_rtx_CONST (Pmode, op[4]);
|
}
|
}
|
|
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
output_asm_insn ("stg\t%0,%1", op);
|
output_asm_insn ("stg\t%0,%1", op);
|
output_asm_insn ("larl\t%2,%3", op);
|
output_asm_insn ("larl\t%2,%3", op);
|
output_asm_insn ("brasl\t%0,%4", op);
|
output_asm_insn ("brasl\t%0,%4", op);
|
output_asm_insn ("lg\t%0,%1", op);
|
output_asm_insn ("lg\t%0,%1", op);
|
}
|
}
|
else if (!flag_pic)
|
else if (!flag_pic)
|
{
|
{
|
op[6] = gen_label_rtx ();
|
op[6] = gen_label_rtx ();
|
|
|
output_asm_insn ("st\t%0,%1", op);
|
output_asm_insn ("st\t%0,%1", op);
|
output_asm_insn ("bras\t%2,%l6", op);
|
output_asm_insn ("bras\t%2,%l6", op);
|
output_asm_insn (".long\t%4", op);
|
output_asm_insn (".long\t%4", op);
|
output_asm_insn (".long\t%3", op);
|
output_asm_insn (".long\t%3", op);
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
|
output_asm_insn ("l\t%0,0(%2)", op);
|
output_asm_insn ("l\t%0,0(%2)", op);
|
output_asm_insn ("l\t%2,4(%2)", op);
|
output_asm_insn ("l\t%2,4(%2)", op);
|
output_asm_insn ("basr\t%0,%0", op);
|
output_asm_insn ("basr\t%0,%0", op);
|
output_asm_insn ("l\t%0,%1", op);
|
output_asm_insn ("l\t%0,%1", op);
|
}
|
}
|
else
|
else
|
{
|
{
|
op[5] = gen_label_rtx ();
|
op[5] = gen_label_rtx ();
|
op[6] = gen_label_rtx ();
|
op[6] = gen_label_rtx ();
|
|
|
output_asm_insn ("st\t%0,%1", op);
|
output_asm_insn ("st\t%0,%1", op);
|
output_asm_insn ("bras\t%2,%l6", op);
|
output_asm_insn ("bras\t%2,%l6", op);
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5]));
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5]));
|
output_asm_insn (".long\t%4-%l5", op);
|
output_asm_insn (".long\t%4-%l5", op);
|
output_asm_insn (".long\t%3-%l5", op);
|
output_asm_insn (".long\t%3-%l5", op);
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
|
output_asm_insn ("lr\t%0,%2", op);
|
output_asm_insn ("lr\t%0,%2", op);
|
output_asm_insn ("a\t%0,0(%2)", op);
|
output_asm_insn ("a\t%0,0(%2)", op);
|
output_asm_insn ("a\t%2,4(%2)", op);
|
output_asm_insn ("a\t%2,4(%2)", op);
|
output_asm_insn ("basr\t%0,%0", op);
|
output_asm_insn ("basr\t%0,%0", op);
|
output_asm_insn ("l\t%0,%1", op);
|
output_asm_insn ("l\t%0,%1", op);
|
}
|
}
|
}
|
}
|
|
|
/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
|
/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
|
into its SYMBOL_REF_FLAGS. */
|
into its SYMBOL_REF_FLAGS. */
|
|
|
static void
|
static void
|
s390_encode_section_info (tree decl, rtx rtl, int first)
|
s390_encode_section_info (tree decl, rtx rtl, int first)
|
{
|
{
|
default_encode_section_info (decl, rtl, first);
|
default_encode_section_info (decl, rtl, first);
|
|
|
/* If a variable has a forced alignment to < 2 bytes, mark it with
|
/* If a variable has a forced alignment to < 2 bytes, mark it with
|
SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand. */
|
SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand. */
|
if (TREE_CODE (decl) == VAR_DECL
|
if (TREE_CODE (decl) == VAR_DECL
|
&& DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
|
&& DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
|
SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
|
SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
|
}
|
}
|
|
|
/* Output thunk to FILE that implements a C++ virtual function call (with
|
/* Output thunk to FILE that implements a C++ virtual function call (with
|
multiple inheritance) to FUNCTION. The thunk adjusts the this pointer
|
multiple inheritance) to FUNCTION. The thunk adjusts the this pointer
|
by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment
|
by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment
|
stored at VCALL_OFFSET in the vtable whose address is located at offset 0
|
stored at VCALL_OFFSET in the vtable whose address is located at offset 0
|
relative to the resulting this pointer. */
|
relative to the resulting this pointer. */
|
|
|
static void
|
static void
|
s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
|
s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
|
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
|
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
|
tree function)
|
tree function)
|
{
|
{
|
rtx op[10];
|
rtx op[10];
|
int nonlocal = 0;
|
int nonlocal = 0;
|
|
|
/* Operand 0 is the target function. */
|
/* Operand 0 is the target function. */
|
op[0] = XEXP (DECL_RTL (function), 0);
|
op[0] = XEXP (DECL_RTL (function), 0);
|
if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
|
if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
|
{
|
{
|
nonlocal = 1;
|
nonlocal = 1;
|
op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]),
|
op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]),
|
TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT);
|
TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT);
|
op[0] = gen_rtx_CONST (Pmode, op[0]);
|
op[0] = gen_rtx_CONST (Pmode, op[0]);
|
}
|
}
|
|
|
/* Operand 1 is the 'this' pointer. */
|
/* Operand 1 is the 'this' pointer. */
|
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
|
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
|
op[1] = gen_rtx_REG (Pmode, 3);
|
op[1] = gen_rtx_REG (Pmode, 3);
|
else
|
else
|
op[1] = gen_rtx_REG (Pmode, 2);
|
op[1] = gen_rtx_REG (Pmode, 2);
|
|
|
/* Operand 2 is the delta. */
|
/* Operand 2 is the delta. */
|
op[2] = GEN_INT (delta);
|
op[2] = GEN_INT (delta);
|
|
|
/* Operand 3 is the vcall_offset. */
|
/* Operand 3 is the vcall_offset. */
|
op[3] = GEN_INT (vcall_offset);
|
op[3] = GEN_INT (vcall_offset);
|
|
|
/* Operand 4 is the temporary register. */
|
/* Operand 4 is the temporary register. */
|
op[4] = gen_rtx_REG (Pmode, 1);
|
op[4] = gen_rtx_REG (Pmode, 1);
|
|
|
/* Operands 5 to 8 can be used as labels. */
|
/* Operands 5 to 8 can be used as labels. */
|
op[5] = NULL_RTX;
|
op[5] = NULL_RTX;
|
op[6] = NULL_RTX;
|
op[6] = NULL_RTX;
|
op[7] = NULL_RTX;
|
op[7] = NULL_RTX;
|
op[8] = NULL_RTX;
|
op[8] = NULL_RTX;
|
|
|
/* Operand 9 can be used for temporary register. */
|
/* Operand 9 can be used for temporary register. */
|
op[9] = NULL_RTX;
|
op[9] = NULL_RTX;
|
|
|
/* Generate code. */
|
/* Generate code. */
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
/* Setup literal pool pointer if required. */
|
/* Setup literal pool pointer if required. */
|
if ((!DISP_IN_RANGE (delta)
|
if ((!DISP_IN_RANGE (delta)
|
&& !CONST_OK_FOR_K (delta)
|
&& !CONST_OK_FOR_K (delta)
|
&& !CONST_OK_FOR_Os (delta))
|
&& !CONST_OK_FOR_Os (delta))
|
|| (!DISP_IN_RANGE (vcall_offset)
|
|| (!DISP_IN_RANGE (vcall_offset)
|
&& !CONST_OK_FOR_K (vcall_offset)
|
&& !CONST_OK_FOR_K (vcall_offset)
|
&& !CONST_OK_FOR_Os (vcall_offset)))
|
&& !CONST_OK_FOR_Os (vcall_offset)))
|
{
|
{
|
op[5] = gen_label_rtx ();
|
op[5] = gen_label_rtx ();
|
output_asm_insn ("larl\t%4,%5", op);
|
output_asm_insn ("larl\t%4,%5", op);
|
}
|
}
|
|
|
/* Add DELTA to this pointer. */
|
/* Add DELTA to this pointer. */
|
if (delta)
|
if (delta)
|
{
|
{
|
if (CONST_OK_FOR_J (delta))
|
if (CONST_OK_FOR_J (delta))
|
output_asm_insn ("la\t%1,%2(%1)", op);
|
output_asm_insn ("la\t%1,%2(%1)", op);
|
else if (DISP_IN_RANGE (delta))
|
else if (DISP_IN_RANGE (delta))
|
output_asm_insn ("lay\t%1,%2(%1)", op);
|
output_asm_insn ("lay\t%1,%2(%1)", op);
|
else if (CONST_OK_FOR_K (delta))
|
else if (CONST_OK_FOR_K (delta))
|
output_asm_insn ("aghi\t%1,%2", op);
|
output_asm_insn ("aghi\t%1,%2", op);
|
else if (CONST_OK_FOR_Os (delta))
|
else if (CONST_OK_FOR_Os (delta))
|
output_asm_insn ("agfi\t%1,%2", op);
|
output_asm_insn ("agfi\t%1,%2", op);
|
else
|
else
|
{
|
{
|
op[6] = gen_label_rtx ();
|
op[6] = gen_label_rtx ();
|
output_asm_insn ("agf\t%1,%6-%5(%4)", op);
|
output_asm_insn ("agf\t%1,%6-%5(%4)", op);
|
}
|
}
|
}
|
}
|
|
|
/* Perform vcall adjustment. */
|
/* Perform vcall adjustment. */
|
if (vcall_offset)
|
if (vcall_offset)
|
{
|
{
|
if (DISP_IN_RANGE (vcall_offset))
|
if (DISP_IN_RANGE (vcall_offset))
|
{
|
{
|
output_asm_insn ("lg\t%4,0(%1)", op);
|
output_asm_insn ("lg\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%1,%3(%4)", op);
|
output_asm_insn ("ag\t%1,%3(%4)", op);
|
}
|
}
|
else if (CONST_OK_FOR_K (vcall_offset))
|
else if (CONST_OK_FOR_K (vcall_offset))
|
{
|
{
|
output_asm_insn ("lghi\t%4,%3", op);
|
output_asm_insn ("lghi\t%4,%3", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
}
|
}
|
else if (CONST_OK_FOR_Os (vcall_offset))
|
else if (CONST_OK_FOR_Os (vcall_offset))
|
{
|
{
|
output_asm_insn ("lgfi\t%4,%3", op);
|
output_asm_insn ("lgfi\t%4,%3", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
}
|
}
|
else
|
else
|
{
|
{
|
op[7] = gen_label_rtx ();
|
op[7] = gen_label_rtx ();
|
output_asm_insn ("llgf\t%4,%7-%5(%4)", op);
|
output_asm_insn ("llgf\t%4,%7-%5(%4)", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%4,0(%1)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
output_asm_insn ("ag\t%1,0(%4)", op);
|
}
|
}
|
}
|
}
|
|
|
/* Jump to target. */
|
/* Jump to target. */
|
output_asm_insn ("jg\t%0", op);
|
output_asm_insn ("jg\t%0", op);
|
|
|
/* Output literal pool if required. */
|
/* Output literal pool if required. */
|
if (op[5])
|
if (op[5])
|
{
|
{
|
output_asm_insn (".align\t4", op);
|
output_asm_insn (".align\t4", op);
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[5]));
|
CODE_LABEL_NUMBER (op[5]));
|
}
|
}
|
if (op[6])
|
if (op[6])
|
{
|
{
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[6]));
|
CODE_LABEL_NUMBER (op[6]));
|
output_asm_insn (".long\t%2", op);
|
output_asm_insn (".long\t%2", op);
|
}
|
}
|
if (op[7])
|
if (op[7])
|
{
|
{
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[7]));
|
CODE_LABEL_NUMBER (op[7]));
|
output_asm_insn (".long\t%3", op);
|
output_asm_insn (".long\t%3", op);
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Setup base pointer if required. */
|
/* Setup base pointer if required. */
|
if (!vcall_offset
|
if (!vcall_offset
|
|| (!DISP_IN_RANGE (delta)
|
|| (!DISP_IN_RANGE (delta)
|
&& !CONST_OK_FOR_K (delta)
|
&& !CONST_OK_FOR_K (delta)
|
&& !CONST_OK_FOR_Os (delta))
|
&& !CONST_OK_FOR_Os (delta))
|
|| (!DISP_IN_RANGE (delta)
|
|| (!DISP_IN_RANGE (delta)
|
&& !CONST_OK_FOR_K (vcall_offset)
|
&& !CONST_OK_FOR_K (vcall_offset)
|
&& !CONST_OK_FOR_Os (vcall_offset)))
|
&& !CONST_OK_FOR_Os (vcall_offset)))
|
{
|
{
|
op[5] = gen_label_rtx ();
|
op[5] = gen_label_rtx ();
|
output_asm_insn ("basr\t%4,0", op);
|
output_asm_insn ("basr\t%4,0", op);
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[5]));
|
CODE_LABEL_NUMBER (op[5]));
|
}
|
}
|
|
|
/* Add DELTA to this pointer. */
|
/* Add DELTA to this pointer. */
|
if (delta)
|
if (delta)
|
{
|
{
|
if (CONST_OK_FOR_J (delta))
|
if (CONST_OK_FOR_J (delta))
|
output_asm_insn ("la\t%1,%2(%1)", op);
|
output_asm_insn ("la\t%1,%2(%1)", op);
|
else if (DISP_IN_RANGE (delta))
|
else if (DISP_IN_RANGE (delta))
|
output_asm_insn ("lay\t%1,%2(%1)", op);
|
output_asm_insn ("lay\t%1,%2(%1)", op);
|
else if (CONST_OK_FOR_K (delta))
|
else if (CONST_OK_FOR_K (delta))
|
output_asm_insn ("ahi\t%1,%2", op);
|
output_asm_insn ("ahi\t%1,%2", op);
|
else if (CONST_OK_FOR_Os (delta))
|
else if (CONST_OK_FOR_Os (delta))
|
output_asm_insn ("afi\t%1,%2", op);
|
output_asm_insn ("afi\t%1,%2", op);
|
else
|
else
|
{
|
{
|
op[6] = gen_label_rtx ();
|
op[6] = gen_label_rtx ();
|
output_asm_insn ("a\t%1,%6-%5(%4)", op);
|
output_asm_insn ("a\t%1,%6-%5(%4)", op);
|
}
|
}
|
}
|
}
|
|
|
/* Perform vcall adjustment. */
|
/* Perform vcall adjustment. */
|
if (vcall_offset)
|
if (vcall_offset)
|
{
|
{
|
if (CONST_OK_FOR_J (vcall_offset))
|
if (CONST_OK_FOR_J (vcall_offset))
|
{
|
{
|
output_asm_insn ("l\t%4,0(%1)", op);
|
output_asm_insn ("l\t%4,0(%1)", op);
|
output_asm_insn ("a\t%1,%3(%4)", op);
|
output_asm_insn ("a\t%1,%3(%4)", op);
|
}
|
}
|
else if (DISP_IN_RANGE (vcall_offset))
|
else if (DISP_IN_RANGE (vcall_offset))
|
{
|
{
|
output_asm_insn ("l\t%4,0(%1)", op);
|
output_asm_insn ("l\t%4,0(%1)", op);
|
output_asm_insn ("ay\t%1,%3(%4)", op);
|
output_asm_insn ("ay\t%1,%3(%4)", op);
|
}
|
}
|
else if (CONST_OK_FOR_K (vcall_offset))
|
else if (CONST_OK_FOR_K (vcall_offset))
|
{
|
{
|
output_asm_insn ("lhi\t%4,%3", op);
|
output_asm_insn ("lhi\t%4,%3", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
}
|
}
|
else if (CONST_OK_FOR_Os (vcall_offset))
|
else if (CONST_OK_FOR_Os (vcall_offset))
|
{
|
{
|
output_asm_insn ("iilf\t%4,%3", op);
|
output_asm_insn ("iilf\t%4,%3", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
}
|
}
|
else
|
else
|
{
|
{
|
op[7] = gen_label_rtx ();
|
op[7] = gen_label_rtx ();
|
output_asm_insn ("l\t%4,%7-%5(%4)", op);
|
output_asm_insn ("l\t%4,%7-%5(%4)", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%4,0(%1)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
output_asm_insn ("a\t%1,0(%4)", op);
|
}
|
}
|
|
|
/* We had to clobber the base pointer register.
|
/* We had to clobber the base pointer register.
|
Re-setup the base pointer (with a different base). */
|
Re-setup the base pointer (with a different base). */
|
op[5] = gen_label_rtx ();
|
op[5] = gen_label_rtx ();
|
output_asm_insn ("basr\t%4,0", op);
|
output_asm_insn ("basr\t%4,0", op);
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[5]));
|
CODE_LABEL_NUMBER (op[5]));
|
}
|
}
|
|
|
/* Jump to target. */
|
/* Jump to target. */
|
op[8] = gen_label_rtx ();
|
op[8] = gen_label_rtx ();
|
|
|
if (!flag_pic)
|
if (!flag_pic)
|
output_asm_insn ("l\t%4,%8-%5(%4)", op);
|
output_asm_insn ("l\t%4,%8-%5(%4)", op);
|
else if (!nonlocal)
|
else if (!nonlocal)
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
/* We cannot call through .plt, since .plt requires %r12 loaded. */
|
/* We cannot call through .plt, since .plt requires %r12 loaded. */
|
else if (flag_pic == 1)
|
else if (flag_pic == 1)
|
{
|
{
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
output_asm_insn ("l\t%4,%0(%4)", op);
|
output_asm_insn ("l\t%4,%0(%4)", op);
|
}
|
}
|
else if (flag_pic == 2)
|
else if (flag_pic == 2)
|
{
|
{
|
op[9] = gen_rtx_REG (Pmode, 0);
|
op[9] = gen_rtx_REG (Pmode, 0);
|
output_asm_insn ("l\t%9,%8-4-%5(%4)", op);
|
output_asm_insn ("l\t%9,%8-4-%5(%4)", op);
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
output_asm_insn ("a\t%4,%8-%5(%4)", op);
|
output_asm_insn ("ar\t%4,%9", op);
|
output_asm_insn ("ar\t%4,%9", op);
|
output_asm_insn ("l\t%4,0(%4)", op);
|
output_asm_insn ("l\t%4,0(%4)", op);
|
}
|
}
|
|
|
output_asm_insn ("br\t%4", op);
|
output_asm_insn ("br\t%4", op);
|
|
|
/* Output literal pool. */
|
/* Output literal pool. */
|
output_asm_insn (".align\t4", op);
|
output_asm_insn (".align\t4", op);
|
|
|
if (nonlocal && flag_pic == 2)
|
if (nonlocal && flag_pic == 2)
|
output_asm_insn (".long\t%0", op);
|
output_asm_insn (".long\t%0", op);
|
if (nonlocal)
|
if (nonlocal)
|
{
|
{
|
op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL;
|
SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL;
|
}
|
}
|
|
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
|
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
|
if (!flag_pic)
|
if (!flag_pic)
|
output_asm_insn (".long\t%0", op);
|
output_asm_insn (".long\t%0", op);
|
else
|
else
|
output_asm_insn (".long\t%0-%5", op);
|
output_asm_insn (".long\t%0-%5", op);
|
|
|
if (op[6])
|
if (op[6])
|
{
|
{
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[6]));
|
CODE_LABEL_NUMBER (op[6]));
|
output_asm_insn (".long\t%2", op);
|
output_asm_insn (".long\t%2", op);
|
}
|
}
|
if (op[7])
|
if (op[7])
|
{
|
{
|
targetm.asm_out.internal_label (file, "L",
|
targetm.asm_out.internal_label (file, "L",
|
CODE_LABEL_NUMBER (op[7]));
|
CODE_LABEL_NUMBER (op[7]));
|
output_asm_insn (".long\t%3", op);
|
output_asm_insn (".long\t%3", op);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
static bool
|
static bool
|
s390_valid_pointer_mode (enum machine_mode mode)
|
s390_valid_pointer_mode (enum machine_mode mode)
|
{
|
{
|
return (mode == SImode || (TARGET_64BIT && mode == DImode));
|
return (mode == SImode || (TARGET_64BIT && mode == DImode));
|
}
|
}
|
|
|
/* Checks whether the given ARGUMENT_LIST would use a caller
|
/* Checks whether the given ARGUMENT_LIST would use a caller
|
saved register. This is used to decide whether sibling call
|
saved register. This is used to decide whether sibling call
|
optimization could be performed on the respective function
|
optimization could be performed on the respective function
|
call. */
|
call. */
|
|
|
static bool
|
static bool
|
s390_call_saved_register_used (tree argument_list)
|
s390_call_saved_register_used (tree argument_list)
|
{
|
{
|
CUMULATIVE_ARGS cum;
|
CUMULATIVE_ARGS cum;
|
tree parameter;
|
tree parameter;
|
enum machine_mode mode;
|
enum machine_mode mode;
|
tree type;
|
tree type;
|
rtx parm_rtx;
|
rtx parm_rtx;
|
int reg;
|
int reg;
|
|
|
INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
|
INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
|
|
|
while (argument_list)
|
while (argument_list)
|
{
|
{
|
parameter = TREE_VALUE (argument_list);
|
parameter = TREE_VALUE (argument_list);
|
argument_list = TREE_CHAIN (argument_list);
|
argument_list = TREE_CHAIN (argument_list);
|
|
|
gcc_assert (parameter);
|
gcc_assert (parameter);
|
|
|
/* For an undeclared variable passed as parameter we will get
|
/* For an undeclared variable passed as parameter we will get
|
an ERROR_MARK node here. */
|
an ERROR_MARK node here. */
|
if (TREE_CODE (parameter) == ERROR_MARK)
|
if (TREE_CODE (parameter) == ERROR_MARK)
|
return true;
|
return true;
|
|
|
type = TREE_TYPE (parameter);
|
type = TREE_TYPE (parameter);
|
gcc_assert (type);
|
gcc_assert (type);
|
|
|
mode = TYPE_MODE (type);
|
mode = TYPE_MODE (type);
|
gcc_assert (mode);
|
gcc_assert (mode);
|
|
|
if (pass_by_reference (&cum, mode, type, true))
|
if (pass_by_reference (&cum, mode, type, true))
|
{
|
{
|
mode = Pmode;
|
mode = Pmode;
|
type = build_pointer_type (type);
|
type = build_pointer_type (type);
|
}
|
}
|
|
|
parm_rtx = s390_function_arg (&cum, mode, type, 0);
|
parm_rtx = s390_function_arg (&cum, mode, type, 0);
|
|
|
s390_function_arg_advance (&cum, mode, type, 0);
|
s390_function_arg_advance (&cum, mode, type, 0);
|
|
|
if (parm_rtx && REG_P (parm_rtx))
|
if (parm_rtx && REG_P (parm_rtx))
|
{
|
{
|
for (reg = 0;
|
for (reg = 0;
|
reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
|
reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
|
reg++)
|
reg++)
|
if (! call_used_regs[reg + REGNO (parm_rtx)])
|
if (! call_used_regs[reg + REGNO (parm_rtx)])
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Return true if the given call expression can be
|
/* Return true if the given call expression can be
|
turned into a sibling call.
|
turned into a sibling call.
|
DECL holds the declaration of the function to be called whereas
|
DECL holds the declaration of the function to be called whereas
|
EXP is the call expression itself. */
|
EXP is the call expression itself. */
|
|
|
static bool
|
static bool
|
s390_function_ok_for_sibcall (tree decl, tree exp)
|
s390_function_ok_for_sibcall (tree decl, tree exp)
|
{
|
{
|
/* The TPF epilogue uses register 1. */
|
/* The TPF epilogue uses register 1. */
|
if (TARGET_TPF_PROFILING)
|
if (TARGET_TPF_PROFILING)
|
return false;
|
return false;
|
|
|
/* The 31 bit PLT code uses register 12 (GOT pointer - caller saved)
|
/* The 31 bit PLT code uses register 12 (GOT pointer - caller saved)
|
which would have to be restored before the sibcall. */
|
which would have to be restored before the sibcall. */
|
if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
|
if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
|
return false;
|
return false;
|
|
|
/* Register 6 on s390 is available as an argument register but unfortunately
|
/* Register 6 on s390 is available as an argument register but unfortunately
|
"caller saved". This makes functions needing this register for arguments
|
"caller saved". This makes functions needing this register for arguments
|
not suitable for sibcalls. */
|
not suitable for sibcalls. */
|
if (TREE_OPERAND (exp, 1)
|
if (TREE_OPERAND (exp, 1)
|
&& s390_call_saved_register_used (TREE_OPERAND (exp, 1)))
|
&& s390_call_saved_register_used (TREE_OPERAND (exp, 1)))
|
return false;
|
return false;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Return the fixed registers used for condition codes. */
|
/* Return the fixed registers used for condition codes. */
|
|
|
static bool
|
static bool
|
s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
|
s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
|
{
|
{
|
*p1 = CC_REGNUM;
|
*p1 = CC_REGNUM;
|
*p2 = INVALID_REGNUM;
|
*p2 = INVALID_REGNUM;
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* This function is used by the call expanders of the machine description.
|
/* This function is used by the call expanders of the machine description.
|
It emits the call insn itself together with the necessary operations
|
It emits the call insn itself together with the necessary operations
|
to adjust the target address and returns the emitted insn.
|
to adjust the target address and returns the emitted insn.
|
ADDR_LOCATION is the target address rtx
|
ADDR_LOCATION is the target address rtx
|
TLS_CALL the location of the thread-local symbol
|
TLS_CALL the location of the thread-local symbol
|
RESULT_REG the register where the result of the call should be stored
|
RESULT_REG the register where the result of the call should be stored
|
RETADDR_REG the register where the return address should be stored
|
RETADDR_REG the register where the return address should be stored
|
If this parameter is NULL_RTX the call is considered
|
If this parameter is NULL_RTX the call is considered
|
to be a sibling call. */
|
to be a sibling call. */
|
|
|
rtx
|
rtx
|
s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
|
s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
|
rtx retaddr_reg)
|
rtx retaddr_reg)
|
{
|
{
|
bool plt_call = false;
|
bool plt_call = false;
|
rtx insn;
|
rtx insn;
|
rtx call;
|
rtx call;
|
rtx clobber;
|
rtx clobber;
|
rtvec vec;
|
rtvec vec;
|
|
|
/* Direct function calls need special treatment. */
|
/* Direct function calls need special treatment. */
|
if (GET_CODE (addr_location) == SYMBOL_REF)
|
if (GET_CODE (addr_location) == SYMBOL_REF)
|
{
|
{
|
/* When calling a global routine in PIC mode, we must
|
/* When calling a global routine in PIC mode, we must
|
replace the symbol itself with the PLT stub. */
|
replace the symbol itself with the PLT stub. */
|
if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
|
if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
|
{
|
{
|
addr_location = gen_rtx_UNSPEC (Pmode,
|
addr_location = gen_rtx_UNSPEC (Pmode,
|
gen_rtvec (1, addr_location),
|
gen_rtvec (1, addr_location),
|
UNSPEC_PLT);
|
UNSPEC_PLT);
|
addr_location = gen_rtx_CONST (Pmode, addr_location);
|
addr_location = gen_rtx_CONST (Pmode, addr_location);
|
plt_call = true;
|
plt_call = true;
|
}
|
}
|
|
|
/* Unless we can use the bras(l) insn, force the
|
/* Unless we can use the bras(l) insn, force the
|
routine address into a register. */
|
routine address into a register. */
|
if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH)
|
if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH)
|
{
|
{
|
if (flag_pic)
|
if (flag_pic)
|
addr_location = legitimize_pic_address (addr_location, 0);
|
addr_location = legitimize_pic_address (addr_location, 0);
|
else
|
else
|
addr_location = force_reg (Pmode, addr_location);
|
addr_location = force_reg (Pmode, addr_location);
|
}
|
}
|
}
|
}
|
|
|
/* If it is already an indirect call or the code above moved the
|
/* If it is already an indirect call or the code above moved the
|
SYMBOL_REF to somewhere else make sure the address can be found in
|
SYMBOL_REF to somewhere else make sure the address can be found in
|
register 1. */
|
register 1. */
|
if (retaddr_reg == NULL_RTX
|
if (retaddr_reg == NULL_RTX
|
&& GET_CODE (addr_location) != SYMBOL_REF
|
&& GET_CODE (addr_location) != SYMBOL_REF
|
&& !plt_call)
|
&& !plt_call)
|
{
|
{
|
emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
|
emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
|
addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
|
addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
|
}
|
}
|
|
|
addr_location = gen_rtx_MEM (QImode, addr_location);
|
addr_location = gen_rtx_MEM (QImode, addr_location);
|
call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
|
call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
|
|
|
if (result_reg != NULL_RTX)
|
if (result_reg != NULL_RTX)
|
call = gen_rtx_SET (VOIDmode, result_reg, call);
|
call = gen_rtx_SET (VOIDmode, result_reg, call);
|
|
|
if (retaddr_reg != NULL_RTX)
|
if (retaddr_reg != NULL_RTX)
|
{
|
{
|
clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
|
clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
|
|
|
if (tls_call != NULL_RTX)
|
if (tls_call != NULL_RTX)
|
vec = gen_rtvec (3, call, clobber,
|
vec = gen_rtvec (3, call, clobber,
|
gen_rtx_USE (VOIDmode, tls_call));
|
gen_rtx_USE (VOIDmode, tls_call));
|
else
|
else
|
vec = gen_rtvec (2, call, clobber);
|
vec = gen_rtvec (2, call, clobber);
|
|
|
call = gen_rtx_PARALLEL (VOIDmode, vec);
|
call = gen_rtx_PARALLEL (VOIDmode, vec);
|
}
|
}
|
|
|
insn = emit_call_insn (call);
|
insn = emit_call_insn (call);
|
|
|
/* 31-bit PLT stubs and tls calls use the GOT register implicitly. */
|
/* 31-bit PLT stubs and tls calls use the GOT register implicitly. */
|
if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
|
if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
|
{
|
{
|
/* s390_function_ok_for_sibcall should
|
/* s390_function_ok_for_sibcall should
|
have denied sibcalls in this case. */
|
have denied sibcalls in this case. */
|
gcc_assert (retaddr_reg != NULL_RTX);
|
gcc_assert (retaddr_reg != NULL_RTX);
|
|
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
|
}
|
}
|
return insn;
|
return insn;
|
}
|
}
|
|
|
/* Implement CONDITIONAL_REGISTER_USAGE. */
|
/* Implement CONDITIONAL_REGISTER_USAGE. */
|
|
|
void
|
void
|
s390_conditional_register_usage (void)
|
s390_conditional_register_usage (void)
|
{
|
{
|
int i;
|
int i;
|
|
|
if (flag_pic)
|
if (flag_pic)
|
{
|
{
|
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
|
}
|
}
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
fixed_regs[BASE_REGNUM] = 0;
|
fixed_regs[BASE_REGNUM] = 0;
|
call_used_regs[BASE_REGNUM] = 0;
|
call_used_regs[BASE_REGNUM] = 0;
|
fixed_regs[RETURN_REGNUM] = 0;
|
fixed_regs[RETURN_REGNUM] = 0;
|
call_used_regs[RETURN_REGNUM] = 0;
|
call_used_regs[RETURN_REGNUM] = 0;
|
}
|
}
|
if (TARGET_64BIT)
|
if (TARGET_64BIT)
|
{
|
{
|
for (i = 24; i < 32; i++)
|
for (i = 24; i < 32; i++)
|
call_used_regs[i] = call_really_used_regs[i] = 0;
|
call_used_regs[i] = call_really_used_regs[i] = 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
for (i = 18; i < 20; i++)
|
for (i = 18; i < 20; i++)
|
call_used_regs[i] = call_really_used_regs[i] = 0;
|
call_used_regs[i] = call_really_used_regs[i] = 0;
|
}
|
}
|
|
|
if (TARGET_SOFT_FLOAT)
|
if (TARGET_SOFT_FLOAT)
|
{
|
{
|
for (i = 16; i < 32; i++)
|
for (i = 16; i < 32; i++)
|
call_used_regs[i] = fixed_regs[i] = 1;
|
call_used_regs[i] = fixed_regs[i] = 1;
|
}
|
}
|
}
|
}
|
|
|
/* Corresponding function to eh_return expander. */
|
/* Corresponding function to eh_return expander. */
|
|
|
static GTY(()) rtx s390_tpf_eh_return_symbol;
|
static GTY(()) rtx s390_tpf_eh_return_symbol;
|
void
|
void
|
s390_emit_tpf_eh_return (rtx target)
|
s390_emit_tpf_eh_return (rtx target)
|
{
|
{
|
rtx insn, reg;
|
rtx insn, reg;
|
|
|
if (!s390_tpf_eh_return_symbol)
|
if (!s390_tpf_eh_return_symbol)
|
s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
|
s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
|
|
|
reg = gen_rtx_REG (Pmode, 2);
|
reg = gen_rtx_REG (Pmode, 2);
|
|
|
emit_move_insn (reg, target);
|
emit_move_insn (reg, target);
|
insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg,
|
insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg,
|
gen_rtx_REG (Pmode, RETURN_REGNUM));
|
gen_rtx_REG (Pmode, RETURN_REGNUM));
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
|
|
|
emit_move_insn (EH_RETURN_HANDLER_RTX, reg);
|
emit_move_insn (EH_RETURN_HANDLER_RTX, reg);
|
}
|
}
|
|
|
/* Rework the prologue/epilogue to avoid saving/restoring
|
/* Rework the prologue/epilogue to avoid saving/restoring
|
registers unnecessarily. */
|
registers unnecessarily. */
|
|
|
static void
|
static void
|
s390_optimize_prologue (void)
|
s390_optimize_prologue (void)
|
{
|
{
|
rtx insn, new_insn, next_insn;
|
rtx insn, new_insn, next_insn;
|
|
|
/* Do a final recompute of the frame-related data. */
|
/* Do a final recompute of the frame-related data. */
|
|
|
s390_update_frame_layout ();
|
s390_update_frame_layout ();
|
|
|
/* If all special registers are in fact used, there's nothing we
|
/* If all special registers are in fact used, there's nothing we
|
can do, so no point in walking the insn list. */
|
can do, so no point in walking the insn list. */
|
|
|
if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM
|
if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM
|
&& cfun_frame_layout.last_save_gpr >= BASE_REGNUM
|
&& cfun_frame_layout.last_save_gpr >= BASE_REGNUM
|
&& (TARGET_CPU_ZARCH
|
&& (TARGET_CPU_ZARCH
|
|| (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM
|
|| (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM
|
&& cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
|
&& cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
|
return;
|
return;
|
|
|
/* Search for prologue/epilogue insns and replace them. */
|
/* Search for prologue/epilogue insns and replace them. */
|
|
|
for (insn = get_insns (); insn; insn = next_insn)
|
for (insn = get_insns (); insn; insn = next_insn)
|
{
|
{
|
int first, last, off;
|
int first, last, off;
|
rtx set, base, offset;
|
rtx set, base, offset;
|
|
|
next_insn = NEXT_INSN (insn);
|
next_insn = NEXT_INSN (insn);
|
|
|
if (GET_CODE (insn) != INSN)
|
if (GET_CODE (insn) != INSN)
|
continue;
|
continue;
|
|
|
if (GET_CODE (PATTERN (insn)) == PARALLEL
|
if (GET_CODE (PATTERN (insn)) == PARALLEL
|
&& store_multiple_operation (PATTERN (insn), VOIDmode))
|
&& store_multiple_operation (PATTERN (insn), VOIDmode))
|
{
|
{
|
set = XVECEXP (PATTERN (insn), 0, 0);
|
set = XVECEXP (PATTERN (insn), 0, 0);
|
first = REGNO (SET_SRC (set));
|
first = REGNO (SET_SRC (set));
|
last = first + XVECLEN (PATTERN (insn), 0) - 1;
|
last = first + XVECLEN (PATTERN (insn), 0) - 1;
|
offset = const0_rtx;
|
offset = const0_rtx;
|
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
|
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
|
off = INTVAL (offset);
|
off = INTVAL (offset);
|
|
|
if (GET_CODE (base) != REG || off < 0)
|
if (GET_CODE (base) != REG || off < 0)
|
continue;
|
continue;
|
if (cfun_frame_layout.first_save_gpr != -1
|
if (cfun_frame_layout.first_save_gpr != -1
|
&& (cfun_frame_layout.first_save_gpr < first
|
&& (cfun_frame_layout.first_save_gpr < first
|
|| cfun_frame_layout.last_save_gpr > last))
|
|| cfun_frame_layout.last_save_gpr > last))
|
continue;
|
continue;
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
continue;
|
continue;
|
if (first > BASE_REGNUM || last < BASE_REGNUM)
|
if (first > BASE_REGNUM || last < BASE_REGNUM)
|
continue;
|
continue;
|
|
|
if (cfun_frame_layout.first_save_gpr != -1)
|
if (cfun_frame_layout.first_save_gpr != -1)
|
{
|
{
|
new_insn = save_gprs (base,
|
new_insn = save_gprs (base,
|
off + (cfun_frame_layout.first_save_gpr
|
off + (cfun_frame_layout.first_save_gpr
|
- first) * UNITS_PER_WORD,
|
- first) * UNITS_PER_WORD,
|
cfun_frame_layout.first_save_gpr,
|
cfun_frame_layout.first_save_gpr,
|
cfun_frame_layout.last_save_gpr);
|
cfun_frame_layout.last_save_gpr);
|
new_insn = emit_insn_before (new_insn, insn);
|
new_insn = emit_insn_before (new_insn, insn);
|
INSN_ADDRESSES_NEW (new_insn, -1);
|
INSN_ADDRESSES_NEW (new_insn, -1);
|
}
|
}
|
|
|
remove_insn (insn);
|
remove_insn (insn);
|
continue;
|
continue;
|
}
|
}
|
|
|
if (cfun_frame_layout.first_save_gpr == -1
|
if (cfun_frame_layout.first_save_gpr == -1
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == REG
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == REG
|
&& (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
|
&& (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
|
|| (!TARGET_CPU_ZARCH
|
|| (!TARGET_CPU_ZARCH
|
&& REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM))
|
&& REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM))
|
&& GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
|
&& GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
|
{
|
{
|
set = PATTERN (insn);
|
set = PATTERN (insn);
|
first = REGNO (SET_SRC (set));
|
first = REGNO (SET_SRC (set));
|
offset = const0_rtx;
|
offset = const0_rtx;
|
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
|
base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
|
off = INTVAL (offset);
|
off = INTVAL (offset);
|
|
|
if (GET_CODE (base) != REG || off < 0)
|
if (GET_CODE (base) != REG || off < 0)
|
continue;
|
continue;
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
continue;
|
continue;
|
|
|
remove_insn (insn);
|
remove_insn (insn);
|
continue;
|
continue;
|
}
|
}
|
|
|
if (GET_CODE (PATTERN (insn)) == PARALLEL
|
if (GET_CODE (PATTERN (insn)) == PARALLEL
|
&& load_multiple_operation (PATTERN (insn), VOIDmode))
|
&& load_multiple_operation (PATTERN (insn), VOIDmode))
|
{
|
{
|
set = XVECEXP (PATTERN (insn), 0, 0);
|
set = XVECEXP (PATTERN (insn), 0, 0);
|
first = REGNO (SET_DEST (set));
|
first = REGNO (SET_DEST (set));
|
last = first + XVECLEN (PATTERN (insn), 0) - 1;
|
last = first + XVECLEN (PATTERN (insn), 0) - 1;
|
offset = const0_rtx;
|
offset = const0_rtx;
|
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
|
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
|
off = INTVAL (offset);
|
off = INTVAL (offset);
|
|
|
if (GET_CODE (base) != REG || off < 0)
|
if (GET_CODE (base) != REG || off < 0)
|
continue;
|
continue;
|
if (cfun_frame_layout.first_restore_gpr != -1
|
if (cfun_frame_layout.first_restore_gpr != -1
|
&& (cfun_frame_layout.first_restore_gpr < first
|
&& (cfun_frame_layout.first_restore_gpr < first
|
|| cfun_frame_layout.last_restore_gpr > last))
|
|| cfun_frame_layout.last_restore_gpr > last))
|
continue;
|
continue;
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
continue;
|
continue;
|
if (first > BASE_REGNUM || last < BASE_REGNUM)
|
if (first > BASE_REGNUM || last < BASE_REGNUM)
|
continue;
|
continue;
|
|
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
if (cfun_frame_layout.first_restore_gpr != -1)
|
{
|
{
|
new_insn = restore_gprs (base,
|
new_insn = restore_gprs (base,
|
off + (cfun_frame_layout.first_restore_gpr
|
off + (cfun_frame_layout.first_restore_gpr
|
- first) * UNITS_PER_WORD,
|
- first) * UNITS_PER_WORD,
|
cfun_frame_layout.first_restore_gpr,
|
cfun_frame_layout.first_restore_gpr,
|
cfun_frame_layout.last_restore_gpr);
|
cfun_frame_layout.last_restore_gpr);
|
new_insn = emit_insn_before (new_insn, insn);
|
new_insn = emit_insn_before (new_insn, insn);
|
INSN_ADDRESSES_NEW (new_insn, -1);
|
INSN_ADDRESSES_NEW (new_insn, -1);
|
}
|
}
|
|
|
remove_insn (insn);
|
remove_insn (insn);
|
continue;
|
continue;
|
}
|
}
|
|
|
if (cfun_frame_layout.first_restore_gpr == -1
|
if (cfun_frame_layout.first_restore_gpr == -1
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (SET_DEST (PATTERN (insn))) == REG
|
&& GET_CODE (SET_DEST (PATTERN (insn))) == REG
|
&& (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
|
&& (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
|
|| (!TARGET_CPU_ZARCH
|
|| (!TARGET_CPU_ZARCH
|
&& REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM))
|
&& REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM))
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
|
&& GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
|
{
|
{
|
set = PATTERN (insn);
|
set = PATTERN (insn);
|
first = REGNO (SET_DEST (set));
|
first = REGNO (SET_DEST (set));
|
offset = const0_rtx;
|
offset = const0_rtx;
|
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
|
base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
|
off = INTVAL (offset);
|
off = INTVAL (offset);
|
|
|
if (GET_CODE (base) != REG || off < 0)
|
if (GET_CODE (base) != REG || off < 0)
|
continue;
|
continue;
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
if (REGNO (base) != STACK_POINTER_REGNUM
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (base) != HARD_FRAME_POINTER_REGNUM)
|
continue;
|
continue;
|
|
|
remove_insn (insn);
|
remove_insn (insn);
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Perform machine-dependent processing. */
|
/* Perform machine-dependent processing. */
|
|
|
static void
|
static void
|
s390_reorg (void)
|
s390_reorg (void)
|
{
|
{
|
bool pool_overflow = false;
|
bool pool_overflow = false;
|
|
|
/* Make sure all splits have been performed; splits after
|
/* Make sure all splits have been performed; splits after
|
machine_dependent_reorg might confuse insn length counts. */
|
machine_dependent_reorg might confuse insn length counts. */
|
split_all_insns_noflow ();
|
split_all_insns_noflow ();
|
|
|
/* From here on decomposed literal pool addresses must be accepted. */
|
/* From here on decomposed literal pool addresses must be accepted. */
|
cfun->machine->decomposed_literal_pool_addresses_ok_p = true;
|
cfun->machine->decomposed_literal_pool_addresses_ok_p = true;
|
|
|
/* Install the main literal pool and the associated base
|
/* Install the main literal pool and the associated base
|
register load insns.
|
register load insns.
|
|
|
In addition, there are two problematic situations we need
|
In addition, there are two problematic situations we need
|
to correct:
|
to correct:
|
|
|
- the literal pool might be > 4096 bytes in size, so that
|
- the literal pool might be > 4096 bytes in size, so that
|
some of its elements cannot be directly accessed
|
some of its elements cannot be directly accessed
|
|
|
- a branch target might be > 64K away from the branch, so that
|
- a branch target might be > 64K away from the branch, so that
|
it is not possible to use a PC-relative instruction.
|
it is not possible to use a PC-relative instruction.
|
|
|
To fix those, we split the single literal pool into multiple
|
To fix those, we split the single literal pool into multiple
|
pool chunks, reloading the pool base register at various
|
pool chunks, reloading the pool base register at various
|
points throughout the function to ensure it always points to
|
points throughout the function to ensure it always points to
|
the pool chunk the following code expects, and / or replace
|
the pool chunk the following code expects, and / or replace
|
PC-relative branches by absolute branches.
|
PC-relative branches by absolute branches.
|
|
|
However, the two problems are interdependent: splitting the
|
However, the two problems are interdependent: splitting the
|
literal pool can move a branch further away from its target,
|
literal pool can move a branch further away from its target,
|
causing the 64K limit to overflow, and on the other hand,
|
causing the 64K limit to overflow, and on the other hand,
|
replacing a PC-relative branch by an absolute branch means
|
replacing a PC-relative branch by an absolute branch means
|
we need to put the branch target address into the literal
|
we need to put the branch target address into the literal
|
pool, possibly causing it to overflow.
|
pool, possibly causing it to overflow.
|
|
|
So, we loop trying to fix up both problems until we manage
|
So, we loop trying to fix up both problems until we manage
|
to satisfy both conditions at the same time. Note that the
|
to satisfy both conditions at the same time. Note that the
|
loop is guaranteed to terminate as every pass of the loop
|
loop is guaranteed to terminate as every pass of the loop
|
strictly decreases the total number of PC-relative branches
|
strictly decreases the total number of PC-relative branches
|
in the function. (This is not completely true as there
|
in the function. (This is not completely true as there
|
might be branch-over-pool insns introduced by chunkify_start.
|
might be branch-over-pool insns introduced by chunkify_start.
|
Those never need to be split however.) */
|
Those never need to be split however.) */
|
|
|
for (;;)
|
for (;;)
|
{
|
{
|
struct constant_pool *pool = NULL;
|
struct constant_pool *pool = NULL;
|
|
|
/* Collect the literal pool. */
|
/* Collect the literal pool. */
|
if (!pool_overflow)
|
if (!pool_overflow)
|
{
|
{
|
pool = s390_mainpool_start ();
|
pool = s390_mainpool_start ();
|
if (!pool)
|
if (!pool)
|
pool_overflow = true;
|
pool_overflow = true;
|
}
|
}
|
|
|
/* If literal pool overflowed, start to chunkify it. */
|
/* If literal pool overflowed, start to chunkify it. */
|
if (pool_overflow)
|
if (pool_overflow)
|
pool = s390_chunkify_start ();
|
pool = s390_chunkify_start ();
|
|
|
/* Split out-of-range branches. If this has created new
|
/* Split out-of-range branches. If this has created new
|
literal pool entries, cancel current chunk list and
|
literal pool entries, cancel current chunk list and
|
recompute it. zSeries machines have large branch
|
recompute it. zSeries machines have large branch
|
instructions, so we never need to split a branch. */
|
instructions, so we never need to split a branch. */
|
if (!TARGET_CPU_ZARCH && s390_split_branches ())
|
if (!TARGET_CPU_ZARCH && s390_split_branches ())
|
{
|
{
|
if (pool_overflow)
|
if (pool_overflow)
|
s390_chunkify_cancel (pool);
|
s390_chunkify_cancel (pool);
|
else
|
else
|
s390_mainpool_cancel (pool);
|
s390_mainpool_cancel (pool);
|
|
|
continue;
|
continue;
|
}
|
}
|
|
|
/* If we made it up to here, both conditions are satisfied.
|
/* If we made it up to here, both conditions are satisfied.
|
Finish up literal pool related changes. */
|
Finish up literal pool related changes. */
|
if (pool_overflow)
|
if (pool_overflow)
|
s390_chunkify_finish (pool);
|
s390_chunkify_finish (pool);
|
else
|
else
|
s390_mainpool_finish (pool);
|
s390_mainpool_finish (pool);
|
|
|
/* We're done splitting branches. */
|
/* We're done splitting branches. */
|
cfun->machine->split_branches_pending_p = false;
|
cfun->machine->split_branches_pending_p = false;
|
break;
|
break;
|
}
|
}
|
|
|
/* Generate out-of-pool execute target insns. */
|
/* Generate out-of-pool execute target insns. */
|
if (TARGET_CPU_ZARCH)
|
if (TARGET_CPU_ZARCH)
|
{
|
{
|
rtx insn, label, target;
|
rtx insn, label, target;
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
{
|
{
|
label = s390_execute_label (insn);
|
label = s390_execute_label (insn);
|
if (!label)
|
if (!label)
|
continue;
|
continue;
|
|
|
gcc_assert (label != const0_rtx);
|
gcc_assert (label != const0_rtx);
|
|
|
target = emit_label (XEXP (label, 0));
|
target = emit_label (XEXP (label, 0));
|
INSN_ADDRESSES_NEW (target, -1);
|
INSN_ADDRESSES_NEW (target, -1);
|
|
|
target = emit_insn (s390_execute_target (insn));
|
target = emit_insn (s390_execute_target (insn));
|
INSN_ADDRESSES_NEW (target, -1);
|
INSN_ADDRESSES_NEW (target, -1);
|
}
|
}
|
}
|
}
|
|
|
/* Try to optimize prologue and epilogue further. */
|
/* Try to optimize prologue and epilogue further. */
|
s390_optimize_prologue ();
|
s390_optimize_prologue ();
|
}
|
}
|
|
|
|
|
/* Initialize GCC target structure. */
|
/* Initialize GCC target structure. */
|
|
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
|
#undef TARGET_ASM_ALIGNED_DI_OP
|
#undef TARGET_ASM_ALIGNED_DI_OP
|
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
|
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
|
#undef TARGET_ASM_INTEGER
|
#undef TARGET_ASM_INTEGER
|
#define TARGET_ASM_INTEGER s390_assemble_integer
|
#define TARGET_ASM_INTEGER s390_assemble_integer
|
|
|
#undef TARGET_ASM_OPEN_PAREN
|
#undef TARGET_ASM_OPEN_PAREN
|
#define TARGET_ASM_OPEN_PAREN ""
|
#define TARGET_ASM_OPEN_PAREN ""
|
|
|
#undef TARGET_ASM_CLOSE_PAREN
|
#undef TARGET_ASM_CLOSE_PAREN
|
#define TARGET_ASM_CLOSE_PAREN ""
|
#define TARGET_ASM_CLOSE_PAREN ""
|
|
|
#undef TARGET_DEFAULT_TARGET_FLAGS
|
#undef TARGET_DEFAULT_TARGET_FLAGS
|
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
|
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
|
#undef TARGET_HANDLE_OPTION
|
#undef TARGET_HANDLE_OPTION
|
#define TARGET_HANDLE_OPTION s390_handle_option
|
#define TARGET_HANDLE_OPTION s390_handle_option
|
|
|
#undef TARGET_ENCODE_SECTION_INFO
|
#undef TARGET_ENCODE_SECTION_INFO
|
#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
|
#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
|
|
|
#ifdef HAVE_AS_TLS
|
#ifdef HAVE_AS_TLS
|
#undef TARGET_HAVE_TLS
|
#undef TARGET_HAVE_TLS
|
#define TARGET_HAVE_TLS true
|
#define TARGET_HAVE_TLS true
|
#endif
|
#endif
|
#undef TARGET_CANNOT_FORCE_CONST_MEM
|
#undef TARGET_CANNOT_FORCE_CONST_MEM
|
#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem
|
#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem
|
|
|
#undef TARGET_DELEGITIMIZE_ADDRESS
|
#undef TARGET_DELEGITIMIZE_ADDRESS
|
#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
|
#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
|
|
|
#undef TARGET_RETURN_IN_MEMORY
|
#undef TARGET_RETURN_IN_MEMORY
|
#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
|
#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
|
|
|
#undef TARGET_INIT_BUILTINS
|
#undef TARGET_INIT_BUILTINS
|
#define TARGET_INIT_BUILTINS s390_init_builtins
|
#define TARGET_INIT_BUILTINS s390_init_builtins
|
#undef TARGET_EXPAND_BUILTIN
|
#undef TARGET_EXPAND_BUILTIN
|
#define TARGET_EXPAND_BUILTIN s390_expand_builtin
|
#define TARGET_EXPAND_BUILTIN s390_expand_builtin
|
|
|
#undef TARGET_ASM_OUTPUT_MI_THUNK
|
#undef TARGET_ASM_OUTPUT_MI_THUNK
|
#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk
|
#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk
|
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
|
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
|
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
|
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
|
|
|
#undef TARGET_SCHED_ADJUST_PRIORITY
|
#undef TARGET_SCHED_ADJUST_PRIORITY
|
#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
|
#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
|
#undef TARGET_SCHED_ISSUE_RATE
|
#undef TARGET_SCHED_ISSUE_RATE
|
#define TARGET_SCHED_ISSUE_RATE s390_issue_rate
|
#define TARGET_SCHED_ISSUE_RATE s390_issue_rate
|
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
|
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
|
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
|
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
|
|
|
#undef TARGET_CANNOT_COPY_INSN_P
|
#undef TARGET_CANNOT_COPY_INSN_P
|
#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p
|
#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p
|
#undef TARGET_RTX_COSTS
|
#undef TARGET_RTX_COSTS
|
#define TARGET_RTX_COSTS s390_rtx_costs
|
#define TARGET_RTX_COSTS s390_rtx_costs
|
#undef TARGET_ADDRESS_COST
|
#undef TARGET_ADDRESS_COST
|
#define TARGET_ADDRESS_COST s390_address_cost
|
#define TARGET_ADDRESS_COST s390_address_cost
|
|
|
#undef TARGET_MACHINE_DEPENDENT_REORG
|
#undef TARGET_MACHINE_DEPENDENT_REORG
|
#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
|
#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
|
|
|
#undef TARGET_VALID_POINTER_MODE
|
#undef TARGET_VALID_POINTER_MODE
|
#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
|
#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
|
|
|
#undef TARGET_BUILD_BUILTIN_VA_LIST
|
#undef TARGET_BUILD_BUILTIN_VA_LIST
|
#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
|
#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
|
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
|
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
|
#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
|
#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
|
|
|
#undef TARGET_PROMOTE_FUNCTION_ARGS
|
#undef TARGET_PROMOTE_FUNCTION_ARGS
|
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
|
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
|
#undef TARGET_PROMOTE_FUNCTION_RETURN
|
#undef TARGET_PROMOTE_FUNCTION_RETURN
|
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
|
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
|
#undef TARGET_PASS_BY_REFERENCE
|
#undef TARGET_PASS_BY_REFERENCE
|
#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference
|
#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference
|
|
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
|
#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
|
|
|
#undef TARGET_FIXED_CONDITION_CODE_REGS
|
#undef TARGET_FIXED_CONDITION_CODE_REGS
|
#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
|
#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
|
|
|
#undef TARGET_CC_MODES_COMPATIBLE
|
#undef TARGET_CC_MODES_COMPATIBLE
|
#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
|
#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
|
|
|
#undef TARGET_INVALID_WITHIN_DOLOOP
|
#undef TARGET_INVALID_WITHIN_DOLOOP
|
#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null
|
#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null
|
|
|
#ifdef HAVE_AS_TLS
|
#ifdef HAVE_AS_TLS
|
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
|
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
|
#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel
|
#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel
|
#endif
|
#endif
|
|
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#undef TARGET_MANGLE_FUNDAMENTAL_TYPE
|
#undef TARGET_MANGLE_FUNDAMENTAL_TYPE
|
#define TARGET_MANGLE_FUNDAMENTAL_TYPE s390_mangle_fundamental_type
|
#define TARGET_MANGLE_FUNDAMENTAL_TYPE s390_mangle_fundamental_type
|
#endif
|
#endif
|
|
|
#undef TARGET_SCALAR_MODE_SUPPORTED_P
|
#undef TARGET_SCALAR_MODE_SUPPORTED_P
|
#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p
|
#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p
|
|
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
#include "gt-s390.h"
|
#include "gt-s390.h"
|
|
|