#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "function.h"
#include "expr.h"
#include "optabs.h"
#include "recog.h"
#include "toplev.h"
#include "ggc.h"
#include "tm_p.h"
#include "debug.h"
#include "target.h"
#include "target-def.h"
#include "cfglayout.h"
static HOST_WIDE_INT apparent_fsize;
static HOST_WIDE_INT actual_fsize;
static int num_gfregs;
rtx sparc_compare_op0, sparc_compare_op1;
bool sparc_emitting_epilogue;
bool sparc_skip_caller_unimp;
char leaf_reg_remap[] =
{ 0, 1, 2, 3, 4, 5, 6, 7,
-1, -1, -1, -1, -1, -1, 14, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
8, 9, 10, 11, 12, 13, -1, 15,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100};
char sparc_leaf_regs[] =
{ 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1};
struct machine_function GTY(())
{
const char *some_ld_name;
};
static const char *frame_base_name;
static HOST_WIDE_INT frame_base_offset;
static void sparc_init_modes (void);
static int save_regs (FILE *, int, int, const char *, int, int, HOST_WIDE_INT);
static int restore_regs (FILE *, int, int, const char *, int, int);
static void build_big_number (FILE *, HOST_WIDE_INT, const char *);
static void scan_record_type (tree, int *, int *, int *);
static int function_arg_slotno (const CUMULATIVE_ARGS *, enum machine_mode,
tree, int, int, int *, int *);
static int supersparc_adjust_cost (rtx, rtx, rtx, int);
static int hypersparc_adjust_cost (rtx, rtx, rtx, int);
static void sparc_output_addr_vec (rtx);
static void sparc_output_addr_diff_vec (rtx);
static void sparc_output_deferred_case_vectors (void);
static int check_return_regs (rtx);
static rtx sparc_builtin_saveregs (void);
static int epilogue_renumber (rtx *, int);
static bool sparc_assemble_integer (rtx, unsigned int, int);
static int set_extends (rtx);
static void output_restore_regs (FILE *, int);
static void sparc_output_function_prologue (FILE *, HOST_WIDE_INT);
static void sparc_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void sparc_function_epilogue (FILE *, HOST_WIDE_INT, int);
static void sparc_function_prologue (FILE *, HOST_WIDE_INT, int);
#ifdef OBJECT_FORMAT_ELF
static void sparc_elf_asm_named_section (const char *, unsigned int);
#endif
static void sparc_aout_select_rtx_section (enum machine_mode, rtx,
unsigned HOST_WIDE_INT)
ATTRIBUTE_UNUSED;
static int sparc_adjust_cost (rtx, rtx, rtx, int);
static int sparc_issue_rate (void);
static void sparc_sched_init (FILE *, int, int);
static int sparc_use_dfa_pipeline_interface (void);
static int sparc_use_sched_lookahead (void);
static void emit_soft_tfmode_libcall (const char *, int, rtx *);
static void emit_soft_tfmode_binop (enum rtx_code, rtx *);
static void emit_soft_tfmode_unop (enum rtx_code, rtx *);
static void emit_soft_tfmode_cvt (enum rtx_code, rtx *);
static void emit_hard_tfmode_operation (enum rtx_code, rtx *);
static bool sparc_function_ok_for_sibcall (tree, tree);
static void sparc_init_libfuncs (void);
static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
static struct machine_function * sparc_init_machine_status (void);
static bool sparc_cannot_force_const_mem (rtx);
static rtx sparc_tls_get_addr (void);
static rtx sparc_tls_got (void);
static const char *get_some_local_dynamic_name (void);
static int get_some_local_dynamic_name_1 (rtx *, void *);
static bool sparc_rtx_costs (rtx, int, int, int *);
static bool sparc_promote_prototypes (tree);
static rtx sparc_struct_value_rtx (tree, int);
static bool sparc_return_in_memory (tree, tree);
static bool sparc_strict_argument_naming (CUMULATIVE_ARGS *);
const char *sparc_cmodel_string;
enum cmodel sparc_cmodel;
char sparc_hard_reg_printed[8];
struct sparc_cpu_select sparc_select[] =
{
{ (char *)0, "default", 1, 1 },
{ (char *)0, "-mcpu=", 1, 1 },
{ (char *)0, "-mtune=", 1, 0 },
{ 0, 0, 0, 0 }
};
enum processor_type sparc_cpu;
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
#ifndef OBJECT_FORMAT_ELF
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#endif
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP "\t.uahalf\t"
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.uaword\t"
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaxword\t"
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER sparc_assemble_integer
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE sparc_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE sparc_output_function_epilogue
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST sparc_adjust_cost
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE sparc_issue_rate
#undef TARGET_SCHED_INIT
#define TARGET_SCHED_INIT sparc_sched_init
#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE sparc_use_dfa_pipeline_interface
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD sparc_use_sched_lookahead
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL sparc_function_ok_for_sibcall
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS sparc_init_libfuncs
#ifdef HAVE_AS_TLS
#undef TARGET_HAVE_TLS
#define TARGET_HAVE_TLS true
#endif
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM sparc_cannot_force_const_mem
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS sparc_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_0
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES sparc_promote_prototypes
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX sparc_struct_value_rtx
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY sparc_return_in_memory
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS sparc_builtin_saveregs
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING sparc_strict_argument_naming
struct gcc_target targetm = TARGET_INITIALIZER;
void
sparc_override_options (void)
{
static struct code_model {
const char *const name;
const int value;
} const cmodels[] = {
{ "32", CM_32 },
{ "medlow", CM_MEDLOW },
{ "medmid", CM_MEDMID },
{ "medany", CM_MEDANY },
{ "embmedany", CM_EMBMEDANY },
{ 0, 0 }
};
const struct code_model *cmodel;
static struct cpu_default {
const int cpu;
const char *const name;
} const cpu_default[] = {
{ TARGET_CPU_sparc, "cypress" },
{ TARGET_CPU_sparclet, "tsc701" },
{ TARGET_CPU_sparclite, "f930" },
{ TARGET_CPU_v8, "v8" },
{ TARGET_CPU_hypersparc, "hypersparc" },
{ TARGET_CPU_sparclite86x, "sparclite86x" },
{ TARGET_CPU_supersparc, "supersparc" },
{ TARGET_CPU_v9, "v9" },
{ TARGET_CPU_ultrasparc, "ultrasparc" },
{ TARGET_CPU_ultrasparc3, "ultrasparc3" },
{ 0, 0 }
};
const struct cpu_default *def;
static struct cpu_table {
const char *const name;
const enum processor_type processor;
const int disable;
const int enable;
} const cpu_table[] = {
{ "v7", PROCESSOR_V7, MASK_ISA, 0 },
{ "cypress", PROCESSOR_CYPRESS, MASK_ISA, 0 },
{ "v8", PROCESSOR_V8, MASK_ISA, MASK_V8 },
{ "supersparc", PROCESSOR_SUPERSPARC, MASK_ISA, MASK_V8 },
{ "sparclite", PROCESSOR_SPARCLITE, MASK_ISA, MASK_SPARCLITE },
{ "f930", PROCESSOR_F930, MASK_ISA|MASK_FPU, MASK_SPARCLITE },
{ "f934", PROCESSOR_F934, MASK_ISA, MASK_SPARCLITE|MASK_FPU },
{ "hypersparc", PROCESSOR_HYPERSPARC, MASK_ISA, MASK_V8|MASK_FPU },
{ "sparclite86x", PROCESSOR_SPARCLITE86X, MASK_ISA|MASK_FPU,
MASK_SPARCLITE },
{ "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET },
{ "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET },
{ "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 },
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9
|MASK_DEPRECATED_V8_INSNS},
{ "ultrasparc3", PROCESSOR_ULTRASPARC3, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
{ 0, 0, 0, 0 }
};
const struct cpu_table *cpu;
const struct sparc_cpu_select *sel;
int fpu;
#ifndef SPARC_BI_ARCH
if (! TARGET_64BIT != DEFAULT_ARCH32_P)
error ("%s is not supported by this configuration",
DEFAULT_ARCH32_P ? "-m64" : "-m32");
#endif
if (TARGET_64BIT && ! TARGET_LONG_DOUBLE_128)
{
error ("-mlong-double-64 not allowed with -m64");
target_flags |= MASK_LONG_DOUBLE_128;
}
sparc_cmodel = SPARC_DEFAULT_CMODEL;
#ifdef SPARC_BI_ARCH
if (TARGET_ARCH32)
sparc_cmodel = CM_32;
#endif
if (sparc_cmodel_string != NULL)
{
if (TARGET_ARCH64)
{
for (cmodel = &cmodels[0]; cmodel->name; cmodel++)
if (strcmp (sparc_cmodel_string, cmodel->name) == 0)
break;
if (cmodel->name == NULL)
error ("bad value (%s) for -mcmodel= switch", sparc_cmodel_string);
else
sparc_cmodel = cmodel->value;
}
else
error ("-mcmodel= is not supported on 32 bit systems");
}
fpu = TARGET_FPU;
for (def = &cpu_default[0]; def->name; ++def)
if (def->cpu == TARGET_CPU_DEFAULT)
break;
if (! def->name)
abort ();
sparc_select[0].string = def->name;
for (sel = &sparc_select[0]; sel->name; ++sel)
{
if (sel->string)
{
for (cpu = &cpu_table[0]; cpu->name; ++cpu)
if (! strcmp (sel->string, cpu->name))
{
if (sel->set_tune_p)
sparc_cpu = cpu->processor;
if (sel->set_arch_p)
{
target_flags &= ~cpu->disable;
target_flags |= cpu->enable;
}
break;
}
if (! cpu->name)
error ("bad value (%s) for %s switch", sel->string, sel->name);
}
}
if (TARGET_FPU_SET)
{
target_flags = (target_flags & ~MASK_FPU) | fpu;
target_flags &= ~MASK_FPU_SET;
}
if (! TARGET_FPU)
target_flags &= ~MASK_VIS;
if (TARGET_VIS || TARGET_ARCH64)
{
target_flags |= MASK_V9;
target_flags &= ~(MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE);
}
if (TARGET_V9 && TARGET_ARCH32)
target_flags |= MASK_DEPRECATED_V8_INSNS;
if (! TARGET_V9 || TARGET_ARCH64)
target_flags &= ~MASK_V8PLUS;
if (TARGET_ARCH32)
target_flags &= ~MASK_STACK_BIAS;
if (align_functions == 0
&& (sparc_cpu == PROCESSOR_ULTRASPARC
|| sparc_cpu == PROCESSOR_ULTRASPARC3))
align_functions = 32;
if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN)
flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1);
if (!TARGET_ARCH64)
targetm.asm_out.unaligned_op.di = NULL;
sparc_init_modes ();
init_machine_status = sparc_init_machine_status;
}
int
v9_regcmp_p (enum rtx_code code)
{
return (code == EQ || code == NE || code == GE || code == LT
|| code == LE || code == GT);
}
int
reg_or_0_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (op == const0_rtx)
return 1;
if (GET_MODE (op) == VOIDmode && GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& CONST_DOUBLE_LOW (op) == 0)
return 1;
if (fp_zero_operand (op, mode))
return 1;
return 0;
}
int
const1_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return op == const1_rtx;
}
int
fp_zero_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE_CLASS (GET_MODE (op)) != MODE_FLOAT)
return 0;
return op == CONST0_RTX (mode);
}
int
fp_register_operand (rtx op, enum machine_mode mode)
{
if (! register_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return GET_CODE (op) == REG && SPARC_FP_REG_P (REGNO (op));
}
int
fp_sethi_p (rtx op)
{
if (GET_CODE (op) == CONST_DOUBLE)
{
REAL_VALUE_TYPE r;
long i;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
if (REAL_VALUES_EQUAL (r, dconst0) &&
! REAL_VALUE_MINUS_ZERO (r))
return 0;
REAL_VALUE_TO_TARGET_SINGLE (r, i);
if (SPARC_SETHI_P (i))
return 1;
}
return 0;
}
int
fp_mov_p (rtx op)
{
if (GET_CODE (op) == CONST_DOUBLE)
{
REAL_VALUE_TYPE r;
long i;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
if (REAL_VALUES_EQUAL (r, dconst0) &&
! REAL_VALUE_MINUS_ZERO (r))
return 0;
REAL_VALUE_TO_TARGET_SINGLE (r, i);
if (SPARC_SIMM13_P (i))
return 1;
}
return 0;
}
int
fp_high_losum_p (rtx op)
{
if (GET_CODE (op) == CONST_DOUBLE)
{
REAL_VALUE_TYPE r;
long i;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
if (REAL_VALUES_EQUAL (r, dconst0) &&
! REAL_VALUE_MINUS_ZERO (r))
return 0;
REAL_VALUE_TO_TARGET_SINGLE (r, i);
if (! SPARC_SETHI_P (i)
&& ! SPARC_SIMM13_P (i))
return 1;
}
return 0;
}
int
intreg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (register_operand (op, SImode)
|| (TARGET_ARCH64 && register_operand (op, DImode)));
}
int
fcc_reg_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) != REG)
return 0;
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (mode == VOIDmode
&& (GET_MODE (op) != CCFPmode && GET_MODE (op) != CCFPEmode))
return 0;
#if 0
if (reg_renumber == 0)
return REGNO (op) >= FIRST_PSEUDO_REGISTER;
return REGNO_OK_FOR_CCFP_P (REGNO (op));
#else
return (unsigned) REGNO (op) - SPARC_FIRST_V9_FCC_REG < 4;
#endif
}
int
fcc0_reg_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) != REG)
return 0;
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (mode == VOIDmode
&& (GET_MODE (op) != CCFPmode && GET_MODE (op) != CCFPEmode))
return 0;
return REGNO (op) == SPARC_FCC_REG;
}
int
icc_or_fcc_reg_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == REG && REGNO (op) == SPARC_ICC_REG)
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (mode == VOIDmode
&& GET_MODE (op) != CCmode && GET_MODE (op) != CCXmode)
return 0;
return 1;
}
return fcc_reg_operand (op, mode);
}
int
restore_operand (rtx op, enum machine_mode mode)
{
return (GET_CODE (op) == REG && GET_MODE (op) == mode
&& (REGNO (op) < 8 || (REGNO (op) >= 24 && REGNO (op) < 32)));
}
int
call_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) != MEM)
abort ();
op = XEXP (op, 0);
return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));
}
int
call_operand_address (rtx op, enum machine_mode mode)
{
return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));
}
int
tls_symbolic_operand (rtx op)
{
if (GET_CODE (op) != SYMBOL_REF)
return 0;
return SYMBOL_REF_TLS_MODEL (op);
}
int
tgd_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return tls_symbolic_operand (op) == TLS_MODEL_GLOBAL_DYNAMIC;
}
int
tld_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return tls_symbolic_operand (op) == TLS_MODEL_LOCAL_DYNAMIC;
}
int
tie_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return tls_symbolic_operand (op) == TLS_MODEL_INITIAL_EXEC;
}
int
tle_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return tls_symbolic_operand (op) == TLS_MODEL_LOCAL_EXEC;
}
int
symbolic_operand (register rtx op, enum machine_mode mode)
{
enum machine_mode omode = GET_MODE (op);
if (omode != mode && omode != VOIDmode && mode != VOIDmode)
return 0;
switch (GET_CODE (op))
{
case SYMBOL_REF:
return !SYMBOL_REF_TLS_MODEL (op);
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return (((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
&& !SYMBOL_REF_TLS_MODEL (XEXP (op, 0)))
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
default:
return 0;
}
}
int
symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
return ((GET_CODE (op) == SYMBOL_REF && !SYMBOL_REF_TLS_MODEL (op))
|| GET_CODE (op) == CONST || GET_CODE (op) == HIGH
|| GET_CODE (op) == LABEL_REF);
}
int
label_ref_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) != LABEL_REF)
return 0;
if (GET_MODE (op) != mode)
return 0;
return 1;
}
int
sp64_medium_pic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != CONST)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != MINUS)
return 0;
if (GET_CODE (XEXP (op, 0)) != SYMBOL_REF)
return 0;
if (GET_CODE (XEXP (op, 1)) != CONST)
return 0;
if (GET_CODE (XEXP (XEXP (op, 1), 0)) != MINUS)
return 0;
return 1;
}
int
data_segment_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case SYMBOL_REF :
return ! SYMBOL_REF_FUNCTION_P (op);
case PLUS :
case CONST :
return data_segment_operand (XEXP (op, 0), VOIDmode);
default :
return 0;
}
}
int
text_segment_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case LABEL_REF :
return 1;
case SYMBOL_REF :
return SYMBOL_REF_FUNCTION_P (op);
case PLUS :
case CONST :
return text_segment_operand (XEXP (op, 0), VOIDmode);
default :
return 0;
}
}
int
reg_or_nonsymb_mem_operand (register rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
return 1;
return 0;
}
int
splittable_symbolic_memory_operand (rtx op,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != MEM)
return 0;
if (! symbolic_operand (XEXP (op, 0), Pmode))
return 0;
return 1;
}
int
splittable_immediate_memory_operand (rtx op,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != MEM)
return 0;
if (! immediate_operand (XEXP (op, 0), Pmode))
return 0;
return 1;
}
int
eq_or_neq (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == EQ || GET_CODE (op) == NE);
}
int
normal_comp_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
enum rtx_code code;
if (!COMPARISON_P (op))
return 0;
if (GET_MODE (XEXP (op, 0)) == CCFPmode
|| GET_MODE (XEXP (op, 0)) == CCFPEmode)
return 1;
code = GET_CODE (op);
return (code != NE && code != EQ && code != GEU && code != LTU);
}
int
noov_compare_op (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
enum rtx_code code;
if (!COMPARISON_P (op))
return 0;
code = GET_CODE (op);
if (GET_MODE (XEXP (op, 0)) == CC_NOOVmode
|| GET_MODE (XEXP (op, 0)) == CCX_NOOVmode)
return (code == EQ || code == NE || code == GE || code == LT);
return 1;
}
int
noov_compare64_op (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
enum rtx_code code;
if (! TARGET_V9)
return 0;
if (!COMPARISON_P (op))
return 0;
code = GET_CODE (op);
if (GET_MODE (XEXP (op, 0)) == CCX_NOOVmode)
return (code == EQ || code == NE || code == GE || code == LT);
return (GET_MODE (XEXP (op, 0)) == CCXmode);
}
int
v9_regcmp_op (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
enum rtx_code code;
if (!COMPARISON_P (op))
return 0;
code = GET_CODE (op);
return v9_regcmp_p (code);
}
int
extend_op (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
}
int
cc_arithop (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == AND
|| GET_CODE (op) == IOR
|| GET_CODE (op) == XOR)
return 1;
return 0;
}
int
cc_arithopn (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == AND
|| GET_CODE (op) == IOR);
}
int
arith_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) != CONST_INT)
return 0;
return SMALL_INT32 (op);
}
int
arith_4096_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != CONST_INT)
return 0;
else
return INTVAL (op) == 4096;
}
int
arith_add_operand (rtx op, enum machine_mode mode)
{
return arith_operand (op, mode) || arith_4096_operand (op, mode);
}
int
const64_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return ((GET_CODE (op) == CONST_INT
&& SPARC_SIMM13_P (INTVAL (op)))
#if HOST_BITS_PER_WIDE_INT != 64
|| (GET_CODE (op) == CONST_DOUBLE
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))
&& (CONST_DOUBLE_HIGH (op) ==
((CONST_DOUBLE_LOW (op) & 0x80000000) != 0 ?
(HOST_WIDE_INT)-1 : 0)))
#endif
);
}
int
const64_high_operand (rtx op, enum machine_mode mode)
{
return ((GET_CODE (op) == CONST_INT
&& (INTVAL (op) & ~(HOST_WIDE_INT)0x3ff) != 0
&& SPARC_SETHI_P (INTVAL (op) & GET_MODE_MASK (mode))
)
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & ~(HOST_WIDE_INT)0x3ff) != 0
&& SPARC_SETHI_P (CONST_DOUBLE_LOW (op))));
}
int
arith11_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SPARC_SIMM11_P (INTVAL (op))));
}
int
arith10_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SPARC_SIMM10_P (INTVAL (op))));
}
int
arith_double_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SMALL_INT (op))
|| (! TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_HIGH (op) + 0x1000) < 0x2000)
|| (TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x1000) == 0x1000)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x1000) == 0))));
}
int
arith_double_4096_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (TARGET_ARCH64 &&
((GET_CODE (op) == CONST_INT && INTVAL (op) == 4096) ||
(GET_CODE (op) == CONST_DOUBLE &&
CONST_DOUBLE_LOW (op) == 4096 &&
CONST_DOUBLE_HIGH (op) == 0)));
}
int
arith_double_add_operand (rtx op, enum machine_mode mode)
{
return arith_double_operand (op, mode) || arith_double_4096_operand (op, mode);
}
int
arith11_double_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x400) < 0x800
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x400) == 0x400)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x400) == 0)))
|| (GET_CODE (op) == CONST_INT
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x400) < 0x800));
}
int
arith10_double_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned) (CONST_DOUBLE_LOW (op) + 0x200) < 0x400
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x200) == 0x200)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x200) == 0)))
|| (GET_CODE (op) == CONST_INT
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x200) < 0x400));
}
int
small_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
}
int
small_int_or_double (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return ((GET_CODE (op) == CONST_INT && SMALL_INT (op))
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))));
}
int
uns_small_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
#if HOST_BITS_PER_WIDE_INT > 32
return (GET_CODE (op) == CONST_INT
&& ((INTVAL (op) >= 0 && INTVAL (op) < 0x1000)
|| (INTVAL (op) >= 0xFFFFF000
&& INTVAL (op) <= 0xFFFFFFFF)));
#else
return ((GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x1000)
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& (unsigned) CONST_DOUBLE_LOW (op) - 0xFFFFF000 < 0x1000));
#endif
}
int
uns_arith_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || uns_small_int (op, mode);
}
int
clobbered_register (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]);
}
int
input_operand (rtx op, enum machine_mode mode)
{
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op))
return 0;
if (GET_MODE_CLASS (mode) == MODE_INT
&& ((GET_CODE (op) == CONST_INT
&& (SPARC_SETHI_P (INTVAL (op) & GET_MODE_MASK (mode))
|| SPARC_SIMM13_P (INTVAL (op))
|| (mode == DImode
&& ! TARGET_ARCH64)))
|| (TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& ((CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SETHI_P (CONST_DOUBLE_LOW (op)))
||
#if HOST_BITS_PER_WIDE_INT == 64
(CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)))
#else
(SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))
&& (((CONST_DOUBLE_LOW (op) & 0x80000000) == 0
&& CONST_DOUBLE_HIGH (op) == 0)
|| (CONST_DOUBLE_HIGH (op) == -1
&& CONST_DOUBLE_LOW (op) & 0x80000000) != 0))
#endif
))))
return 1;
if (! TARGET_ARCH64
&& mode == DImode
&& GET_CODE (op) == CONST_DOUBLE)
return 1;
if (register_operand (op, mode))
return 1;
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_CODE (op) == CONST_DOUBLE)
return 1;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (GET_CODE (inside) == LO_SUM)
{
if (! TARGET_V9
&& GET_MODE (op) == TFmode)
return 0;
return (register_operand (XEXP (inside, 0), Pmode)
&& CONSTANT_P (XEXP (inside, 1)));
}
return memory_address_p (mode, inside);
}
return 0;
}
void
sparc_emit_set_const32 (rtx op0, rtx op1)
{
enum machine_mode mode = GET_MODE (op0);
rtx temp;
if (GET_CODE (op1) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (op1);
if (SPARC_SETHI_P (value & GET_MODE_MASK (mode))
|| SPARC_SIMM13_P (value))
abort ();
}
if (reload_in_progress || reload_completed)
temp = op0;
else
temp = gen_reg_rtx (mode);
if (GET_CODE (op1) == CONST_INT)
{
if (TARGET_ARCH64
&& HOST_BITS_PER_WIDE_INT != 64
&& (INTVAL (op1) & 0x80000000) != 0)
emit_insn (gen_rtx_SET
(VOIDmode, temp,
immed_double_const (INTVAL (op1) & ~(HOST_WIDE_INT)0x3ff,
0, DImode)));
else
emit_insn (gen_rtx_SET (VOIDmode, temp,
GEN_INT (INTVAL (op1)
& ~(HOST_WIDE_INT)0x3ff)));
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_IOR (mode, temp,
GEN_INT (INTVAL (op1) & 0x3ff))));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, temp,
gen_rtx_HIGH (mode, op1)));
emit_insn (gen_rtx_SET (VOIDmode,
op0, gen_rtx_LO_SUM (mode, temp, op1)));
}
}
void
sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
{
rtx ti_temp1 = 0;
if (temp1 && GET_MODE (temp1) == TImode)
{
ti_temp1 = temp1;
temp1 = gen_rtx_REG (DImode, REGNO (temp1));
}
switch (sparc_cmodel)
{
case CM_MEDLOW:
emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1)));
emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1)));
break;
case CM_MEDMID:
emit_insn (gen_seth44 (op0, op1));
emit_insn (gen_setm44 (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, temp1,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (12))));
emit_insn (gen_setl44 (op0, temp1, op1));
break;
case CM_MEDANY:
if (rtx_equal_p (temp1, op0))
{
if (ti_temp1)
temp1 = gen_rtx_REG (DImode, REGNO (temp1) + 1);
else
abort();
}
emit_insn (gen_sethh (op0, op1));
emit_insn (gen_setlm (temp1, op1));
emit_insn (gen_sethm (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, op0, temp1)));
emit_insn (gen_setlo (op0, op0, op1));
break;
case CM_EMBMEDANY:
if (data_segment_operand (op1, GET_MODE (op1)))
{
emit_insn (gen_embmedany_sethi (temp1, op1));
emit_insn (gen_embmedany_brsum (op0, temp1));
emit_insn (gen_embmedany_losum (op0, op0, op1));
}
else
{
if (rtx_equal_p (temp1, op0))
{
if (ti_temp1)
temp1 = gen_rtx_REG (DImode, REGNO (temp1) + 1);
else
abort();
}
emit_insn (gen_embmedany_textuhi (op0, op1));
emit_insn (gen_embmedany_texthi (temp1, op1));
emit_insn (gen_embmedany_textulo (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, op0, temp1)));
emit_insn (gen_embmedany_textlo (op0, op0, op1));
}
break;
default:
abort();
}
}
static void sparc_emit_set_safe_HIGH64 (rtx, HOST_WIDE_INT);
static rtx gen_safe_SET64 (rtx, HOST_WIDE_INT);
static rtx gen_safe_OR64 (rtx, HOST_WIDE_INT);
static rtx gen_safe_XOR64 (rtx, HOST_WIDE_INT);
#if HOST_BITS_PER_WIDE_INT == 64
#define GEN_HIGHINT64(__x) GEN_INT ((__x) & ~(HOST_WIDE_INT)0x3ff)
#define GEN_INT64(__x) GEN_INT (__x)
#else
#define GEN_HIGHINT64(__x) \
immed_double_const ((__x) & ~(HOST_WIDE_INT)0x3ff, 0, DImode)
#define GEN_INT64(__x) \
immed_double_const ((__x) & 0xffffffff, \
((__x) & 0x80000000 ? -1 : 0), DImode)
#endif
static void
sparc_emit_set_safe_HIGH64 (rtx dest, HOST_WIDE_INT val)
{
emit_insn (gen_rtx_SET (VOIDmode, dest, GEN_HIGHINT64 (val)));
}
static rtx
gen_safe_SET64 (rtx dest, HOST_WIDE_INT val)
{
return gen_rtx_SET (VOIDmode, dest, GEN_INT64 (val));
}
static rtx
gen_safe_OR64 (rtx src, HOST_WIDE_INT val)
{
return gen_rtx_IOR (DImode, src, GEN_INT64 (val));
}
static rtx
gen_safe_XOR64 (rtx src, HOST_WIDE_INT val)
{
return gen_rtx_XOR (DImode, src, GEN_INT64 (val));
}
static void sparc_emit_set_const64_quick1 (rtx, rtx,
unsigned HOST_WIDE_INT, int);
static void
sparc_emit_set_const64_quick1 (rtx op0, rtx temp,
unsigned HOST_WIDE_INT low_bits, int is_neg)
{
unsigned HOST_WIDE_INT high_bits;
if (is_neg)
high_bits = (~low_bits) & 0xffffffff;
else
high_bits = low_bits;
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if (!is_neg)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
}
else
{
if ((low_bits & 0x3ff) == 0x3ff)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_NOT (DImode, temp)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_XOR64 (temp,
(-(HOST_WIDE_INT)0x400
| (low_bits & 0x3ff)))));
}
}
}
static void sparc_emit_set_const64_quick2 (rtx, rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int);
static void
sparc_emit_set_const64_quick2 (rtx op0, rtx temp,
unsigned HOST_WIDE_INT high_bits,
unsigned HOST_WIDE_INT low_immediate,
int shift_count)
{
rtx temp2 = op0;
if ((high_bits & 0xfffffc00) != 0)
{
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if ((high_bits & ~0xfffffc00) != 0)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
else
temp2 = temp;
}
else
{
emit_insn (gen_safe_SET64 (temp, high_bits));
temp2 = temp;
}
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, temp2,
GEN_INT (shift_count))));
if (low_immediate != 0)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (op0, low_immediate)));
}
static void sparc_emit_set_const64_longway (rtx, rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT);
static void
sparc_emit_set_const64_longway (rtx op0, rtx temp,
unsigned HOST_WIDE_INT high_bits,
unsigned HOST_WIDE_INT low_bits)
{
rtx sub_temp;
if (reload_in_progress || reload_completed)
sub_temp = op0;
else
sub_temp = gen_reg_rtx (DImode);
if ((high_bits & 0xfffffc00) != 0)
{
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if ((high_bits & ~0xfffffc00) != 0)
emit_insn (gen_rtx_SET (VOIDmode,
sub_temp,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
else
sub_temp = temp;
}
else
{
emit_insn (gen_safe_SET64 (temp, high_bits));
sub_temp = temp;
}
if (!reload_in_progress && !reload_completed)
{
rtx temp2 = gen_reg_rtx (DImode);
rtx temp3 = gen_reg_rtx (DImode);
rtx temp4 = gen_reg_rtx (DImode);
emit_insn (gen_rtx_SET (VOIDmode, temp4,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (32))));
sparc_emit_set_safe_HIGH64 (temp2, low_bits);
if ((low_bits & ~0xfffffc00) != 0)
{
emit_insn (gen_rtx_SET (VOIDmode, temp3,
gen_safe_OR64 (temp2, (low_bits & 0x3ff))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, temp4, temp3)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, temp4, temp2)));
}
}
else
{
rtx low1 = GEN_INT ((low_bits >> (32 - 12)) & 0xfff);
rtx low2 = GEN_INT ((low_bits >> (32 - 12 - 12)) & 0xfff);
rtx low3 = GEN_INT ((low_bits >> (32 - 12 - 12 - 8)) & 0x0ff);
int to_shift = 12;
if (low1 != const0_rtx)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low1)));
sub_temp = op0;
to_shift = 12;
}
else
{
to_shift += 12;
}
if (low2 != const0_rtx)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low2)));
sub_temp = op0;
to_shift = 8;
}
else
{
to_shift += 8;
}
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
if (low3 != const0_rtx)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low3)));
}
}
static void analyze_64bit_constant (unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
int *, int *, int *);
static void
analyze_64bit_constant (unsigned HOST_WIDE_INT high_bits,
unsigned HOST_WIDE_INT low_bits,
int *hbsp, int *lbsp, int *abbasp)
{
int lowest_bit_set, highest_bit_set, all_bits_between_are_set;
int i;
lowest_bit_set = highest_bit_set = -1;
i = 0;
do
{
if ((lowest_bit_set == -1)
&& ((low_bits >> i) & 1))
lowest_bit_set = i;
if ((highest_bit_set == -1)
&& ((high_bits >> (32 - i - 1)) & 1))
highest_bit_set = (64 - i - 1);
}
while (++i < 32
&& ((highest_bit_set == -1)
|| (lowest_bit_set == -1)));
if (i == 32)
{
i = 0;
do
{
if ((lowest_bit_set == -1)
&& ((high_bits >> i) & 1))
lowest_bit_set = i + 32;
if ((highest_bit_set == -1)
&& ((low_bits >> (32 - i - 1)) & 1))
highest_bit_set = 32 - i - 1;
}
while (++i < 32
&& ((highest_bit_set == -1)
|| (lowest_bit_set == -1)));
}
if (lowest_bit_set == -1
|| highest_bit_set == -1)
abort ();
all_bits_between_are_set = 1;
for (i = lowest_bit_set; i <= highest_bit_set; i++)
{
if (i < 32)
{
if ((low_bits & (1 << i)) != 0)
continue;
}
else
{
if ((high_bits & (1 << (i - 32))) != 0)
continue;
}
all_bits_between_are_set = 0;
break;
}
*hbsp = highest_bit_set;
*lbsp = lowest_bit_set;
*abbasp = all_bits_between_are_set;
}
static int const64_is_2insns (unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT);
static int
const64_is_2insns (unsigned HOST_WIDE_INT high_bits,
unsigned HOST_WIDE_INT low_bits)
{
int highest_bit_set, lowest_bit_set, all_bits_between_are_set;
if (high_bits == 0
|| high_bits == 0xffffffff)
return 1;
analyze_64bit_constant (high_bits, low_bits,
&highest_bit_set, &lowest_bit_set,
&all_bits_between_are_set);
if ((highest_bit_set == 63
|| lowest_bit_set == 0)
&& all_bits_between_are_set != 0)
return 1;
if ((highest_bit_set - lowest_bit_set) < 21)
return 1;
return 0;
}
static unsigned HOST_WIDE_INT create_simple_focus_bits (unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
int, int);
static unsigned HOST_WIDE_INT
create_simple_focus_bits (unsigned HOST_WIDE_INT high_bits,
unsigned HOST_WIDE_INT low_bits,
int lowest_bit_set, int shift)
{
HOST_WIDE_INT hi, lo;
if (lowest_bit_set < 32)
{
lo = (low_bits >> lowest_bit_set) << shift;
hi = ((high_bits << (32 - lowest_bit_set)) << shift);
}
else
{
lo = 0;
hi = ((high_bits >> (lowest_bit_set - 32)) << shift);
}
if (hi & lo)
abort ();
return (hi | lo);
}
void
sparc_emit_set_const64 (rtx op0, rtx op1)
{
unsigned HOST_WIDE_INT high_bits, low_bits;
int lowest_bit_set, highest_bit_set;
int all_bits_between_are_set;
rtx temp;
if (! TARGET_ARCH64)
abort ();
if (GET_CODE (op0) != SUBREG)
{
if (GET_CODE (op0) != REG
|| (REGNO (op0) >= SPARC_FIRST_FP_REG
&& REGNO (op0) <= SPARC_LAST_V9_FP_REG))
abort ();
}
if (reload_in_progress || reload_completed)
temp = op0;
else
temp = gen_reg_rtx (DImode);
if (GET_CODE (op1) != CONST_DOUBLE
&& GET_CODE (op1) != CONST_INT)
{
sparc_emit_set_symbolic_const64 (op0, op1, temp);
return;
}
if (GET_CODE (op1) == CONST_DOUBLE)
{
#if HOST_BITS_PER_WIDE_INT == 64
high_bits = (CONST_DOUBLE_LOW (op1) >> 32) & 0xffffffff;
low_bits = CONST_DOUBLE_LOW (op1) & 0xffffffff;
#else
high_bits = CONST_DOUBLE_HIGH (op1);
low_bits = CONST_DOUBLE_LOW (op1);
#endif
}
else
{
#if HOST_BITS_PER_WIDE_INT == 64
high_bits = ((INTVAL (op1) >> 32) & 0xffffffff);
low_bits = (INTVAL (op1) & 0xffffffff);
#else
high_bits = ((INTVAL (op1) < 0) ?
0xffffffff :
0x00000000);
low_bits = INTVAL (op1);
#endif
}
analyze_64bit_constant (high_bits, low_bits,
&highest_bit_set, &lowest_bit_set,
&all_bits_between_are_set);
if (((highest_bit_set == 63
|| lowest_bit_set == 0)
&& all_bits_between_are_set != 0)
|| ((highest_bit_set - lowest_bit_set) < 12))
{
HOST_WIDE_INT the_const = -1;
int shift = lowest_bit_set;
if ((highest_bit_set != 63
&& lowest_bit_set != 0)
|| all_bits_between_are_set == 0)
{
the_const =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 0);
}
else if (lowest_bit_set == 0)
shift = -(63 - highest_bit_set);
if (! SPARC_SIMM13_P (the_const))
abort ();
emit_insn (gen_safe_SET64 (temp, the_const));
if (shift > 0)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_ASHIFT (DImode,
temp,
GEN_INT (shift))));
else if (shift < 0)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_LSHIFTRT (DImode,
temp,
GEN_INT (-shift))));
else
abort ();
return;
}
if ((highest_bit_set - lowest_bit_set) < 21)
{
unsigned HOST_WIDE_INT focus_bits =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 10);
if (! SPARC_SETHI_P (focus_bits))
abort ();
sparc_emit_set_safe_HIGH64 (temp, focus_bits);
if (lowest_bit_set < 10)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_LSHIFTRT (DImode, temp,
GEN_INT (10 - lowest_bit_set))));
else if (lowest_bit_set > 10)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_ASHIFT (DImode, temp,
GEN_INT (lowest_bit_set - 10))));
else
abort ();
return;
}
if (high_bits == 0
|| high_bits == 0xffffffff)
{
sparc_emit_set_const64_quick1 (op0, temp, low_bits,
(high_bits == 0xffffffff));
return;
}
if (low_bits == 0)
{
sparc_emit_set_const64_quick2 (op0, temp, high_bits, 0, 32);
return;
}
if (const64_is_2insns ((~high_bits) & 0xffffffff,
(~low_bits) & 0xfffffc00))
{
unsigned HOST_WIDE_INT trailing_bits = low_bits & 0x3ff;
if ((((~high_bits) & 0xffffffff) == 0
&& ((~low_bits) & 0x80000000) == 0)
|| (((~high_bits) & 0xffffffff) == 0xffffffff
&& ((~low_bits) & 0x80000000) != 0))
{
int fast_int = (~low_bits & 0xffffffff);
if ((SPARC_SETHI_P (fast_int)
&& (~high_bits & 0xffffffff) == 0)
|| SPARC_SIMM13_P (fast_int))
emit_insn (gen_safe_SET64 (temp, fast_int));
else
sparc_emit_set_const64 (temp, GEN_INT64 (fast_int));
}
else
{
rtx negated_const;
#if HOST_BITS_PER_WIDE_INT == 64
negated_const = GEN_INT (((~low_bits) & 0xfffffc00) |
(((HOST_WIDE_INT)((~high_bits) & 0xffffffff))<<32));
#else
negated_const = immed_double_const ((~low_bits) & 0xfffffc00,
(~high_bits) & 0xffffffff,
DImode);
#endif
sparc_emit_set_const64 (temp, negated_const);
}
if (trailing_bits == 0x3ff)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_NOT (DImode, temp)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_safe_XOR64 (temp,
(-0x400 | trailing_bits))));
}
return;
}
if ((highest_bit_set - lowest_bit_set) < 32)
{
unsigned HOST_WIDE_INT focus_bits =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 0);
if (highest_bit_set < 32
|| lowest_bit_set >= 32)
abort ();
sparc_emit_set_const64_quick2 (op0, temp,
focus_bits, 0,
lowest_bit_set);
return;
}
if (SPARC_SIMM13_P(low_bits)
&& ((int)low_bits > 0))
{
sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_bits, 32);
return;
}
#if 0
printf ("sparc_emit_set_const64: Hard constant [%08lx%08lx] neg[%08lx%08lx]\n",
high_bits, low_bits, ~high_bits, ~low_bits);
#endif
sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits);
}
enum machine_mode
select_cc_mode (enum rtx_code op, rtx x, rtx y ATTRIBUTE_UNUSED)
{
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
switch (op)
{
case EQ:
case NE:
case UNORDERED:
case ORDERED:
case UNLT:
case UNLE:
case UNGT:
case UNGE:
case UNEQ:
case LTGT:
return CCFPmode;
case LT:
case LE:
case GT:
case GE:
return CCFPEmode;
default:
abort ();
}
}
else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
|| GET_CODE (x) == NEG || GET_CODE (x) == ASHIFT)
{
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
return CCX_NOOVmode;
else
return CC_NOOVmode;
}
else
{
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
return CCXmode;
else
return CCmode;
}
}
rtx
gen_compare_reg (enum rtx_code code, rtx x, rtx y)
{
enum machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg;
if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
#if 1
{
int reg;
static int next_fcc_reg = 0;
static rtx prev_args[4][2];
for (reg = 0; reg < 4; reg++)
if (prev_args[reg][0] == x && prev_args[reg][1] == y)
break;
if (reg == 4)
{
reg = next_fcc_reg;
prev_args[reg][0] = x;
prev_args[reg][1] = y;
next_fcc_reg = (next_fcc_reg + 1) & 3;
}
cc_reg = gen_rtx_REG (mode, reg + SPARC_FIRST_V9_FCC_REG);
}
#else
cc_reg = gen_reg_rtx (mode);
#endif
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
cc_reg = gen_rtx_REG (mode, SPARC_FCC_REG);
else
cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG);
emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
gen_rtx_COMPARE (mode, x, y)));
return cc_reg;
}
int
gen_v9_scc (enum rtx_code compare_code, register rtx *operands)
{
rtx temp, op0, op1;
if (! TARGET_ARCH64
&& (GET_MODE (sparc_compare_op0) == DImode
|| GET_MODE (operands[0]) == DImode))
return 0;
op0 = sparc_compare_op0;
op1 = sparc_compare_op1;
if (TARGET_ARCH64
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& op1 == const0_rtx
&& v9_regcmp_p (compare_code))
{
if (compare_code == NE
&& GET_MODE (operands[0]) == DImode
&& rtx_equal_p (op0, operands[0]))
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (DImode,
gen_rtx_fmt_ee (compare_code, DImode,
op0, const0_rtx),
const1_rtx,
operands[0])));
return 1;
}
if (reg_overlap_mentioned_p (operands[0], op0))
{
op0 = gen_reg_rtx (GET_MODE (sparc_compare_op0));
emit_move_insn (op0, sparc_compare_op0);
}
emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
if (GET_MODE (op0) != DImode)
{
temp = gen_reg_rtx (DImode);
convert_move (temp, op0, 0);
}
else
temp = op0;
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
gen_rtx_fmt_ee (compare_code, DImode,
temp, const0_rtx),
const1_rtx,
operands[0])));
return 1;
}
else
{
operands[1] = gen_compare_reg (compare_code, op0, op1);
switch (GET_MODE (operands[1]))
{
case CCmode :
case CCXmode :
case CCFPEmode :
case CCFPmode :
break;
default :
abort ();
}
emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
gen_rtx_fmt_ee (compare_code,
GET_MODE (operands[1]),
operands[1], const0_rtx),
const1_rtx, operands[0])));
return 1;
}
}
void
emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
{
emit_jump_insn (gen_rtx_SET (VOIDmode,
pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_fmt_ee (code, GET_MODE (op0),
op0, const0_rtx),
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx)));
}
rtx
gen_df_reg (rtx reg, int low)
{
int regno = REGNO (reg);
if ((WORDS_BIG_ENDIAN == 0) ^ (low != 0))
regno += (TARGET_ARCH64 && regno < 32) ? 1 : 2;
return gen_rtx_REG (DFmode, regno);
}
static void
emit_soft_tfmode_libcall (const char *func_name, int nargs, rtx *operands)
{
rtx ret_slot = NULL, arg[3], func_sym;
int i;
if (nargs < 2 || nargs > 3)
abort ();
for (i = 0; i < nargs; ++i)
{
rtx this_arg = operands[i];
rtx this_slot;
if (GET_MODE (this_arg) == TFmode)
{
int force_stack_temp;
force_stack_temp = 0;
if (TARGET_BUGGY_QP_LIB && i == 0)
force_stack_temp = 1;
if (GET_CODE (this_arg) == MEM
&& ! force_stack_temp)
this_arg = XEXP (this_arg, 0);
else if (CONSTANT_P (this_arg)
&& ! force_stack_temp)
{
this_slot = force_const_mem (TFmode, this_arg);
this_arg = XEXP (this_slot, 0);
}
else
{
this_slot = assign_stack_temp (TFmode, GET_MODE_SIZE (TFmode), 0);
if (i > 0)
emit_move_insn (this_slot, this_arg);
else
ret_slot = this_slot;
this_arg = XEXP (this_slot, 0);
}
}
arg[i] = this_arg;
}
func_sym = gen_rtx_SYMBOL_REF (Pmode, func_name);
if (GET_MODE (operands[0]) == TFmode)
{
if (nargs == 2)
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 2,
arg[0], GET_MODE (arg[0]),
arg[1], GET_MODE (arg[1]));
else
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 3,
arg[0], GET_MODE (arg[0]),
arg[1], GET_MODE (arg[1]),
arg[2], GET_MODE (arg[2]));
if (ret_slot)
emit_move_insn (operands[0], ret_slot);
}
else
{
rtx ret;
if (nargs != 2)
abort ();
ret = emit_library_call_value (func_sym, operands[0], LCT_NORMAL,
GET_MODE (operands[0]), 1,
arg[1], GET_MODE (arg[1]));
if (ret != operands[0])
emit_move_insn (operands[0], ret);
}
}
static void
emit_soft_tfmode_binop (enum rtx_code code, rtx *operands)
{
const char *func;
switch (code)
{
case PLUS:
func = "_Qp_add";
break;
case MINUS:
func = "_Qp_sub";
break;
case MULT:
func = "_Qp_mul";
break;
case DIV:
func = "_Qp_div";
break;
default:
abort ();
}
emit_soft_tfmode_libcall (func, 3, operands);
}
static void
emit_soft_tfmode_unop (enum rtx_code code, rtx *operands)
{
const char *func;
switch (code)
{
case SQRT:
func = "_Qp_sqrt";
break;
default:
abort ();
}
emit_soft_tfmode_libcall (func, 2, operands);
}
static void
emit_soft_tfmode_cvt (enum rtx_code code, rtx *operands)
{
const char *func;
switch (code)
{
case FLOAT_EXTEND:
switch (GET_MODE (operands[1]))
{
case SFmode:
func = "_Qp_stoq";
break;
case DFmode:
func = "_Qp_dtoq";
break;
default:
abort ();
}
break;
case FLOAT_TRUNCATE:
switch (GET_MODE (operands[0]))
{
case SFmode:
func = "_Qp_qtos";
break;
case DFmode:
func = "_Qp_qtod";
break;
default:
abort ();
}
break;
case FLOAT:
switch (GET_MODE (operands[1]))
{
case SImode:
func = "_Qp_itoq";
break;
case DImode:
func = "_Qp_xtoq";
break;
default:
abort ();
}
break;
case UNSIGNED_FLOAT:
switch (GET_MODE (operands[1]))
{
case SImode:
func = "_Qp_uitoq";
break;
case DImode:
func = "_Qp_uxtoq";
break;
default:
abort ();
}
break;
case FIX:
switch (GET_MODE (operands[0]))
{
case SImode:
func = "_Qp_qtoi";
break;
case DImode:
func = "_Qp_qtox";
break;
default:
abort ();
}
break;
case UNSIGNED_FIX:
switch (GET_MODE (operands[0]))
{
case SImode:
func = "_Qp_qtoui";
break;
case DImode:
func = "_Qp_qtoux";
break;
default:
abort ();
}
break;
default:
abort ();
}
emit_soft_tfmode_libcall (func, 2, operands);
}
static void
emit_hard_tfmode_operation (enum rtx_code code, rtx *operands)
{
rtx op, dest;
if (GET_RTX_CLASS (code) == RTX_UNARY)
{
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
op = gen_rtx_fmt_e (code, GET_MODE (operands[0]), operands[1]);
}
else
{
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
op = gen_rtx_fmt_ee (code, GET_MODE (operands[0]),
operands[1], operands[2]);
}
if (register_operand (operands[0], VOIDmode))
dest = operands[0];
else
dest = gen_reg_rtx (GET_MODE (operands[0]));
emit_insn (gen_rtx_SET (VOIDmode, dest, op));
if (dest != operands[0])
emit_move_insn (operands[0], dest);
}
void
emit_tfmode_binop (enum rtx_code code, rtx *operands)
{
if (TARGET_HARD_QUAD)
emit_hard_tfmode_operation (code, operands);
else
emit_soft_tfmode_binop (code, operands);
}
void
emit_tfmode_unop (enum rtx_code code, rtx *operands)
{
if (TARGET_HARD_QUAD)
emit_hard_tfmode_operation (code, operands);
else
emit_soft_tfmode_unop (code, operands);
}
void
emit_tfmode_cvt (enum rtx_code code, rtx *operands)
{
if (TARGET_HARD_QUAD)
emit_hard_tfmode_operation (code, operands);
else
emit_soft_tfmode_cvt (code, operands);
}
int
leaf_return_peephole_ok (void)
{
return (actual_fsize == 0);
}
int
empty_delay_slot (rtx insn)
{
rtx seq;
if (PREV_INSN (insn) == NULL)
return 1;
seq = NEXT_INSN (PREV_INSN (insn));
if (GET_CODE (PATTERN (seq)) == SEQUENCE)
return 0;
return 1;
}
int
eligible_for_epilogue_delay (rtx trial, int slot)
{
rtx pat, src;
if (slot >= 1)
return 0;
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
return 0;
if (get_attr_length (trial) != 1)
return 0;
if (num_gfregs)
return 0;
if (current_function_calls_eh_return)
return 0;
if (current_function_uses_only_leaf_regs)
{
if (leaf_return_peephole_ok ())
return ((get_attr_in_uncond_branch_delay (trial)
== IN_BRANCH_DELAY_TRUE));
return 0;
}
pat = PATTERN (trial);
if (GET_CODE (SET_DEST (pat)) != REG
|| REGNO (SET_DEST (pat)) < 24)
return 0;
if (REGNO (SET_DEST (pat)) >= 32)
{
if (TARGET_V9 && ! epilogue_renumber (&pat, 1)
&& (get_attr_in_uncond_branch_delay (trial) == IN_BRANCH_DELAY_TRUE))
return 1;
return 0;
}
src = SET_SRC (pat);
if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
&& arith_operand (src, GET_MODE (src)))
{
if (TARGET_ARCH64)
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
else
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
}
else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
&& arith_double_operand (src, GET_MODE (src)))
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
&& register_operand (src, SFmode))
return 1;
else if (TARGET_V9 && ! epilogue_renumber (&pat, 1)
&& (get_attr_in_uncond_branch_delay (trial) == IN_BRANCH_DELAY_TRUE))
return 1;
else if (GET_CODE (src) == PLUS
&& arith_operand (XEXP (src, 0), SImode)
&& arith_operand (XEXP (src, 1), SImode)
&& (register_operand (XEXP (src, 0), SImode)
|| register_operand (XEXP (src, 1), SImode)))
return 1;
else if (GET_CODE (src) == PLUS
&& arith_double_operand (XEXP (src, 0), DImode)
&& arith_double_operand (XEXP (src, 1), DImode)
&& (register_operand (XEXP (src, 0), DImode)
|| register_operand (XEXP (src, 1), DImode)))
return 1;
else if (GET_CODE (src) == LO_SUM
&& ! TARGET_CM_MEDMID
&& ((register_operand (XEXP (src, 0), SImode)
&& immediate_operand (XEXP (src, 1), SImode))
|| (TARGET_ARCH64
&& register_operand (XEXP (src, 0), DImode)
&& immediate_operand (XEXP (src, 1), DImode))))
return 1;
else if (GET_CODE (src) == ASHIFT
&& (register_operand (XEXP (src, 0), SImode)
|| register_operand (XEXP (src, 0), DImode))
&& XEXP (src, 1) == const1_rtx)
return 1;
return 0;
}
int
tls_call_delay (rtx trial)
{
rtx pat, unspec;
if (TARGET_GNU_TLS || !TARGET_TLS)
return 1;
pat = PATTERN (trial);
if (GET_CODE (pat) != SET || GET_CODE (SET_DEST (pat)) != PLUS)
return 1;
unspec = XEXP (SET_DEST (pat), 1);
if (GET_CODE (unspec) != UNSPEC
|| (XINT (unspec, 1) != UNSPEC_TLSGD
&& XINT (unspec, 1) != UNSPEC_TLSLDM))
return 1;
return 0;
}
int
eligible_for_sibcall_delay (rtx trial)
{
rtx pat, src;
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
return 0;
if (get_attr_length (trial) != 1)
return 0;
pat = PATTERN (trial);
if (current_function_uses_only_leaf_regs)
{
if ((TARGET_ARCH64 && ! TARGET_CM_MEDLOW) || flag_pic)
return 0;
if (reg_mentioned_p (gen_rtx_REG (Pmode, 1), pat))
return 0;
return 1;
}
if (GET_CODE (SET_DEST (pat)) != REG
|| REGNO (SET_DEST (pat)) < 24
|| REGNO (SET_DEST (pat)) >= 32)
return 0;
if (reg_mentioned_p (gen_rtx_REG (Pmode, 15), pat))
return 0;
src = SET_SRC (pat);
if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
&& arith_operand (src, GET_MODE (src)))
{
if (TARGET_ARCH64)
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
else
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
}
else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
&& arith_double_operand (src, GET_MODE (src)))
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
&& register_operand (src, SFmode))
return 1;
else if (GET_CODE (src) == PLUS
&& arith_operand (XEXP (src, 0), SImode)
&& arith_operand (XEXP (src, 1), SImode)
&& (register_operand (XEXP (src, 0), SImode)
|| register_operand (XEXP (src, 1), SImode)))
return 1;
else if (GET_CODE (src) == PLUS
&& arith_double_operand (XEXP (src, 0), DImode)
&& arith_double_operand (XEXP (src, 1), DImode)
&& (register_operand (XEXP (src, 0), DImode)
|| register_operand (XEXP (src, 1), DImode)))
return 1;
else if (GET_CODE (src) == LO_SUM
&& ! TARGET_CM_MEDMID
&& ((register_operand (XEXP (src, 0), SImode)
&& immediate_operand (XEXP (src, 1), SImode))
|| (TARGET_ARCH64
&& register_operand (XEXP (src, 0), DImode)
&& immediate_operand (XEXP (src, 1), DImode))))
return 1;
else if (GET_CODE (src) == ASHIFT
&& (register_operand (XEXP (src, 0), SImode)
|| register_operand (XEXP (src, 0), DImode))
&& XEXP (src, 1) == const1_rtx)
return 1;
return 0;
}
static int
check_return_regs (rtx x)
{
switch (GET_CODE (x))
{
case REG:
return IN_OR_GLOBAL_P (x);
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 1;
case SET:
case IOR:
case AND:
case XOR:
case PLUS:
case MINUS:
if (check_return_regs (XEXP (x, 1)) == 0)
return 0;
case NOT:
case NEG:
case MEM:
return check_return_regs (XEXP (x, 0));
default:
return 0;
}
}
int
short_branch (int uid1, int uid2)
{
int delta = INSN_ADDRESSES (uid1) - INSN_ADDRESSES (uid2);
if (delta >= -1023 && delta <= 1022)
return 1;
return 0;
}
int
reg_unused_after (rtx reg, rtx insn)
{
enum rtx_code code, prev_code = UNKNOWN;
while ((insn = NEXT_INSN (insn)))
{
if (prev_code == CALL_INSN && call_used_regs[REGNO (reg)])
return 1;
code = GET_CODE (insn);
if (GET_CODE (insn) == CODE_LABEL)
return 1;
if (INSN_P (insn))
{
rtx set = single_set (insn);
int in_src = set && reg_overlap_mentioned_p (reg, SET_SRC (set));
if (set && in_src)
return 0;
if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
return 1;
if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
return 0;
}
prev_code = code;
}
return 1;
}
static bool
sparc_cannot_force_const_mem (rtx x)
{
switch (GET_CODE (x))
{
case CONST_INT:
case CONST_DOUBLE:
return false;
case LABEL_REF:
return flag_pic != 0;
case SYMBOL_REF:
if (SYMBOL_REF_TLS_MODEL (x))
return true;
else
return flag_pic != 0;
case CONST:
return sparc_cannot_force_const_mem (XEXP (x, 0));
case PLUS:
case MINUS:
return sparc_cannot_force_const_mem (XEXP (x, 0))
|| sparc_cannot_force_const_mem (XEXP (x, 1));
case UNSPEC:
return true;
default:
abort ();
}
}
static GTY(()) rtx global_offset_table;
static GTY(()) rtx get_pc_symbol;
static GTY(()) char get_pc_symbol_name[256];
int
check_pic (int i)
{
switch (flag_pic)
{
case 1:
if (GET_CODE (recog_data.operand[i]) == SYMBOL_REF
|| (GET_CODE (recog_data.operand[i]) == CONST
&& ! (GET_CODE (XEXP (recog_data.operand[i], 0)) == MINUS
&& (XEXP (XEXP (recog_data.operand[i], 0), 0)
== global_offset_table)
&& (GET_CODE (XEXP (XEXP (recog_data.operand[i], 0), 1))
== CONST))))
abort ();
case 2:
default:
return 1;
}
}
int
pic_address_needs_scratch (rtx x)
{
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
return 1;
return 0;
}
bool
legitimate_constant_p (rtx x)
{
rtx inner;
switch (GET_CODE (x))
{
case SYMBOL_REF:
if (SYMBOL_REF_TLS_MODEL (x))
return false;
break;
case CONST:
inner = XEXP (x, 0);
if (GET_CODE (inner) == PLUS
&& tls_symbolic_operand (XEXP (inner, 0)))
return false;
break;
case CONST_DOUBLE:
if (GET_MODE (x) == VOIDmode)
return true;
if (TARGET_VIS
&& (GET_MODE (x) == SFmode
|| GET_MODE (x) == DFmode
|| GET_MODE (x) == TFmode)
&& fp_zero_operand (x, GET_MODE (x)))
return true;
return false;
default:
break;
}
return true;
}
bool
constant_address_p (rtx x)
{
switch (GET_CODE (x))
{
case LABEL_REF:
case CONST_INT:
case HIGH:
return true;
case CONST:
if (flag_pic && pic_address_needs_scratch (x))
return false;
return legitimate_constant_p (x);
case SYMBOL_REF:
return !flag_pic && legitimate_constant_p (x);
default:
return false;
}
}
bool
legitimate_pic_operand_p (rtx x)
{
if (pic_address_needs_scratch (x))
return false;
if (tls_symbolic_operand (x)
|| (GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == PLUS
&& tls_symbolic_operand (XEXP (XEXP (x, 0), 0))))
return false;
return true;
}
int
legitimate_address_p (enum machine_mode mode, rtx addr, int strict)
{
rtx rs1 = NULL, rs2 = NULL, imm1 = NULL, imm2;
if (REG_P (addr) || GET_CODE (addr) == SUBREG)
rs1 = addr;
else if (GET_CODE (addr) == PLUS)
{
rs1 = XEXP (addr, 0);
rs2 = XEXP (addr, 1);
if (!REG_P (rs1)
&& GET_CODE (rs1) != SUBREG
&& (REG_P (rs2)
|| GET_CODE (rs2) == SUBREG
|| (GET_CODE (rs2) == LO_SUM && GET_CODE (rs1) != LO_SUM)))
{
rs1 = XEXP (addr, 1);
rs2 = XEXP (addr, 0);
}
if ((flag_pic == 1
&& rs1 == pic_offset_table_rtx
&& !REG_P (rs2)
&& GET_CODE (rs2) != SUBREG
&& GET_CODE (rs2) != LO_SUM
&& GET_CODE (rs2) != MEM
&& !tls_symbolic_operand (rs2)
&& (! symbolic_operand (rs2, VOIDmode) || mode == Pmode)
&& (GET_CODE (rs2) != CONST_INT || SMALL_INT (rs2)))
|| ((REG_P (rs1)
|| GET_CODE (rs1) == SUBREG)
&& RTX_OK_FOR_OFFSET_P (rs2)))
{
imm1 = rs2;
rs2 = NULL;
}
else if ((REG_P (rs1) || GET_CODE (rs1) == SUBREG)
&& (REG_P (rs2) || GET_CODE (rs2) == SUBREG))
{
if (mode == TFmode
&& !(TARGET_FPU && TARGET_ARCH64 && TARGET_V9
&& TARGET_HARD_QUAD))
return 0;
if (TARGET_ARCH32 && !optimize
&& (mode == DFmode || mode == DImode))
return 0;
}
else if (USE_AS_OFFSETABLE_LO10
&& GET_CODE (rs1) == LO_SUM
&& TARGET_ARCH64
&& ! TARGET_CM_MEDMID
&& RTX_OK_FOR_OLO10_P (rs2))
{
imm2 = rs2;
rs2 = NULL;
imm1 = XEXP (rs1, 1);
rs1 = XEXP (rs1, 0);
if (! CONSTANT_P (imm1) || tls_symbolic_operand (rs1))
return 0;
}
}
else if (GET_CODE (addr) == LO_SUM)
{
rs1 = XEXP (addr, 0);
imm1 = XEXP (addr, 1);
if (! CONSTANT_P (imm1) || tls_symbolic_operand (rs1))
return 0;
if (mode == TFmode && !TARGET_V9)
return 0;
}
else if (GET_CODE (addr) == CONST_INT && SMALL_INT (addr))
return 1;
else
return 0;
if (GET_CODE (rs1) == SUBREG)
rs1 = SUBREG_REG (rs1);
if (!REG_P (rs1))
return 0;
if (rs2)
{
if (GET_CODE (rs2) == SUBREG)
rs2 = SUBREG_REG (rs2);
if (!REG_P (rs2))
return 0;
}
if (strict)
{
if (!REGNO_OK_FOR_BASE_P (REGNO (rs1))
|| (rs2 && !REGNO_OK_FOR_BASE_P (REGNO (rs2))))
return 0;
}
else
{
if ((REGNO (rs1) >= 32
&& REGNO (rs1) != FRAME_POINTER_REGNUM
&& REGNO (rs1) < FIRST_PSEUDO_REGISTER)
|| (rs2
&& (REGNO (rs2) >= 32
&& REGNO (rs2) != FRAME_POINTER_REGNUM
&& REGNO (rs2) < FIRST_PSEUDO_REGISTER)))
return 0;
}
return 1;
}
static GTY(()) rtx sparc_tls_symbol;
static rtx
sparc_tls_get_addr (void)
{
if (!sparc_tls_symbol)
sparc_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_addr");
return sparc_tls_symbol;
}
static rtx
sparc_tls_got (void)
{
rtx temp;
if (flag_pic)
{
current_function_uses_pic_offset_table = 1;
return pic_offset_table_rtx;
}
if (!global_offset_table)
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
temp = gen_reg_rtx (Pmode);
emit_move_insn (temp, global_offset_table);
return temp;
}
rtx
legitimize_tls_address (rtx addr)
{
rtx temp1, temp2, temp3, ret, o0, got, insn;
if (no_new_pseudos)
abort ();
if (GET_CODE (addr) == SYMBOL_REF)
switch (SYMBOL_REF_TLS_MODEL (addr))
{
case TLS_MODEL_GLOBAL_DYNAMIC:
start_sequence ();
temp1 = gen_reg_rtx (SImode);
temp2 = gen_reg_rtx (SImode);
ret = gen_reg_rtx (Pmode);
o0 = gen_rtx_REG (Pmode, 8);
got = sparc_tls_got ();
emit_insn (gen_tgd_hi22 (temp1, addr));
emit_insn (gen_tgd_lo10 (temp2, temp1, addr));
if (TARGET_ARCH32)
{
emit_insn (gen_tgd_add32 (o0, got, temp2, addr));
insn = emit_call_insn (gen_tgd_call32 (o0, sparc_tls_get_addr (),
addr, const1_rtx));
}
else
{
emit_insn (gen_tgd_add64 (o0, got, temp2, addr));
insn = emit_call_insn (gen_tgd_call64 (o0, sparc_tls_get_addr (),
addr, const1_rtx));
}
CALL_INSN_FUNCTION_USAGE (insn)
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
CALL_INSN_FUNCTION_USAGE (insn));
insn = get_insns ();
end_sequence ();
emit_libcall_block (insn, ret, o0, addr);
break;
case TLS_MODEL_LOCAL_DYNAMIC:
start_sequence ();
temp1 = gen_reg_rtx (SImode);
temp2 = gen_reg_rtx (SImode);
temp3 = gen_reg_rtx (Pmode);
ret = gen_reg_rtx (Pmode);
o0 = gen_rtx_REG (Pmode, 8);
got = sparc_tls_got ();
emit_insn (gen_tldm_hi22 (temp1));
emit_insn (gen_tldm_lo10 (temp2, temp1));
if (TARGET_ARCH32)
{
emit_insn (gen_tldm_add32 (o0, got, temp2));
insn = emit_call_insn (gen_tldm_call32 (o0, sparc_tls_get_addr (),
const1_rtx));
}
else
{
emit_insn (gen_tldm_add64 (o0, got, temp2));
insn = emit_call_insn (gen_tldm_call64 (o0, sparc_tls_get_addr (),
const1_rtx));
}
CALL_INSN_FUNCTION_USAGE (insn)
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
CALL_INSN_FUNCTION_USAGE (insn));
insn = get_insns ();
end_sequence ();
emit_libcall_block (insn, temp3, o0,
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
UNSPEC_TLSLD_BASE));
temp1 = gen_reg_rtx (SImode);
temp2 = gen_reg_rtx (SImode);
emit_insn (gen_tldo_hix22 (temp1, addr));
emit_insn (gen_tldo_lox10 (temp2, temp1, addr));
if (TARGET_ARCH32)
emit_insn (gen_tldo_add32 (ret, temp3, temp2, addr));
else
emit_insn (gen_tldo_add64 (ret, temp3, temp2, addr));
break;
case TLS_MODEL_INITIAL_EXEC:
temp1 = gen_reg_rtx (SImode);
temp2 = gen_reg_rtx (SImode);
temp3 = gen_reg_rtx (Pmode);
got = sparc_tls_got ();
emit_insn (gen_tie_hi22 (temp1, addr));
emit_insn (gen_tie_lo10 (temp2, temp1, addr));
if (TARGET_ARCH32)
emit_insn (gen_tie_ld32 (temp3, got, temp2, addr));
else
emit_insn (gen_tie_ld64 (temp3, got, temp2, addr));
if (TARGET_SUN_TLS)
{
ret = gen_reg_rtx (Pmode);
if (TARGET_ARCH32)
emit_insn (gen_tie_add32 (ret, gen_rtx_REG (Pmode, 7),
temp3, addr));
else
emit_insn (gen_tie_add64 (ret, gen_rtx_REG (Pmode, 7),
temp3, addr));
}
else
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp3);
break;
case TLS_MODEL_LOCAL_EXEC:
temp1 = gen_reg_rtx (Pmode);
temp2 = gen_reg_rtx (Pmode);
if (TARGET_ARCH32)
{
emit_insn (gen_tle_hix22_sp32 (temp1, addr));
emit_insn (gen_tle_lox10_sp32 (temp2, temp1, addr));
}
else
{
emit_insn (gen_tle_hix22_sp64 (temp1, addr));
emit_insn (gen_tle_lox10_sp64 (temp2, temp1, addr));
}
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp2);
break;
default:
abort ();
}
else
abort ();
return ret;
}
rtx
legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
rtx reg)
{
if (GET_CODE (orig) == SYMBOL_REF)
{
rtx pic_ref, address;
rtx insn;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (flag_pic == 2)
{
rtx temp_reg = ((reload_in_progress || reload_completed)
? reg : gen_reg_rtx (Pmode));
if (Pmode == SImode)
{
emit_insn (gen_movsi_high_pic (temp_reg, orig));
emit_insn (gen_movsi_lo_sum_pic (temp_reg, temp_reg, orig));
}
else
{
emit_insn (gen_movdi_high_pic (temp_reg, orig));
emit_insn (gen_movdi_lo_sum_pic (temp_reg, temp_reg, orig));
}
address = temp_reg;
}
else
address = orig;
pic_ref = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode,
pic_offset_table_rtx, address));
current_function_uses_pic_offset_table = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
insn = emit_move_insn (reg, pic_ref);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig,
REG_NOTES (insn));
return reg;
}
else if (GET_CODE (orig) == CONST)
{
rtx base, offset;
if (GET_CODE (XEXP (orig, 0)) == PLUS
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
else
abort ();
if (GET_CODE (offset) == CONST_INT)
{
if (SMALL_INT (offset))
return plus_constant (base, INTVAL (offset));
else if (! reload_in_progress && ! reload_completed)
offset = force_reg (Pmode, offset);
else
abort ();
}
return gen_rtx_PLUS (Pmode, base, offset);
}
else if (GET_CODE (orig) == LABEL_REF)
current_function_uses_pic_offset_table = 1;
return orig;
}
rtx
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, enum machine_mode mode)
{
rtx orig_x = x;
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT)
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
force_operand (XEXP (x, 0), NULL_RTX));
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == MULT)
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
force_operand (XEXP (x, 1), NULL_RTX));
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS)
x = gen_rtx_PLUS (Pmode, force_operand (XEXP (x, 0), NULL_RTX),
XEXP (x, 1));
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == PLUS)
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
force_operand (XEXP (x, 1), NULL_RTX));
if (x != orig_x && legitimate_address_p (mode, x, FALSE))
return x;
if (tls_symbolic_operand (x))
x = legitimize_tls_address (x);
else if (flag_pic)
x = legitimize_pic_address (x, mode, 0);
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 1)))
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
copy_to_mode_reg (Pmode, XEXP (x, 1)));
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 0)))
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
copy_to_mode_reg (Pmode, XEXP (x, 0)));
else if (GET_CODE (x) == SYMBOL_REF
|| GET_CODE (x) == CONST
|| GET_CODE (x) == LABEL_REF)
x = copy_to_suggested_reg (x, NULL_RTX, Pmode);
return x;
}
void
load_pic_register (void)
{
int orig_flag_pic = flag_pic;
if (! flag_pic)
abort ();
if (get_pc_symbol_name[0] == 0)
{
int align;
ASM_GENERATE_INTERNAL_LABEL (get_pc_symbol_name, "LGETPC", 0);
text_section ();
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (align > 0)
ASM_OUTPUT_ALIGN (asm_out_file, align);
(*targetm.asm_out.internal_label) (asm_out_file, "LGETPC", 0);
fputs ("\tretl\n\tadd\t%o7, %l7, %l7\n", asm_out_file);
}
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
get_pc_symbol = gen_rtx_SYMBOL_REF (Pmode, get_pc_symbol_name);
flag_pic = 0;
emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table,
get_pc_symbol));
flag_pic = orig_flag_pic;
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
}
int
mem_min_alignment (rtx mem, int desired)
{
rtx addr, base, offset;
if (GET_CODE (mem) != MEM)
return 0;
addr = XEXP (mem, 0);
base = offset = NULL_RTX;
if (GET_CODE (addr) == PLUS)
{
if (GET_CODE (XEXP (addr, 0)) == REG)
{
base = XEXP (addr, 0);
if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
offset = XEXP (addr, 1);
else
offset = const0_rtx;
}
}
else if (GET_CODE (addr) == REG)
{
base = addr;
offset = const0_rtx;
}
if (base != NULL_RTX)
{
int regno = REGNO (base);
if (regno != HARD_FRAME_POINTER_REGNUM && regno != STACK_POINTER_REGNUM)
{
if (((cfun != 0
&& REGNO_POINTER_ALIGN (regno) >= desired * BITS_PER_UNIT)
|| (optimize && reload_completed))
&& (INTVAL (offset) & (desired - 1)) == 0)
return 1;
}
else
{
if (((INTVAL (offset) - SPARC_STACK_BIAS) & (desired - 1)) == 0)
return 1;
}
}
else if (! TARGET_UNALIGNED_DOUBLES
|| CONSTANT_P (addr)
|| GET_CODE (addr) == LO_SUM)
{
return 1;
}
return 0;
}
enum sparc_mode_class {
S_MODE, D_MODE, T_MODE, O_MODE,
SF_MODE, DF_MODE, TF_MODE, OF_MODE,
CC_MODE, CCFP_MODE
};
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
#define O_MODES (T_MODES | (1 << (int) O_MODE) | (1 << (int) OF_MODE))
#define SF_MODES (S_MODES)
#define DF_MODES (S_MODES | D_MODES)
#define DF_MODES_NO_S ((1 << (int) D_MODE) | (1 << (int) DF_MODE))
#define TF_ONLY_MODES (1 << (int) TF_MODE)
#define TF_MODES (DF_MODES | TF_ONLY_MODES)
#define TF_MODES_NO_S (DF_MODES_NO_S | TF_ONLY_MODES)
#define OF_ONLY_MODES (1 << (int) OF_MODE)
#define OF_MODES (TF_MODES | OF_ONLY_MODES)
#define OF_MODES_NO_S (TF_MODES_NO_S | OF_ONLY_MODES)
#define CC_MODES (1 << (int) CC_MODE)
#define CCFP_MODES (1 << (int) CCFP_MODE)
const int *hard_regno_mode_classes;
static const int hard_32bit_mode_classes[] = {
S_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
CC_MODES
};
static const int hard_64bit_mode_classes[] = {
D_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
CC_MODES
};
int sparc_mode_class [NUM_MACHINE_MODES];
enum reg_class sparc_regno_reg_class[FIRST_PSEUDO_REGISTER];
static void
sparc_init_modes (void)
{
int i;
for (i = 0; i < NUM_MACHINE_MODES; i++)
{
switch (GET_MODE_CLASS (i))
{
case MODE_INT:
case MODE_PARTIAL_INT:
case MODE_COMPLEX_INT:
if (GET_MODE_SIZE (i) <= 4)
sparc_mode_class[i] = 1 << (int) S_MODE;
else if (GET_MODE_SIZE (i) == 8)
sparc_mode_class[i] = 1 << (int) D_MODE;
else if (GET_MODE_SIZE (i) == 16)
sparc_mode_class[i] = 1 << (int) T_MODE;
else if (GET_MODE_SIZE (i) == 32)
sparc_mode_class[i] = 1 << (int) O_MODE;
else
sparc_mode_class[i] = 0;
break;
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:
if (GET_MODE_SIZE (i) <= 4)
sparc_mode_class[i] = 1 << (int) SF_MODE;
else if (GET_MODE_SIZE (i) == 8)
sparc_mode_class[i] = 1 << (int) DF_MODE;
else if (GET_MODE_SIZE (i) == 16)
sparc_mode_class[i] = 1 << (int) TF_MODE;
else if (GET_MODE_SIZE (i) == 32)
sparc_mode_class[i] = 1 << (int) OF_MODE;
else
sparc_mode_class[i] = 0;
break;
case MODE_CC:
if (i == (int) CCFPmode || i == (int) CCFPEmode)
sparc_mode_class[i] = 1 << (int) CCFP_MODE;
else
sparc_mode_class[i] = 1 << (int) CC_MODE;
break;
default:
sparc_mode_class[i] = 0;
break;
}
}
if (TARGET_ARCH64)
hard_regno_mode_classes = hard_64bit_mode_classes;
else
hard_regno_mode_classes = hard_32bit_mode_classes;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
if (i < 16 && TARGET_V8PLUS)
sparc_regno_reg_class[i] = I64_REGS;
else if (i < 32 || i == FRAME_POINTER_REGNUM)
sparc_regno_reg_class[i] = GENERAL_REGS;
else if (i < 64)
sparc_regno_reg_class[i] = FP_REGS;
else if (i < 96)
sparc_regno_reg_class[i] = EXTRA_FP_REGS;
else if (i < 100)
sparc_regno_reg_class[i] = FPCC_REGS;
else
sparc_regno_reg_class[i] = NO_REGS;
}
}
static int
save_regs (FILE *file, int low, int high, const char *base,
int offset, int n_regs, HOST_WIDE_INT real_offset)
{
int i;
if (TARGET_ARCH64 && high <= 32)
{
for (i = low; i < high; i++)
{
if (regs_ever_live[i] && ! call_used_regs[i])
{
fprintf (file, "\tstx\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
n_regs += 2;
}
}
}
else
{
for (i = low; i < high; i += 2)
{
if (regs_ever_live[i] && ! call_used_regs[i])
{
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
{
fprintf (file, "\tstd\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
{
char *l = dwarf2out_cfi_label ();
dwarf2out_reg_save (l, i, real_offset + 4 * n_regs);
dwarf2out_reg_save (l, i+1, real_offset + 4 * n_regs + 4);
}
n_regs += 2;
}
else
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
n_regs += 2;
}
}
else
{
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[i+1], base, offset + 4 * n_regs + 4);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i + 1, real_offset + 4 * n_regs + 4);
n_regs += 2;
}
}
}
}
return n_regs;
}
static int
restore_regs (FILE *file, int low, int high, const char *base,
int offset, int n_regs)
{
int i;
if (TARGET_ARCH64 && high <= 32)
{
for (i = low; i < high; i++)
{
if (regs_ever_live[i] && ! call_used_regs[i])
fprintf (file, "\tldx\t[%s+%d], %s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
}
}
else
{
for (i = low; i < high; i += 2)
{
if (regs_ever_live[i] && ! call_used_regs[i])
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
fprintf (file, "\tldd\t[%s+%d], %s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
else
fprintf (file, "\tld\t[%s+%d], %s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
else if (regs_ever_live[i+1] && ! call_used_regs[i+1])
fprintf (file, "\tld\t[%s+%d], %s\n",
base, offset + 4 * n_regs + 4, reg_names[i+1]),
n_regs += 2;
}
}
return n_regs;
}
HOST_WIDE_INT
compute_frame_size (HOST_WIDE_INT size, int leaf_function)
{
int n_regs = 0, i;
int outgoing_args_size = (current_function_outgoing_args_size
+ REG_PARM_STACK_SPACE (current_function_decl));
if (TARGET_ARCH64)
{
for (i = 0; i < 8; i++)
if (regs_ever_live[i] && ! call_used_regs[i])
n_regs += 2;
}
else
{
for (i = 0; i < 8; i += 2)
if ((regs_ever_live[i] && ! call_used_regs[i])
|| (regs_ever_live[i+1] && ! call_used_regs[i+1]))
n_regs += 2;
}
for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
if ((regs_ever_live[i] && ! call_used_regs[i])
|| (regs_ever_live[i+1] && ! call_used_regs[i+1]))
n_regs += 2;
num_gfregs = n_regs;
if (leaf_function && n_regs == 0
&& size == 0 && current_function_outgoing_args_size == 0)
{
actual_fsize = apparent_fsize = 0;
}
else
{
apparent_fsize = (size - STARTING_FRAME_OFFSET + 7) & -8;
apparent_fsize += n_regs * 4;
actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8);
}
if (leaf_function == 0 || size > 0)
actual_fsize += (16 * UNITS_PER_WORD) + (TARGET_ARCH64 ? 0 : 8);
return SPARC_STACK_ALIGN (actual_fsize);
}
static void
build_big_number (FILE *file, HOST_WIDE_INT num, const char *reg)
{
#if HOST_BITS_PER_WIDE_INT == 64
HOST_WIDE_INT high_bits = (num >> 32) & 0xffffffff;
if (high_bits == 0
#else
if (num >= 0
#endif
|| ! TARGET_ARCH64)
{
fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
num, reg);
if ((num & 0x3ff) != 0)
fprintf (file, "\tor\t%s, %%lo("HOST_WIDE_INT_PRINT_DEC"), %s\n",
reg, num, reg);
}
#if HOST_BITS_PER_WIDE_INT == 64
else if (high_bits == 0xffffffff)
#else
else
#endif
{
HOST_WIDE_INT inv = ~num;
HOST_WIDE_INT low = -0x400 + (num & 0x3ff);
fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
inv, reg);
fprintf (file, "\txor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
reg, low, reg);
}
#if HOST_BITS_PER_WIDE_INT == 64
else
{
HOST_WIDE_INT low1 = (num >> (32 - 12)) & 0xfff;
HOST_WIDE_INT low2 = (num >> (32 - 12 - 12)) & 0xfff;
HOST_WIDE_INT low3 = (num >> (32 - 12 - 12 - 8)) & 0x0ff;
int to_shift = 12;
fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
high_bits, reg);
if ((high_bits & 0x3ff) != 0)
fprintf (file, "\tor\t%s, %%lo("HOST_WIDE_INT_PRINT_DEC"), %s\n",
reg, high_bits, reg);
if (low1 != 0)
{
fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
reg, low1, reg);
to_shift = 12;
}
else
{
to_shift += 12;
}
if (low2 != 0)
{
fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
reg, low2, reg);
to_shift = 8;
}
else
{
to_shift += 8;
}
fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
if (low3 != 0)
fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
reg, low3, reg);
}
#endif
}
void
sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED)
{
#ifdef HAVE_AS_REGISTER_PSEUDO_OP
int i;
if (TARGET_ARCH32)
return;
for (i = 2; i < 8; i++)
{
if (regs_ever_live [i]
&& ! sparc_hard_reg_printed [i])
{
sparc_hard_reg_printed [i] = 1;
fprintf (file, "\t.register\t%%g%d, #scratch\n", i);
}
if (i == 3) i = 5;
}
#endif
}
static void
sparc_output_function_prologue (FILE *file, HOST_WIDE_INT size)
{
sparc_function_prologue (file, size,
current_function_uses_only_leaf_regs);
}
static void
sparc_function_prologue (FILE *file, HOST_WIDE_INT size, int leaf_function)
{
sparc_output_scratch_registers (file);
actual_fsize = compute_frame_size (size, leaf_function);
if (leaf_function)
{
frame_base_name = "%sp";
frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
}
else
{
frame_base_name = "%fp";
frame_base_offset = SPARC_STACK_BIAS;
}
fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START);
if (actual_fsize == 0)
;
else if (! leaf_function)
{
if (actual_fsize <= 4096)
fprintf (file, "\tsave\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
actual_fsize);
else if (actual_fsize <= 8192)
{
fprintf (file, "\tsave\t%%sp, -4096, %%sp\n");
fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
actual_fsize - 4096);
}
else
{
build_big_number (file, -actual_fsize, "%g1");
fprintf (file, "\tsave\t%%sp, %%g1, %%sp\n");
}
}
else
{
if (actual_fsize <= 4096)
fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
actual_fsize);
else if (actual_fsize <= 8192)
{
fprintf (file, "\tadd\t%%sp, -4096, %%sp\n");
fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
actual_fsize - 4096);
}
else
{
build_big_number (file, -actual_fsize, "%g1");
fprintf (file, "\tadd\t%%sp, %%g1, %%sp\n");
}
}
if (dwarf2out_do_frame () && actual_fsize)
{
char *label = dwarf2out_cfi_label ();
dwarf2out_def_cfa (label, (leaf_function ? STACK_POINTER_REGNUM
: HARD_FRAME_POINTER_REGNUM),
frame_base_offset);
if (! leaf_function)
{
dwarf2out_window_save (label);
dwarf2out_return_reg (label, 31);
}
}
if (! flag_pic)
fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START);
if (num_gfregs)
{
HOST_WIDE_INT offset, real_offset;
int n_regs;
const char *base;
real_offset = -apparent_fsize;
offset = -apparent_fsize + frame_base_offset;
if (offset < -4096 || offset + num_gfregs * 4 > 4096)
{
build_big_number (file, offset, "%g1");
fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
base = "%g1";
offset = 0;
}
else
{
base = frame_base_name;
}
n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset);
save_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs,
real_offset);
}
}
static void
output_restore_regs (FILE *file, int leaf_function ATTRIBUTE_UNUSED)
{
HOST_WIDE_INT offset;
int n_regs;
const char *base;
offset = -apparent_fsize + frame_base_offset;
if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 )
{
build_big_number (file, offset, "%g1");
fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
base = "%g1";
offset = 0;
}
else
{
base = frame_base_name;
}
n_regs = restore_regs (file, 0, 8, base, offset, 0);
restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs);
}
static void
sparc_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
{
sparc_function_epilogue (file, size,
current_function_uses_only_leaf_regs);
}
static void
sparc_function_epilogue (FILE *file,
HOST_WIDE_INT size ATTRIBUTE_UNUSED,
int leaf_function)
{
const char *ret;
sparc_skip_caller_unimp =
! TARGET_ARCH64
&& current_function_returns_struct
&& (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
== INTEGER_CST)
&& ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl)));
if (current_function_epilogue_delay_list == 0)
{
rtx insn, last_real_insn;
insn = get_last_insn ();
last_real_insn = prev_real_insn (insn);
if (last_real_insn
&& GET_CODE (last_real_insn) == INSN
&& GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
fputs("\tnop\n", file);
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
goto output_vectors;
}
if (num_gfregs)
output_restore_regs (file, leaf_function);
if (leaf_function)
ret = (sparc_skip_caller_unimp ? "jmp\t%o7+12" : "retl");
else
ret = (sparc_skip_caller_unimp ? "jmp\t%i7+12" : "ret");
if (! leaf_function)
{
if (current_function_calls_eh_return)
{
if (current_function_epilogue_delay_list)
abort ();
if (sparc_skip_caller_unimp)
abort ();
fputs ("\trestore\n\tretl\n\tadd\t%sp, %g1, %sp\n", file);
}
else if (current_function_epilogue_delay_list)
{
rtx delay = PATTERN (XEXP (current_function_epilogue_delay_list, 0));
if (TARGET_V9 && ! epilogue_renumber (&delay, 1))
{
epilogue_renumber (&delay, 0);
fputs (sparc_skip_caller_unimp
? "\treturn\t%i7+12\n"
: "\treturn\t%i7+8\n", file);
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
file, 1, 0, 0, NULL);
}
else
{
rtx insn, src;
if (GET_CODE (delay) != SET)
abort();
src = SET_SRC (delay);
if (GET_CODE (src) == ASHIFT)
{
if (XEXP (src, 1) != const1_rtx)
abort();
SET_SRC (delay)
= gen_rtx_PLUS (GET_MODE (src), XEXP (src, 0),
XEXP (src, 0));
}
insn = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, delay,
gen_rtx_RETURN (VOIDmode)));
insn = emit_jump_insn (insn);
sparc_emitting_epilogue = true;
final_scan_insn (insn, file, 1, 0, 1, NULL);
sparc_emitting_epilogue = false;
}
}
else if (TARGET_V9 && ! sparc_skip_caller_unimp)
fputs ("\treturn\t%i7+8\n\tnop\n", file);
else
fprintf (file, "\t%s\n\trestore\n", ret);
}
else if (current_function_calls_eh_return)
abort ();
else if (current_function_epilogue_delay_list)
{
if (actual_fsize != 0)
abort ();
fprintf (file, "\t%s\n", ret);
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
file, 1, 0, 1, NULL);
}
else if (actual_fsize == 0)
fprintf (file, "\t%s\n\tnop\n", ret);
else if (actual_fsize <= 4096)
fprintf (file, "\t%s\n\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
ret, actual_fsize);
else if (actual_fsize <= 8192)
fprintf (file, "\tsub\t%%sp, -4096, %%sp\n\t%s\n\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
ret, actual_fsize - 4096);
else
{
build_big_number (file, actual_fsize, "%g1");
fprintf (file, "\t%s\n\tadd\t%%sp, %%g1, %%sp\n", ret);
}
output_vectors:
sparc_output_deferred_case_vectors ();
}
const char *
output_sibcall (rtx insn, rtx call_operand)
{
int leaf_regs = current_function_uses_only_leaf_regs;
rtx operands[3];
int delay_slot = dbr_sequence_length () > 0;
if (num_gfregs)
{
if (delay_slot)
{
rtx delay = NEXT_INSN (insn);
if (! delay)
abort ();
final_scan_insn (delay, asm_out_file, 1, 0, 1, NULL);
PATTERN (delay) = gen_blockage ();
INSN_CODE (delay) = -1;
delay_slot = 0;
}
output_restore_regs (asm_out_file, leaf_regs);
}
operands[0] = call_operand;
if (leaf_regs)
{
#ifdef HAVE_AS_RELAX_OPTION
int spare_slot = 0;
#else
int spare_slot = ((TARGET_ARCH32 || TARGET_CM_MEDLOW) && ! flag_pic);
#endif
HOST_WIDE_INT size = 0;
if ((actual_fsize || ! spare_slot) && delay_slot)
{
rtx delay = NEXT_INSN (insn);
if (! delay)
abort ();
final_scan_insn (delay, asm_out_file, 1, 0, 1, NULL);
PATTERN (delay) = gen_blockage ();
INSN_CODE (delay) = -1;
delay_slot = 0;
}
if (actual_fsize)
{
if (actual_fsize <= 4096)
size = actual_fsize;
else if (actual_fsize <= 8192)
{
fputs ("\tsub\t%sp, -4096, %sp\n", asm_out_file);
size = actual_fsize - 4096;
}
else
{
build_big_number (asm_out_file, actual_fsize, "%g1");
fputs ("\tadd\t%%sp, %%g1, %%sp\n", asm_out_file);
}
}
if (spare_slot)
{
output_asm_insn ("sethi\t%%hi(%a0), %%g1", operands);
output_asm_insn ("jmpl\t%%g1 + %%lo(%a0), %%g0", operands);
if (size)
fprintf (asm_out_file, "\t sub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n", size);
else if (! delay_slot)
fputs ("\t nop\n", asm_out_file);
}
else
{
if (size)
fprintf (asm_out_file, "\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n", size);
output_asm_insn ("or\t%%o7, %%g0, %%g1", operands);
output_asm_insn ("call\t%a0, 0", operands);
output_asm_insn (" or\t%%g1, %%g0, %%o7", operands);
}
return "";
}
output_asm_insn ("call\t%a0, 0", operands);
if (delay_slot)
{
rtx delay = NEXT_INSN (insn), pat;
if (! delay)
abort ();
pat = PATTERN (delay);
if (GET_CODE (pat) != SET)
abort ();
operands[0] = SET_DEST (pat);
pat = SET_SRC (pat);
switch (GET_CODE (pat))
{
case PLUS:
operands[1] = XEXP (pat, 0);
operands[2] = XEXP (pat, 1);
output_asm_insn (" restore %r1, %2, %Y0", operands);
break;
case LO_SUM:
operands[1] = XEXP (pat, 0);
operands[2] = XEXP (pat, 1);
output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
break;
case ASHIFT:
operands[1] = XEXP (pat, 0);
output_asm_insn (" restore %r1, %r1, %Y0", operands);
break;
default:
operands[1] = pat;
output_asm_insn (" restore %%g0, %1, %Y0", operands);
break;
}
PATTERN (delay) = gen_blockage ();
INSN_CODE (delay) = -1;
}
else
fputs ("\t restore\n", asm_out_file);
return "";
}
#define SPARC_INT_ARG_MAX 6
#define SPARC_FP_ARG_MAX 16
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
void
init_cumulative_args (struct sparc_args *cum, tree fntype,
rtx libname ATTRIBUTE_UNUSED,
tree fndecl ATTRIBUTE_UNUSED)
{
cum->words = 0;
cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype);
cum->libcall_p = fntype == 0;
}
static bool
sparc_promote_prototypes (tree fntype ATTRIBUTE_UNUSED)
{
return TARGET_ARCH32 ? true : false;
}
static bool
sparc_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
{
return TARGET_ARCH64 ? true : false;
}
static void
scan_record_type (tree type, int *intregs_p, int *fpregs_p, int *packed_p)
{
tree field;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
scan_record_type (TREE_TYPE (field), intregs_p, fpregs_p, 0);
else if (FLOAT_TYPE_P (TREE_TYPE (field)) && TARGET_FPU)
*fpregs_p = 1;
else
*intregs_p = 1;
if (packed_p && DECL_PACKED (field))
*packed_p = 1;
}
}
}
static int
function_arg_slotno (const struct sparc_args *cum, enum machine_mode mode,
tree type, int named, int incoming_p,
int *pregno, int *ppadding)
{
int regbase = (incoming_p
? SPARC_INCOMING_INT_ARG_FIRST
: SPARC_OUTGOING_INT_ARG_FIRST);
int slotno = cum->words;
int regno;
*ppadding = 0;
if (type && TREE_ADDRESSABLE (type))
return -1;
if (TARGET_ARCH32
&& mode == BLKmode
&& type
&& TYPE_ALIGN (type) % PARM_BOUNDARY != 0)
return -1;
if (TARGET_ARCH64
&& GET_MODE_ALIGNMENT (mode) >= 2 * BITS_PER_WORD
&& (slotno & 1) != 0)
slotno++, *ppadding = 1;
switch (GET_MODE_CLASS (mode))
{
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:
case MODE_VECTOR_INT:
case MODE_VECTOR_FLOAT:
if (TARGET_ARCH64 && TARGET_FPU && named)
{
if (slotno >= SPARC_FP_ARG_MAX)
return -1;
regno = SPARC_FP_ARG_FIRST + slotno * 2;
if (GET_MODE_SIZE (mode) <= 4)
regno++;
break;
}
case MODE_INT:
case MODE_COMPLEX_INT:
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
break;
case MODE_RANDOM:
if (mode == VOIDmode)
return -1;
if (mode != BLKmode)
abort ();
if (TARGET_ARCH64
&& type
&& TYPE_ALIGN (type) >= 2 * BITS_PER_WORD
&& (slotno & 1) != 0)
slotno++, *ppadding = 1;
if (TARGET_ARCH32 || (type && TREE_CODE (type) == UNION_TYPE))
{
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
}
else
{
int intregs_p = 0, fpregs_p = 0, packed_p = 0;
scan_record_type (type, &intregs_p, &fpregs_p, &packed_p);
if (packed_p || !named)
fpregs_p = 0, intregs_p = 1;
if (fpregs_p && slotno >= SPARC_FP_ARG_MAX)
return -1;
if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX)
return -1;
return slotno;
}
break;
default :
abort ();
}
*pregno = regno;
return slotno;
}
struct function_arg_record_value_parms
{
rtx ret;
int slotno;
int named;
int regbase;
int stack;
int intoffset;
unsigned int nregs;
};
static void function_arg_record_value_3
(HOST_WIDE_INT, struct function_arg_record_value_parms *);
static void function_arg_record_value_2
(tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
static void function_arg_record_value_1
(tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
static rtx function_arg_record_value (tree, enum machine_mode, int, int, int);
static rtx function_arg_union_value (int, int);
static void
function_arg_record_value_1 (tree type, HOST_WIDE_INT startbitpos,
struct function_arg_record_value_parms *parms,
bool packed_p)
{
tree field;
if (! packed_p)
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{
packed_p = true;
break;
}
}
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
HOST_WIDE_INT bitpos = startbitpos;
if (DECL_SIZE (field) != 0
&& host_integerp (bit_position (field), 1))
bitpos += int_bit_position (field);
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
function_arg_record_value_1 (TREE_TYPE (field),
bitpos,
parms,
packed_p);
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
&& TARGET_FPU
&& parms->named
&& ! packed_p)
{
if (parms->intoffset != -1)
{
unsigned int startbit, endbit;
int intslots, this_slotno;
startbit = parms->intoffset & -BITS_PER_WORD;
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = parms->slotno + parms->intoffset
/ BITS_PER_WORD;
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
{
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
parms->stack = 1;
}
parms->nregs += intslots;
parms->intoffset = -1;
}
parms->nregs += 1;
if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
parms->nregs += 1;
}
else
{
if (parms->intoffset == -1)
parms->intoffset = bitpos;
}
}
}
}
static void
function_arg_record_value_3 (HOST_WIDE_INT bitpos,
struct function_arg_record_value_parms *parms)
{
enum machine_mode mode;
unsigned int regno;
unsigned int startbit, endbit;
int this_slotno, intslots, intoffset;
rtx reg;
if (parms->intoffset == -1)
return;
intoffset = parms->intoffset;
parms->intoffset = -1;
startbit = intoffset & -BITS_PER_WORD;
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
if (intslots <= 0)
return;
if (intoffset % BITS_PER_WORD != 0)
mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
MODE_INT, 0);
else
mode = word_mode;
intoffset /= BITS_PER_UNIT;
do
{
regno = parms->regbase + this_slotno;
reg = gen_rtx_REG (mode, regno);
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
this_slotno += 1;
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
mode = word_mode;
parms->nregs += 1;
intslots -= 1;
}
while (intslots > 0);
}
static void
function_arg_record_value_2 (tree type, HOST_WIDE_INT startbitpos,
struct function_arg_record_value_parms *parms,
bool packed_p)
{
tree field;
if (! packed_p)
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{
packed_p = true;
break;
}
}
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
HOST_WIDE_INT bitpos = startbitpos;
if (DECL_SIZE (field) != 0
&& host_integerp (bit_position (field), 1))
bitpos += int_bit_position (field);
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
function_arg_record_value_2 (TREE_TYPE (field),
bitpos,
parms,
packed_p);
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
&& TARGET_FPU
&& parms->named
&& ! packed_p)
{
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
int regno;
enum machine_mode mode = DECL_MODE (field);
rtx reg;
function_arg_record_value_3 (bitpos, parms);
switch (mode)
{
case SCmode: mode = SFmode; break;
case DCmode: mode = DFmode; break;
case TCmode: mode = TFmode; break;
default: break;
}
regno = SPARC_FP_ARG_FIRST + this_slotno * 2;
if (GET_MODE_SIZE (mode) <= 4 && (bitpos & 32) != 0)
regno++;
reg = gen_rtx_REG (mode, regno);
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT (bitpos / BITS_PER_UNIT));
parms->nregs += 1;
if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
{
regno += GET_MODE_SIZE (mode) / 4;
reg = gen_rtx_REG (mode, regno);
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT ((bitpos + GET_MODE_BITSIZE (mode))
/ BITS_PER_UNIT));
parms->nregs += 1;
}
}
else
{
if (parms->intoffset == -1)
parms->intoffset = bitpos;
}
}
}
}
static rtx
function_arg_record_value (tree type, enum machine_mode mode,
int slotno, int named, int regbase)
{
HOST_WIDE_INT typesize = int_size_in_bytes (type);
struct function_arg_record_value_parms parms;
unsigned int nregs;
parms.ret = NULL_RTX;
parms.slotno = slotno;
parms.named = named;
parms.regbase = regbase;
parms.stack = 0;
parms.nregs = 0;
parms.intoffset = 0;
function_arg_record_value_1 (type, 0, &parms, false);
if (parms.intoffset != -1)
{
unsigned int startbit, endbit;
int intslots, this_slotno;
startbit = parms.intoffset & -BITS_PER_WORD;
endbit = (typesize*BITS_PER_UNIT + BITS_PER_WORD - 1) & -BITS_PER_WORD;
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
{
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
parms.stack = 1;
}
parms.nregs += intslots;
}
nregs = parms.nregs;
if (nregs == 0)
{
if (typesize <= 0)
{
return gen_rtx_REG (mode, regbase);
}
else
{
nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
if (nregs + slotno > SPARC_INT_ARG_MAX)
nregs = SPARC_INT_ARG_MAX - slotno;
}
if (nregs == 0)
abort ();
parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (parms.stack + nregs));
if (parms.stack)
XVECEXP (parms.ret, 0, 0)
= gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
parms.nregs = 0;
parms.intoffset = 0;
function_arg_record_value_2 (type, 0, &parms, false);
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
if (parms.nregs != nregs)
abort ();
return parms.ret;
}
static rtx
function_arg_union_value (int size, int regno)
{
enum machine_mode mode;
rtx reg;
if (size <= UNITS_PER_WORD)
mode = word_mode;
else
mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
reg = gen_rtx_REG (mode, regno);
return gen_rtx_PARALLEL (mode,
gen_rtvec (1, gen_rtx_EXPR_LIST (VOIDmode,
reg,
const0_rtx)));
}
rtx
function_arg (const struct sparc_args *cum, enum machine_mode mode,
tree type, int named, int incoming_p)
{
int regbase = (incoming_p
? SPARC_INCOMING_INT_ARG_FIRST
: SPARC_OUTGOING_INT_ARG_FIRST);
int slotno, regno, padding;
rtx reg;
slotno = function_arg_slotno (cum, mode, type, named, incoming_p,
®no, &padding);
if (slotno == -1)
return 0;
if (TARGET_ARCH32)
{
reg = gen_rtx_REG (mode, regno);
return reg;
}
if (type && TREE_CODE (type) == RECORD_TYPE)
{
if (int_size_in_bytes (type) > 16)
abort ();
return function_arg_record_value (type, mode, slotno, named, regbase);
}
else if (type && TREE_CODE (type) == UNION_TYPE)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
if (size > 16)
abort ();
return function_arg_union_value (size, regno);
}
else if ((GET_MODE_CLASS (mode) == MODE_FLOAT
|| GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
|| GET_MODE_CLASS (mode) == MODE_VECTOR_INT
|| GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
&& SPARC_FP_REG_P (regno))
{
reg = gen_rtx_REG (mode, regno);
if (cum->prototype_p || cum->libcall_p)
{
#if 0
if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
return gen_rtx_PARALLEL (mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
reg, const0_rtx)));
else
#else
#endif
return reg;
}
else
{
rtx v0, v1;
if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
{
int intreg;
if (incoming_p)
return reg;
intreg = (SPARC_OUTGOING_INT_ARG_FIRST
+ (regno - SPARC_FP_ARG_FIRST) / 2);
v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg),
const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
}
else
{
v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
}
}
}
else
{
reg = gen_rtx_REG (mode, regno);
}
return reg;
}
int
function_arg_partial_nregs (const struct sparc_args *cum,
enum machine_mode mode, tree type, int named)
{
int slotno, regno, padding;
slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
if (slotno == -1)
return 0;
if (TARGET_ARCH32)
{
if ((slotno + (mode == BLKmode
? ROUND_ADVANCE (int_size_in_bytes (type))
: ROUND_ADVANCE (GET_MODE_SIZE (mode))))
> SPARC_INT_ARG_MAX)
return SPARC_INT_ARG_MAX - slotno;
}
else
{
if (type && AGGREGATE_TYPE_P (type))
{
int size = int_size_in_bytes (type);
if (size > UNITS_PER_WORD
&& slotno == SPARC_INT_ARG_MAX - 1)
return 1;
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
|| (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
&& ! (TARGET_FPU && named)))
{
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
&& slotno == SPARC_INT_ARG_MAX - 1)
return 1;
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
{
if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD)
> SPARC_FP_ARG_MAX)
return 1;
}
}
return 0;
}
int
function_arg_pass_by_reference (const struct sparc_args *cum ATTRIBUTE_UNUSED,
enum machine_mode mode, tree type,
int named ATTRIBUTE_UNUSED)
{
if (TARGET_ARCH32)
{
return ((type && AGGREGATE_TYPE_P (type))
|| mode == SCmode
|| GET_MODE_SIZE (mode) > 8);
}
else
{
return ((type && TREE_CODE (type) == ARRAY_TYPE)
|| (type
&& AGGREGATE_TYPE_P (type)
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 16)
|| GET_MODE_SIZE (mode) > 16);
}
}
void
function_arg_advance (struct sparc_args *cum, enum machine_mode mode,
tree type, int named)
{
int slotno, regno, padding;
slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
if (slotno != -1)
cum->words += padding;
if (TARGET_ARCH32)
{
cum->words += (mode != BLKmode
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
else
{
if (type && AGGREGATE_TYPE_P (type))
{
int size = int_size_in_bytes (type);
if (size <= 8)
++cum->words;
else if (size <= 16)
cum->words += 2;
else
++cum->words;
}
else
{
cum->words += (mode != BLKmode
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
}
}
enum direction
function_arg_padding (enum machine_mode mode, tree type)
{
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type))
return upward;
return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
}
static bool
sparc_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
if (TARGET_ARCH32)
return (TYPE_MODE (type) == BLKmode
|| TYPE_MODE (type) == TFmode
|| (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_VECTOR_INT
&& GET_MODE_SIZE (TYPE_MODE (type)) > 8)
|| (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_VECTOR_FLOAT
&& GET_MODE_SIZE (TYPE_MODE (type)) > 32));
else
return ((TYPE_MODE (type) == BLKmode
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 32)
|| GET_MODE_SIZE (TYPE_MODE (type)) > 32);
}
static rtx
sparc_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED, int incoming)
{
if (TARGET_ARCH64)
return 0;
else
{
if (incoming)
return gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx,
STRUCT_VALUE_OFFSET));
else
return gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
STRUCT_VALUE_OFFSET));
}
}
rtx
function_value (tree type, enum machine_mode mode, int incoming_p)
{
int regbase = (incoming_p
? SPARC_OUTGOING_INT_ARG_FIRST
: SPARC_INCOMING_INT_ARG_FIRST);
int regno;
if (TARGET_ARCH64 && type)
{
if (TREE_CODE (type) == RECORD_TYPE)
{
if (int_size_in_bytes (type) > 32)
abort ();
return function_arg_record_value (type, mode, 0, 1, regbase);
}
else if (TREE_CODE (type) == UNION_TYPE)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
if (size > 32)
abort ();
return function_arg_union_value (size, regbase);
}
else if (AGGREGATE_TYPE_P (type))
{
HOST_WIDE_INT bytes = int_size_in_bytes (type);
if (bytes > 32)
abort ();
mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
}
else if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
mode = word_mode;
}
if (TARGET_FPU && (FLOAT_MODE_P (mode) || VECTOR_MODE_P (mode)))
regno = SPARC_FP_ARG_FIRST;
else
regno = regbase;
return gen_rtx_REG (mode, regno);
}
static rtx
sparc_builtin_saveregs (void)
{
int first_reg = current_function_args_info.words;
rtx address;
int regno;
for (regno = first_reg; regno < SPARC_INT_ARG_MAX; regno++)
emit_move_insn (gen_rtx_MEM (word_mode,
gen_rtx_PLUS (Pmode,
frame_pointer_rtx,
GEN_INT (FIRST_PARM_OFFSET (0)
+ (UNITS_PER_WORD
* regno)))),
gen_rtx_REG (word_mode,
SPARC_INCOMING_INT_ARG_FIRST + regno));
address = gen_rtx_PLUS (Pmode,
frame_pointer_rtx,
GEN_INT (FIRST_PARM_OFFSET (0)
+ UNITS_PER_WORD * first_reg));
return address;
}
void
sparc_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
std_expand_builtin_va_start (valist, nextarg);
}
rtx
sparc_va_arg (tree valist, tree type)
{
HOST_WIDE_INT size, rsize, align;
tree addr, incr;
rtx addr_rtx;
bool indirect;
if (function_arg_pass_by_reference (0, TYPE_MODE (type), type, 0))
{
indirect = true;
size = rsize = UNITS_PER_WORD;
align = 0;
}
else
{
indirect = false;
size = int_size_in_bytes (type);
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
align = 0;
if (TARGET_ARCH64)
{
if (TYPE_ALIGN (type) >= 2 * (unsigned) BITS_PER_WORD)
align = 2 * UNITS_PER_WORD;
if (AGGREGATE_TYPE_P (type))
{
if (size == 0)
size = rsize = UNITS_PER_WORD;
else
size = rsize;
}
}
}
incr = valist;
if (align)
{
incr = fold (build (PLUS_EXPR, ptr_type_node, incr,
build_int_2 (align - 1, 0)));
incr = fold (build (BIT_AND_EXPR, ptr_type_node, incr,
build_int_2 (-align, -1)));
}
addr = incr = save_expr (incr);
if (BYTES_BIG_ENDIAN && size < rsize)
{
addr = fold (build (PLUS_EXPR, ptr_type_node, incr,
build_int_2 (rsize - size, 0)));
}
incr = fold (build (PLUS_EXPR, ptr_type_node, incr,
build_int_2 (rsize, 0)));
incr = build (MODIFY_EXPR, ptr_type_node, valist, incr);
TREE_SIDE_EFFECTS (incr) = 1;
expand_expr (incr, const0_rtx, VOIDmode, EXPAND_NORMAL);
addr_rtx = expand_expr (addr, NULL, Pmode, EXPAND_NORMAL);
if (align == 0
&& TYPE_ALIGN (type) > BITS_PER_WORD
&& !indirect)
{
rtx tmp = assign_temp (type, 0, 1, 0);
rtx dest_addr;
addr_rtx = force_reg (Pmode, addr_rtx);
addr_rtx = gen_rtx_MEM (BLKmode, addr_rtx);
set_mem_alias_set (addr_rtx, get_varargs_alias_set ());
set_mem_align (addr_rtx, BITS_PER_WORD);
tmp = shallow_copy_rtx (tmp);
PUT_MODE (tmp, BLKmode);
set_mem_alias_set (tmp, 0);
dest_addr = emit_block_move (tmp, addr_rtx, GEN_INT (rsize),
BLOCK_OP_NORMAL);
if (dest_addr != NULL_RTX)
addr_rtx = dest_addr;
else
addr_rtx = XCEXP (tmp, 0, MEM);
}
if (indirect)
{
addr_rtx = force_reg (Pmode, addr_rtx);
addr_rtx = gen_rtx_MEM (Pmode, addr_rtx);
set_mem_alias_set (addr_rtx, get_varargs_alias_set ());
}
return addr_rtx;
}
char *
output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
int noop, rtx insn)
{
static char string[50];
enum rtx_code code = GET_CODE (op);
rtx cc_reg = XEXP (op, 0);
enum machine_mode mode = GET_MODE (cc_reg);
const char *labelno, *branch;
int spaces = 8, far;
char *p;
far = get_attr_length (insn) >= 3;
if (reversed ^ far)
{
if (mode == CCFPmode || mode == CCFPEmode)
code = reverse_condition_maybe_unordered (code);
else
code = reverse_condition (code);
}
if (mode == CCFPmode || mode == CCFPEmode)
{
switch (code)
{
case NE:
branch = "fbne";
break;
case EQ:
branch = "fbe";
break;
case GE:
branch = "fbge";
break;
case GT:
branch = "fbg";
break;
case LE:
branch = "fble";
break;
case LT:
branch = "fbl";
break;
case UNORDERED:
branch = "fbu";
break;
case ORDERED:
branch = "fbo";
break;
case UNGT:
branch = "fbug";
break;
case UNLT:
branch = "fbul";
break;
case UNEQ:
branch = "fbue";
break;
case UNGE:
branch = "fbuge";
break;
case UNLE:
branch = "fbule";
break;
case LTGT:
branch = "fblg";
break;
default:
abort ();
}
string[0] = '\0';
if (! TARGET_V9)
strcpy (string, "nop\n\t");
strcat (string, branch);
}
else
{
switch (code)
{
case NE:
branch = "bne";
break;
case EQ:
branch = "be";
break;
case GE:
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
branch = "bpos";
else
branch = "bge";
break;
case GT:
branch = "bg";
break;
case LE:
branch = "ble";
break;
case LT:
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
branch = "bneg";
else
branch = "bl";
break;
case GEU:
branch = "bgeu";
break;
case GTU:
branch = "bgu";
break;
case LEU:
branch = "bleu";
break;
case LTU:
branch = "blu";
break;
default:
abort ();
}
strcpy (string, branch);
}
spaces -= strlen (branch);
p = strchr (string, '\0');
if (annul && ! far)
{
strcpy (p, ",a");
p += 2;
spaces -= 2;
}
if (! TARGET_V9)
labelno = "";
else
{
rtx note;
int v8 = 0;
if (! far && insn && INSN_ADDRESSES_SET_P ())
{
int delta = (INSN_ADDRESSES (INSN_UID (dest))
- INSN_ADDRESSES (INSN_UID (insn)));
if (delta < -260000 || delta >= 260000)
v8 = 1;
}
if (mode == CCFPmode || mode == CCFPEmode)
{
static char v9_fcc_labelno[] = "%%fccX, ";
v9_fcc_labelno[5] = REGNO (cc_reg) - SPARC_FIRST_V9_FCC_REG + '0';
labelno = v9_fcc_labelno;
if (v8)
{
if (REGNO (cc_reg) == SPARC_FCC_REG)
labelno = "";
else
abort ();
}
}
else if (mode == CCXmode || mode == CCX_NOOVmode)
{
labelno = "%%xcc, ";
if (v8)
abort ();
}
else
{
labelno = "%%icc, ";
if (v8)
labelno = "";
}
if (*labelno && insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
{
strcpy (p,
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
? ",pt" : ",pn");
p += 3;
spaces -= 3;
}
}
if (spaces > 0)
*p++ = '\t';
else
*p++ = ' ';
strcpy (p, labelno);
p = strchr (p, '\0');
if (far)
{
strcpy (p, ".+12\n\tnop\n\tb\t");
if (annul || noop)
p[3] = '6';
p += 13;
}
*p++ = '%';
*p++ = 'l';
*p++ = label + '0';
*p = '\0';
if (noop)
strcpy (p, "\n\tnop");
return string;
}
void
sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
{
const char *qpfunc;
rtx slot0, slot1, result, tem, tem2;
enum machine_mode mode;
switch (comparison)
{
case EQ:
qpfunc = (TARGET_ARCH64) ? "_Qp_feq" : "_Q_feq";
break;
case NE:
qpfunc = (TARGET_ARCH64) ? "_Qp_fne" : "_Q_fne";
break;
case GT:
qpfunc = (TARGET_ARCH64) ? "_Qp_fgt" : "_Q_fgt";
break;
case GE:
qpfunc = (TARGET_ARCH64) ? "_Qp_fge" : "_Q_fge";
break;
case LT:
qpfunc = (TARGET_ARCH64) ? "_Qp_flt" : "_Q_flt";
break;
case LE:
qpfunc = (TARGET_ARCH64) ? "_Qp_fle" : "_Q_fle";
break;
case ORDERED:
case UNORDERED:
case UNGT:
case UNLT:
case UNEQ:
case UNGE:
case UNLE:
case LTGT:
qpfunc = (TARGET_ARCH64) ? "_Qp_cmp" : "_Q_cmp";
break;
default:
abort();
break;
}
if (TARGET_ARCH64)
{
if (GET_CODE (x) != MEM)
{
slot0 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
emit_insn (gen_rtx_SET (VOIDmode, slot0, x));
}
else
slot0 = x;
if (GET_CODE (y) != MEM)
{
slot1 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
emit_insn (gen_rtx_SET (VOIDmode, slot1, y));
}
else
slot1 = y;
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL,
DImode, 2,
XEXP (slot0, 0), Pmode,
XEXP (slot1, 0), Pmode);
mode = DImode;
}
else
{
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL,
SImode, 2,
x, TFmode, y, TFmode);
mode = SImode;
}
result = gen_reg_rtx (mode);
emit_move_insn (result, hard_libcall_value (mode));
switch (comparison)
{
default:
emit_cmp_insn (result, const0_rtx, NE, NULL_RTX, mode, 0);
break;
case ORDERED:
case UNORDERED:
emit_cmp_insn (result, GEN_INT(3), comparison == UNORDERED ? EQ : NE,
NULL_RTX, mode, 0);
break;
case UNGT:
case UNGE:
emit_cmp_insn (result, const1_rtx,
comparison == UNGT ? GT : NE, NULL_RTX, mode, 0);
break;
case UNLE:
emit_cmp_insn (result, const2_rtx, NE, NULL_RTX, mode, 0);
break;
case UNLT:
tem = gen_reg_rtx (mode);
if (TARGET_ARCH32)
emit_insn (gen_andsi3 (tem, result, const1_rtx));
else
emit_insn (gen_anddi3 (tem, result, const1_rtx));
emit_cmp_insn (tem, const0_rtx, NE, NULL_RTX, mode, 0);
break;
case UNEQ:
case LTGT:
tem = gen_reg_rtx (mode);
if (TARGET_ARCH32)
emit_insn (gen_addsi3 (tem, result, const1_rtx));
else
emit_insn (gen_adddi3 (tem, result, const1_rtx));
tem2 = gen_reg_rtx (mode);
if (TARGET_ARCH32)
emit_insn (gen_andsi3 (tem2, tem, const2_rtx));
else
emit_insn (gen_anddi3 (tem2, tem, const2_rtx));
emit_cmp_insn (tem2, const0_rtx, comparison == UNEQ ? EQ : NE,
NULL_RTX, mode, 0);
break;
}
}
void
sparc_emit_floatunsdi (rtx *operands, enum machine_mode mode)
{
rtx neglab, donelab, i0, i1, f0, in, out;
out = operands[0];
in = force_reg (DImode, operands[1]);
neglab = gen_label_rtx ();
donelab = gen_label_rtx ();
i0 = gen_reg_rtx (DImode);
i1 = gen_reg_rtx (DImode);
f0 = gen_reg_rtx (mode);
emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, DImode, 0, neglab);
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in)));
emit_jump_insn (gen_jump (donelab));
emit_barrier ();
emit_label (neglab);
emit_insn (gen_lshrdi3 (i0, in, const1_rtx));
emit_insn (gen_anddi3 (i1, in, const1_rtx));
emit_insn (gen_iordi3 (i0, i0, i1));
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_FLOAT (mode, i0)));
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0)));
emit_label (donelab);
}
void
sparc_emit_fixunsdi (rtx *operands, enum machine_mode mode)
{
rtx neglab, donelab, i0, i1, f0, in, out, limit;
out = operands[0];
in = force_reg (mode, operands[1]);
neglab = gen_label_rtx ();
donelab = gen_label_rtx ();
i0 = gen_reg_rtx (DImode);
i1 = gen_reg_rtx (DImode);
limit = gen_reg_rtx (mode);
f0 = gen_reg_rtx (mode);
emit_move_insn (limit,
CONST_DOUBLE_FROM_REAL_VALUE (
REAL_VALUE_ATOF ("9223372036854775808.0", mode), mode));
emit_cmp_and_jump_insns (in, limit, GE, NULL_RTX, mode, 0, neglab);
emit_insn (gen_rtx_SET (VOIDmode,
out,
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, in))));
emit_jump_insn (gen_jump (donelab));
emit_barrier ();
emit_label (neglab);
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_MINUS (mode, in, limit)));
emit_insn (gen_rtx_SET (VOIDmode,
i0,
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, f0))));
emit_insn (gen_movdi (i1, const1_rtx));
emit_insn (gen_ashldi3 (i1, i1, GEN_INT (63)));
emit_insn (gen_xordi3 (out, i0, i1));
emit_label (donelab);
}
char *
output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
int annul, int noop, rtx insn)
{
static char string[50];
enum rtx_code code = GET_CODE (op);
enum machine_mode mode = GET_MODE (XEXP (op, 0));
rtx note;
int far;
char *p;
far = get_attr_length (insn) >= 3;
if (reversed ^ far)
code = reverse_condition (code);
if (mode != DImode)
abort ();
switch (code)
{
case NE:
strcpy (string, "brnz");
break;
case EQ:
strcpy (string, "brz");
break;
case GE:
strcpy (string, "brgez");
break;
case LT:
strcpy (string, "brlz");
break;
case LE:
strcpy (string, "brlez");
break;
case GT:
strcpy (string, "brgz");
break;
default:
abort ();
}
p = strchr (string, '\0');
if (annul && ! far)
{
strcpy (p, ",a");
p += 2;
}
if (insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
{
strcpy (p,
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
? ",pt" : ",pn");
p += 3;
}
*p = p < string + 8 ? '\t' : ' ';
p++;
*p++ = '%';
*p++ = '0' + reg;
*p++ = ',';
*p++ = ' ';
if (far)
{
int veryfar = 1, delta;
if (INSN_ADDRESSES_SET_P ())
{
delta = (INSN_ADDRESSES (INSN_UID (dest))
- INSN_ADDRESSES (INSN_UID (insn)));
if (delta >= -260000 && delta < 260000)
veryfar = 0;
}
strcpy (p, ".+12\n\tnop\n\t");
if (annul || noop)
p[3] = '6';
p += 11;
if (veryfar)
{
strcpy (p, "b\t");
p += 2;
}
else
{
strcpy (p, "ba,pt\t%%xcc, ");
p += 13;
}
}
*p++ = '%';
*p++ = 'l';
*p++ = '0' + label;
*p = '\0';
if (noop)
strcpy (p, "\n\tnop");
return string;
}
static int
epilogue_renumber (register rtx *where, int test)
{
register const char *fmt;
register int i;
register enum rtx_code code;
if (*where == 0)
return 0;
code = GET_CODE (*where);
switch (code)
{
case REG:
if (REGNO (*where) >= 8 && REGNO (*where) < 24)
return 1;
if (! test && REGNO (*where) >= 24 && REGNO (*where) < 32)
*where = gen_rtx_REG (GET_MODE (*where), OUTGOING_REGNO (REGNO(*where)));
case SCRATCH:
case CC0:
case PC:
case CONST_INT:
case CONST_DOUBLE:
return 0;
case PLUS:
if (GET_CODE (XEXP (*where, 0)) == REG
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM
&& (GET_CODE (XEXP (*where, 1)) != CONST_INT
|| INTVAL (XEXP (*where, 1)) < SPARC_STACK_BIAS))
return 1;
break;
case MEM:
if (SPARC_STACK_BIAS
&& GET_CODE (XEXP (*where, 0)) == REG
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM)
return 1;
break;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (*where, i) - 1; j >= 0; j--)
if (epilogue_renumber (&(XVECEXP (*where, i, j)), test))
return 1;
}
else if (fmt[i] == 'e'
&& epilogue_renumber (&(XEXP (*where, i)), test))
return 1;
}
return 0;
}
static const int
reg_leaf_alloc_order[] = REG_LEAF_ALLOC_ORDER;
static const int
reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER;
static const int *const reg_alloc_orders[] = {
reg_leaf_alloc_order,
reg_nonleaf_alloc_order};
void
order_regs_for_local_alloc (void)
{
static int last_order_nonleaf = 1;
if (regs_ever_live[15] != last_order_nonleaf)
{
last_order_nonleaf = !last_order_nonleaf;
memcpy ((char *) reg_alloc_order,
(const char *) reg_alloc_orders[last_order_nonleaf],
FIRST_PSEUDO_REGISTER * sizeof (int));
}
}
int
sparc_splitdi_legitimate (rtx reg, rtx mem)
{
if (! reload_completed)
abort ();
if (! offsettable_memref_p (mem))
return 0;
if ((REGNO (reg) % 2) == 0
&& mem_min_alignment (mem, 8))
return 0;
return 1;
}
int
sparc_absnegfloat_split_legitimate (rtx x, rtx y)
{
if (GET_CODE (x) != REG)
return 0;
if (GET_CODE (y) != REG)
return 0;
if (REGNO (x) == REGNO (y))
return 0;
return 1;
}
int
registers_ok_for_ldd_peep (rtx reg1, rtx reg2)
{
if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
return 0;
if (REGNO (reg1) % 2 != 0)
return 0;
if (TARGET_V9 && REGNO (reg1) < 32)
return 0;
return (REGNO (reg1) == REGNO (reg2) - 1);
}
int
mems_ok_for_ldd_peep (rtx mem1, rtx mem2, rtx dependent_reg_rtx)
{
rtx addr1, addr2;
unsigned int reg1;
HOST_WIDE_INT offset1;
if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
return 0;
if (MEM_ALIGN (mem1) < 64)
return 0;
addr1 = XEXP (mem1, 0);
addr2 = XEXP (mem2, 0);
if (GET_CODE (addr1) == PLUS)
{
if (GET_CODE (XEXP (addr1, 0)) != REG)
return 0;
else
{
reg1 = REGNO (XEXP (addr1, 0));
if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
return 0;
offset1 = INTVAL (XEXP (addr1, 1));
}
}
else if (GET_CODE (addr1) != REG)
return 0;
else
{
reg1 = REGNO (addr1);
offset1 = 0;
}
if (GET_CODE (addr2) != PLUS)
return 0;
if (GET_CODE (XEXP (addr2, 0)) != REG
|| GET_CODE (XEXP (addr2, 1)) != CONST_INT)
return 0;
if (reg1 != REGNO (XEXP (addr2, 0)))
return 0;
if (dependent_reg_rtx != NULL_RTX && reg1 == REGNO (dependent_reg_rtx))
return 0;
if (offset1 % 8 != 0)
return 0;
if (INTVAL (XEXP (addr2, 1)) != offset1 + 4)
return 0;
return 1;
}
int
register_ok_for_ldd (rtx reg)
{
if (GET_CODE (reg) != REG)
return 0;
if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
return (REGNO (reg) % 2 == 0);
else
return 1;
}
void
print_operand (FILE *file, rtx x, int code)
{
switch (code)
{
case '#':
if (dbr_sequence_length () == 0)
fputs ("\n\t nop", file);
return;
case '*':
if (dbr_sequence_length () == 0
&& (optimize && (int)sparc_cpu < PROCESSOR_V9))
fputs (",a", file);
return;
case '(':
if (dbr_sequence_length () == 0
&& ! (optimize && (int)sparc_cpu < PROCESSOR_V9))
fputs ("\n\t nop", file);
return;
case '_':
fputs (EMBMEDANY_BASE_REG, file);
return;
case '@':
fprintf (file, "%s+"HOST_WIDE_INT_PRINT_DEC, frame_base_name, frame_base_offset);
return;
case '&':
assemble_name (file, get_some_local_dynamic_name ());
return;
case 'Y':
if (GET_CODE (x) == CONST_INT)
break;
else if (GET_CODE (x) != REG)
output_operand_lossage ("invalid %%Y operand");
else if (REGNO (x) < 8)
fputs (reg_names[REGNO (x)], file);
else if (REGNO (x) >= 24 && REGNO (x) < 32)
fputs (reg_names[REGNO (x)-16], file);
else
output_operand_lossage ("invalid %%Y operand");
return;
case 'L':
if (WORDS_BIG_ENDIAN)
fputs (reg_names[REGNO (x)+1], file);
else
fputs (reg_names[REGNO (x)], file);
return;
case 'H':
if (WORDS_BIG_ENDIAN)
fputs (reg_names[REGNO (x)], file);
else
fputs (reg_names[REGNO (x)+1], file);
return;
case 'R':
fputs (reg_names[REGNO (x)+1], file);
return;
case 'S':
fputs (reg_names[REGNO (x)+2], file);
return;
case 'T':
fputs (reg_names[REGNO (x)+3], file);
return;
case 'x':
if (REGNO (x) == SPARC_ICC_REG)
{
if (GET_MODE (x) == CCmode)
fputs ("%icc", file);
else if (GET_MODE (x) == CCXmode)
fputs ("%xcc", file);
else
abort ();
}
else
fputs (reg_names[REGNO (x)], file);
return;
case 'm':
output_address (XEXP (x, 0));
return;
case 'r':
if (x == const0_rtx
|| (GET_MODE (x) != VOIDmode && x == CONST0_RTX (GET_MODE (x))))
{
fputs ("%g0", file);
return;
}
else
break;
case 'A':
switch (GET_CODE (x))
{
case IOR: fputs ("or", file); break;
case AND: fputs ("and", file); break;
case XOR: fputs ("xor", file); break;
default: output_operand_lossage ("invalid %%A operand");
}
return;
case 'B':
switch (GET_CODE (x))
{
case IOR: fputs ("orn", file); break;
case AND: fputs ("andn", file); break;
case XOR: fputs ("xnor", file); break;
default: output_operand_lossage ("invalid %%B operand");
}
return;
case 'c' :
case 'C':
{
enum rtx_code rc = GET_CODE (x);
if (code == 'c')
{
enum machine_mode mode = GET_MODE (XEXP (x, 0));
if (mode == CCFPmode || mode == CCFPEmode)
rc = reverse_condition_maybe_unordered (GET_CODE (x));
else
rc = reverse_condition (GET_CODE (x));
}
switch (rc)
{
case NE: fputs ("ne", file); break;
case EQ: fputs ("e", file); break;
case GE: fputs ("ge", file); break;
case GT: fputs ("g", file); break;
case LE: fputs ("le", file); break;
case LT: fputs ("l", file); break;
case GEU: fputs ("geu", file); break;
case GTU: fputs ("gu", file); break;
case LEU: fputs ("leu", file); break;
case LTU: fputs ("lu", file); break;
case LTGT: fputs ("lg", file); break;
case UNORDERED: fputs ("u", file); break;
case ORDERED: fputs ("o", file); break;
case UNLT: fputs ("ul", file); break;
case UNLE: fputs ("ule", file); break;
case UNGT: fputs ("ug", file); break;
case UNGE: fputs ("uge", file); break;
case UNEQ: fputs ("ue", file); break;
default: output_operand_lossage (code == 'c'
? "invalid %%c operand"
: "invalid %%C operand");
}
return;
}
case 'd':
case 'D':
{
enum rtx_code rc = (code == 'd'
? reverse_condition (GET_CODE (x))
: GET_CODE (x));
switch (rc)
{
case NE: fputs ("ne", file); break;
case EQ: fputs ("e", file); break;
case GE: fputs ("gez", file); break;
case LT: fputs ("lz", file); break;
case LE: fputs ("lez", file); break;
case GT: fputs ("gz", file); break;
default: output_operand_lossage (code == 'd'
? "invalid %%d operand"
: "invalid %%D operand");
}
return;
}
case 'b':
{
int i = trunc_int_for_mode (INTVAL (x), QImode);
fprintf (file, "%d", i);
return;
}
case 'f':
if (GET_CODE (x) != MEM)
output_operand_lossage ("invalid %%f operand");
output_address (XEXP (x, 0));
return;
case 's':
{
HOST_WIDE_INT i;
if (GET_CODE(x) == CONST_INT)
i = INTVAL (x);
else if (GET_CODE(x) == CONST_DOUBLE)
i = CONST_DOUBLE_LOW (x);
else
{
output_operand_lossage ("invalid %%s operand");
return;
}
i = trunc_int_for_mode (i, SImode);
fprintf (file, HOST_WIDE_INT_PRINT_DEC, i);
return;
}
case 0:
break;
default:
output_operand_lossage ("invalid operand output code");
}
if (GET_CODE (x) == REG)
fputs (reg_names[REGNO (x)], file);
else if (GET_CODE (x) == MEM)
{
fputc ('[', file);
if (CONSTANT_P (XEXP (x, 0)))
fputs ("%g0+", file);
output_address (XEXP (x, 0));
fputc (']', file);
}
else if (GET_CODE (x) == HIGH)
{
fputs ("%hi(", file);
output_addr_const (file, XEXP (x, 0));
fputc (')', file);
}
else if (GET_CODE (x) == LO_SUM)
{
print_operand (file, XEXP (x, 0), 0);
if (TARGET_CM_MEDMID)
fputs ("+%l44(", file);
else
fputs ("+%lo(", file);
output_addr_const (file, XEXP (x, 1));
fputc (')', file);
}
else if (GET_CODE (x) == CONST_DOUBLE
&& (GET_MODE (x) == VOIDmode
|| GET_MODE_CLASS (GET_MODE (x)) == MODE_INT))
{
if (CONST_DOUBLE_HIGH (x) == 0)
fprintf (file, "%u", (unsigned int) CONST_DOUBLE_LOW (x));
else if (CONST_DOUBLE_HIGH (x) == -1
&& CONST_DOUBLE_LOW (x) < 0)
fprintf (file, "%d", (int) CONST_DOUBLE_LOW (x));
else
output_operand_lossage ("long long constant not a valid immediate operand");
}
else if (GET_CODE (x) == CONST_DOUBLE)
output_operand_lossage ("floating point constant not a valid immediate operand");
else { output_addr_const (file, x); }
}
static bool
sparc_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
if (aligned_p && size == 8
&& (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE))
{
if (TARGET_V9)
{
assemble_integer_with_op ("\t.xword\t", x);
return true;
}
else
{
assemble_aligned_integer (4, const0_rtx);
assemble_aligned_integer (4, x);
return true;
}
}
return default_assemble_integer (x, size, aligned_p);
}
#ifndef SHORT_TYPE_SIZE
#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2)
#endif
#ifndef INT_TYPE_SIZE
#define INT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_TYPE_SIZE
#define LONG_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_LONG_TYPE_SIZE
#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef FLOAT_TYPE_SIZE
#define FLOAT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef DOUBLE_TYPE_SIZE
#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef LONG_DOUBLE_TYPE_SIZE
#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
unsigned long
sparc_type_code (register tree type)
{
register unsigned long qualifiers = 0;
register unsigned shift;
for (shift = 6; shift < 30; shift += 2, type = TREE_TYPE (type))
{
switch (TREE_CODE (type))
{
case ERROR_MARK:
return qualifiers;
case ARRAY_TYPE:
qualifiers |= (3 << shift);
break;
case FUNCTION_TYPE:
case METHOD_TYPE:
qualifiers |= (2 << shift);
break;
case POINTER_TYPE:
case REFERENCE_TYPE:
case OFFSET_TYPE:
qualifiers |= (1 << shift);
break;
case RECORD_TYPE:
return (qualifiers | 8);
case UNION_TYPE:
case QUAL_UNION_TYPE:
return (qualifiers | 9);
case ENUMERAL_TYPE:
return (qualifiers | 10);
case VOID_TYPE:
return (qualifiers | 16);
case INTEGER_TYPE:
if (TREE_TYPE (type) != 0)
break;
if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE)
return (qualifiers | (TYPE_UNSIGNED (type) ? 12 : 2));
else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE)
return (qualifiers | (TYPE_UNSIGNED (type) ? 13 : 3));
else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE)
return (qualifiers | (TYPE_UNSIGNED (type) ? 14 : 4));
else
return (qualifiers | (TYPE_UNSIGNED (type) ? 15 : 5));
case REAL_TYPE:
if (TREE_TYPE (type) != 0)
break;
if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
return (qualifiers | 6);
else
return (qualifiers | 7);
case COMPLEX_TYPE:
return (qualifiers | 7);
case VECTOR_TYPE:
case CHAR_TYPE:
case BOOLEAN_TYPE:
case FILE_TYPE:
case SET_TYPE:
case LANG_TYPE:
return qualifiers;
default:
abort ();
}
}
return qualifiers;
}
void
sparc_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
{
emit_move_insn
(gen_rtx_MEM (SImode, plus_constant (tramp, 0)),
expand_binop (SImode, ior_optab,
expand_shift (RSHIFT_EXPR, SImode, fnaddr,
size_int (10), 0, 1),
GEN_INT (trunc_int_for_mode (0x03000000, SImode)),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn
(gen_rtx_MEM (SImode, plus_constant (tramp, 4)),
expand_binop (SImode, ior_optab,
expand_shift (RSHIFT_EXPR, SImode, cxt,
size_int (10), 0, 1),
GEN_INT (trunc_int_for_mode (0x05000000, SImode)),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn
(gen_rtx_MEM (SImode, plus_constant (tramp, 8)),
expand_binop (SImode, ior_optab,
expand_and (SImode, fnaddr, GEN_INT (0x3ff), NULL_RTX),
GEN_INT (trunc_int_for_mode (0x81c06000, SImode)),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn
(gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
expand_binop (SImode, ior_optab,
expand_and (SImode, cxt, GEN_INT (0x3ff), NULL_RTX),
GEN_INT (trunc_int_for_mode (0x8410a000, SImode)),
NULL_RTX, 1, OPTAB_DIRECT));
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode, tramp))));
if (sparc_cpu != PROCESSOR_ULTRASPARC
&& sparc_cpu != PROCESSOR_ULTRASPARC3)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode,
plus_constant (tramp, 8)))));
#ifdef TRANSFER_FROM_TRAMPOLINE
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
#endif
}
void
sparc64_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
{
emit_move_insn (gen_rtx_MEM (SImode, tramp),
GEN_INT (trunc_int_for_mode (0x83414000, SImode)));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)),
GEN_INT (trunc_int_for_mode (0xca586018, SImode)));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)),
GEN_INT (trunc_int_for_mode (0x81c14000, SImode)));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
GEN_INT (trunc_int_for_mode (0xca586010, SImode)));
emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 16)), cxt);
emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 24)), fnaddr);
emit_insn (gen_flushdi (validize_mem (gen_rtx_MEM (DImode, tramp))));
if (sparc_cpu != PROCESSOR_ULTRASPARC
&& sparc_cpu != PROCESSOR_ULTRASPARC3)
emit_insn (gen_flushdi (validize_mem (gen_rtx_MEM (DImode, plus_constant (tramp, 8)))));
#ifdef TRANSFER_FROM_TRAMPOLINE
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
#endif
}
static int
supersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
enum attr_type insn_type;
if (! recog_memoized (insn))
return 0;
insn_type = get_attr_type (insn);
if (REG_NOTE_KIND (link) == 0)
{
if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD)
return cost + 3;
if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE)
{
rtx pat = PATTERN(insn);
rtx dep_pat = PATTERN (dep_insn);
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
return cost;
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
return cost;
return cost + 3;
}
if (insn_type == TYPE_SHIFT)
return cost + 3;
}
else
{
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
return 0;
}
return cost;
}
static int
hypersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
enum attr_type insn_type, dep_type;
rtx pat = PATTERN(insn);
rtx dep_pat = PATTERN (dep_insn);
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
return cost;
insn_type = get_attr_type (insn);
dep_type = get_attr_type (dep_insn);
switch (REG_NOTE_KIND (link))
{
case 0:
switch (insn_type)
{
case TYPE_STORE:
case TYPE_FPSTORE:
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
return cost;
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
return cost;
return cost + 3;
case TYPE_LOAD:
case TYPE_SLOAD:
case TYPE_FPLOAD:
if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE)
{
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|| GET_CODE (SET_SRC (pat)) != MEM
|| ! rtx_equal_p (XEXP (SET_DEST (dep_pat), 0),
XEXP (SET_SRC (pat), 0)))
return cost + 2;
return cost + 8;
}
break;
case TYPE_BRANCH:
if (dep_type == TYPE_COMPARE)
return 0;
if (dep_type == TYPE_FPCMP)
return cost - 1;
break;
default:
break;
}
break;
case REG_DEP_ANTI:
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
return 0;
break;
default:
break;
}
return cost;
}
static int
sparc_adjust_cost(rtx insn, rtx link, rtx dep, int cost)
{
switch (sparc_cpu)
{
case PROCESSOR_SUPERSPARC:
cost = supersparc_adjust_cost (insn, link, dep, cost);
break;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
cost = hypersparc_adjust_cost (insn, link, dep, cost);
break;
default:
break;
}
return cost;
}
static void
sparc_sched_init (FILE *dump ATTRIBUTE_UNUSED,
int sched_verbose ATTRIBUTE_UNUSED,
int max_ready ATTRIBUTE_UNUSED)
{
}
static int
sparc_use_dfa_pipeline_interface (void)
{
if ((1 << sparc_cpu) &
((1 << PROCESSOR_ULTRASPARC) | (1 << PROCESSOR_CYPRESS) |
(1 << PROCESSOR_SUPERSPARC) | (1 << PROCESSOR_HYPERSPARC) |
(1 << PROCESSOR_SPARCLITE86X) | (1 << PROCESSOR_TSC701) |
(1 << PROCESSOR_ULTRASPARC3)))
return 1;
return 0;
}
static int
sparc_use_sched_lookahead (void)
{
if (sparc_cpu == PROCESSOR_ULTRASPARC
|| sparc_cpu == PROCESSOR_ULTRASPARC3)
return 4;
if ((1 << sparc_cpu) &
((1 << PROCESSOR_SUPERSPARC) | (1 << PROCESSOR_HYPERSPARC) |
(1 << PROCESSOR_SPARCLITE86X)))
return 3;
return 0;
}
static int
sparc_issue_rate (void)
{
switch (sparc_cpu)
{
default:
return 1;
case PROCESSOR_V9:
return 2;
case PROCESSOR_SUPERSPARC:
return 3;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
return 2;
case PROCESSOR_ULTRASPARC:
case PROCESSOR_ULTRASPARC3:
return 4;
}
}
static int
set_extends (rtx insn)
{
register rtx pat = PATTERN (insn);
switch (GET_CODE (SET_SRC (pat)))
{
case MEM:
case ZERO_EXTEND:
case HIGH:
case LO_SUM:
case LT: case LTU:
case GT: case GTU:
case LE: case LEU:
case GE: case GEU:
case EQ:
case NE:
return 1;
case AND:
{
rtx op0 = XEXP (SET_SRC (pat), 0);
rtx op1 = XEXP (SET_SRC (pat), 1);
if (GET_CODE (op1) == CONST_INT)
return INTVAL (op1) >= 0;
if (GET_CODE (op0) != REG)
return 0;
if (sparc_check_64 (op0, insn) == 1)
return 1;
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
}
case IOR:
case XOR:
{
rtx op0 = XEXP (SET_SRC (pat), 0);
rtx op1 = XEXP (SET_SRC (pat), 1);
if (GET_CODE (op0) != REG || sparc_check_64 (op0, insn) <= 0)
return 0;
if (GET_CODE (op1) == CONST_INT)
return INTVAL (op1) >= 0;
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
}
case LSHIFTRT:
return GET_MODE (SET_SRC (pat)) == SImode;
case CONST_DOUBLE:
return ! (CONST_DOUBLE_LOW (SET_SRC (pat)) & 0x80000000);
case CONST_INT:
return ! (INTVAL (SET_SRC (pat)) & 0x80000000);
case ASHIFTRT:
case SIGN_EXTEND:
return - (GET_MODE (SET_SRC (pat)) == SImode);
case REG:
return sparc_check_64 (SET_SRC (pat), insn);
default:
return 0;
}
}
static GTY(()) rtx sparc_addr_diff_list;
static GTY(()) rtx sparc_addr_list;
void
sparc_defer_case_vector (rtx lab, rtx vec, int diff)
{
vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec);
if (diff)
sparc_addr_diff_list
= gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_diff_list);
else
sparc_addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_list);
}
static void
sparc_output_addr_vec (rtx vec)
{
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
int idx, vlen = XVECLEN (body, 0);
#ifdef ASM_OUTPUT_ADDR_VEC_START
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
#endif
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
NEXT_INSN (lab));
#else
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
#endif
for (idx = 0; idx < vlen; idx++)
{
ASM_OUTPUT_ADDR_VEC_ELT
(asm_out_file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
}
#ifdef ASM_OUTPUT_ADDR_VEC_END
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
#endif
}
static void
sparc_output_addr_diff_vec (rtx vec)
{
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
rtx base = XEXP (XEXP (body, 0), 0);
int idx, vlen = XVECLEN (body, 1);
#ifdef ASM_OUTPUT_ADDR_VEC_START
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
#endif
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
NEXT_INSN (lab));
#else
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
#endif
for (idx = 0; idx < vlen; idx++)
{
ASM_OUTPUT_ADDR_DIFF_ELT
(asm_out_file,
body,
CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
CODE_LABEL_NUMBER (base));
}
#ifdef ASM_OUTPUT_ADDR_VEC_END
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
#endif
}
static void
sparc_output_deferred_case_vectors (void)
{
rtx t;
int align;
if (sparc_addr_list == NULL_RTX
&& sparc_addr_diff_list == NULL_RTX)
return;
function_section (current_function_decl);
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (align > 0)
ASM_OUTPUT_ALIGN (asm_out_file, align);
for (t = sparc_addr_list; t ; t = XEXP (t, 1))
sparc_output_addr_vec (XEXP (t, 0));
for (t = sparc_addr_diff_list; t ; t = XEXP (t, 1))
sparc_output_addr_diff_vec (XEXP (t, 0));
sparc_addr_list = sparc_addr_diff_list = NULL_RTX;
}
int
sparc_check_64 (rtx x, rtx insn)
{
int set_once = 0;
rtx y = x;
if (GET_CODE (x) != REG)
abort ();
if (GET_MODE (x) == DImode)
y = gen_rtx_REG (SImode, REGNO (x) + WORDS_BIG_ENDIAN);
if (flag_expensive_optimizations
&& REG_N_SETS (REGNO (y)) == 1)
set_once = 1;
if (insn == 0)
{
if (set_once)
insn = get_last_insn_anywhere ();
else
return 0;
}
while ((insn = PREV_INSN (insn)))
{
switch (GET_CODE (insn))
{
case JUMP_INSN:
case NOTE:
break;
case CODE_LABEL:
case CALL_INSN:
default:
if (! set_once)
return 0;
break;
case INSN:
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) != SET)
return 0;
if (rtx_equal_p (x, SET_DEST (pat)))
return set_extends (insn);
if (y && rtx_equal_p (y, SET_DEST (pat)))
return set_extends (insn);
if (reg_overlap_mentioned_p (SET_DEST (pat), y))
return 0;
}
}
}
return 0;
}
char *
sparc_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
{
static char asm_code[60];
if (which_alternative != 2)
operands[3] = operands[0];
if (GET_CODE (operands[2]) == CONST_INT)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("mov\t%1, %3", operands);
}
else
{
output_asm_insn ("sllx\t%H1, 32, %3", operands);
if (sparc_check_64 (operands[1], insn) <= 0)
output_asm_insn ("srl\t%L1, 0, %L1", operands);
output_asm_insn ("or\t%L1, %3, %3", operands);
}
strcpy(asm_code, opcode);
if (which_alternative != 2)
return strcat (asm_code, "\t%0, %2, %L0\n\tsrlx\t%L0, 32, %H0");
else
return strcat (asm_code, "\t%3, %2, %3\n\tsrlx\t%3, 32, %H0\n\tmov\t%3, %L0");
}
void
sparc_profile_hook (int labelno)
{
char buf[32];
rtx lab, fun;
ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
fun = gen_rtx_SYMBOL_REF (Pmode, MCOUNT_FUNCTION);
emit_library_call (fun, LCT_NORMAL, VOIDmode, 1, lab, Pmode);
}
#ifdef OBJECT_FORMAT_ELF
static void
sparc_elf_asm_named_section (const char *name, unsigned int flags)
{
if (flags & SECTION_MERGE)
{
default_elf_asm_named_section (name, flags);
return;
}
fprintf (asm_out_file, "\t.section\t\"%s\"", name);
if (!(flags & SECTION_DEBUG))
fputs (",#alloc", asm_out_file);
if (flags & SECTION_WRITE)
fputs (",#write", asm_out_file);
if (flags & SECTION_TLS)
fputs (",#tls", asm_out_file);
if (flags & SECTION_CODE)
fputs (",#execinstr", asm_out_file);
fputc ('\n', asm_out_file);
}
#endif
static bool
sparc_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
return (decl && (TARGET_ARCH64 || ! current_function_returns_struct));
}
#include "config/gofast.h"
static void
sparc_init_libfuncs (void)
{
if (TARGET_ARCH32)
{
set_optab_libfunc (smul_optab, SImode, "*.umul");
set_optab_libfunc (sdiv_optab, SImode, "*.div");
set_optab_libfunc (udiv_optab, SImode, "*.udiv");
set_optab_libfunc (smod_optab, SImode, "*.rem");
set_optab_libfunc (umod_optab, SImode, "*.urem");
set_optab_libfunc (add_optab, TFmode, "_Q_add");
set_optab_libfunc (sub_optab, TFmode, "_Q_sub");
set_optab_libfunc (neg_optab, TFmode, "_Q_neg");
set_optab_libfunc (smul_optab, TFmode, "_Q_mul");
set_optab_libfunc (sdiv_optab, TFmode, "_Q_div");
if (TARGET_FPU)
set_optab_libfunc (sqrt_optab, TFmode, "_Q_sqrt");
set_optab_libfunc (eq_optab, TFmode, "_Q_feq");
set_optab_libfunc (ne_optab, TFmode, "_Q_fne");
set_optab_libfunc (gt_optab, TFmode, "_Q_fgt");
set_optab_libfunc (ge_optab, TFmode, "_Q_fge");
set_optab_libfunc (lt_optab, TFmode, "_Q_flt");
set_optab_libfunc (le_optab, TFmode, "_Q_fle");
set_conv_libfunc (sext_optab, TFmode, SFmode, "_Q_stoq");
set_conv_libfunc (sext_optab, TFmode, DFmode, "_Q_dtoq");
set_conv_libfunc (trunc_optab, SFmode, TFmode, "_Q_qtos");
set_conv_libfunc (trunc_optab, DFmode, TFmode, "_Q_qtod");
set_conv_libfunc (sfix_optab, SImode, TFmode, "_Q_qtoi");
set_conv_libfunc (ufix_optab, SImode, TFmode, "_Q_qtou");
set_conv_libfunc (sfloat_optab, TFmode, SImode, "_Q_itoq");
if (DITF_CONVERSION_LIBFUNCS)
{
set_conv_libfunc (sfix_optab, DImode, TFmode, "_Q_qtoll");
set_conv_libfunc (ufix_optab, DImode, TFmode, "_Q_qtoull");
set_conv_libfunc (sfloat_optab, TFmode, DImode, "_Q_lltoq");
}
if (SUN_CONVERSION_LIBFUNCS)
{
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll");
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull");
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll");
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull");
}
}
if (TARGET_ARCH64)
{
set_optab_libfunc (smul_optab, SImode, 0);
set_optab_libfunc (sdiv_optab, SImode, 0);
set_optab_libfunc (udiv_optab, SImode, 0);
set_optab_libfunc (smod_optab, SImode, 0);
set_optab_libfunc (umod_optab, SImode, 0);
if (SUN_INTEGER_MULTIPLY_64)
{
set_optab_libfunc (smul_optab, DImode, "__mul64");
set_optab_libfunc (sdiv_optab, DImode, "__div64");
set_optab_libfunc (udiv_optab, DImode, "__udiv64");
set_optab_libfunc (smod_optab, DImode, "__rem64");
set_optab_libfunc (umod_optab, DImode, "__urem64");
}
if (SUN_CONVERSION_LIBFUNCS)
{
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftol");
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoul");
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtol");
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoul");
}
}
gofast_maybe_init_libfuncs ();
}
static void
sparc_aout_select_rtx_section (enum machine_mode mode, rtx x,
unsigned HOST_WIDE_INT align)
{
if (align <= MAX_TEXT_ALIGN
&& ! (flag_pic && symbolic_operand (x, mode)))
readonly_data_section ();
else
data_section ();
}
int
sparc_extra_constraint_check (rtx op, int c, int strict)
{
int reload_ok_mem;
if (TARGET_ARCH64
&& (c == 'T' || c == 'U'))
return 0;
switch (c)
{
case 'Q':
return fp_sethi_p (op);
case 'R':
return fp_mov_p (op);
case 'S':
return fp_high_losum_p (op);
case 'U':
if (! strict
|| (GET_CODE (op) == REG
&& (REGNO (op) < FIRST_PSEUDO_REGISTER
|| reg_renumber[REGNO (op)] >= 0)))
return register_ok_for_ldd (op);
return 0;
case 'W':
case 'T':
break;
default:
return 0;
}
if (GET_CODE (op) == MEM)
{
reload_ok_mem = 0;
if ((TARGET_ARCH64 || mem_min_alignment (op, 8))
&& (! strict
|| strict_memory_address_p (Pmode, XEXP (op, 0))))
reload_ok_mem = 1;
}
else
{
reload_ok_mem = (reload_in_progress
&& GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER
&& reg_renumber [REGNO (op)] < 0);
}
return reload_ok_mem;
}
static bool
sparc_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case PLUS: case MINUS: case ABS: case NEG:
case FLOAT: case UNSIGNED_FLOAT:
case FIX: case UNSIGNED_FIX:
case FLOAT_EXTEND: case FLOAT_TRUNCATE:
if (FLOAT_MODE_P (GET_MODE (x)))
{
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
case PROCESSOR_ULTRASPARC3:
*total = COSTS_N_INSNS (4);
return true;
case PROCESSOR_SUPERSPARC:
*total = COSTS_N_INSNS (3);
return true;
case PROCESSOR_CYPRESS:
*total = COSTS_N_INSNS (5);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
default:
*total = COSTS_N_INSNS (1);
return true;
}
}
*total = COSTS_N_INSNS (1);
return true;
case SQRT:
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (13);
else
*total = COSTS_N_INSNS (23);
return true;
case PROCESSOR_ULTRASPARC3:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (20);
else
*total = COSTS_N_INSNS (29);
return true;
case PROCESSOR_SUPERSPARC:
*total = COSTS_N_INSNS (12);
return true;
case PROCESSOR_CYPRESS:
*total = COSTS_N_INSNS (63);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
*total = COSTS_N_INSNS (17);
return true;
default:
*total = COSTS_N_INSNS (30);
return true;
}
case COMPARE:
if (FLOAT_MODE_P (GET_MODE (x)))
{
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
case PROCESSOR_ULTRASPARC3:
*total = COSTS_N_INSNS (1);
return true;
case PROCESSOR_SUPERSPARC:
*total = COSTS_N_INSNS (3);
return true;
case PROCESSOR_CYPRESS:
*total = COSTS_N_INSNS (5);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
default:
*total = COSTS_N_INSNS (1);
return true;
}
}
*total = COSTS_N_INSNS (1);
return true;
case MULT:
if (FLOAT_MODE_P (GET_MODE (x)))
{
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
case PROCESSOR_ULTRASPARC3:
*total = COSTS_N_INSNS (4);
return true;
case PROCESSOR_SUPERSPARC:
*total = COSTS_N_INSNS (3);
return true;
case PROCESSOR_CYPRESS:
*total = COSTS_N_INSNS (7);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
*total = COSTS_N_INSNS (1);
return true;
default:
*total = COSTS_N_INSNS (5);
return true;
}
}
if (sparc_cpu == PROCESSOR_ULTRASPARC)
{
*total = (GET_MODE (x) == DImode
? COSTS_N_INSNS (34) : COSTS_N_INSNS (19));
return true;
}
if (sparc_cpu == PROCESSOR_ULTRASPARC3)
{
*total = COSTS_N_INSNS (6);
return true;
}
if (sparc_cpu == PROCESSOR_HYPERSPARC
|| sparc_cpu == PROCESSOR_SPARCLITE86X)
{
*total = COSTS_N_INSNS (17);
return true;
}
*total = (TARGET_HARD_MUL ? COSTS_N_INSNS (5) : COSTS_N_INSNS (25));
return true;
case DIV:
case UDIV:
case MOD:
case UMOD:
if (FLOAT_MODE_P (GET_MODE (x)))
{
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (13);
else
*total = COSTS_N_INSNS (23);
return true;
case PROCESSOR_ULTRASPARC3:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (17);
else
*total = COSTS_N_INSNS (20);
return true;
case PROCESSOR_SUPERSPARC:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (6);
else
*total = COSTS_N_INSNS (9);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (8);
else
*total = COSTS_N_INSNS (12);
return true;
default:
*total = COSTS_N_INSNS (7);
return true;
}
}
if (sparc_cpu == PROCESSOR_ULTRASPARC)
*total = (GET_MODE (x) == DImode
? COSTS_N_INSNS (68) : COSTS_N_INSNS (37));
else if (sparc_cpu == PROCESSOR_ULTRASPARC3)
*total = (GET_MODE (x) == DImode
? COSTS_N_INSNS (71) : COSTS_N_INSNS (40));
else
*total = COSTS_N_INSNS (25);
return true;
case IF_THEN_ELSE:
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
*total = COSTS_N_INSNS (2);
return true;
case PROCESSOR_ULTRASPARC3:
if (FLOAT_MODE_P (GET_MODE (x)))
*total = COSTS_N_INSNS (3);
else
*total = COSTS_N_INSNS (2);
return true;
default:
*total = COSTS_N_INSNS (1);
return true;
}
case MEM:
switch (sparc_cpu)
{
case PROCESSOR_ULTRASPARC:
if (outer_code == ZERO_EXTEND)
*total = COSTS_N_INSNS (1);
else
*total = COSTS_N_INSNS (2);
return true;
case PROCESSOR_ULTRASPARC3:
if (outer_code == ZERO_EXTEND)
{
if (GET_MODE (x) == QImode
|| GET_MODE (x) == HImode
|| outer_code == SIGN_EXTEND)
*total = COSTS_N_INSNS (2);
else
*total = COSTS_N_INSNS (1);
}
else
{
*total = COSTS_N_INSNS (2);
}
return true;
case PROCESSOR_SUPERSPARC:
if (FLOAT_MODE_P (GET_MODE (x))
|| outer_code == ZERO_EXTEND
|| outer_code == SIGN_EXTEND)
*total = COSTS_N_INSNS (0);
else
*total = COSTS_N_INSNS (1);
return true;
case PROCESSOR_TSC701:
if (outer_code == ZERO_EXTEND
|| outer_code == SIGN_EXTEND)
*total = COSTS_N_INSNS (2);
else
*total = COSTS_N_INSNS (3);
return true;
case PROCESSOR_CYPRESS:
if (outer_code == ZERO_EXTEND
|| outer_code == SIGN_EXTEND)
*total = COSTS_N_INSNS (1);
else
*total = COSTS_N_INSNS (2);
return true;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
default:
if (outer_code == ZERO_EXTEND
|| outer_code == SIGN_EXTEND)
*total = COSTS_N_INSNS (0);
else
*total = COSTS_N_INSNS (1);
return true;
}
case CONST_INT:
if (INTVAL (x) < 0x1000 && INTVAL (x) >= -0x1000)
{
*total = 0;
return true;
}
case HIGH:
*total = 2;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
*total = 4;
return true;
case CONST_DOUBLE:
if (GET_MODE (x) == DImode
&& ((XINT (x, 3) == 0
&& (unsigned HOST_WIDE_INT) XINT (x, 2) < 0x1000)
|| (XINT (x, 3) == -1
&& XINT (x, 2) < 0
&& XINT (x, 2) >= -0x1000)))
*total = 0;
else
*total = 8;
return true;
default:
return false;
}
}
static void
sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta,
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
tree function)
{
rtx this, insn, funexp, delta_rtx, tmp;
reload_completed = 1;
epilogue_completed = 1;
no_new_pseudos = 1;
current_function_uses_only_leaf_regs = 1;
emit_note (NOTE_INSN_PROLOGUE_END);
if (TARGET_ARCH64 && aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
this = gen_rtx_REG (Pmode, SPARC_INCOMING_INT_ARG_FIRST + 1);
else
this = gen_rtx_REG (Pmode, SPARC_INCOMING_INT_ARG_FIRST);
delta_rtx = GEN_INT (delta);
if (!SPARC_SIMM13_P (delta))
{
rtx scratch = gen_rtx_REG (Pmode, 1);
if (input_operand (delta_rtx, GET_MODE (scratch)))
emit_insn (gen_rtx_SET (VOIDmode, scratch, delta_rtx));
else
{
if (TARGET_ARCH64)
sparc_emit_set_const64 (scratch, delta_rtx);
else
sparc_emit_set_const32 (scratch, delta_rtx);
}
delta_rtx = scratch;
}
tmp = gen_rtx_PLUS (Pmode, this, delta_rtx);
emit_insn (gen_rtx_SET (VOIDmode, this, tmp));
if (! TREE_USED (function))
{
assemble_external (function);
TREE_USED (function) = 1;
}
funexp = XEXP (DECL_RTL (function), 0);
funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
insn = emit_call_insn (gen_sibcall (funexp));
SIBLING_CALL_P (insn) = 1;
emit_barrier ();
insn = get_insns ();
insn_locators_initialize ();
shorten_branches (insn);
final_start_function (insn, file, 1);
final (insn, file, 1, 0);
final_end_function ();
reload_completed = 0;
epilogue_completed = 0;
no_new_pseudos = 0;
}
static struct machine_function *
sparc_init_machine_status (void)
{
return ggc_alloc_cleared (sizeof (struct machine_function));
}
static const char *
get_some_local_dynamic_name (void)
{
rtx insn;
if (cfun->machine->some_ld_name)
return cfun->machine->some_ld_name;
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
return cfun->machine->some_ld_name;
abort ();
}
static int
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
{
rtx x = *px;
if (x
&& GET_CODE (x) == SYMBOL_REF
&& SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC)
{
cfun->machine->some_ld_name = XSTR (x, 0);
return 1;
}
return 0;
}
void
sparc_output_dwarf_dtprel (FILE *file, int size, rtx x)
{
switch (size)
{
case 4:
fputs ("\t.word\t%r_tls_dtpoff32(", file);
break;
case 8:
fputs ("\t.xword\t%r_tls_dtpoff64(", file);
break;
default:
abort ();
}
output_addr_const (file, x);
fputs (")", file);
}
#include "gt-sparc.h"