blob: e55821fe2ee3e3ccf262d604ad96963d7b9ded82 [file] [log] [blame]
/* GCC backend functions for C-SKY targets.
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Contributed by C-SKY Microsystems and Mentor Graphics.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3, or (at your
option) any later version.
GCC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define IN_TARGET_CODE 1
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "memmodel.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
#include "cfghooks.h"
#include "df.h"
#include "tm_p.h"
#include "stringpool.h"
#include "attribs.h"
#include "optabs.h"
#include "regs.h"
#include "emit-rtl.h"
#include "recog.h"
#include "cgraph.h"
#include "c-family/c-common.h"
#include "cpplib.h"
#include "diagnostic-core.h"
#include "alias.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "calls.h"
#include "varasm.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "reload.h"
#include "explow.h"
#include "expr.h"
#include "cfgrtl.h"
#include "sched-int.h"
#include "common/common-target.h"
#include "langhooks.h"
#include "intl.h"
#include "libfuncs.h"
#include "opts.h"
#include "dumpfile.h"
#include "target-globals.h"
#include "builtins.h"
#include "tm-constrs.h"
#include "rtl-iter.h"
#include "pass_manager.h"
#include "tree-pass.h"
#include "context.h"
/* This file should be included last. */
#include "target-def.h"
/* Stack and register size macros. */
#define CSKY_NUM_WORDS(SIZE) \
(((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
#define CSKY_NUM_REGS(MODE) \
CSKY_NUM_WORDS (GET_MODE_SIZE (MODE))
#define CSKY_STACK_ALIGN(SIZE) \
(CSKY_NUM_WORDS (SIZE) * UNITS_PER_WORD)
/* Offsets and range macros. */
#define CSKY_LD16_MAX_OFFSET(MODE) \
(31 * GET_MODE_SIZE (MODE))
#define CSKY_LD32_MAX_OFFSET(MODE) \
(4095 * GET_MODE_SIZE (MODE))
#define CSKY_LD16_OFFSET_MASK(MODE) \
(CSKY_LD16_MAX_OFFSET (MODE) + GET_MODE_SIZE (MODE) - 1)
#define CSKY_ADDI16_MAX_IMM 256
#define CSKY_SUBI16_MAX_IMM 256
#define CSKY_CONSTPOOL_LABEL_PREFIX "LCP"
/* Array of the smallest class containing reg number REGNO, indexed by
REGNO. Used by REGNO_REG_CLASS. */
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
{
/* Registers r0-r7. */
MINI_REGS, MINI_REGS, MINI_REGS, MINI_REGS,
MINI_REGS, MINI_REGS, MINI_REGS, MINI_REGS,
/* Registers r8-r15. */
LOW_REGS, LOW_REGS, LOW_REGS, LOW_REGS,
LOW_REGS, LOW_REGS, SP_REGS, LOW_REGS,
/* Registers r16-r31. */
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
/* Reserved. */
RESERVE_REGS,
/* CC,HI,LO registers. */
C_REGS, HILO_REGS, HILO_REGS,
/* Reserved. */
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
/* Vec registers. */
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
/* Reserved. */
RESERVE_REGS, RESERVE_REGS,
/* Register epc. */
OTHER_REGS,
/* Vec registers. */
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
V_REGS, V_REGS, V_REGS, V_REGS,
/* Reserved. */
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
/* Reserved. */
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS,
RESERVE_REGS, RESERVE_REGS, RESERVE_REGS
};
/* Arrays that map GCC register numbers to debugger register numbers,
'-1' means that is INVALID_REGNUM.
TODO: which rules according to here ? */
const int csky_dbx_regno[FIRST_PSEUDO_REGISTER] =
{
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
-1, -1, 36, 37,
75, 79, 83, 87, 91, 95, 99, 103,
107, 111, 115, 119, 123, 127, 131, 135,
74, 78, 82, 86, 90, 94, 98, 102,
106, 110, 114, 118, 122, 126, 130, 134,
-1, -1, 72,
/* vr: 71 - 86 */
139, 143, 147, 151, 155, 159, 163, 167,
171, 175, 179, 183, 187, 191, 195, 199,
138, 142, 146, 150, 154, 158, 162, 166,
170, 174, 178, 182, 186, 190, 194, 198,
/* resereved */
-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, -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
};
/* Table of machine attributes. */
static tree csky_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
static tree csky_handle_isr_attribute (tree *, tree, tree, int, bool *);
static const struct attribute_spec csky_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
affects_type_identity, handler, exclude } */
{ "naked", 0, 0, true, false, false, false, csky_handle_fndecl_attribute, NULL },
/* Interrupt Service Routines have special prologue and epilogue requirements. */
{ "interrupt", 0, 1, false, false, false, false, csky_handle_isr_attribute, NULL },
{ "isr", 0, 1, false, false, false, false, csky_handle_isr_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
/* A C structure for machine-specific, per-function data.
This is added to the cfun structure. */
typedef struct GTY(()) machine_function
{
/* Records if LR has to be saved for far jumps. */
int far_jump_used;
/* Records the type of the current function. */
unsigned long func_type;
/* Record if the function has a variable argument list. */
int uses_anonymous_args;
/* Stack frame layout information. If frame_init_p is true,
these fields have been initialized and don't need to be
recomputed. */
unsigned int reg_mask; /* non-volatile reg saves */
int arg_size; /* stdarg spills (bytes) */
int reg_size; /* non-volatile reg saves (bytes) */
int local_size; /* locals */
int outbound_size; /* arg overflow on calls out */
int frame_size; /* total static size of stack frame */
int local_offset;
int reg_offset;
int arg_offset;
int frame_init_p;
} machine_function;
/* These macros are for the func_type values above. */
#define CSKY_FT_TYPE_MASK ((1 << 3) - 1)
#define CSKY_FT_UNKNOWN 0 /* Type not been determined */
#define CSKY_FT_NORMAL 1 /* Normal function */
#define CSKY_FT_ISR 4 /* Interrupt service routine */
#define CSKY_FT_FIQ 5 /* Fast interrupt service routine */
#define CSKY_FT_EXCEPTION 6 /* Exception handler */
#define CSKY_FT_INTERRUPT (1 << 2) /* overlap CSKY_FT_ISR */
#define CSKY_FT_NAKED (1 << 3) /* No prologue and epilogue */
#define CSKY_FUNCTION_TYPE(t) ((t) & CSKY_FT_TYPE_MASK)
#define CSKY_FUNCTION_IS_INTERRUPT(t) ((t) & CSKY_FT_INTERRUPT)
#define CSKY_FUNCTION_IS_NAKED(t) ((t) & CSKY_FT_NAKED)
struct csky_processors
{
const char *const name;
enum csky_processor_type core;
const char *arch;
enum csky_base_architecture base_arch;
enum csky_isa_feature isa_bits[CSKY_ISA_FEATURE_GET (max)];
};
static struct csky_processors all_cores[] =
{
#undef CSKY_CORE
#define CSKY_CORE(NAME, CORE, X, ARCH, ISA) \
{NAME, TARGET_CPU_##CORE, #ARCH, CSKY_BASE_ARCH_##ARCH, \
{ISA CSKY_ISA_FEATURE_GET (none)}},
#include "csky_cores.def"
#undef CSKY_CORE
{NULL, TARGET_CPU_csky_none, NULL, CSKY_BASE_ARCH_NONE, \
{CSKY_ISA_FEATURE_GET (none)}}
};
static struct csky_processors all_architectures[] =
{
#undef CSKY_ARCH
#define CSKY_ARCH(NAME, CORE, ARCH, ISA) \
{NAME, TARGET_CPU_##CORE, #ARCH, CSKY_BASE_ARCH_##ARCH, \
{ISA CSKY_ISA_FEATURE_GET (none)}},
#include "csky_cores.def"
#undef CSKY_ARCH
{NULL, TARGET_CPU_csky_none, NULL, CSKY_BASE_ARCH_NONE, \
{CSKY_ISA_FEATURE_GET (none)}}
};
struct csky_fpu_desc
{
const char *name;
enum csky_isa_feature isa_bits[CSKY_ISA_FEATURE_GET (max)];
};
static const struct csky_fpu_desc all_fpus[] =
{
#undef CSKY_FPU
#define CSKY_FPU(NAME, CNAME, ISA) \
{NAME, {ISA CSKY_ISA_FEATURE_GET (none)}},
#include "csky_cores.def"
#undef CSKY_FPU
};
/* Active target architecture. */
struct csky_build_target
{
/* Name of the target CPU, if known, or NULL if the target CPU was not
specified by the user (and inferred from the -march option). */
const char *core_name;
/* Name of the target ARCH. NULL if there is a selected CPU. */
const char *arch_name;
/* Preprocessor substring (never NULL). */
const char *arch_pp_name;
/* CPU identifier for the core we're compiling for (architecturally). */
enum csky_processor_type arch_core;
/* The base architecture value. */
enum csky_base_architecture base_arch;
/* Bitmap encapsulating the isa_bits for the target environment. */
sbitmap isa;
};
struct csky_build_target csky_active_target;
/* The following are used in the .md file as equivalents to bits. */
int csky_arch_isa_features[CSKY_ISA_FEATURE_GET (max)] = {0};
/* The highest CSKY architecture version supported by the target. */
enum csky_base_architecture csky_base_arch = CSKY_TARGET_ARCH_GET (NONE);
/* Forward definitions of types. */
typedef struct minipool_node Mnode;
typedef struct minipool_fixup Mfix;
static GTY(()) int tls_labelno;
/* Maximum constant offset that can be added/subtracted from SP in a
single instruction. For ck801, this is for addsp/subsp, otherwise
it is the range of addi/subi. */
#define CSKY_MAX_SP_ADJUST \
(CSKY_TARGET_ARCH (CK801) ? 508 : 4096)
/* Implement TARGET_CPU_CPP_BUILTINS. */
#define builtin_define(MACRO) cpp_define (pfile, MACRO)
void
csky_cpu_cpp_builtins (cpp_reader *pfile)
{
const char *arch_name = csky_active_target.arch_pp_name;
char *pp_name = (char *) alloca (1 + strlen (arch_name) + 4);
sprintf (pp_name, "__%s__", arch_name);
builtin_define (pp_name);
builtin_define ("__csky__=2");
builtin_define ("__CSKY__=2");
builtin_define ("__ckcore__=2");
builtin_define ("__CKCORE__=2");
builtin_define ("__CSKYABIV2__");
builtin_define ("__cskyabiv2__");
builtin_define ("__CSKYABI__=2");
builtin_define ("__cskyabi__=2");
if (TARGET_BIG_ENDIAN)
{
builtin_define ("__ckcoreBE__");
builtin_define ("__cskyBE__");
builtin_define ("__cskybe__");
builtin_define ("__CSKYBE__");
}
else
{
builtin_define ("__ckcoreLE__");
builtin_define ("__cskyLE__");
builtin_define ("__cskyle__");
builtin_define ("__CSKYLE__");
}
if (TARGET_HARD_FLOAT)
{
builtin_define ("__csky_hard_float__");
builtin_define ("__CSKY_HARD_FLOAT__");
if (TARGET_HARD_FLOAT_ABI)
{
builtin_define ("__csky_hard_float_abi__");
builtin_define ("__CSKY_HARD_FLOAT_ABI__");
}
if (TARGET_SINGLE_FPU)
{
builtin_define ("__csky_hard_float_fpu_sf__");
builtin_define ("__CSKY_HARD_FLOAT_FPU_SF__");
}
}
else
{
builtin_define ("__csky_soft_float__");
builtin_define ("__CSKY_SOFT_FLOAT__");
}
if (CSKY_ISA_FEATURE (fpv2_sf))
{
builtin_define ("__csky_fpuv2__");
builtin_define ("__CSKY_FPUV2__");
}
if (TARGET_SUPPORT_FPV3)
{
builtin_define ("__csky_fpuv3__");
builtin_define ("__CSKY_FPUV3__");
}
if (TARGET_ELRW)
{
builtin_define ("__csky_elrw__");
builtin_define ("__CSKY_ELRW__");
}
if (TARGET_ISTACK)
{
builtin_define ("__csky_istack__");
builtin_define ("__CSKY_ISTACK__");
}
if (TARGET_MP)
{
builtin_define ("__csky_mp__");
builtin_define ("__CSKY_MP__");
}
if (TARGET_CP)
{
builtin_define ("__csky_cp__");
builtin_define ("__CSKY_CP__");
}
if (TARGET_CACHE)
{
builtin_define ("__csky_cache__");
builtin_define ("__CSKY_CACHE__");
}
if (TARGET_SECURITY)
{
builtin_define ("__csky_security__");
builtin_define ("__CSKY_SECURITY__");
}
if (TARGET_TRUST)
{
builtin_define ("__csky_trust__");
builtin_define ("__CSKY_TRUST__");
}
if (TARGET_DSP)
{
builtin_define ("__csky_dsp__");
builtin_define ("__CSKY_DSP__");
}
if (TARGET_EDSP)
{
builtin_define ("__csky_edsp__");
builtin_define ("__CSKY_EDSP__");
}
if (TARGET_VDSP)
{
builtin_define ("__csky_vdsp__");
builtin_define ("__CSKY_VDSP__");
}
}
/******************************************************************
* Storage Layout *
******************************************************************/
#undef TARGET_PROMOTE_FUNCTION_MODE
#define TARGET_PROMOTE_FUNCTION_MODE \
default_promote_function_mode_always_promote
#undef TARGET_CONSTANT_ALIGNMENT
#define TARGET_CONSTANT_ALIGNMENT csky_constant_alignment
#undef TARGET_MANGLE_TYPE
#define TARGET_MANGLE_TYPE csky_mangle_type
/******************************************************************
* Stack Layout and Calling Conventions *
******************************************************************/
#undef TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE csky_can_eliminate
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG csky_function_arg
#undef TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE csky_function_arg_advance
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE csky_function_value
#undef TARGET_LIBCALL_VALUE
#define TARGET_LIBCALL_VALUE csky_libcall_value
#undef TARGET_FUNCTION_VALUE_REGNO_P
#define TARGET_FUNCTION_VALUE_REGNO_P csky_function_value_regno_p
#undef TARGET_SPLIT_COMPLEX_ARG
#define TARGET_SPLIT_COMPLEX_ARG hook_bool_const_tree_true
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES csky_arg_partial_bytes
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK csky_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK \
hook_bool_const_tree_hwi_hwi_const_tree_true
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE csky_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE csky_output_function_epilogue
#undef TARGET_WARN_FUNC_RETURN
#define TARGET_WARN_FUNC_RETURN csky_warn_func_return
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY csky_return_in_memory
/******************************************************************
* Implementing the Varargs Macros *
******************************************************************/
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS csky_setup_incoming_varargs
/******************************************************************
* Implicit Calls to Library Routines *
******************************************************************/
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS csky_init_libfuncs
/******************************************************************
* Dividing the Output into Sections (Texts, Data, . . . ) *
******************************************************************/
#undef TARGET_HAVE_TLS
#define TARGET_HAVE_TLS TARGET_CSKY_LINUX
/******************************************************************
* Defining target-specific uses of __attribute__ *
******************************************************************/
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE csky_attribute_table
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE csky_option_override
/* Implement the BRANCH_COST target macro. */
int
csky_default_branch_cost (bool speed_p ATTRIBUTE_UNUSED,
bool predictable_p ATTRIBUTE_UNUSED)
{
return csky_branch_cost;
}
bool
csky_default_logical_op_non_short_circuit (void)
{
return BRANCH_COST (optimize_function_for_speed_p (cfun), false) >= 2;
}
/******************************************************************
* Register Usage *
******************************************************************/
#undef TARGET_HARD_REGNO_NREGS
#define TARGET_HARD_REGNO_NREGS csky_hard_regno_nregs
#undef TARGET_HARD_REGNO_MODE_OK
#define TARGET_HARD_REGNO_MODE_OK csky_hard_regno_mode_ok
#undef TARGET_MODES_TIEABLE_P
#define TARGET_MODES_TIEABLE_P csky_modes_tieable_p
#undef TARGET_CONDITIONAL_REGISTER_USAGE
#define TARGET_CONDITIONAL_REGISTER_USAGE csky_conditional_register_usage
#undef TARGET_CLASS_LIKELY_SPILLED_P
#define TARGET_CLASS_LIKELY_SPILLED_P csky_class_likely_spilled_p
#undef TARGET_PREFERRED_RELOAD_CLASS
#define TARGET_PREFERRED_RELOAD_CLASS csky_preferred_reload_class
#undef TARGET_CLASS_MAX_NREGS
#define TARGET_CLASS_MAX_NREGS csky_class_max_nregs
#undef TARGET_SECONDARY_RELOAD
#define TARGET_SECONDARY_RELOAD csky_secondary_reload
#undef TARGET_SPILL_CLASS
#define TARGET_SPILL_CLASS csky_spill_class
/******************************************************************
* Addressing Modes *
******************************************************************/
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM csky_cannot_force_const_mem
#undef TARGET_LEGITIMATE_CONSTANT_P
#define TARGET_LEGITIMATE_CONSTANT_P csky_legitimate_constant_p
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS csky_legitimize_address
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P csky_legitimate_address_p
/******************************************************************
* Others *
******************************************************************/
#undef TARGET_CANNOT_COPY_INSN_P
#define TARGET_CANNOT_COPY_INSN_P csky_cannot_copy_insn_p
/******************************************************************
* Assembler Format *
******************************************************************/
#undef TARGET_PRINT_OPERAND
#define TARGET_PRINT_OPERAND csky_print_operand
#undef TARGET_PRINT_OPERAND_ADDRESS
#define TARGET_PRINT_OPERAND_ADDRESS csky_print_operand_address
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t"
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"
#undef TARGET_DWARF_REGISTER_SPAN
#define TARGET_DWARF_REGISTER_SPAN csky_dwarf_register_span
/******************************************************************
* Miscellaneous Parameters *
******************************************************************/
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG csky_reorg
#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS csky_allocate_stack_slots_for_args
#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
/******************************************************************
* Trampolines for Nested Functions *
******************************************************************/
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
#define TARGET_ASM_TRAMPOLINE_TEMPLATE csky_asm_trampoline_template
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT csky_trampoline_init
/* The low bit is ignored by jsr and jmp instructions so is safe to use. */
#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS
#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1
/******************************************************************
* Describing Relative Costs of Operations *
******************************************************************/
#undef TARGET_REGISTER_MOVE_COST
#define TARGET_REGISTER_MOVE_COST csky_register_move_cost
#undef TARGET_MEMORY_MOVE_COST
#define TARGET_MEMORY_MOVE_COST csky_memory_move_cost
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS csky_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST csky_address_cost
/******************************************************************
* Anchor address *
******************************************************************/
/* FIXME: the max offset is related to mode size, the following is
defined according to SImode. How to deal with HImode and
QImode, and should the min offset be defined? */
#undef TARGET_MAX_ANCHOR_OFFSET
#define TARGET_MAX_ANCHOR_OFFSET \
((TARGET_MINI_REGISTERS && optimize_size) ? 127 : 4095)
/******************************************************************
* Condition Code Status *
******************************************************************/
#undef TARGET_FIXED_CONDITION_CODE_REGS
#define TARGET_FIXED_CONDITION_CODE_REGS csky_fixed_condition_code_regs
/******************************************************************
* Adjusting the Instruction Scheduler *
******************************************************************/
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE csky_sched_issue_rate
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST csky_sched_adjust_cost
/******************************************************************
* Builtin *
******************************************************************/
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS csky_init_builtins
/* The declaration of functions. */
static void push_csky_minipool_fix (rtx_insn *, HOST_WIDE_INT, rtx *,
machine_mode, rtx);
static void csky_print_operand (FILE *stream, rtx x, int code);
/* Define a table to map ISR attribute arguments onto function type
modifiers. */
typedef struct
{
const char *const arg;
const unsigned long return_value;
} isr_attribute_entry;
static const isr_attribute_entry isr_attribute_map[] =
{
{"irq", CSKY_FT_ISR },
{"IRQ", CSKY_FT_ISR },
{"fiq", CSKY_FT_FIQ },
{"FIQ", CSKY_FT_FIQ },
{NULL, CSKY_FT_NORMAL }
};
/* Return the function type of the current function, if it has not been
determined, return CSKY_FT_UNKNOWN. */
static unsigned long
get_csky_isr_type (tree argument)
{
const isr_attribute_entry *ptr;
const char *arg;
/* if argument is NULL, set default value ISR. */
if (argument == NULL_TREE)
return CSKY_FT_ISR;
if (TREE_VALUE (argument) == NULL_TREE
|| TREE_CODE (TREE_VALUE (argument)) != STRING_CST)
return CSKY_FT_UNKNOWN;
arg = TREE_STRING_POINTER (TREE_VALUE (argument));
for (ptr = isr_attribute_map; ptr->arg != NULL; ptr++)
if (strcmp (arg, ptr->arg) == 0)
return ptr->return_value;
return CSKY_FT_UNKNOWN;
}
/* Classify cfun as a normal function or some sort of interrupt
handler, and set the corresponding bits in cfun->machine->func_type. */
static unsigned long
get_csky_current_func_type (void)
{
if (CSKY_FUNCTION_TYPE (cfun->machine->func_type) == CSKY_FT_UNKNOWN)
{
unsigned long type = CSKY_FT_UNKNOWN;
tree a;
tree attr;
gcc_assert (TREE_CODE (current_function_decl) == FUNCTION_DECL);
attr = DECL_ATTRIBUTES (current_function_decl);
a = lookup_attribute ("naked", attr);
if (a != NULL_TREE)
type |= CSKY_FT_NAKED;
a = lookup_attribute ("isr", attr);
if (a == NULL_TREE)
a = lookup_attribute ("interrupt", attr);
if (a == NULL_TREE)
type |= CSKY_FT_NORMAL;
else
type |= get_csky_isr_type (TREE_VALUE (a));
cfun->machine->func_type = type;
}
return cfun->machine->func_type;
}
/* These typedefs are located at the start of this file, so that
they can be used in the prototypes there. This comment is to
remind readers of that fact so that the following structures
can be understood more easily.
typedef struct minipool_node Mnode;
typedef struct minipool_fixup Mfix; */
struct minipool_node
{
/* Doubly linked chain of entries. */
Mnode *next;
Mnode *prev;
/* The maximum offset into the code that this entry can be placed. While
pushing fixes for forward references, all entries are sorted in order
of increasing max_address. */
HOST_WIDE_INT max_address;
/* Similarly for an entry inserted for a backwards ref. */
HOST_WIDE_INT min_address;
/* The number of fixes referencing this entry. This can become zero
if we "unpush" an entry. In this case we ignore the entry when we
come to emit the code. */
int refcount;
/* The offset from the start of the minipool. */
HOST_WIDE_INT offset;
/* The value in table. */
rtx value;
/* The mode of value. */
machine_mode mode;
/* The size of the value. */
int fix_size;
};
struct minipool_fixup
{
Mfix *next;
rtx_insn *insn;
HOST_WIDE_INT address;
rtx *loc;
machine_mode mode;
int fix_size;
rtx value;
Mnode *minipool;
HOST_WIDE_INT forwards;
HOST_WIDE_INT backwards;
};
static Mnode *minipool_vector_head;
static Mnode *minipool_vector_tail;
static rtx minipool_vector_label;
static HOST_WIDE_INT constpool_label_no = 0;
/* Obstack for minipool constant handling. */
static struct obstack minipool_obstack;
static char *minipool_startobj;
/* The linked list of all minipool fixes required for this function. */
Mfix *minipool_fix_head;
Mfix *minipool_fix_tail;
/* The fix entry for the current minipool, once it has been placed. */
Mfix *minipool_barrier;
/* Allow GC scanning of the minipool obstack. */
static void
csky_add_gc_roots (void)
{
gcc_obstack_init (&minipool_obstack);
minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
}
/* Implement TARGET_CONSTANT_ALIGNMENT.
Make strings word-aligned so strcpy from constants will be faster. */
static HOST_WIDE_INT
csky_constant_alignment (const_tree exp, HOST_WIDE_INT align)
{
if (TREE_CODE (exp) == STRING_CST
&& !optimize_size
&& align < BITS_PER_WORD)
return BITS_PER_WORD;
return align;
}
/* Record that there is a natural barrier in the insn stream at
ADDRESS. */
static void
push_csky_minipool_barrier (rtx_insn *insn, HOST_WIDE_INT address)
{
Mfix *fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (*fix));
fix->insn = insn;
fix->address = address;
fix->next = NULL;
if (minipool_fix_head != NULL)
minipool_fix_tail->next = fix;
else
minipool_fix_head = fix;
minipool_fix_tail = fix;
}
/* Compute the size of a vector jump table. */
static HOST_WIDE_INT
get_csky_jump_table_size (rtx insn)
{
/* ADDR_VECs only take room if read-only data does into the text
section. */
if (JUMP_TABLES_IN_TEXT_SECTION || readonly_data_section == text_section)
{
rtx body = PATTERN (insn);
int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0;
HOST_WIDE_INT size;
HOST_WIDE_INT modesize;
modesize = GET_MODE_SIZE (GET_MODE (body));
size = modesize * XVECLEN (body, elt);
switch (modesize)
{
case 1:
/* Round up size of TBB table to a halfword boundary. */
size = (size + 1) & ~(HOST_WIDE_INT)1;
break;
case 2:
/* No padding necessary for TBH. */
break;
case 4:
break;
default:
gcc_unreachable ();
}
return size;
}
return 0;
}
/* Scan INSN and note any of its operands that need fixing.
If DO_PUSHES is false we do not actually push any of the fixups
needed. The function returns TRUE if any fixups were needed/pushed. */
static bool
note_csky_invalid_constants (rtx_insn *insn, HOST_WIDE_INT address,
int do_pushes)
{
bool result = false;
int opno;
extract_constrain_insn (insn);
if (recog_data.n_alternatives == 0)
return false;
/* Fill in recog_op_alt with information about the constraints of
this insn. */
preprocess_constraints (insn);
const operand_alternative *op_alt = which_op_alt ();
for (opno = 0; opno < recog_data.n_operands; opno++)
{
/* Things we need to fix can only occur in inputs. */
if (recog_data.operand_type[opno] != OP_IN)
continue;
/* If this alternative is a memory reference, then any mention
of constants in this alternative is really to fool reload
into allowing us to accept one there. We need to fix them up
now so that we output the right code. */
if (op_alt[opno].memory_ok)
{
rtx op = recog_data.operand[opno];
if (CONSTANT_P (op))
{
if (do_pushes)
push_csky_minipool_fix (insn, address,
recog_data.operand_loc[opno],
recog_data.operand_mode[opno], op);
result = true;
}
}
}
return result;
}
/* Add a constant to the minipool for a forward reference. Returns the
node added or NULL if the constant will not fit in this pool. */
static Mnode *
add_csky_minipool_forward_ref (Mfix *fix)
{
/* If set, max_mp is the first pool_entry that has a lower
constraint than the one we are trying to add. */
Mnode *max_mp = NULL;
HOST_WIDE_INT max_address = fix->address + fix->forwards;
Mnode *mp;
/* If the minipool starts before the end of FIX->INSN then this FIX
cannot be placed into the current pool. Furthermore, adding the
new constant pool entry may cause the pool to start FIX_SIZE bytes
earlier. */
if (minipool_vector_head
&& (fix->address + get_attr_length (fix->insn)
>= minipool_vector_head->max_address - fix->fix_size))
return NULL;
/* Scan the pool to see if a constant with the same value has
already been added. While we are doing this, also note the
location where we must insert the constant if it doesn't already
exist. */
for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
{
if (GET_CODE (fix->value) == GET_CODE (mp->value)
&& fix->mode == mp->mode
&& (GET_CODE (fix->value) != CODE_LABEL
|| (CODE_LABEL_NUMBER (fix->value)
== CODE_LABEL_NUMBER (mp->value)))
&& rtx_equal_p (fix->value, mp->value))
{
/* More than one fix references this entry. */
mp->refcount++;
return mp;
}
/* Note the insertion point if necessary. */
if (max_mp == NULL && mp->max_address > max_address)
max_mp = mp;
}
/* The value is not currently in the minipool, so we need to create
a new entry for it. If MAX_MP is NULL, the entry will be put on
the end of the list since the placement is less constrained than
any existing entry. Otherwise, we insert the new fix before
MAX_MP and, if necessary, adjust the constraints on the other
entries. */
mp = XNEW (Mnode);
mp->fix_size = fix->fix_size;
mp->mode = fix->mode;
mp->value = fix->value;
mp->refcount = 1;
/* Not yet required for a backwards ref. */
mp->min_address = -65536;
if (max_mp == NULL)
{
mp->max_address = max_address;
mp->next = NULL;
mp->prev = minipool_vector_tail;
if (mp->prev == NULL)
{
minipool_vector_head = mp;
minipool_vector_label
= gen_csky_constpool_label (gen_rtx_CONST_INT (VOIDmode,
constpool_label_no++));
}
else
mp->prev->next = mp;
minipool_vector_tail = mp;
}
else
{
if (max_address > max_mp->max_address - mp->fix_size)
mp->max_address = max_mp->max_address - mp->fix_size;
else
mp->max_address = max_address;
mp->next = max_mp;
mp->prev = max_mp->prev;
max_mp->prev = mp;
if (mp->prev != NULL)
mp->prev->next = mp;
else
minipool_vector_head = mp;
}
/* Save the new entry. */
max_mp = mp;
/* Scan over the preceding entries and adjust their addresses as
required. */
while (mp->prev != NULL
&& mp->prev->max_address > mp->max_address - mp->prev->fix_size)
{
mp->prev->max_address = mp->max_address - mp->prev->fix_size;
mp = mp->prev;
}
return max_mp;
}
/* Return the cost of forcibly inserting a barrier after INSN. */
static int
get_csky_barrier_cost (rtx_insn *insn)
{
/* Basing the location of the pool on the loop depth is preferable,
but at the moment, the basic block information seems to be
corrupt by this stage of the compilation. */
int base_cost = 50;
rtx next = next_nonnote_insn (insn);
if (next != NULL && GET_CODE (next) == CODE_LABEL)
base_cost -= 20;
switch (GET_CODE (insn))
{
case CODE_LABEL:
/* It will always be better to place the table before the label, rather
than after it. */
return 50;
case INSN:
case CALL_INSN:
return base_cost;
case JUMP_INSN:
return base_cost - 10;
default:
return base_cost + 10;
}
}
/* Find the best place in the insn stream in the range
(FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier.
Create the barrier by inserting a jump and add a new fix entry for
it. */
static Mfix *
create_csky_fix_barrier (Mfix *fix, Mfix *fix_next,
HOST_WIDE_INT max_address)
{
rtx_barrier *barrier;
rtx_insn *from = (fix ? fix->insn : get_insns ());
/* The instruction after which we will insert the jump. */
rtx_insn *selected = NULL;
int selected_cost;
/* The address at which the jump instruction will be placed. */
HOST_WIDE_INT selected_address = 0;
Mfix *new_fix;
HOST_WIDE_INT count = (fix ? fix->address : 0);
HOST_WIDE_INT max_count = max_address;
rtx_code_label *label = gen_label_rtx ();
selected_cost = get_csky_barrier_cost (from);
while (from && count < max_count)
{
int new_cost;
rtx_jump_table_data *table;
/* Count the length of this insn. */
count += get_attr_length (from);
/* If there is a jump table, add its length. */
if (tablejump_p (from, NULL, &table))
{
count += get_csky_jump_table_size (table);
/* Jump tables aren't in a basic block, so base the cost on
the dispatch insn. If we select this location, we will
still put the pool after the table. */
new_cost = get_csky_barrier_cost (from);
if (count < max_count
&& (!selected || new_cost <= selected_cost))
{
selected = table;
selected_cost = new_cost;
selected_address = count;
}
/* Continue after the dispatch table. */
from = NEXT_INSN (table);
continue;
}
new_cost = get_csky_barrier_cost (from);
if (count < max_count
&& (!selected || new_cost <= selected_cost))
{
selected = from;
selected_cost = new_cost;
selected_address = count;
}
from = NEXT_INSN (from);
}
/* Make sure that we found a place to insert the jump. */
gcc_assert (selected);
/* Create a new JUMP_INSN that branches around a barrier. */
from = emit_jump_insn_after (gen_jump (label), selected);
JUMP_LABEL (from) = label;
barrier = emit_barrier_after (from);
emit_label_after (label, barrier);
/* Create a minipool barrier entry for the new barrier. */
new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix));
new_fix->insn = barrier;
new_fix->address = selected_address;
if (fix)
{
new_fix->next = fix->next;
fix->next = new_fix;
}
else
new_fix->next = fix_next;
return new_fix;
}
/* Print a symbolic form of the constant X to the dump file F.
This is used for dump output for -mconstpool in the target-dependent
reorg pass. */
static void
print_csky_value (FILE *f, rtx x)
{
switch (GET_CODE (x))
{
case CONST_INT:
fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
return;
case CONST_DOUBLE:
fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3));
return;
case CONST_VECTOR:
{
int i;
fprintf (f, "<");
for (i = 0; i < CONST_VECTOR_NUNITS (x); i++)
{
fprintf (f, HOST_WIDE_INT_PRINT_HEX,
INTVAL (CONST_VECTOR_ELT (x, i)));
if (i < (CONST_VECTOR_NUNITS (x) - 1))
fputc (',', f);
}
fprintf (f, ">");
}
return;
case CONST_STRING:
fprintf (f, "\"%s\"", XSTR (x, 0));
return;
case SYMBOL_REF:
fprintf (f, "`%s'", XSTR (x, 0));
return;
case LABEL_REF:
fprintf (f, "L%d", INSN_UID (XEXP (x, 0)));
return;
case CONST:
print_csky_value (f, XEXP (x, 0));
return;
case PLUS:
print_csky_value (f, XEXP (x, 0));
fprintf (f, "+");
print_csky_value (f, XEXP (x, 1));
return;
case PC:
fprintf (f, "pc");
return;
default:
fprintf (f, "????");
return;
}
}
/* Record INSN, which will need fixing up to load a value from the
minipool. ADDRESS is the offset of the insn since the start of the
function; LOC is a pointer to the part of the insn which requires
fixing; VALUE is the constant that must be loaded, which is of type
MODE. */
static void
push_csky_minipool_fix (rtx_insn *insn, HOST_WIDE_INT address, rtx *loc,
machine_mode mode, rtx value)
{
#define CSKY_ELRW16_RANGE 1400
#define CSKY_LRW16_RANGE 700
#define CSKY_CONSTANT_POOL_RANGE (TARGET_ELRW ? CSKY_ELRW16_RANGE \
: CSKY_LRW16_RANGE)
/* Fixes less than a word need padding out to a word boundary. */
#define CSKY_MINIPOOL_FIX_SIZE(mode) \
(GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4)
Mfix *fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (*fix));
fix->insn = insn;
fix->address = address;
fix->loc = loc;
fix->mode = mode;
fix->fix_size = CSKY_MINIPOOL_FIX_SIZE (mode);
fix->value = value;
fix->forwards = CSKY_CONSTANT_POOL_RANGE;
fix->backwards = 0;
fix->minipool = NULL;
/* If an insn doesn't have a range defined for it, then it isn't
expecting to be reworked by this code. Better to stop now than
to generate duff assembly code. */
gcc_assert (fix->forwards || fix->backwards);
if (dump_file)
{
fprintf (dump_file,
";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ",
GET_MODE_NAME (mode),
INSN_UID (insn), (unsigned long) address,
-1 * (long)fix->backwards, (long)fix->forwards);
print_csky_value (dump_file, fix->value);
fprintf (dump_file, "\n");
}
/* Add it to the chain of fixes. */
fix->next = NULL;
if (minipool_fix_head != NULL)
minipool_fix_tail->next = fix;
else
minipool_fix_head = fix;
minipool_fix_tail = fix;
}
/* Fill in the offsets for minipool entries. */
static void
assign_csky_minipool_offsets (Mfix *barrier)
{
HOST_WIDE_INT offset = 0;
Mnode *mp;
minipool_barrier = barrier;
for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
{
mp->offset = offset;
if (mp->refcount > 0)
offset += mp->fix_size;
}
}
/* Output the literal table. */
static HOST_WIDE_INT
dump_csky_minipool (rtx_insn *scan)
{
Mnode *mp;
Mnode *nmp;
HOST_WIDE_INT pool_length = 0;
if (dump_file)
fprintf (dump_file,
";; Emitting minipool after insn %u;\
address %ld; align %d (bytes)\n",
INSN_UID (scan), (unsigned long) minipool_barrier->address, 4);
scan = emit_insn_after (gen_align_4 (), scan);
scan = emit_insn_after (minipool_vector_label, scan);
for (mp = minipool_vector_head; mp != NULL; mp = nmp)
{
if (mp->refcount > 0)
{
if (dump_file)
{
fprintf (dump_file, ";; Offset %u, min %ld, max %ld ",
(unsigned) mp->offset, (unsigned long) mp->min_address,
(unsigned long) mp->max_address);
print_csky_value (dump_file, mp->value);
fputc ('\n', dump_file);
}
switch (mp->fix_size)
{
case 4:
scan = emit_insn_after (gen_consttable_4 (mp->value), scan);
pool_length += 4;
break;
case 8:
scan = emit_insn_after (gen_consttable_8 (mp->value), scan);
pool_length += 8;
break;
default:
gcc_unreachable ();
}
}
nmp = mp->next;
free (mp);
}
minipool_vector_head = minipool_vector_tail = NULL;
scan = emit_barrier_after (scan);
return pool_length;
}
/* Return true if INSN is a minipool load or instruction that will be
converted to one. It is assumed that INSN has type attribute "load". */
bool
csky_minipool_load_p (rtx_insn *insn)
{
rtx op1, addr;
extract_insn_cached (insn);
op1 = recog_data.operand[1];
/* This is a constant that has not yet been turned into
a minipool load. */
if (CONSTANT_P (op1))
return true;
/* Constant pool loads are label_refs. */
if (GET_CODE (op1) == ZERO_EXTEND || GET_CODE (op1) == SIGN_EXTEND)
op1 = XEXP (op1, 0);
if (GET_CODE (op1) != MEM)
return false;
addr = XEXP (op1, 0);
if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1)))
addr = XEXP (addr, 0);
return GET_CODE (addr) == LABEL_REF;
}
/* Compute the attribute "length" of push or pop insn, according to
the registers it uses. */
int
csky_compute_pushpop_length (rtx *operands)
{
rtx parallel_op = operands[2];
/* Initialize to elements number of PARALLEL. */
unsigned indx = XVECLEN (parallel_op, 0) - 1;
unsigned first_indx = 0;
unsigned regno = REGNO (operands[1]);
if (regno > CSKY_LR_REGNUM)
return 4;
/* Check each register in the list. */
for (; indx > first_indx; indx--)
{
regno = REGNO (XEXP (XVECEXP (parallel_op, 0, indx), 0));
/* If a register number higher than 15 is included, a 32-bit insn
is used. */
if (regno > CSKY_LR_REGNUM)
return 4;
}
return 2;
}
/* Emit constant pools for -mconstpool. */
static void
csky_emit_constant_pools (void)
{
rtx_insn *insn;
HOST_WIDE_INT address = 0;
Mfix *fix;
minipool_fix_head = minipool_fix_tail = NULL;
/* The first insn must always be a note, or the code below won't
scan it properly. */
insn = get_insns ();
gcc_assert (NOTE_P (insn));
/* Scan the insns and record the operands that need fixing. */
for (insn = next_nonnote_insn (insn); insn;
insn = next_nonnote_insn (insn))
{
if (BARRIER_P (insn))
push_csky_minipool_barrier (insn, address);
else if (INSN_P (insn))
{
rtx_jump_table_data *table;
note_csky_invalid_constants (insn, address, true);
address += get_attr_length (insn);
/* If the insn is a vector jump, add the size of the table
and skip the table. */
if (tablejump_p (insn, NULL, &table))
{
address += get_csky_jump_table_size (table);
insn = table;
}
}
}
fix = minipool_fix_head;
/* Now scan the fixups and perform the required changes. */
while (fix)
{
Mfix *ftmp;
Mfix *last_added_fix;
Mfix *last_barrier = NULL;
Mfix *this_fix;
Mnode *mp;
bool has_pending_const = false;
/* Check if there is any pending constant not processed. */
for (mp = minipool_vector_head; mp; mp = mp->next)
if (mp->refcount > 0)
{
has_pending_const = true;
break;
}
/* If no pending constant, skip over barrier insns. */
if (has_pending_const == false)
{
while (fix && BARRIER_P (fix->insn))
fix = fix->next;
if (fix == NULL)
break;
}
last_added_fix = NULL;
for (ftmp = fix; ftmp; ftmp = ftmp->next)
{
if (BARRIER_P (ftmp->insn))
{
if (minipool_vector_head
&& ftmp->address >= minipool_vector_head->max_address)
break;
last_barrier = ftmp;
}
else
{
ftmp->minipool = add_csky_minipool_forward_ref (ftmp);
if (ftmp->minipool == NULL)
break;
}
last_added_fix = ftmp; /* Keep track of the last fix added. */
}
/* If the last added fix is a barrier, dump minipool after it. */
if (last_added_fix && BARRIER_P (last_added_fix->insn))
ftmp = last_barrier;
else
{
/* ftmp is first fix that we can't fit into this pool.
Insert a new barrier in the code somewhere between the previous
fix and this one, and arrange to jump around it. */
HOST_WIDE_INT max_address;
/* The last item on the list of fixes must be a barrier, so
we can never run off the end of the list of fixes without
last_barrier being set. */
gcc_assert (ftmp);
/* Check that there isn't another fix that is in range that
we couldn't fit into this pool because the pool was
already too large: we need to put the pool before such an
instruction. The pool itself may come just after the
fix because create_csky_fix_barrier also allows space for a
jump instruction. */
max_address = minipool_vector_head->max_address;
if (ftmp->address < max_address)
max_address = ftmp->address + 1;
last_barrier = create_csky_fix_barrier (last_added_fix, ftmp,
max_address);
}
assign_csky_minipool_offsets (last_barrier);
/* Scan over the fixes we have identified for this pool, fixing them
up and adding the constants to the pool itself. */
for (this_fix = fix; this_fix && ftmp != this_fix;
this_fix = this_fix->next)
{
if (GET_CODE (this_fix->insn) != BARRIER)
{
rtx addr
= plus_constant (Pmode,
gen_rtx_LABEL_REF (VOIDmode,
minipool_vector_label),
this_fix->minipool->offset);
rtx insn_body = PATTERN (this_fix->insn);
rtx src = XEXP (insn_body, 1);
*this_fix->loc = gen_rtx_MEM (this_fix->mode, addr);
if (GET_CODE (this_fix->value) == SYMBOL_REF)
emit_insn_after (gen_rtx_UNSPEC_VOLATILE (VOIDmode,
gen_rtvec (1, src),
VUNSPEC_SYMBOL_REF),
this_fix->insn);
}
}
dump_csky_minipool (last_barrier->insn);
fix = ftmp;
if (fix->next == NULL)
break;
}
/* Free the minipool memory. */
obstack_free (&minipool_obstack, minipool_startobj);
}
/* Implement TARGET_MACHINE_DEPENDENT_REORG. This handles
-mconstpool output. */
static void
csky_reorg (void)
{
if (TARGET_CONSTANT_POOL)
csky_emit_constant_pools ();
}
/* Check to see if the current function contains a branch insn with the
far jump attribute set. Such a function uses the LR register. */
static bool
csky_far_jump_used_p (void)
{
rtx_insn *insn;
if (cfun->machine->far_jump_used)
return true;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == JUMP_INSN
/* Ignore tablejump patterns. */
&& GET_CODE (PATTERN (insn)) != ADDR_VEC
&& GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
&& get_attr_far_jump (insn) == FAR_JUMP_YES)
{
cfun->machine->far_jump_used = 1;
return true;
}
return false;
}
/* Return the mask of registers used by the current function. Set
COUNT to the number of registers used. */
static unsigned int
get_csky_live_regs (int *count)
{
int reg;
unsigned int live_regs_mask = 0;
*count = 0;
for (reg = 0; reg < CSKY_NGPR_REGS; reg++)
{
bool save = false;
/* Ignore unsupported registers. */
if (CSKY_TARGET_ARCH (CK801) && reg > 8 && reg < 13)
continue;
if ((CSKY_TARGET_ARCH (CK801)
|| CSKY_TARGET_ARCH (CK802)
|| CSKY_TARGET_ARCH (CK803))
&& reg > 15)
break;
/* Caller-saved registers marked as used. */
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
save = true;
/* Frame pointer marked used. */
else if (frame_pointer_needed && reg == HARD_FRAME_POINTER_REGNUM)
save = true;
/* This is required for CK801/802 where FP is a fixed reg, otherwise
we end up with no FP value available to the DWARF-2 unwinder. */
else if (crtl->calls_eh_return && reg == HARD_FRAME_POINTER_REGNUM)
save = true;
/* CK801/802 also need special handling for LR because it's clobbered
by far jumps. */
else if ((CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802))
&& reg == CSKY_LR_REGNUM
&& (!crtl->is_leaf || csky_far_jump_used_p ()))
save = true;
/* Register is used for EH data return. */
else if (crtl->calls_eh_return
&& reg >= CSKY_FIRST_EH_RETDATA_REGNUM
&& reg <= CSKY_LAST_EH_RETDATA_REGNUM)
save = true;
/* We need a temporary reg to hold the offset for adjusting the SP
for a large stack frame. */
if (reg == CSKY_STACKADJUST_REGNUM
&& cfun->machine->reg_offset > CSKY_MAX_SP_ADJUST * 2)
save = true;
/* Add reg to the mask. */
if (save)
{
(*count)++;
live_regs_mask |= (1 << reg);
}
}
return live_regs_mask;
}
/* Compute the stack frame layout, storing sizes of the various pieces
in cfun->machine.
Stack frames constructed in the prologue look like:
... caller's frame ...
incoming SP -> caller's outbound argument overflow
argument spill
optional FP -> register save
local variables
alloca() space
adjusted SP -> outbound argument overflow
with SP/FP pointing at the base (low address) of the respective area,
and each area aligned to a word boundary. */
static void
csky_layout_stack_frame (void)
{
machine_function *infp = cfun->machine;
int reg_count;
if (infp->frame_init_p)
return;
/* Get sizes of local variables & outbound arguments. */
infp->outbound_size = CSKY_STACK_ALIGN (crtl->outgoing_args_size);
infp->local_offset = infp->outbound_size;
infp->local_size = CSKY_STACK_ALIGN (get_frame_size ());
infp->reg_offset = infp->local_offset + infp->local_size;
/* Now compute size of argument spill + saved regs. These do not
need explicit alignment since they are already word-sized. */
infp->reg_mask = get_csky_live_regs (&reg_count);
infp->reg_size = reg_count * UNITS_PER_WORD;
infp->arg_offset = infp->reg_offset + infp->reg_size;
infp->arg_size = crtl->args.pretend_args_size;
infp->frame_size = infp->arg_offset + infp->arg_size;
infp->frame_init_p = reload_completed;
}
/* Implement TARGET_CAN_ELIMINATE. */
static bool
csky_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
{
if (to == FRAME_POINTER_REGNUM)
return from != ARG_POINTER_REGNUM;
if (to == STACK_POINTER_REGNUM)
return !frame_pointer_needed;
return true;
}
/* Worker function for INITIAL_ELIMINATION_OFFSET macro.
Define the offset between two registers, one to be eliminated, and
the other its replacement, at the start of a routine. */
HOST_WIDE_INT
csky_initial_elimination_offset (int from, int to)
{
int offset;
csky_layout_stack_frame ();
/* Set OFFSET to the offset to the initial stack pointer. */
switch (from)
{
case FRAME_POINTER_REGNUM:
case HARD_FRAME_POINTER_REGNUM:
offset = cfun->machine->reg_offset;
break;
case ARG_POINTER_REGNUM:
offset = cfun->machine->arg_offset;
break;
default:
gcc_unreachable ();
}
/* If we are asked for the offset to the frame pointer instead,
then subtract the difference between the frame pointer and stack
pointer. */
if (to == FRAME_POINTER_REGNUM || to == HARD_FRAME_POINTER_REGNUM)
offset -= cfun->machine->reg_offset;
return offset;
}
/* Determine where to put an argument to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
ARG is a description of the argument. */
static rtx
csky_function_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
int reg = pcum->reg;
machine_mode mode = arg.mode;
if (FUNCTION_VARG_MODE_P(mode)
&& !pcum->is_stdarg)
{
reg = pcum->freg;
if (reg < CSKY_NPARM_FREGS)
return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM + reg);
else
return NULL_RTX;
}
if (reg < CSKY_NPARM_REGS)
return gen_rtx_REG (mode, CSKY_FIRST_PARM_REGNUM + reg);
return NULL_RTX;
}
/* Return the number of registers (words) needed to pass an argument of
MODE and TYPE. */
static int
csky_num_arg_regs (machine_mode mode, const_tree type, bool is_stdarg)
{
int size;
if (type && mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
if (TARGET_HARD_FLOAT_ABI
&& !is_stdarg)
{
if (CSKY_VREG_MODE_P(mode)
&& !TARGET_SINGLE_FPU)
return ((CSKY_NUM_WORDS (size) + 1) / 2);
}
return CSKY_NUM_WORDS (size);
}
/* Implement TARGET_FUNCTION_ARG_ADVANCE. */
static void
csky_function_arg_advance (cumulative_args_t pcum_v,
const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
int *reg = &pcum->reg;
machine_mode mode = arg.mode;
int param_size = csky_num_arg_regs (mode, arg.type, pcum->is_stdarg);
int param_regs_nums = CSKY_NPARM_REGS;
if (FUNCTION_VARG_MODE_P(mode)
&& !pcum->is_stdarg)
{
reg = &pcum->freg;
param_regs_nums = CSKY_NPARM_FREGS;
}
if (*reg + param_size > param_regs_nums)
*reg = param_regs_nums;
else
*reg += param_size;
}
/* Implement TARGET_FUNCTION_VALUE. */
static rtx
csky_function_value (const_tree type, const_tree func,
bool outgoing ATTRIBUTE_UNUSED)
{
machine_mode mode;
int unsignedp ATTRIBUTE_UNUSED;
int size;
mode = TYPE_MODE (type);
size = int_size_in_bytes (type);
if (FUNCTION_VARG_MODE_P(mode))
{
mode = promote_function_mode (type, mode, &unsignedp, func, 1);
return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM);
}
/* Since we promote return types, we must promote the mode here too. */
if (INTEGRAL_TYPE_P (type))
{
mode = promote_function_mode (type, mode, &unsignedp, func, 1);
return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM);
}
if (mode == BLKmode && size > UNITS_PER_WORD
&& size <= UNITS_PER_WORD * 2)
{
rtx ret_regs[2];
ret_regs[0] = gen_rtx_EXPR_LIST (SImode,
gen_rtx_REG (SImode,
CSKY_FIRST_RET_REGNUM),
GEN_INT (0 * UNITS_PER_WORD));
ret_regs[1] = gen_rtx_EXPR_LIST (SImode,
gen_rtx_REG (SImode,
CSKY_FIRST_RET_REGNUM + 1),
GEN_INT (1 * UNITS_PER_WORD));
rtvec vec = gen_rtvec (2, ret_regs[0], ret_regs[1]);
return gen_rtx_PARALLEL (mode, vec);
}
return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM);
}
/* Implement TARGET_LIBCALL_VALUE. */
static rtx
csky_libcall_value (machine_mode mode,
const_rtx libcall ATTRIBUTE_UNUSED)
{
if (FUNCTION_VARG_MODE_P(mode))
{
return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM);
}
return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM);
}
/* Implement TARGET_FUNCTION_VALUE_REGNO_P.
On C-SKY, only r0 can return results. */
static bool
csky_function_value_regno_p (const unsigned int regno)
{
if (regno == CSKY_FIRST_RET_REGNUM
|| (TARGET_HARD_FLOAT_ABI
&& regno == CSKY_FIRST_VFP_REGNUM))
return true;
return false;
}
/* Return an RTX indicating where the return address to the
calling function can be found. */
rtx
csky_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
{
if (count != 0)
return NULL_RTX;
return get_hard_reg_initial_val (Pmode, CSKY_LR_REGNUM);
}
/* Implement TARGET_ARG_PARTIAL_BYTES.
Return the number of bytes at the beginning of an argument
that must be put in registers. The value must be zero for arguments
that are passed entirely in registers or
that are entirely pushed on the stack. */
static int
csky_arg_partial_bytes (cumulative_args_t pcum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
int param_size = csky_num_arg_regs (arg.mode, arg.type, pcum->is_stdarg);
int reg = pcum->reg;
if (FUNCTION_VARG_MODE_P(arg.mode)
&& !pcum->is_stdarg)
return 0;
if (reg < CSKY_NPARM_REGS
&& reg + param_size > CSKY_NPARM_REGS)
return (CSKY_NPARM_REGS - reg) * UNITS_PER_WORD;
return 0;
}
/* Implement TARGET_SETUP_INCOMING_VARARGS.
On C-Sky the copy from the argument registers to the stack is emitted
by the prologue hooks, so here we just have to note how much stack space
to save. */
static void
csky_setup_incoming_varargs (cumulative_args_t pcum_v,
const function_arg_info &arg,
int *pretend_size,
int second_time ATTRIBUTE_UNUSED)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
CUMULATIVE_ARGS local_cum;
cumulative_args_t local_cum_v = pack_cumulative_args (&local_cum);
int regs_to_push;
cfun->machine->uses_anonymous_args = 1;
local_cum = *pcum;
csky_function_arg_advance (local_cum_v, arg);
regs_to_push = CSKY_NPARM_REGS - local_cum.reg;
if (regs_to_push)
*pretend_size = regs_to_push * UNITS_PER_WORD;
}
/* Implement TARGET_ASM_OUTPUT_MI_THUNK.
Output code to add DELTA to the first argument, and then jump
to FUNCTION. Used for C++ multiple inheritance. */
static void
csky_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta,
HOST_WIDE_INT vcall_offset,
tree function)
{
const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk));
const char *thiz = "a0";
const char *reg0 = "t0";
const char *reg1 = "t1";
int maxoff = 4096; /* Constant range for addi/subi. */
assemble_start_function (thunk, fnname);
final_start_function (emit_barrier (), file, 1);
rtx fnaddr = XEXP (DECL_RTL (function), 0);
if (CSKY_TARGET_ARCH (CK801))
{
/* CK801 can't use t registers and has only 16-bit addi/subi. */
reg0 = "l0";
reg1 = "l1";
maxoff = 256;
if (vcall_offset > maxoff || vcall_offset < -maxoff)
fprintf (file, "\tpush\tl0, l1\n");
else if (delta > maxoff || delta < -maxoff)
fprintf (file, "\tpush\tl0\n");
}
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
thiz = "a1";
/* Add delta to this_rtx. */
if (delta != 0)
{
if (delta > maxoff || delta < -maxoff)
{
fprintf (file, "\tlrw\t%s, %ld\n", reg0, (long)delta);
fprintf (file, "\taddu\t%s, %s, %s\n", thiz, thiz, reg0);
}
else
fprintf (file, "\t%s\t%s, %s, %ld\n",
(delta > 0 ? "addi" : "subi"), thiz, thiz,
(long)(delta > 0 ? delta : -delta));
}
/* If needed, add *(*this_rtx + vcall_offset) to this_rtx. */
if (vcall_offset != 0)
{
fprintf (file, "\tld.w\t%s, (%s, 0)\n", reg0, thiz);
if (vcall_offset > maxoff || vcall_offset < -maxoff)
{
fprintf (file, "\tlrw\t%s, %ld\n", reg1, (long)vcall_offset);
fprintf (file, "\taddu\t%s, %s, %s\n", reg0, reg0, reg1);
}
else
fprintf (file, "\t%s\t%s, %s, %ld\n",
(vcall_offset > 0 ? "addi" : "subi"), reg0, reg0,
(long)(vcall_offset > 0 ? vcall_offset : -vcall_offset));
/* Load the offset and add it to this_rtx */
fprintf (file, "\tld.w\t%s, (%s, 0)\n", reg0, reg0);
fprintf (file, "\taddu\t%s, %s, %s\n", thiz, thiz, reg0);
}
/* We must pop the scratch regs individually instead of using the
"pop" insn, which also does a return. */
if (CSKY_TARGET_ARCH (CK801))
{
if (vcall_offset > maxoff || vcall_offset < -maxoff)
{
fprintf (file, "\tld.w\tl0, (sp, 0)\n");
fprintf (file, "\tld.w\tl1, (sp, 4)\n");
fprintf (file, "\taddi\t sp, sp, 8\n");
}
else if (delta > maxoff || delta < -maxoff)
{
fprintf (file, "\tld.w\tl0, (sp, 0)\n");
fprintf (file, "\taddi\tsp, sp, 4\n");
}
}
fprintf (file, "\tjbr\t");
output_addr_const (file, fnaddr);
fprintf (file, "\n");
final_end_function ();
assemble_end_function (thunk, fnname);
}
/* Implement TARGET_CONDITIONAL_REGISTER_USAGE.
Conditionally modify five variables fixed_regs, call_used_regs, global_regs,
reg_names, and reg_class_contents, to take into account any dependence of
these register sets on target flags.
CK801 has registers r0-r8 and r13-r15. CK802 and CK803 have registers
r0-r15 (the "low" registers). Other cpus use registers r0-r31 with
-mhigh-registers, otherwise also only r0-r15.
CK801 only has 16-bit instructions, most of which can only reference
r0-r7 (the "mini" registers). So we mark regs outside that range as
fixed. -msmart can be used on other arch variants to force the same
behavior because it results in smaller code size.
TODO: investigate whether it's beneficial to use r8-r13 as a spill
class when TARGET_MINI_REGISTERS instead of making them unusable by
the register allocator. */
static void
csky_conditional_register_usage (void)
{
/* Only use mini registers in smart mode or 801. */
if (TARGET_MINI_REGISTERS)
{
int i;
for (i = (CSKY_LAST_MINI_REGNUM + 1); i < 32; i++)
{
fixed_regs[i] = 1;
call_used_regs[i] = 1;
}
}
/* For some targets, the high registers are not supported.
CPUs other than ck801/ck802/ck803 use high registers
depending on -mhigh-registers option. */
else if (CSKY_TARGET_ARCH (CK802)
|| CSKY_TARGET_ARCH (CK803)
|| !TARGET_HIGH_REGISTERS)
{
int i;
for (i = CSKY_FIRST_HIGH_REGNUM; i <= CSKY_LAST_HIGH_REGNUM; i++)
{
fixed_regs[i] = 1;
call_used_regs[i] = 1;
}
}
/* On CK801/CK802 we must mark lr as a fixed register because it is
used to implement far jumps.
FIXME: perhaps there should be a command-line option controlling
use of lr for far jumps on ck802 when !TARGET_MINI_REGS, when
you really want lr to be available to the register allocator and
you know there are no far jumps in the code. */
if (CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802))
{
fixed_regs[CSKY_LR_REGNUM] = 1;
call_used_regs[CSKY_LR_REGNUM] = 0;
}
/* The hi/lo registers are only supported in dsp mode. */
if (!TARGET_DSP)
{
fixed_regs[CSKY_HI_REGNUM] = 1;
call_used_regs[CSKY_HI_REGNUM] = 1;
fixed_regs[CSKY_LO_REGNUM] = 1;
call_used_regs[CSKY_LO_REGNUM] = 1;
}
/* The V_REGS are only supported in hard float mode. */
if (!TARGET_HARD_FLOAT)
{
int regno;
for (regno = CSKY_FIRST_VFP_REGNUM;
regno <= CSKY_LAST_VFP3_REGNUM; regno++)
{
fixed_regs[regno] = 1;
call_used_regs[regno] = 1;
}
}
if (!TARGET_SUPPORT_FPV3)
{
int regno;
for (regno = CSKY_FIRST_VFP3_REGNUM;
regno <= CSKY_LAST_VFP3_REGNUM; regno++)
{
fixed_regs[regno] = 1;
call_used_regs[regno] = 1;
}
}
/* In pic mode, the gb register is not available for register
allocation. Since gb is not clobbered by function
calls, set its call_used_regs to 0. */
if (flag_pic)
{
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 0;
}
}
/* Implement TARGET_HARD_REGNO_NREGS. */
static unsigned int
csky_hard_regno_nregs (unsigned int regno, machine_mode mode)
{
if (regno >= CSKY_FIRST_VFP_REGNUM && !CSKY_TARGET_ARCH (CK803))
return 1;
else
return CSKY_NUM_REGS (mode);
}
/* Implement TARGET_HARD_REGNO_MODE_OK. Return true if REGNO is a
valid register for holding a quantity of type MODE. */
static bool
csky_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
int nregs = CSKY_NUM_REGS (mode);
/* We can't handle more than doubleword sizes for any register. */
if (nregs > 2)
return false;
/* For general registers, return true if mode is one word size.
When the size is larger than one word size, there should
be two successive hard registers to put the data. */
if (regno < CSKY_NGPR_REGS)
{
if (nregs < 2)
return true;
else if (TARGET_MINI_REGISTERS)
return (regno < CSKY_LAST_MINI_REGNUM);
else if (CSKY_TARGET_ARCH (CK802)
|| CSKY_TARGET_ARCH (CK803)
|| !TARGET_HIGH_REGISTERS)
/* Without high register, r15 cannot hold doubleword data. */
return (regno < (CSKY_SP_REGNUM - 1));
else
return (regno < (CSKY_SP_REGNUM - 1)
|| (regno >= CSKY_LR_REGNUM
&& regno < CSKY_LAST_HIGH_UNFIXED_REGNUM));
}
else if (regno == CSKY_CC_REGNUM)
return (mode == CCmode);
else if (regno == CSKY_HI_REGNUM || regno == CSKY_LO_REGNUM)
{
/* Don't allocate hi,lo register for float data even
if in dsp mode, because it will cause high cost
to reload data from hi,lo register. */
if (!TARGET_DSP || mode == SFmode || mode == DFmode)
return false;
else if (nregs == 2)
return (regno == CSKY_HI_REGNUM);
else
return true;
}
else if (CSKY_VREG_P (regno) && TARGET_HARD_FLOAT)
return true;
return false;
}
/* Implement TARGET_MODES_TIEABLE_P. We can't tie DFmode with other modes
when V_REGs might be in use because those registers mess with the stored
bits. */
static bool
csky_modes_tieable_p (machine_mode mode1, machine_mode mode2)
{
return !(TARGET_HARD_FLOAT
&& mode1 != mode2
&& (mode1 == DFmode || mode2 == DFmode));
}
/* Implement TARGET_CLASS_LIKELY_SPILLED_P.
We need to define this for MINI_REGS when we only use r0 - r7.
Otherwise we can end up using r0-r4 for function arguments, and don't
have enough left over to do doubleword arithmetic. */
static bool
csky_class_likely_spilled_p (reg_class_t rclass)
{
if ((TARGET_MINI_REGISTERS && rclass == MINI_REGS)
|| rclass == C_REGS)
return true;
return false;
}
/* Implement TARGET_PREFERRED_RELOAD_CLASS.
Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use.
In general this is just CLASS. */
static reg_class_t
csky_preferred_reload_class (rtx x, reg_class_t rclass)
{
if (TARGET_HARD_FLOAT
&& CONST_DOUBLE_P (x)
&& (GET_MODE (x) == DFmode || GET_MODE (x) == SFmode)
&& rclass == NO_REGS)
return GENERAL_REGS;
return rclass;
}
/* Implement TARGET_CLASS_MAX_NREGS.
Return the maximum number of consecutive registers of class rclass needed
to hold a value of mode mode.
On the csky, this is the size of MODE in words,
except in the FP regs, where a single reg is always enough. */
static unsigned char
csky_class_max_nregs (reg_class_t rclass, machine_mode mode)
{
if (rclass == V_REGS)
return 1;
else
return CSKY_NUM_REGS (mode);
}
/* Implement TARGET_SECONDARY_RELOAD.
If copying a register of RCLASS from/to X requires an intermediate
register, the hook should return the REGISTER_CLASS required for this
intermediate register.
If no intermediate register is required, it should return NO_REGS.
If more than one intermediate register is required, describe the one
that is closest in the copy chain to the reload register. */
reg_class_t
csky_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x,
reg_class_t rclass,
machine_mode mode,
secondary_reload_info *sri ATTRIBUTE_UNUSED)
{
int regno = -1;
/* Extract the real regno from X. */
if (GET_CODE (x) == SIGN_EXTEND)
{
int off = 0;
x = XEXP (x, 0);
if (reg_renumber)
regno = true_regnum (x);
else
{
while (GET_CODE (x) == SUBREG)
{
off += subreg_regno_offset (REGNO (SUBREG_REG (x)),
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x), GET_MODE (x));
x = SUBREG_REG (x);
}
if (GET_CODE (x) == REG)
regno = REGNO (x) + off;
}
}
else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
regno = true_regnum (x);
/* We always require a general register when copying anything to
HI/LO_REGNUM, except when copying an SImode value from HI/LO_REGNUM
to a general register, or when copying from register 0. */
if (rclass == HILO_REGS && !CSKY_GENERAL_REGNO_P (regno))
return GENERAL_REGS;
if (rclass == V_REGS && !CSKY_GENERAL_REGNO_P (regno))
{
/* Reload between vector reg and memory does not need an
intermediate register. */
if (MEM_P (x) && (mode == SFmode || mode == DFmode))
return NO_REGS;
else
return GENERAL_REGS;
}
return NO_REGS;
}
/* Implement TARGET_SPILL_CLASS.
Try spilling to a larger register class before spilling to memory. */
static reg_class_t
csky_spill_class (reg_class_t rclass, machine_mode mode ATTRIBUTE_UNUSED)
{
if ((rclass == MINI_REGS && !TARGET_MINI_REGISTERS)
|| (rclass == LOW_REGS && TARGET_HIGH_REGISTERS))
return GENERAL_REGS;
return NO_REGS;
}
/* Convert a static initializer array of feature bits to sbitmap
representation. */
static void
csky_initialize_isa (sbitmap isa, const enum csky_isa_feature *isa_bits)
{
bitmap_clear (isa);
while (*isa_bits != CSKY_ISA_FEATURE_GET (none))
bitmap_set_bit (isa, *(isa_bits++));
}
/* Configure a build target TARGET from the user-specified options OPTS and
OPTS_SET. */
static void
csky_configure_build_target (struct csky_build_target *target,
struct cl_target_option *opts,
struct gcc_options *opts_set)
{
const struct csky_processors *csky_selected_tune = NULL;
struct csky_processors *csky_selected_cpu = NULL;
struct csky_processors *csky_selected_arch = NULL;
sbitmap all_sbits = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max));
bitmap_clear (all_sbits);
bitmap_clear (target->isa);
target->core_name = NULL;
target->arch_name = NULL;
if (opts_set->x_csky_arch_option)
csky_selected_arch = &all_architectures[opts->x_csky_arch_option];
if (opts_set->x_csky_cpu_option)
{
csky_selected_cpu = &all_cores[opts->x_csky_cpu_option];
csky_selected_tune = &all_cores[opts->x_csky_cpu_option];
}
if (csky_selected_cpu)
{
/* TODO: support combination of features
between different cpu & arch, should based on arch. */
if (csky_selected_arch
&& (csky_selected_cpu->base_arch != csky_selected_arch->base_arch))
warning (0, "cpu %s is not based on arch %s, ignoring the arch",
csky_selected_cpu->name, csky_selected_arch->name);
if (!csky_selected_arch)
csky_selected_arch = &all_architectures[csky_selected_cpu->base_arch];
csky_initialize_isa (all_sbits, csky_selected_arch->isa_bits);
target->core_name = csky_selected_cpu->name;
}
else if (csky_selected_arch)
{
csky_selected_cpu = csky_selected_arch;
target->arch_name = csky_selected_arch->name;
}
else /* If the user did not specify a processor, choose one for them. */
{
csky_selected_cpu = &all_cores[TARGET_CPU_DEFAULT];
csky_selected_arch = &all_architectures[csky_selected_cpu->base_arch];
csky_initialize_isa (all_sbits, csky_selected_arch->isa_bits);
target->core_name = csky_selected_cpu->name;
}
/* The selected cpu may be an architecture, so lookup tuning by core ID. */
if (!csky_selected_tune)
csky_selected_tune = &all_cores[csky_selected_cpu->core];
gcc_assert (csky_selected_tune);
gcc_assert (csky_selected_arch);
gcc_assert (csky_selected_cpu);
csky_initialize_isa (target->isa, csky_selected_cpu->isa_bits);
bitmap_ior (target->isa, target->isa, all_sbits);
/* Finish initializing the target structure. */
target->arch_pp_name = csky_selected_cpu->arch;
target->base_arch = csky_selected_cpu->base_arch;
target->arch_core = csky_selected_cpu->core;
sbitmap_free (all_sbits);
}
/* Implement TARGET_OPTION_OVERRIDE. */
static void
csky_option_override (void)
{
csky_active_target.isa = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max));
/* Create the default target_options structure. We need this early
to configure the overall build target. */
target_option_default_node = target_option_current_node
= build_target_option_node (&global_options, &global_options_set);
csky_configure_build_target (&csky_active_target,
TREE_TARGET_OPTION (target_option_default_node),
&global_options_set);
#ifdef SUBTARGET_OVERRIDE_OPTIONS
SUBTARGET_OVERRIDE_OPTIONS;
#endif
csky_base_arch = csky_active_target.base_arch;
if (flag_pic && !(CSKY_TARGET_ARCH (CK807)
|| CSKY_TARGET_ARCH (CK810)
|| CSKY_TARGET_ARCH (CK860)))
{
flag_pic = 0;
warning (0, "%qs is not supported by arch %s",
"-fPIC", csky_active_target.arch_pp_name);
}
/* Check floating-point options for consistency. */
if (TARGET_HARD_FLOAT)
{
const struct csky_fpu_desc *csky_selected_fpu = NULL;
if (csky_fpu_index == TARGET_FPU_auto)
{
const char *target_fpu_name;
bool ok;
int fpu_index;
if (csky_active_target.core_name != NULL
&& !strchr (csky_active_target.core_name, 'f'))
target_fpu_name = "auto";
else if (CSKY_TARGET_ARCH (CK803) || !TARGET_DOUBLE_FLOAT)
target_fpu_name = "fpv2_sf";
else if (CSKY_TARGET_ARCH (CK860))
target_fpu_name = "fpv3";
else if (TARGET_DOUBLE_FLOAT && TARGET_FDIVDU)
target_fpu_name = "fpv2_divd";
else
#ifdef CSKY_FPUTYPE_DEFAULT
target_fpu_name = CSKY_FPUTYPE_DEFAULT;
#else
target_fpu_name = "fpv2";
#endif
ok = opt_enum_arg_to_value (OPT_mfpu_, target_fpu_name, &fpu_index,
CL_TARGET);
gcc_assert (ok);
csky_fpu_index = (enum csky_fpu_type) fpu_index;
}
if (CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802))
error ("%qs is not supported by arch %s",
"-mhard-float", csky_active_target.arch_pp_name);
else if (csky_fpu_index == TARGET_FPU_auto)
error ("%<-mhard-float%> is not supported by the selected CPU");
else
{
csky_selected_fpu = &all_fpus[csky_fpu_index];
sbitmap fpu_bits = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max));
csky_initialize_isa (fpu_bits, csky_selected_fpu->isa_bits);
bitmap_ior (csky_active_target.isa, csky_active_target.isa,
fpu_bits);
sbitmap_free (fpu_bits);
}
}
else
{
if (TARGET_DOUBLE_FLOAT > 0)
warning (0, "%<-mdouble-float%> ignored without %<-mhard-float%>");
TARGET_DOUBLE_FLOAT = 0;
if (TARGET_FDIVDU > 0)
warning (0, "%<-mfdivdu%> ignored without %<-mhard-float%>");
TARGET_FDIVDU = 0;
}
/* Initialize boolean versions of the architectural flags, for use
in the .md file. */
#undef CSKY_ISA
#define CSKY_ISA(IDENT, DESC) \
{ \
csky_arch_isa_features[CSKY_ISA_FEATURE_GET (IDENT)] = \
bitmap_bit_p (csky_active_target.isa, CSKY_ISA_FEATURE_GET (IDENT)); \
}
#include "csky_isa.def"
#undef CSKY_ISA
/* Extended LRW instructions are enabled by default on CK801, disabled
otherwise. */
if (TARGET_ELRW == -1)
TARGET_ELRW = CSKY_TARGET_ARCH (CK801);
/* DSP is enabled either by the processor feature or -mdsp
command-line option. There is no -mno-dsp option as the assembler
doesn't take one. */
if (!TARGET_DSP)
TARGET_DSP = CSKY_ISA_FEATURE (dsp);
/* There's both -mdiv and -mno-div. Take default from processor if
neither is specified explicitly. */
if (TARGET_DIV == -1)
TARGET_DIV = CSKY_ISA_FEATURE (div);
/* TARGET_CONSTANT_POOL is mandatory for CK801 and CK802 and optional
for other CPUs.
The reason why the compiler has to generate constant pools for CK801/2
instead of deferring to the assembler is that these cores don't have a
long branch instruction other than jbsr, which clobbers lr. So for
the compiler to correctly save/restore lr it has to know whether there
are long branches, which depends on having accurate branch length
counts, which in turn depends on having control over where constant
pools are placed. */
if ((CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802))
&& !TARGET_CONSTANT_POOL)
error ("%qs is not supported by arch %s",
"-mno-constpool", csky_active_target.arch_pp_name);
else if (TARGET_CONSTANT_POOL == -1)
TARGET_CONSTANT_POOL = (CSKY_TARGET_ARCH (CK801)
|| CSKY_TARGET_ARCH (CK802));
/* TARGET_MINI_REGISTERS is mandatory for CK801, the default for CK802,
and optional for other CPUs. TARGET_HIGH_REGISTERS is incompatible
with TARGET_MINI_REGISTERS, is not supported by CK801/802/803,
and is the default for other processors.
See csky_conditional_register_usage. */
if (TARGET_MINI_REGISTERS > 0 && TARGET_HIGH_REGISTERS > 0)
error ("%<-msmart%> is incompatible with %<-mhigh-registers%>");
else if (CSKY_TARGET_ARCH (CK801)
|| CSKY_TARGET_ARCH (CK802)
|| CSKY_TARGET_ARCH (CK803))
{
if (CSKY_TARGET_ARCH (CK801)
|| (CSKY_TARGET_ARCH (CK802) && TARGET_MINI_REGISTERS == -1))
TARGET_MINI_REGISTERS = 1;
else if (TARGET_MINI_REGISTERS == -1)
TARGET_MINI_REGISTERS = 0;
if (TARGET_HIGH_REGISTERS > 0)
warning (0, "%qs is not supported by arch %s",
"-mhigh-registers", csky_active_target.arch_pp_name);
TARGET_HIGH_REGISTERS = 0;
}
else
{
if (TARGET_MINI_REGISTERS == -1)
TARGET_MINI_REGISTERS = 0;
if (TARGET_HIGH_REGISTERS == -1)
TARGET_HIGH_REGISTERS = !TARGET_MINI_REGISTERS;
}
/* -mmultiple-stld is the default for everything but CK801, which
doesn't support it. */
if (CSKY_TARGET_ARCH (CK801))
{
if (TARGET_MULTIPLE_STLD > 0)
warning (0, "%qs is not supported by arch %s",
"-mmultiple-stld", csky_active_target.arch_pp_name);
TARGET_MULTIPLE_STLD = 0;
}
/* TODO */
/* Resynchronize the saved target options. */
cl_target_option_save (TREE_TARGET_OPTION (target_option_default_node),
&global_options, &global_options_set);
#ifdef ENABLE_TPF_DEBUG
/* Don't emit DWARF4 unless specifically selected. The TPF
debuggers do not yet support DWARF 3/4. */
if (!global_options_set.x_dwarf_strict)
dwarf_strict = 1;
if (!global_options_set.x_dwarf_version)
dwarf_version = 3;
#endif
/* Don't run the scheduler before reload by default,
since it tends to increase register pressure. */
if (!global_options_set.x_flag_schedule_insns)
flag_schedule_insns = 0;
csky_add_gc_roots ();
}
/* Return TRUE if X contains any references to TLS symbols. */
bool
csky_tls_referenced_p (rtx x)
{
if (!TARGET_TLS)
return false;
subrtx_iterator::array_type array;
FOR_EACH_SUBRTX (iter, array, x, ALL)
{
const_rtx x = *iter;
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0)
return true;
/* Don't recurse into UNSPEC_TLS looking for TLS symbols; these are
TLS offsets, not real symbol references. */
if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLS)
iter.skip_subrtxes ();
}
return false;
}
/* Implement TARGET_CANNOT_FORCE_CONST_MEM.
Determine if it's legal to put X into the constant pool. This
is not possible for the address of thread-local symbols, which
is checked above. */
static bool
csky_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED,
rtx x)
{
return csky_tls_referenced_p (x);
}
/* Implement TARGET_LEGITIMATE_CONSTANT_P. Returns nonzero if the
constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
static bool
csky_legitimate_constant_p (machine_mode mode, rtx x)
{
return (!csky_cannot_force_const_mem (mode, x)
&& CONSTANT_P (x));
}
/* Return true if X is valid as an CSKY addressing register. */
static bool
is_csky_address_register_rtx_p (rtx x, int strict_p)
{
int regno;
if (!x)
return false;
if (!REG_P (x))
return false;
regno = REGNO (x);
if (strict_p)
return (CSKY_GENERAL_REGNO_P (regno)
|| CSKY_GENERAL_REGNO_P (reg_renumber[regno]));
else
return CSKY_GENERAL_REGNO_P (regno) || regno >= FIRST_PSEUDO_REGISTER;
}
/* Return TRUE if X is a thread-local symbol. */
static bool
csky_tls_symbol_p (rtx x)
{
if (!TARGET_TLS)
return false;
if (GET_CODE (x) != SYMBOL_REF)
return false;
return SYMBOL_REF_TLS_MODEL (x) != 0;
}
/* Handle lazy initialization of __tls_get_addr libfunc. */
static GTY(()) rtx tls_get_addr_libfunc;
static rtx
get_tls_get_addr (void)
{
if (!tls_get_addr_libfunc)
tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
return tls_get_addr_libfunc;
}
/* Emit a call to __tls_get_addr. */
static rtx_insn *
csky_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
{
rtx label, labelno, unspec, tmp;
rtx_insn *insns;
start_sequence ();
labelno = GEN_INT (tls_labelno++);
label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_TLS_LABEL);
unspec = gen_rtx_UNSPEC (Pmode,
gen_rtvec (3, x, GEN_INT (reloc), label),
UNSPEC_TLS);
tmp = gen_reg_rtx (SImode);
emit_move_insn (reg, unspec);
emit_move_insn (tmp, label);
emit_insn (gen_addsi3 (reg, reg, tmp));
*valuep = emit_library_call_value (get_tls_get_addr (),
NULL_RTX, LCT_PURE, /* LCT_CONST? */
Pmode, reg, Pmode);
insns = get_insns ();
end_sequence ();
return insns;
}
/* Helper function for csky_legitimize_address, to handle the TLS cases.
REG is a scratch register and may be null. */
rtx
csky_legitimize_tls_address (rtx x, rtx reg)
{
rtx dest, tp, label, labelno, unspec, ret, eqv, addend, tmp;
rtx_insn *insns;
unsigned int model = SYMBOL_REF_TLS_MODEL (x);
if (!reg)
reg = gen_reg_rtx (SImode);
switch (model)
{
case TLS_MODEL_GLOBAL_DYNAMIC:
insns = csky_call_tls_get_addr (x, reg, &ret, TLS_GD32);
dest = gen_reg_rtx (Pmode);
emit_libcall_block (insns, dest, ret, x);
return dest;
case TLS_MODEL_LOCAL_DYNAMIC:
insns = csky_call_tls_get_addr (x, reg, &ret, TLS_LDM32);
/* Attach a unique REG_EQUIV, to allow the RTL optimizers to
share the LDM result with other LD model accesses. */
eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_TLS);
dest = gen_reg_rtx (Pmode);
emit_libcall_block (insns, dest, ret, eqv);
/* Load the addend. */
addend = gen_rtx_UNSPEC (Pmode,
gen_rtvec (2, x, GEN_INT (TLS_LDO32)),
UNSPEC_TLS);
addend = force_reg (SImode, addend);
return gen_rtx_PLUS (Pmode, dest, addend);
case TLS_MODEL_INITIAL_EXEC:
labelno = GEN_INT (tls_labelno++);
label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_TLS_LABEL);
unspec = gen_rtx_UNSPEC (Pmode,
gen_rtvec (3, x, GEN_INT (TLS_IE32), label),
UNSPEC_TLS);
tmp = gen_reg_rtx (SImode);
emit_move_insn (reg, unspec);
emit_move_insn (tmp, label);
emit_insn (gen_addsi3 (reg, reg, tmp));
emit_move_insn (reg, gen_const_mem (Pmode, reg));
tp = gen_rtx_REG (SImode, CSKY_TLS_REGNUM);
return gen_rtx_PLUS (Pmode, tp, reg);
case TLS_MODEL_LOCAL_EXEC:
unspec = gen_rtx_UNSPEC (Pmode,
gen_rtvec (2, x, GEN_INT (TLS_LE32)),
UNSPEC_TLS);
emit_move_insn (reg, unspec);
tp = gen_rtx_REG (SImode, CSKY_TLS_REGNUM);
return gen_rtx_PLUS (Pmode, tp, reg);
default:
abort ();
}
}
/* Implement TARGET_LEGITIMIZE_ADDRESS. */
static rtx
csky_legitimize_address (rtx x, rtx orig_x ATTRIBUTE_UNUSED,
machine_mode mode)
{
if (csky_tls_symbol_p (x))
return csky_legitimize_tls_address (x, NULL_RTX);
if (GET_CODE (x) == PLUS)
{
rtx xop0 = XEXP (x, 0);
rtx xop1 = XEXP (x, 1);
if (is_csky_address_register_rtx_p (xop0, 0)
&& CONST_INT_P (xop1))
{
HOST_WIDE_INT offset = INTVAL (xop1);
/* Try to replace ld32 rx,(ry, offset), to addi16 rz, oimm8
and ld16 rx,(rz, new_ld_offset) to avoid emitting a
32-bit ld, but this addi has a range limitation. */
if (optimize_size
&& offset > CSKY_LD16_MAX_OFFSET (mode)
&& offset <= (CSKY_ADDI16_MAX_IMM
+ CSKY_LD16_MAX_OFFSET (mode)))
{
HOST_WIDE_INT new_ld_offset
= offset & CSKY_LD16_OFFSET_MASK (mode);
xop0 = force_operand (plus_constant (Pmode, xop0,
offset - new_ld_offset),
NULL_RTX);
x = plus_constant (Pmode, xop0, new_ld_offset);
}
else if (offset < 0 && offset >= (-CSKY_SUBI16_MAX_IMM))
x = force_operand (x, NULL_RTX);
else if (offset > CSKY_LD16_MAX_OFFSET (mode)
|| offset < 0)
{
/* For the remaining cases, force the constant into a
register. */
xop1 = force_reg (SImode, xop1);
x = gen_rtx_PLUS (SImode, xop0, xop1);
}
}
/* If the index is store in register, force the
base to register. */
if (is_csky_address_register_rtx_p (xop1, 0)
&& !is_csky_address_register_rtx_p (xop0, 0))
{
xop0 = force_operand (xop0, NULL_RTX);
x = gen_rtx_PLUS (SImode, xop0, xop1);
}
}
/* Make sure to take full advantage of the pre-indexed addressing mode
with absolute addresses which often allows for the base register to
be factorized for multiple adjacent memory references, and it might
even allows for the mini pool to be avoided entirely. */
else if (CONST_INT_P (x) && optimize > 0)
{
HOST_WIDE_INT mask, base, index;
rtx base_reg;
mask = CSKY_LD16_OFFSET_MASK (mode);
base = INTVAL (x) & ~mask;
index = INTVAL (x) & mask;
base_reg = force_reg (SImode, GEN_INT (base));
x = plus_constant (Pmode, base_reg, index);
}
return x;
}
/* Return nonzero if INDEX is valid for an address index operand.
ck801 use 16 bits ld
ck802 use 16 and 32 bits ld
others use ld and ldr. */
static int
ck801_legitimate_index_p (machine_mode mode, rtx index,
int strict_p ATTRIBUTE_UNUSED)
{
enum rtx_code code = GET_CODE (index);
/* When the mode size is larger than 4, we may use two ld instruction
to get data, the index and (index+1) should be valid. */
if (GET_MODE_SIZE (mode) >= 8)
return (code == CONST_INT
&& INTVAL (index) < CSKY_LD16_MAX_OFFSET (SImode)
&& INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0);
if (code == CONST_INT && GET_MODE_SIZE (mode) > 0
&& INTVAL (index) <= CSKY_LD16_MAX_OFFSET (mode)
&& INTVAL (index) >= 0)
return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0);
return 0;
}
static int
ck802_legitimate_index_p (machine_mode mode, rtx index,
int strict_p ATTRIBUTE_UNUSED)
{
enum rtx_code code = GET_CODE (index);
/* When the mode size is larger than 4, we may use two ld instruction
to get data, the index and (index+1) should be valid. */
if (GET_MODE_SIZE (mode) >= 8)
return (code == CONST_INT
&& INTVAL (index) < CSKY_LD32_MAX_OFFSET (SImode)
&& INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0);
if (code == CONST_INT && GET_MODE_SIZE (mode) > 0
&& INTVAL (index) <= CSKY_LD32_MAX_OFFSET (mode)
&& INTVAL (index) >= 0)
return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0);
return 0;
}
/* The instruction ldr rz, (rx, ry << i), i can be 0,1,2,3.
Check that SHIFT is valid, that the code is MULT, and that
the shift is a power of 2. */
static bool
is_ldr_shift_p (HOST_WIDE_INT shift, enum rtx_code code)
{
if (code == ASHIFT)
return (shift >= 0 && shift <= 3);
else if (code == MULT)
return (shift == 1
|| shift == 2
|| shift == 4
|| shift == 8);
else
return false;
}
static int
ck810_legitimate_index_p (machine_mode mode, rtx index, int strict_p)
{
enum rtx_code code = GET_CODE (index);
if (code == CONST_INT && TARGET_HARD_FLOAT && CSKY_VREG_MODE_P (mode))
return (INTVAL (index) < 1024 && INTVAL (index) >= 0
&& (INTVAL (index) & 3) == 0);
if (code == CONST_INT)
{
/* When the mode size is larger than 4, we may use two ld instruction
to get data, the index and (index+1) should be valid. */
if (GET_MODE_SIZE (mode) >= 8)
return (INTVAL (index) < CSKY_LD32_MAX_OFFSET (SImode)
&& INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0);
if (GET_MODE_SIZE (mode) > 0
&& INTVAL (index) <= CSKY_LD32_MAX_OFFSET (mode)
&& INTVAL (index) >= 0)
return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0);
}
/* Allow ld.w rx, (gb, sym@got) when -fpic specially. */
else if (code == UNSPEC)
return (flag_pic == 1
&& (XINT (index, 1) == UNSPEC_PIC_SYMBOL_PLT
|| XINT (index, 1) == UNSPEC_PIC_SYMBOL_GOT));
/* The follow index is for ldr instruction, the ldr cannot
load dword data, so the mode size should not be larger than
4. */
else if (GET_MODE_SIZE (mode) <= 4
|| (TARGET_HARD_FLOAT && CSKY_VREG_MODE_P (mode)))
{
if (is_csky_address_register_rtx_p (index, strict_p))
return 1;
else if (code == MULT || code == ASHIFT)
{
rtx xiop0 = XEXP (index, 0);
rtx xiop1 = XEXP (index, 1);
/* FIXME can the xiop1 be the reg and xiop0 be the int when mult? */
return (is_csky_address_register_rtx_p (xiop0, strict_p)
&& CONST_INT_P (xiop1)
&& is_ldr_shift_p (INTVAL (xiop1), code));
}
}
return 0;
}
static int
csky_legitimate_index_p (machine_mode mode, rtx index, int strict_p)
{
if (CSKY_TARGET_ARCH (CK801))
return ck801_legitimate_index_p (mode, index, strict_p);
else if (CSKY_TARGET_ARCH (CK802))
return ck802_legitimate_index_p (mode, index, strict_p);
else
return ck810_legitimate_index_p (mode, index, strict_p);
}
/* Implement TARGET_LEGITIMATE_ADDRESS_P.
Recognizes RTL expressions that are valid memory addresses for an
instruction. The MODE argument is the machine mode for the MEM
expression that wants to use this address.
It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should
convert common non-canonical forms to canonical form so that they will
be recognized. */
static bool
csky_legitimate_address_p (machine_mode mode, rtx addr, bool strict_p)
{
enum rtx_code code = GET_CODE (addr);
/* Match the RTX form emitted for constant pool references.
After reload constants split into minipools will have addresses
from a LABEL_REF. */
if (reload_completed
&& ((code == LABEL_REF)
|| (code == CONST
&& GET_CODE (XEXP (addr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (addr, 0), 0)) == LABEL_REF
&& CONST_INT_P (XEXP (XEXP (addr, 0), 1)))))
return 1;
if (is_csky_address_register_rtx_p (addr, strict_p))
return 1;
/* It is a pc-relative load, may be generated for constpool. */
else if (GET_CODE (addr) == LABEL_REF)
return 1;
if (code == PLUS)
{
rtx xop0 = XEXP (addr, 0);
rtx xop1 = XEXP (addr, 1);
return ((is_csky_address_register_rtx_p (xop0, strict_p)
&& csky_legitimate_index_p (mode, xop1, strict_p))
|| (is_csky_address_register_rtx_p (xop1, strict_p)
&& csky_legitimate_index_p (mode, xop0, strict_p)));
}
return 0;
}
/* Functions to save and restore machine-specific function data. */
static struct machine_function *
csky_init_machine_status (void)
{
struct machine_function *machine;
machine = ggc_cleared_alloc<machine_function> ();
#if CSKY_FT_UNKNOWN != 0
machine->func_type = CSKY_FT_UNKNOWN;
#endif
return machine;
}
/* Implement INIT_EXPANDERS. */
void
csky_init_expanders (void)
{
/* Arrange to initialize and mark the machine per-function status. */
init_machine_status = csky_init_machine_status;
}
/* Implement TARGET_CANNOT_COPY_INSN_P.
We must not copy any rtx that uses a pc-relative address. */
static bool
csky_cannot_copy_insn_p (rtx_insn *insn)
{
subrtx_iterator::array_type array;
FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL)
{
const_rtx x = *iter;
if (GET_CODE (x) == UNSPEC
&& (XINT (x, 1) == UNSPEC_TLS_LABEL
|| XINT (x,