GCC Bugzilla – Attachment 49470 Details for
Bug 97417
RISC-V Unnecessary andi instruction when loading volatile bool
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
optimization fix for BUG 97417
97417.patch (text/plain), 329.30 KB, created by
Levy Hsu
on 2020-10-30 08:35:04 UTC
(
hide
)
Description:
optimization fix for BUG 97417
Filename:
MIME Type:
Creator:
Levy Hsu
Created:
2020-10-30 08:35:04 UTC
Size:
329.30 KB
patch
obsolete
>--- riscv.c.original 2020-10-30 14:19:23.568170000 +0800 >+++ riscv.c 2020-10-30 22:44:57.851128000 +0800 >@@ -1,5434 +1,5454 @@ >-/* Subroutines used for code generation for RISC-V. >- Copyright (C) 2011-2020 Free Software Foundation, Inc. >- Contributed by Andrew Waterman (andrew@sifive.com). >- Based on MIPS target for GNU compiler. >- >-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 >- >-#define INCLUDE_STRING >-#include "config.h" >-#include "system.h" >-#include "coretypes.h" >-#include "tm.h" >-#include "rtl.h" >-#include "regs.h" >-#include "insn-config.h" >-#include "insn-attr.h" >-#include "recog.h" >-#include "output.h" >-#include "alias.h" >-#include "tree.h" >-#include "stringpool.h" >-#include "attribs.h" >-#include "varasm.h" >-#include "stor-layout.h" >-#include "calls.h" >-#include "function.h" >-#include "explow.h" >-#include "memmodel.h" >-#include "emit-rtl.h" >-#include "reload.h" >-#include "tm_p.h" >-#include "target.h" >-#include "target-def.h" >-#include "basic-block.h" >-#include "expr.h" >-#include "optabs.h" >-#include "bitmap.h" >-#include "df.h" >-#include "diagnostic.h" >-#include "builtins.h" >-#include "predict.h" >-#include "tree-pass.h" >- >-/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */ >-#define UNSPEC_ADDRESS_P(X) \ >- (GET_CODE (X) == UNSPEC \ >- && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \ >- && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES) >- >-/* Extract the symbol or label from UNSPEC wrapper X. */ >-#define UNSPEC_ADDRESS(X) \ >- XVECEXP (X, 0, 0) >- >-/* Extract the symbol type from UNSPEC wrapper X. */ >-#define UNSPEC_ADDRESS_TYPE(X) \ >- ((enum riscv_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST)) >- >-/* True if bit BIT is set in VALUE. */ >-#define BITSET_P(VALUE, BIT) (((VALUE) & (1ULL << (BIT))) != 0) >- >-/* Classifies an address. >- >- ADDRESS_REG >- A natural register + offset address. The register satisfies >- riscv_valid_base_register_p and the offset is a const_arith_operand. >- >- ADDRESS_LO_SUM >- A LO_SUM rtx. The first operand is a valid base register and >- the second operand is a symbolic address. >- >- ADDRESS_CONST_INT >- A signed 16-bit constant address. >- >- ADDRESS_SYMBOLIC: >- A constant symbolic address. */ >-enum riscv_address_type { >- ADDRESS_REG, >- ADDRESS_LO_SUM, >- ADDRESS_CONST_INT, >- ADDRESS_SYMBOLIC >-}; >- >-/* Information about a function's frame layout. */ >-struct GTY(()) riscv_frame_info { >- /* The size of the frame in bytes. */ >- HOST_WIDE_INT total_size; >- >- /* Bit X is set if the function saves or restores GPR X. */ >- unsigned int mask; >- >- /* Likewise FPR X. */ >- unsigned int fmask; >- >- /* How much the GPR save/restore routines adjust sp (or 0 if unused). */ >- unsigned save_libcall_adjustment; >- >- /* Offsets of fixed-point and floating-point save areas from frame bottom */ >- HOST_WIDE_INT gp_sp_offset; >- HOST_WIDE_INT fp_sp_offset; >- >- /* Offset of virtual frame pointer from stack pointer/frame bottom */ >- HOST_WIDE_INT frame_pointer_offset; >- >- /* Offset of hard frame pointer from stack pointer/frame bottom */ >- HOST_WIDE_INT hard_frame_pointer_offset; >- >- /* The offset of arg_pointer_rtx from the bottom of the frame. */ >- HOST_WIDE_INT arg_pointer_offset; >-}; >- >-enum riscv_privilege_levels { >- UNKNOWN_MODE, USER_MODE, SUPERVISOR_MODE, MACHINE_MODE >-}; >- >-struct GTY(()) machine_function { >- /* The number of extra stack bytes taken up by register varargs. >- This area is allocated by the callee at the very top of the frame. */ >- int varargs_size; >- >- /* True if current function is a naked function. */ >- bool naked_p; >- >- /* True if current function is an interrupt function. */ >- bool interrupt_handler_p; >- /* For an interrupt handler, indicates the privilege level. */ >- enum riscv_privilege_levels interrupt_mode; >- >- /* True if attributes on current function have been checked. */ >- bool attributes_checked_p; >- >- /* The current frame information, calculated by riscv_compute_frame_info. */ >- struct riscv_frame_info frame; >-}; >- >-/* Information about a single argument. */ >-struct riscv_arg_info { >- /* True if the argument is at least partially passed on the stack. */ >- bool stack_p; >- >- /* The number of integer registers allocated to this argument. */ >- unsigned int num_gprs; >- >- /* The offset of the first register used, provided num_gprs is nonzero. >- If passed entirely on the stack, the value is MAX_ARGS_IN_REGISTERS. */ >- unsigned int gpr_offset; >- >- /* The number of floating-point registers allocated to this argument. */ >- unsigned int num_fprs; >- >- /* The offset of the first register used, provided num_fprs is nonzero. */ >- unsigned int fpr_offset; >-}; >- >-/* Information about an address described by riscv_address_type. >- >- ADDRESS_CONST_INT >- No fields are used. >- >- ADDRESS_REG >- REG is the base register and OFFSET is the constant offset. >- >- ADDRESS_LO_SUM >- REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE >- is the type of symbol it references. >- >- ADDRESS_SYMBOLIC >- SYMBOL_TYPE is the type of symbol that the address references. */ >-struct riscv_address_info { >- enum riscv_address_type type; >- rtx reg; >- rtx offset; >- enum riscv_symbol_type symbol_type; >-}; >- >-/* One stage in a constant building sequence. These sequences have >- the form: >- >- A = VALUE[0] >- A = A CODE[1] VALUE[1] >- A = A CODE[2] VALUE[2] >- ... >- >- where A is an accumulator, each CODE[i] is a binary rtl operation >- and each VALUE[i] is a constant integer. CODE[0] is undefined. */ >-struct riscv_integer_op { >- enum rtx_code code; >- unsigned HOST_WIDE_INT value; >-}; >- >-/* The largest number of operations needed to load an integer constant. >- The worst case is LUI, ADDI, SLLI, ADDI, SLLI, ADDI, SLLI, ADDI. */ >-#define RISCV_MAX_INTEGER_OPS 8 >- >-/* Costs of various operations on the different architectures. */ >- >-struct riscv_tune_info >-{ >- unsigned short fp_add[2]; >- unsigned short fp_mul[2]; >- unsigned short fp_div[2]; >- unsigned short int_mul[2]; >- unsigned short int_div[2]; >- unsigned short issue_rate; >- unsigned short branch_cost; >- unsigned short memory_cost; >- bool slow_unaligned_access; >-}; >- >-/* Information about one CPU we know about. */ >-struct riscv_cpu_info { >- /* This CPU's canonical name. */ >- const char *name; >- >- /* Which automaton to use for tuning. */ >- enum riscv_microarchitecture_type microarchitecture; >- >- /* Tuning parameters for this CPU. */ >- const struct riscv_tune_info *tune_info; >-}; >- >-/* Global variables for machine-dependent things. */ >- >-/* Whether unaligned accesses execute very slowly. */ >-bool riscv_slow_unaligned_access_p; >- >-/* Stack alignment to assume/maintain. */ >-unsigned riscv_stack_boundary; >- >-/* If non-zero, this is an offset to be added to SP to redefine the CFA >- when restoring the FP register from the stack. Only valid when generating >- the epilogue. */ >-static int epilogue_cfa_sp_offset; >- >-/* Which tuning parameters to use. */ >-static const struct riscv_tune_info *tune_info; >- >-/* Which automaton to use for tuning. */ >-enum riscv_microarchitecture_type riscv_microarchitecture; >- >-/* Index R is the smallest register class that contains register R. */ >-const enum reg_class riscv_regno_to_class[FIRST_PSEUDO_REGISTER] = { >- GR_REGS, GR_REGS, GR_REGS, GR_REGS, >- GR_REGS, GR_REGS, SIBCALL_REGS, SIBCALL_REGS, >- JALR_REGS, JALR_REGS, SIBCALL_REGS, SIBCALL_REGS, >- SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, >- SIBCALL_REGS, SIBCALL_REGS, JALR_REGS, JALR_REGS, >- JALR_REGS, JALR_REGS, JALR_REGS, JALR_REGS, >- JALR_REGS, JALR_REGS, JALR_REGS, JALR_REGS, >- SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FP_REGS, FP_REGS, FP_REGS, FP_REGS, >- FRAME_REGS, FRAME_REGS, >-}; >- >-/* Costs to use when optimizing for rocket. */ >-static const struct riscv_tune_info rocket_tune_info = { >- {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ >- {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ >- {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ >- {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ >- {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ >- 1, /* issue_rate */ >- 3, /* branch_cost */ >- 5, /* memory_cost */ >- true, /* slow_unaligned_access */ >-}; >- >-/* Costs to use when optimizing for Sifive 7 Series. */ >-static const struct riscv_tune_info sifive_7_tune_info = { >- {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ >- {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ >- {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ >- {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ >- {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ >- 2, /* issue_rate */ >- 4, /* branch_cost */ >- 3, /* memory_cost */ >- true, /* slow_unaligned_access */ >-}; >- >-/* Costs to use when optimizing for size. */ >-static const struct riscv_tune_info optimize_size_tune_info = { >- {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_add */ >- {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_mul */ >- {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_div */ >- {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* int_mul */ >- {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* int_div */ >- 1, /* issue_rate */ >- 1, /* branch_cost */ >- 2, /* memory_cost */ >- false, /* slow_unaligned_access */ >-}; >- >-static tree riscv_handle_fndecl_attribute (tree *, tree, tree, int, bool *); >-static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *); >- >-/* Defining target-specific uses of __attribute__. */ >-static const struct attribute_spec riscv_attribute_table[] = >-{ >- /* Syntax: { name, min_len, max_len, decl_required, type_required, >- function_type_required, affects_type_identity, handler, >- exclude } */ >- >- /* The attribute telling no prologue/epilogue. */ >- { "naked", 0, 0, true, false, false, false, >- riscv_handle_fndecl_attribute, NULL }, >- /* This attribute generates prologue/epilogue for interrupt handlers. */ >- { "interrupt", 0, 1, false, true, true, false, >- riscv_handle_type_attribute, NULL }, >- >- /* The last attribute spec is set to be NULL. */ >- { NULL, 0, 0, false, false, false, false, NULL, NULL } >-}; >- >-/* Order for the CLOBBERs/USEs of gpr_save. */ >-static const unsigned gpr_save_reg_order[] = { >- INVALID_REGNUM, T0_REGNUM, T1_REGNUM, RETURN_ADDR_REGNUM, >- S0_REGNUM, S1_REGNUM, S2_REGNUM, S3_REGNUM, S4_REGNUM, >- S5_REGNUM, S6_REGNUM, S7_REGNUM, S8_REGNUM, S9_REGNUM, >- S10_REGNUM, S11_REGNUM >-}; >- >-/* A table describing all the processors GCC knows about. */ >-static const struct riscv_cpu_info riscv_cpu_info_table[] = { >- { "rocket", generic, &rocket_tune_info }, >- { "sifive-3-series", generic, &rocket_tune_info }, >- { "sifive-5-series", generic, &rocket_tune_info }, >- { "sifive-7-series", sifive_7, &sifive_7_tune_info }, >- { "size", generic, &optimize_size_tune_info }, >-}; >- >-/* Return the riscv_cpu_info entry for the given name string. */ >- >-static const struct riscv_cpu_info * >-riscv_parse_cpu (const char *cpu_string) >-{ >- for (unsigned i = 0; i < ARRAY_SIZE (riscv_cpu_info_table); i++) >- if (strcmp (riscv_cpu_info_table[i].name, cpu_string) == 0) >- return riscv_cpu_info_table + i; >- >- error ("unknown cpu %qs for %<-mtune%>", cpu_string); >- return riscv_cpu_info_table; >-} >- >-/* Helper function for riscv_build_integer; arguments are as for >- riscv_build_integer. */ >- >-static int >-riscv_build_integer_1 (struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS], >- HOST_WIDE_INT value, machine_mode mode) >-{ >- HOST_WIDE_INT low_part = CONST_LOW_PART (value); >- int cost = RISCV_MAX_INTEGER_OPS + 1, alt_cost; >- struct riscv_integer_op alt_codes[RISCV_MAX_INTEGER_OPS]; >- >- if (SMALL_OPERAND (value) || LUI_OPERAND (value)) >- { >- /* Simply ADDI or LUI. */ >- codes[0].code = UNKNOWN; >- codes[0].value = value; >- return 1; >- } >- >- /* End with ADDI. When constructing HImode constants, do not generate any >- intermediate value that is not itself a valid HImode constant. The >- XORI case below will handle those remaining HImode constants. */ >- if (low_part != 0 >- && (mode != HImode >- || value - low_part <= ((1 << (GET_MODE_BITSIZE (HImode) - 1)) - 1))) >- { >- alt_cost = 1 + riscv_build_integer_1 (alt_codes, value - low_part, mode); >- if (alt_cost < cost) >- { >- alt_codes[alt_cost-1].code = PLUS; >- alt_codes[alt_cost-1].value = low_part; >- memcpy (codes, alt_codes, sizeof (alt_codes)); >- cost = alt_cost; >- } >- } >- >- /* End with XORI. */ >- if (cost > 2 && (low_part < 0 || mode == HImode)) >- { >- alt_cost = 1 + riscv_build_integer_1 (alt_codes, value ^ low_part, mode); >- if (alt_cost < cost) >- { >- alt_codes[alt_cost-1].code = XOR; >- alt_codes[alt_cost-1].value = low_part; >- memcpy (codes, alt_codes, sizeof (alt_codes)); >- cost = alt_cost; >- } >- } >- >- /* Eliminate trailing zeros and end with SLLI. */ >- if (cost > 2 && (value & 1) == 0) >- { >- int shift = ctz_hwi (value); >- unsigned HOST_WIDE_INT x = value; >- x = sext_hwi (x >> shift, HOST_BITS_PER_WIDE_INT - shift); >- >- /* Don't eliminate the lower 12 bits if LUI might apply. */ >- if (shift > IMM_BITS && !SMALL_OPERAND (x) && LUI_OPERAND (x << IMM_BITS)) >- shift -= IMM_BITS, x <<= IMM_BITS; >- >- alt_cost = 1 + riscv_build_integer_1 (alt_codes, x, mode); >- if (alt_cost < cost) >- { >- alt_codes[alt_cost-1].code = ASHIFT; >- alt_codes[alt_cost-1].value = shift; >- memcpy (codes, alt_codes, sizeof (alt_codes)); >- cost = alt_cost; >- } >- } >- >- gcc_assert (cost <= RISCV_MAX_INTEGER_OPS); >- return cost; >-} >- >-/* Fill CODES with a sequence of rtl operations to load VALUE. >- Return the number of operations needed. */ >- >-static int >-riscv_build_integer (struct riscv_integer_op *codes, HOST_WIDE_INT value, >- machine_mode mode) >-{ >- int cost = riscv_build_integer_1 (codes, value, mode); >- >- /* Eliminate leading zeros and end with SRLI. */ >- if (value > 0 && cost > 2) >- { >- struct riscv_integer_op alt_codes[RISCV_MAX_INTEGER_OPS]; >- int alt_cost, shift = clz_hwi (value); >- HOST_WIDE_INT shifted_val; >- >- /* Try filling trailing bits with 1s. */ >- shifted_val = (value << shift) | ((((HOST_WIDE_INT) 1) << shift) - 1); >- alt_cost = 1 + riscv_build_integer_1 (alt_codes, shifted_val, mode); >- if (alt_cost < cost) >- { >- alt_codes[alt_cost-1].code = LSHIFTRT; >- alt_codes[alt_cost-1].value = shift; >- memcpy (codes, alt_codes, sizeof (alt_codes)); >- cost = alt_cost; >- } >- >- /* Try filling trailing bits with 0s. */ >- shifted_val = value << shift; >- alt_cost = 1 + riscv_build_integer_1 (alt_codes, shifted_val, mode); >- if (alt_cost < cost) >- { >- alt_codes[alt_cost-1].code = LSHIFTRT; >- alt_codes[alt_cost-1].value = shift; >- memcpy (codes, alt_codes, sizeof (alt_codes)); >- cost = alt_cost; >- } >- } >- >- return cost; >-} >- >-/* Return the cost of constructing VAL in the event that a scratch >- register is available. */ >- >-static int >-riscv_split_integer_cost (HOST_WIDE_INT val) >-{ >- int cost; >- unsigned HOST_WIDE_INT loval = sext_hwi (val, 32); >- unsigned HOST_WIDE_INT hival = sext_hwi ((val - loval) >> 32, 32); >- struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >- >- cost = 2 + riscv_build_integer (codes, loval, VOIDmode); >- if (loval != hival) >- cost += riscv_build_integer (codes, hival, VOIDmode); >- >- return cost; >-} >- >-/* Return the cost of constructing the integer constant VAL. */ >- >-static int >-riscv_integer_cost (HOST_WIDE_INT val) >-{ >- struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >- return MIN (riscv_build_integer (codes, val, VOIDmode), >- riscv_split_integer_cost (val)); >-} >- >-/* Try to split a 64b integer into 32b parts, then reassemble. */ >- >-static rtx >-riscv_split_integer (HOST_WIDE_INT val, machine_mode mode) >-{ >- unsigned HOST_WIDE_INT loval = sext_hwi (val, 32); >- unsigned HOST_WIDE_INT hival = sext_hwi ((val - loval) >> 32, 32); >- rtx hi = gen_reg_rtx (mode), lo = gen_reg_rtx (mode); >- >- riscv_move_integer (hi, hi, hival, mode, FALSE); >- riscv_move_integer (lo, lo, loval, mode, FALSE); >- >- hi = gen_rtx_fmt_ee (ASHIFT, mode, hi, GEN_INT (32)); >- hi = force_reg (mode, hi); >- >- return gen_rtx_fmt_ee (PLUS, mode, hi, lo); >-} >- >-/* Return true if X is a thread-local symbol. */ >- >-static bool >-riscv_tls_symbol_p (const_rtx x) >-{ >- return SYMBOL_REF_P (x) && SYMBOL_REF_TLS_MODEL (x) != 0; >-} >- >-/* Return true if symbol X binds locally. */ >- >-static bool >-riscv_symbol_binds_local_p (const_rtx x) >-{ >- if (SYMBOL_REF_P (x)) >- return (SYMBOL_REF_DECL (x) >- ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) >- : SYMBOL_REF_LOCAL_P (x)); >- else >- return false; >-} >- >-/* Return the method that should be used to access SYMBOL_REF or >- LABEL_REF X. */ >- >-static enum riscv_symbol_type >-riscv_classify_symbol (const_rtx x) >-{ >- if (riscv_tls_symbol_p (x)) >- return SYMBOL_TLS; >- >- if (GET_CODE (x) == SYMBOL_REF && flag_pic && !riscv_symbol_binds_local_p (x)) >- return SYMBOL_GOT_DISP; >- >- return riscv_cmodel == CM_MEDLOW ? SYMBOL_ABSOLUTE : SYMBOL_PCREL; >-} >- >-/* Classify the base of symbolic expression X. */ >- >-enum riscv_symbol_type >-riscv_classify_symbolic_expression (rtx x) >-{ >- rtx offset; >- >- split_const (x, &x, &offset); >- if (UNSPEC_ADDRESS_P (x)) >- return UNSPEC_ADDRESS_TYPE (x); >- >- return riscv_classify_symbol (x); >-} >- >-/* Return true if X is a symbolic constant. If it is, store the type of >- the symbol in *SYMBOL_TYPE. */ >- >-bool >-riscv_symbolic_constant_p (rtx x, enum riscv_symbol_type *symbol_type) >-{ >- rtx offset; >- >- split_const (x, &x, &offset); >- if (UNSPEC_ADDRESS_P (x)) >- { >- *symbol_type = UNSPEC_ADDRESS_TYPE (x); >- x = UNSPEC_ADDRESS (x); >- } >- else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) >- *symbol_type = riscv_classify_symbol (x); >- else >- return false; >- >- if (offset == const0_rtx) >- return true; >- >- /* Nonzero offsets are only valid for references that don't use the GOT. */ >- switch (*symbol_type) >- { >- case SYMBOL_ABSOLUTE: >- case SYMBOL_PCREL: >- case SYMBOL_TLS_LE: >- /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */ >- return sext_hwi (INTVAL (offset), 32) == INTVAL (offset); >- >- default: >- return false; >- } >-} >- >-/* Returns the number of instructions necessary to reference a symbol. */ >- >-static int riscv_symbol_insns (enum riscv_symbol_type type) >-{ >- switch (type) >- { >- case SYMBOL_TLS: return 0; /* Depends on the TLS model. */ >- case SYMBOL_ABSOLUTE: return 2; /* LUI + the reference. */ >- case SYMBOL_PCREL: return 2; /* AUIPC + the reference. */ >- case SYMBOL_TLS_LE: return 3; /* LUI + ADD TP + the reference. */ >- case SYMBOL_GOT_DISP: return 3; /* AUIPC + LD GOT + the reference. */ >- default: gcc_unreachable (); >- } >-} >- >-/* Implement TARGET_LEGITIMATE_CONSTANT_P. */ >- >-static bool >-riscv_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x) >-{ >- return riscv_const_insns (x) > 0; >-} >- >-/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */ >- >-static bool >-riscv_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x) >-{ >- enum riscv_symbol_type type; >- rtx base, offset; >- >- /* There is no assembler syntax for expressing an address-sized >- high part. */ >- if (GET_CODE (x) == HIGH) >- return true; >- >- split_const (x, &base, &offset); >- if (riscv_symbolic_constant_p (base, &type)) >- { >- /* As an optimization, don't spill symbolic constants that are as >- cheap to rematerialize as to access in the constant pool. */ >- if (SMALL_OPERAND (INTVAL (offset)) && riscv_symbol_insns (type) > 0) >- return true; >- >- /* As an optimization, avoid needlessly generate dynamic relocations. */ >- if (flag_pic) >- return true; >- } >- >- /* TLS symbols must be computed by riscv_legitimize_move. */ >- if (tls_referenced_p (x)) >- return true; >- >- return false; >-} >- >-/* Return true if register REGNO is a valid base register for mode MODE. >- STRICT_P is true if REG_OK_STRICT is in effect. */ >- >-int >-riscv_regno_mode_ok_for_base_p (int regno, >- machine_mode mode ATTRIBUTE_UNUSED, >- bool strict_p) >-{ >- if (!HARD_REGISTER_NUM_P (regno)) >- { >- if (!strict_p) >- return true; >- regno = reg_renumber[regno]; >- } >- >- /* These fake registers will be eliminated to either the stack or >- hard frame pointer, both of which are usually valid base registers. >- Reload deals with the cases where the eliminated form isn't valid. */ >- if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM) >- return true; >- >- return GP_REG_P (regno); >-} >- >-/* Return true if X is a valid base register for mode MODE. >- STRICT_P is true if REG_OK_STRICT is in effect. */ >- >-static bool >-riscv_valid_base_register_p (rtx x, machine_mode mode, bool strict_p) >-{ >- if (!strict_p && GET_CODE (x) == SUBREG) >- x = SUBREG_REG (x); >- >- return (REG_P (x) >- && riscv_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p)); >-} >- >-/* Return true if, for every base register BASE_REG, (plus BASE_REG X) >- can address a value of mode MODE. */ >- >-static bool >-riscv_valid_offset_p (rtx x, machine_mode mode) >-{ >- /* Check that X is a signed 12-bit number. */ >- if (!const_arith_operand (x, Pmode)) >- return false; >- >- /* We may need to split multiword moves, so make sure that every word >- is accessible. */ >- if (GET_MODE_SIZE (mode) > UNITS_PER_WORD >- && !SMALL_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD)) >- return false; >- >- return true; >-} >- >-/* Should a symbol of type SYMBOL_TYPE should be split in two? */ >- >-bool >-riscv_split_symbol_type (enum riscv_symbol_type symbol_type) >-{ >- if (symbol_type == SYMBOL_TLS_LE) >- return true; >- >- if (!TARGET_EXPLICIT_RELOCS) >- return false; >- >- return symbol_type == SYMBOL_ABSOLUTE || symbol_type == SYMBOL_PCREL; >-} >- >-/* Return true if a LO_SUM can address a value of mode MODE when the >- LO_SUM symbol has type SYM_TYPE. X is the LO_SUM second operand, which >- is used when the mode is BLKmode. */ >- >-static bool >-riscv_valid_lo_sum_p (enum riscv_symbol_type sym_type, machine_mode mode, >- rtx x) >-{ >- int align, size; >- >- /* Check that symbols of type SYMBOL_TYPE can be used to access values >- of mode MODE. */ >- if (riscv_symbol_insns (sym_type) == 0) >- return false; >- >- /* Check that there is a known low-part relocation. */ >- if (!riscv_split_symbol_type (sym_type)) >- return false; >- >- /* We can't tell size or alignment when we have BLKmode, so try extracing a >- decl from the symbol if possible. */ >- if (mode == BLKmode) >- { >- rtx offset; >- >- /* Extract the symbol from the LO_SUM operand, if any. */ >- split_const (x, &x, &offset); >- >- /* Might be a CODE_LABEL. We can compute align but not size for that, >- so don't bother trying to handle it. */ >- if (!SYMBOL_REF_P (x)) >- return false; >- >- /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ >- align = (SYMBOL_REF_DECL (x) >- ? DECL_ALIGN (SYMBOL_REF_DECL (x)) >- : 1); >- size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) >- ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) >- : 2*BITS_PER_WORD); >- } >- else >- { >- align = GET_MODE_ALIGNMENT (mode); >- size = GET_MODE_BITSIZE (mode); >- } >- >- /* We may need to split multiword moves, so make sure that each word >- can be accessed without inducing a carry. */ >- if (size > BITS_PER_WORD >- && (!TARGET_STRICT_ALIGN || size > align)) >- return false; >- >- return true; >-} >- >-/* Return true if X is a valid address for machine mode MODE. If it is, >- fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in >- effect. */ >- >-static bool >-riscv_classify_address (struct riscv_address_info *info, rtx x, >- machine_mode mode, bool strict_p) >-{ >- switch (GET_CODE (x)) >- { >- case REG: >- case SUBREG: >- info->type = ADDRESS_REG; >- info->reg = x; >- info->offset = const0_rtx; >- return riscv_valid_base_register_p (info->reg, mode, strict_p); >- >- case PLUS: >- info->type = ADDRESS_REG; >- info->reg = XEXP (x, 0); >- info->offset = XEXP (x, 1); >- return (riscv_valid_base_register_p (info->reg, mode, strict_p) >- && riscv_valid_offset_p (info->offset, mode)); >- >- case LO_SUM: >- info->type = ADDRESS_LO_SUM; >- info->reg = XEXP (x, 0); >- info->offset = XEXP (x, 1); >- /* We have to trust the creator of the LO_SUM to do something vaguely >- sane. Target-independent code that creates a LO_SUM should also >- create and verify the matching HIGH. Target-independent code that >- adds an offset to a LO_SUM must prove that the offset will not >- induce a carry. Failure to do either of these things would be >- a bug, and we are not required to check for it here. The RISC-V >- backend itself should only create LO_SUMs for valid symbolic >- constants, with the high part being either a HIGH or a copy >- of _gp. */ >- info->symbol_type >- = riscv_classify_symbolic_expression (info->offset); >- return (riscv_valid_base_register_p (info->reg, mode, strict_p) >- && riscv_valid_lo_sum_p (info->symbol_type, mode, info->offset)); >- >- case CONST_INT: >- /* Small-integer addresses don't occur very often, but they >- are legitimate if x0 is a valid base register. */ >- info->type = ADDRESS_CONST_INT; >- return SMALL_OPERAND (INTVAL (x)); >- >- default: >- return false; >- } >-} >- >-/* Implement TARGET_LEGITIMATE_ADDRESS_P. */ >- >-static bool >-riscv_legitimate_address_p (machine_mode mode, rtx x, bool strict_p) >-{ >- struct riscv_address_info addr; >- >- return riscv_classify_address (&addr, x, mode, strict_p); >-} >- >-/* Return true if hard reg REGNO can be used in compressed instructions. */ >- >-static bool >-riscv_compressed_reg_p (int regno) >-{ >- /* x8-x15/f8-f15 are compressible registers. */ >- return (TARGET_RVC && (IN_RANGE (regno, GP_REG_FIRST + 8, GP_REG_FIRST + 15) >- || IN_RANGE (regno, FP_REG_FIRST + 8, FP_REG_FIRST + 15))); >-} >- >-/* Return true if x is an unsigned 5-bit immediate scaled by 4. */ >- >-static bool >-riscv_compressed_lw_offset_p (rtx x) >-{ >- return (CONST_INT_P (x) >- && (INTVAL (x) & 3) == 0 >- && IN_RANGE (INTVAL (x), 0, CSW_MAX_OFFSET)); >-} >- >-/* Return true if load/store from/to address x can be compressed. */ >- >-static bool >-riscv_compressed_lw_address_p (rtx x) >-{ >- struct riscv_address_info addr; >- bool result = riscv_classify_address (&addr, x, GET_MODE (x), >- reload_completed); >- >- /* Before reload, assuming all load/stores of valid addresses get compressed >- gives better code size than checking if the address is reg + small_offset >- early on. */ >- if (result && !reload_completed) >- return true; >- >- /* Return false if address is not compressed_reg + small_offset. */ >- if (!result >- || addr.type != ADDRESS_REG >- || (!riscv_compressed_reg_p (REGNO (addr.reg)) >- && addr.reg != stack_pointer_rtx) >- || !riscv_compressed_lw_offset_p (addr.offset)) >- return false; >- >- return result; >-} >- >-/* Return the number of instructions needed to load or store a value >- of mode MODE at address X. Return 0 if X isn't valid for MODE. >- Assume that multiword moves may need to be split into word moves >- if MIGHT_SPLIT_P, otherwise assume that a single load or store is >- enough. */ >- >-int >-riscv_address_insns (rtx x, machine_mode mode, bool might_split_p) >-{ >- struct riscv_address_info addr = {}; >- int n = 1; >- >- if (!riscv_classify_address (&addr, x, mode, false)) >- { >- /* This could be a pattern from the pic.md file. In which case we want >- this address to always have a cost of 3 to make it as expensive as the >- most expensive symbol. This prevents constant propagation from >- preferring symbols over register plus offset. */ >- return 3; >- } >- >- /* BLKmode is used for single unaligned loads and stores and should >- not count as a multiword mode. */ >- if (mode != BLKmode && might_split_p) >- n += (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >- >- if (addr.type == ADDRESS_LO_SUM) >- n += riscv_symbol_insns (addr.symbol_type) - 1; >- >- return n; >-} >- >-/* Return the number of instructions needed to load constant X. >- Return 0 if X isn't a valid constant. */ >- >-int >-riscv_const_insns (rtx x) >-{ >- enum riscv_symbol_type symbol_type; >- rtx offset; >- >- switch (GET_CODE (x)) >- { >- case HIGH: >- if (!riscv_symbolic_constant_p (XEXP (x, 0), &symbol_type) >- || !riscv_split_symbol_type (symbol_type)) >- return 0; >- >- /* This is simply an LUI. */ >- return 1; >- >- case CONST_INT: >- { >- int cost = riscv_integer_cost (INTVAL (x)); >- /* Force complicated constants to memory. */ >- return cost < 4 ? cost : 0; >- } >- >- case CONST_DOUBLE: >- case CONST_VECTOR: >- /* We can use x0 to load floating-point zero. */ >- return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0; >- >- case CONST: >- /* See if we can refer to X directly. */ >- if (riscv_symbolic_constant_p (x, &symbol_type)) >- return riscv_symbol_insns (symbol_type); >- >- /* Otherwise try splitting the constant into a base and offset. */ >- split_const (x, &x, &offset); >- if (offset != 0) >- { >- int n = riscv_const_insns (x); >- if (n != 0) >- return n + riscv_integer_cost (INTVAL (offset)); >- } >- return 0; >- >- case SYMBOL_REF: >- case LABEL_REF: >- return riscv_symbol_insns (riscv_classify_symbol (x)); >- >- default: >- return 0; >- } >-} >- >-/* X is a doubleword constant that can be handled by splitting it into >- two words and loading each word separately. Return the number of >- instructions required to do this. */ >- >-int >-riscv_split_const_insns (rtx x) >-{ >- unsigned int low, high; >- >- low = riscv_const_insns (riscv_subword (x, false)); >- high = riscv_const_insns (riscv_subword (x, true)); >- gcc_assert (low > 0 && high > 0); >- return low + high; >-} >- >-/* Return the number of instructions needed to implement INSN, >- given that it loads from or stores to MEM. */ >- >-int >-riscv_load_store_insns (rtx mem, rtx_insn *insn) >-{ >- machine_mode mode; >- bool might_split_p; >- rtx set; >- >- gcc_assert (MEM_P (mem)); >- mode = GET_MODE (mem); >- >- /* Try to prove that INSN does not need to be split. */ >- might_split_p = true; >- if (GET_MODE_BITSIZE (mode) <= 32) >- might_split_p = false; >- else if (GET_MODE_BITSIZE (mode) == 64) >- { >- set = single_set (insn); >- if (set && !riscv_split_64bit_move_p (SET_DEST (set), SET_SRC (set))) >- might_split_p = false; >- } >- >- return riscv_address_insns (XEXP (mem, 0), mode, might_split_p); >-} >- >-/* Emit a move from SRC to DEST. Assume that the move expanders can >- handle all moves if !can_create_pseudo_p (). The distinction is >- important because, unlike emit_move_insn, the move expanders know >- how to force Pmode objects into the constant pool even when the >- constant pool address is not itself legitimate. */ >- >-rtx >-riscv_emit_move (rtx dest, rtx src) >-{ >- return (can_create_pseudo_p () >- ? emit_move_insn (dest, src) >- : emit_move_insn_1 (dest, src)); >-} >- >-/* Emit an instruction of the form (set TARGET SRC). */ >- >-static rtx >-riscv_emit_set (rtx target, rtx src) >-{ >- emit_insn (gen_rtx_SET (target, src)); >- return target; >-} >- >-/* Emit an instruction of the form (set DEST (CODE X Y)). */ >- >-static rtx >-riscv_emit_binary (enum rtx_code code, rtx dest, rtx x, rtx y) >-{ >- return riscv_emit_set (dest, gen_rtx_fmt_ee (code, GET_MODE (dest), x, y)); >-} >- >-/* Compute (CODE X Y) and store the result in a new register >- of mode MODE. Return that new register. */ >- >-static rtx >-riscv_force_binary (machine_mode mode, enum rtx_code code, rtx x, rtx y) >-{ >- return riscv_emit_binary (code, gen_reg_rtx (mode), x, y); >-} >- >-/* Copy VALUE to a register and return that register. If new pseudos >- are allowed, copy it into a new register, otherwise use DEST. */ >- >-static rtx >-riscv_force_temporary (rtx dest, rtx value, bool in_splitter) >-{ >- /* We can't call gen_reg_rtx from a splitter, because this might realloc >- the regno_reg_rtx array, which would invalidate reg rtx pointers in the >- combine undo buffer. */ >- if (can_create_pseudo_p () && !in_splitter) >- return force_reg (Pmode, value); >- else >- { >- riscv_emit_move (dest, value); >- return dest; >- } >-} >- >-/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE, >- then add CONST_INT OFFSET to the result. */ >- >-static rtx >-riscv_unspec_address_offset (rtx base, rtx offset, >- enum riscv_symbol_type symbol_type) >-{ >- base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), >- UNSPEC_ADDRESS_FIRST + symbol_type); >- if (offset != const0_rtx) >- base = gen_rtx_PLUS (Pmode, base, offset); >- return gen_rtx_CONST (Pmode, base); >-} >- >-/* Return an UNSPEC address with underlying address ADDRESS and symbol >- type SYMBOL_TYPE. */ >- >-rtx >-riscv_unspec_address (rtx address, enum riscv_symbol_type symbol_type) >-{ >- rtx base, offset; >- >- split_const (address, &base, &offset); >- return riscv_unspec_address_offset (base, offset, symbol_type); >-} >- >-/* If OP is an UNSPEC address, return the address to which it refers, >- otherwise return OP itself. */ >- >-static rtx >-riscv_strip_unspec_address (rtx op) >-{ >- rtx base, offset; >- >- split_const (op, &base, &offset); >- if (UNSPEC_ADDRESS_P (base)) >- op = plus_constant (Pmode, UNSPEC_ADDRESS (base), INTVAL (offset)); >- return op; >-} >- >-/* If riscv_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the >- high part to BASE and return the result. Just return BASE otherwise. >- TEMP is as for riscv_force_temporary. >- >- The returned expression can be used as the first operand to a LO_SUM. */ >- >-static rtx >-riscv_unspec_offset_high (rtx temp, rtx addr, enum riscv_symbol_type symbol_type) >-{ >- addr = gen_rtx_HIGH (Pmode, riscv_unspec_address (addr, symbol_type)); >- return riscv_force_temporary (temp, addr, FALSE); >-} >- >-/* Load an entry from the GOT for a TLS GD access. */ >- >-static rtx riscv_got_load_tls_gd (rtx dest, rtx sym) >-{ >- if (Pmode == DImode) >- return gen_got_load_tls_gddi (dest, sym); >- else >- return gen_got_load_tls_gdsi (dest, sym); >-} >- >-/* Load an entry from the GOT for a TLS IE access. */ >- >-static rtx riscv_got_load_tls_ie (rtx dest, rtx sym) >-{ >- if (Pmode == DImode) >- return gen_got_load_tls_iedi (dest, sym); >- else >- return gen_got_load_tls_iesi (dest, sym); >-} >- >-/* Add in the thread pointer for a TLS LE access. */ >- >-static rtx riscv_tls_add_tp_le (rtx dest, rtx base, rtx sym) >-{ >- rtx tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); >- if (Pmode == DImode) >- return gen_tls_add_tp_ledi (dest, base, tp, sym); >- else >- return gen_tls_add_tp_lesi (dest, base, tp, sym); >-} >- >-/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise >- it appears in a MEM of that mode. Return true if ADDR is a legitimate >- constant in that context and can be split into high and low parts. >- If so, and if LOW_OUT is nonnull, emit the high part and store the >- low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise. >- >- TEMP is as for riscv_force_temporary and is used to load the high >- part into a register. >- >- When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be >- a legitimize SET_SRC for an .md pattern, otherwise the low part >- is guaranteed to be a legitimate address for mode MODE. */ >- >-bool >-riscv_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out, >- bool in_splitter) >-{ >- enum riscv_symbol_type symbol_type; >- >- if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE) >- || !riscv_symbolic_constant_p (addr, &symbol_type) >- || riscv_symbol_insns (symbol_type) == 0 >- || !riscv_split_symbol_type (symbol_type)) >- return false; >- >- if (low_out) >- switch (symbol_type) >- { >- case SYMBOL_ABSOLUTE: >- { >- rtx high = gen_rtx_HIGH (Pmode, copy_rtx (addr)); >- high = riscv_force_temporary (temp, high, in_splitter); >- *low_out = gen_rtx_LO_SUM (Pmode, high, addr); >- } >- break; >- >- case SYMBOL_PCREL: >- { >- static unsigned seqno; >- char buf[32]; >- rtx label; >- >- ssize_t bytes = snprintf (buf, sizeof (buf), ".LA%u", seqno); >- gcc_assert ((size_t) bytes < sizeof (buf)); >- >- label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); >- SYMBOL_REF_FLAGS (label) |= SYMBOL_FLAG_LOCAL; >- /* ??? Ugly hack to make weak symbols work. May need to change the >- RTL for the auipc and/or low patterns to get a better fix for >- this. */ >- if (! nonzero_address_p (addr)) >- SYMBOL_REF_WEAK (label) = 1; >- >- if (temp == NULL) >- temp = gen_reg_rtx (Pmode); >- >- if (Pmode == DImode) >- emit_insn (gen_auipcdi (temp, copy_rtx (addr), GEN_INT (seqno))); >- else >- emit_insn (gen_auipcsi (temp, copy_rtx (addr), GEN_INT (seqno))); >- >- *low_out = gen_rtx_LO_SUM (Pmode, temp, label); >- >- seqno++; >- } >- break; >- >- default: >- gcc_unreachable (); >- } >- >- return true; >-} >- >-/* Return a legitimate address for REG + OFFSET. TEMP is as for >- riscv_force_temporary; it is only needed when OFFSET is not a >- SMALL_OPERAND. */ >- >-static rtx >-riscv_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset) >-{ >- if (!SMALL_OPERAND (offset)) >- { >- rtx high; >- >- /* Leave OFFSET as a 16-bit offset and put the excess in HIGH. >- The addition inside the macro CONST_HIGH_PART may cause an >- overflow, so we need to force a sign-extension check. */ >- high = gen_int_mode (CONST_HIGH_PART (offset), Pmode); >- offset = CONST_LOW_PART (offset); >- high = riscv_force_temporary (temp, high, FALSE); >- reg = riscv_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg), >- FALSE); >- } >- return plus_constant (Pmode, reg, offset); >-} >- >-/* The __tls_get_attr symbol. */ >-static GTY(()) rtx riscv_tls_symbol; >- >-/* Return an instruction sequence that calls __tls_get_addr. SYM is >- the TLS symbol we are referencing and TYPE is the symbol type to use >- (either global dynamic or local dynamic). RESULT is an RTX for the >- return value location. */ >- >-static rtx_insn * >-riscv_call_tls_get_addr (rtx sym, rtx result) >-{ >- rtx a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST), func; >- rtx_insn *insn; >- >- if (!riscv_tls_symbol) >- riscv_tls_symbol = init_one_libfunc ("__tls_get_addr"); >- func = gen_rtx_MEM (FUNCTION_MODE, riscv_tls_symbol); >- >- start_sequence (); >- >- emit_insn (riscv_got_load_tls_gd (a0, sym)); >- insn = emit_call_insn (gen_call_value (result, func, const0_rtx, NULL)); >- RTL_CONST_CALL_P (insn) = 1; >- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); >- insn = get_insns (); >- >- end_sequence (); >- >- return insn; >-} >- >-/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return >- its address. The return value will be both a valid address and a valid >- SET_SRC (either a REG or a LO_SUM). */ >- >-static rtx >-riscv_legitimize_tls_address (rtx loc) >-{ >- rtx dest, tp, tmp; >- enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); >- >-#if 0 >- /* TLS copy relocs are now deprecated and should not be used. */ >- /* Since we support TLS copy relocs, non-PIC TLS accesses may all use LE. */ >- if (!flag_pic) >- model = TLS_MODEL_LOCAL_EXEC; >-#endif >- >- switch (model) >- { >- case TLS_MODEL_LOCAL_DYNAMIC: >- /* Rely on section anchors for the optimization that LDM TLS >- provides. The anchor's address is loaded with GD TLS. */ >- case TLS_MODEL_GLOBAL_DYNAMIC: >- tmp = gen_rtx_REG (Pmode, GP_RETURN); >- dest = gen_reg_rtx (Pmode); >- emit_libcall_block (riscv_call_tls_get_addr (loc, tmp), dest, tmp, loc); >- break; >- >- case TLS_MODEL_INITIAL_EXEC: >- /* la.tls.ie; tp-relative add */ >- tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); >- tmp = gen_reg_rtx (Pmode); >- emit_insn (riscv_got_load_tls_ie (tmp, loc)); >- dest = gen_reg_rtx (Pmode); >- emit_insn (gen_add3_insn (dest, tmp, tp)); >- break; >- >- case TLS_MODEL_LOCAL_EXEC: >- tmp = riscv_unspec_offset_high (NULL, loc, SYMBOL_TLS_LE); >- dest = gen_reg_rtx (Pmode); >- emit_insn (riscv_tls_add_tp_le (dest, tmp, loc)); >- dest = gen_rtx_LO_SUM (Pmode, dest, >- riscv_unspec_address (loc, SYMBOL_TLS_LE)); >- break; >- >- default: >- gcc_unreachable (); >- } >- return dest; >-} >- >-/* If X is not a valid address for mode MODE, force it into a register. */ >- >-static rtx >-riscv_force_address (rtx x, machine_mode mode) >-{ >- if (!riscv_legitimate_address_p (mode, x, false)) >- x = force_reg (Pmode, x); >- return x; >-} >- >-/* Modify base + offset so that offset fits within a compressed load/store insn >- and the excess is added to base. */ >- >-static rtx >-riscv_shorten_lw_offset (rtx base, HOST_WIDE_INT offset) >-{ >- rtx addr, high; >- /* Leave OFFSET as an unsigned 5-bit offset scaled by 4 and put the excess >- into HIGH. */ >- high = GEN_INT (offset & ~CSW_MAX_OFFSET); >- offset &= CSW_MAX_OFFSET; >- if (!SMALL_OPERAND (INTVAL (high))) >- high = force_reg (Pmode, high); >- base = force_reg (Pmode, gen_rtx_PLUS (Pmode, high, base)); >- addr = plus_constant (Pmode, base, offset); >- return addr; >-} >- >-/* This function is used to implement LEGITIMIZE_ADDRESS. If X can >- be legitimized in a way that the generic machinery might not expect, >- return a new address, otherwise return NULL. MODE is the mode of >- the memory being accessed. */ >- >-static rtx >-riscv_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, >- machine_mode mode) >-{ >- rtx addr; >- >- if (riscv_tls_symbol_p (x)) >- return riscv_legitimize_tls_address (x); >- >- /* See if the address can split into a high part and a LO_SUM. */ >- if (riscv_split_symbol (NULL, x, mode, &addr, FALSE)) >- return riscv_force_address (addr, mode); >- >- /* Handle BASE + OFFSET. */ >- if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)) >- && INTVAL (XEXP (x, 1)) != 0) >- { >- rtx base = XEXP (x, 0); >- HOST_WIDE_INT offset = INTVAL (XEXP (x, 1)); >- >- if (!riscv_valid_base_register_p (base, mode, false)) >- base = copy_to_mode_reg (Pmode, base); >- if (optimize_function_for_size_p (cfun) >- && (strcmp (current_pass->name, "shorten_memrefs") == 0) >- && mode == SImode) >- /* Convert BASE + LARGE_OFFSET into NEW_BASE + SMALL_OFFSET to allow >- possible compressed load/store. */ >- addr = riscv_shorten_lw_offset (base, offset); >- else >- addr = riscv_add_offset (NULL, base, offset); >- return riscv_force_address (addr, mode); >- } >- >- return x; >-} >- >-/* Load VALUE into DEST. TEMP is as for riscv_force_temporary. ORIG_MODE >- is the original src mode before promotion. */ >- >-void >-riscv_move_integer (rtx temp, rtx dest, HOST_WIDE_INT value, >- machine_mode orig_mode, bool in_splitter) >-{ >- struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >- machine_mode mode; >- int i, num_ops; >- rtx x; >- >- /* We can't call gen_reg_rtx from a splitter, because this might realloc >- the regno_reg_rtx array, which would invalidate reg rtx pointers in the >- combine undo buffer. */ >- bool can_create_pseudo = can_create_pseudo_p () && ! in_splitter; >- >- mode = GET_MODE (dest); >- /* We use the original mode for the riscv_build_integer call, because HImode >- values are given special treatment. */ >- num_ops = riscv_build_integer (codes, value, orig_mode); >- >- if (can_create_pseudo && num_ops > 2 /* not a simple constant */ >- && num_ops >= riscv_split_integer_cost (value)) >- x = riscv_split_integer (value, mode); >- else >- { >- /* Apply each binary operation to X. */ >- x = GEN_INT (codes[0].value); >- >- for (i = 1; i < num_ops; i++) >- { >- if (!can_create_pseudo) >- x = riscv_emit_set (temp, x); >- else >- x = force_reg (mode, x); >- >- x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value)); >- } >- } >- >- riscv_emit_set (dest, x); >-} >- >-/* Subroutine of riscv_legitimize_move. Move constant SRC into register >- DEST given that SRC satisfies immediate_operand but doesn't satisfy >- move_operand. */ >- >-static void >-riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src) >-{ >- rtx base, offset; >- >- /* Split moves of big integers into smaller pieces. */ >- if (splittable_const_int_operand (src, mode)) >- { >- riscv_move_integer (dest, dest, INTVAL (src), mode, FALSE); >- return; >- } >- >- /* Split moves of symbolic constants into high/low pairs. */ >- if (riscv_split_symbol (dest, src, MAX_MACHINE_MODE, &src, FALSE)) >- { >- riscv_emit_set (dest, src); >- return; >- } >- >- /* Generate the appropriate access sequences for TLS symbols. */ >- if (riscv_tls_symbol_p (src)) >- { >- riscv_emit_move (dest, riscv_legitimize_tls_address (src)); >- return; >- } >- >- /* If we have (const (plus symbol offset)), and that expression cannot >- be forced into memory, load the symbol first and add in the offset. Also >- prefer to do this even if the constant _can_ be forced into memory, as it >- usually produces better code. */ >- split_const (src, &base, &offset); >- if (offset != const0_rtx >- && (targetm.cannot_force_const_mem (mode, src) || can_create_pseudo_p ())) >- { >- base = riscv_force_temporary (dest, base, FALSE); >- riscv_emit_move (dest, riscv_add_offset (NULL, base, INTVAL (offset))); >- return; >- } >- >- src = force_const_mem (mode, src); >- >- /* When using explicit relocs, constant pool references are sometimes >- not legitimate addresses. */ >- riscv_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0), FALSE); >- riscv_emit_move (dest, src); >-} >- >-/* If (set DEST SRC) is not a valid move instruction, emit an equivalent >- sequence that is valid. */ >- >-bool >-riscv_legitimize_move (machine_mode mode, rtx dest, rtx src) >-{ >- if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode)) >- { >- rtx reg; >- >- if (GET_CODE (src) == CONST_INT) >- { >- /* Apply the equivalent of PROMOTE_MODE here for constants to >- improve cse. */ >- machine_mode promoted_mode = mode; >- if (GET_MODE_CLASS (mode) == MODE_INT >- && GET_MODE_SIZE (mode) < UNITS_PER_WORD) >- promoted_mode = word_mode; >- >- if (splittable_const_int_operand (src, mode)) >- { >- reg = gen_reg_rtx (promoted_mode); >- riscv_move_integer (reg, reg, INTVAL (src), mode, FALSE); >- } >- else >- reg = force_reg (promoted_mode, src); >- >- if (promoted_mode != mode) >- reg = gen_lowpart (mode, reg); >- } >- else >- reg = force_reg (mode, src); >- riscv_emit_move (dest, reg); >- return true; >- } >- >- /* We need to deal with constants that would be legitimate >- immediate_operands but aren't legitimate move_operands. */ >- if (CONSTANT_P (src) && !move_operand (src, mode)) >- { >- riscv_legitimize_const_move (mode, dest, src); >- set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src)); >- return true; >- } >- >- /* RISC-V GCC may generate non-legitimate address due to we provide some >- pattern for optimize access PIC local symbol and it's make GCC generate >- unrecognizable instruction during optmizing. */ >- >- if (MEM_P (dest) && !riscv_legitimate_address_p (mode, XEXP (dest, 0), >- reload_completed)) >- { >- XEXP (dest, 0) = riscv_force_address (XEXP (dest, 0), mode); >- } >- >- if (MEM_P (src) && !riscv_legitimate_address_p (mode, XEXP (src, 0), >- reload_completed)) >- { >- XEXP (src, 0) = riscv_force_address (XEXP (src, 0), mode); >- } >- >- return false; >-} >- >-/* Return true if there is an instruction that implements CODE and accepts >- X as an immediate operand. */ >- >-static int >-riscv_immediate_operand_p (int code, HOST_WIDE_INT x) >-{ >- switch (code) >- { >- case ASHIFT: >- case ASHIFTRT: >- case LSHIFTRT: >- /* All shift counts are truncated to a valid constant. */ >- return true; >- >- case AND: >- case IOR: >- case XOR: >- case PLUS: >- case LT: >- case LTU: >- /* These instructions take 12-bit signed immediates. */ >- return SMALL_OPERAND (x); >- >- case LE: >- /* We add 1 to the immediate and use SLT. */ >- return SMALL_OPERAND (x + 1); >- >- case LEU: >- /* Likewise SLTU, but reject the always-true case. */ >- return SMALL_OPERAND (x + 1) && x + 1 != 0; >- >- case GE: >- case GEU: >- /* We can emulate an immediate of 1 by using GT/GTU against x0. */ >- return x == 1; >- >- default: >- /* By default assume that x0 can be used for 0. */ >- return x == 0; >- } >-} >- >-/* Return the cost of binary operation X, given that the instruction >- sequence for a word-sized or smaller operation takes SIGNLE_INSNS >- instructions and that the sequence of a double-word operation takes >- DOUBLE_INSNS instructions. */ >- >-static int >-riscv_binary_cost (rtx x, int single_insns, int double_insns) >-{ >- if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2) >- return COSTS_N_INSNS (double_insns); >- return COSTS_N_INSNS (single_insns); >-} >- >-/* Return the cost of sign- or zero-extending OP. */ >- >-static int >-riscv_extend_cost (rtx op, bool unsigned_p) >-{ >- if (MEM_P (op)) >- return 0; >- >- if (unsigned_p && GET_MODE (op) == QImode) >- /* We can use ANDI. */ >- return COSTS_N_INSNS (1); >- >- if (!unsigned_p && GET_MODE (op) == SImode) >- /* We can use SEXT.W. */ >- return COSTS_N_INSNS (1); >- >- /* We need to use a shift left and a shift right. */ >- return COSTS_N_INSNS (2); >-} >- >-/* Implement TARGET_RTX_COSTS. */ >- >-#define SINGLE_SHIFT_COST 1 >- >-static bool >-riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UNUSED, >- int *total, bool speed) >-{ >- bool float_mode_p = FLOAT_MODE_P (mode); >- int cost; >- >- switch (GET_CODE (x)) >- { >- case CONST_INT: >- if (riscv_immediate_operand_p (outer_code, INTVAL (x))) >- { >- *total = 0; >- return true; >- } >- /* Fall through. */ >- >- case SYMBOL_REF: >- case LABEL_REF: >- case CONST_DOUBLE: >- case CONST: >- if ((cost = riscv_const_insns (x)) > 0) >- { >- /* If the constant is likely to be stored in a GPR, SETs of >- single-insn constants are as cheap as register sets; we >- never want to CSE them. */ >- if (cost == 1 && outer_code == SET) >- *total = 0; >- /* When we load a constant more than once, it usually is better >- to duplicate the last operation in the sequence than to CSE >- the constant itself. */ >- else if (outer_code == SET || GET_MODE (x) == VOIDmode) >- *total = COSTS_N_INSNS (1); >- } >- else /* The instruction will be fetched from the constant pool. */ >- *total = COSTS_N_INSNS (riscv_symbol_insns (SYMBOL_ABSOLUTE)); >- return true; >- >- case MEM: >- /* If the address is legitimate, return the number of >- instructions it needs. */ >- if ((cost = riscv_address_insns (XEXP (x, 0), mode, true)) > 0) >- { >- *total = COSTS_N_INSNS (cost + tune_info->memory_cost); >- return true; >- } >- /* Otherwise use the default handling. */ >- return false; >- >- case NOT: >- *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1); >- return false; >- >- case AND: >- case IOR: >- case XOR: >- /* Double-word operations use two single-word operations. */ >- *total = riscv_binary_cost (x, 1, 2); >- return false; >- >- case ZERO_EXTRACT: >- /* This is an SImode shift. */ >- if (outer_code == SET >- && CONST_INT_P (XEXP (x, 1)) >- && CONST_INT_P (XEXP (x, 2)) >- && (INTVAL (XEXP (x, 2)) > 0) >- && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (x, 2)) == 32)) >- { >- *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >- return true; >- } >- return false; >- >- case ASHIFT: >- case ASHIFTRT: >- case LSHIFTRT: >- *total = riscv_binary_cost (x, SINGLE_SHIFT_COST, >- CONSTANT_P (XEXP (x, 1)) ? 4 : 9); >- return false; >- >- case ABS: >- *total = COSTS_N_INSNS (float_mode_p ? 1 : 3); >- return false; >- >- case LO_SUM: >- *total = set_src_cost (XEXP (x, 0), mode, speed); >- return true; >- >- case LT: >- /* This is an SImode shift. */ >- if (outer_code == SET && GET_MODE (x) == DImode >- && GET_MODE (XEXP (x, 0)) == SImode) >- { >- *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >- return true; >- } >- /* Fall through. */ >- case LTU: >- case LE: >- case LEU: >- case GT: >- case GTU: >- case GE: >- case GEU: >- case EQ: >- case NE: >- /* Branch comparisons have VOIDmode, so use the first operand's >- mode instead. */ >- mode = GET_MODE (XEXP (x, 0)); >- if (float_mode_p) >- *total = tune_info->fp_add[mode == DFmode]; >- else >- *total = riscv_binary_cost (x, 1, 3); >- return false; >- >- case UNORDERED: >- case ORDERED: >- /* (FEQ(A, A) & FEQ(B, B)) compared against 0. */ >- mode = GET_MODE (XEXP (x, 0)); >- *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2); >- return false; >- >- case UNEQ: >- /* (FEQ(A, A) & FEQ(B, B)) compared against FEQ(A, B). */ >- mode = GET_MODE (XEXP (x, 0)); >- *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (3); >- return false; >- >- case LTGT: >- /* (FLT(A, A) || FGT(B, B)). */ >- mode = GET_MODE (XEXP (x, 0)); >- *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2); >- return false; >- >- case UNGE: >- case UNGT: >- case UNLE: >- case UNLT: >- /* FLT or FLE, but guarded by an FFLAGS read and write. */ >- mode = GET_MODE (XEXP (x, 0)); >- *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (4); >- return false; >- >- case MINUS: >- case PLUS: >- if (float_mode_p) >- *total = tune_info->fp_add[mode == DFmode]; >- else >- *total = riscv_binary_cost (x, 1, 4); >- return false; >- >- case NEG: >- { >- rtx op = XEXP (x, 0); >- if (GET_CODE (op) == FMA && !HONOR_SIGNED_ZEROS (mode)) >- { >- *total = (tune_info->fp_mul[mode == DFmode] >- + set_src_cost (XEXP (op, 0), mode, speed) >- + set_src_cost (XEXP (op, 1), mode, speed) >- + set_src_cost (XEXP (op, 2), mode, speed)); >- return true; >- } >- } >- >- if (float_mode_p) >- *total = tune_info->fp_add[mode == DFmode]; >- else >- *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1); >- return false; >- >- case MULT: >- if (float_mode_p) >- *total = tune_info->fp_mul[mode == DFmode]; >- else if (!TARGET_MUL) >- /* Estimate the cost of a library call. */ >- *total = COSTS_N_INSNS (speed ? 32 : 6); >- else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) >- *total = 3 * tune_info->int_mul[0] + COSTS_N_INSNS (2); >- else if (!speed) >- *total = COSTS_N_INSNS (1); >- else >- *total = tune_info->int_mul[mode == DImode]; >- return false; >- >- case DIV: >- case SQRT: >- case MOD: >- if (float_mode_p) >- { >- *total = tune_info->fp_div[mode == DFmode]; >- return false; >- } >- /* Fall through. */ >- >- case UDIV: >- case UMOD: >- if (!TARGET_DIV) >- /* Estimate the cost of a library call. */ >- *total = COSTS_N_INSNS (speed ? 32 : 6); >- else if (speed) >- *total = tune_info->int_div[mode == DImode]; >- else >- *total = COSTS_N_INSNS (1); >- return false; >- >- case ZERO_EXTEND: >- /* This is an SImode shift. */ >- if (GET_CODE (XEXP (x, 0)) == LSHIFTRT) >- { >- *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >- return true; >- } >- /* Fall through. */ >- case SIGN_EXTEND: >- *total = riscv_extend_cost (XEXP (x, 0), GET_CODE (x) == ZERO_EXTEND); >- return false; >- >- case FLOAT: >- case UNSIGNED_FLOAT: >- case FIX: >- case FLOAT_EXTEND: >- case FLOAT_TRUNCATE: >- *total = tune_info->fp_add[mode == DFmode]; >- return false; >- >- case FMA: >- *total = (tune_info->fp_mul[mode == DFmode] >- + set_src_cost (XEXP (x, 0), mode, speed) >- + set_src_cost (XEXP (x, 1), mode, speed) >- + set_src_cost (XEXP (x, 2), mode, speed)); >- return true; >- >- case UNSPEC: >- if (XINT (x, 1) == UNSPEC_AUIPC) >- { >- /* Make AUIPC cheap to avoid spilling its result to the stack. */ >- *total = 1; >- return true; >- } >- return false; >- >- default: >- return false; >- } >-} >- >-/* Implement TARGET_ADDRESS_COST. */ >- >-static int >-riscv_address_cost (rtx addr, machine_mode mode, >- addr_space_t as ATTRIBUTE_UNUSED, >- bool speed ATTRIBUTE_UNUSED) >-{ >- /* When optimizing for size, make uncompressible 32-bit addresses more >- * expensive so that compressible 32-bit addresses are preferred. */ >- if (TARGET_RVC && !speed && riscv_mshorten_memrefs && mode == SImode >- && !riscv_compressed_lw_address_p (addr)) >- return riscv_address_insns (addr, mode, false) + 1; >- return riscv_address_insns (addr, mode, false); >-} >- >-/* Return one word of double-word value OP. HIGH_P is true to select the >- high part or false to select the low part. */ >- >-rtx >-riscv_subword (rtx op, bool high_p) >-{ >- unsigned int byte = high_p ? UNITS_PER_WORD : 0; >- machine_mode mode = GET_MODE (op); >- >- if (mode == VOIDmode) >- mode = TARGET_64BIT ? TImode : DImode; >- >- if (MEM_P (op)) >- return adjust_address (op, word_mode, byte); >- >- if (REG_P (op)) >- gcc_assert (!FP_REG_RTX_P (op)); >- >- return simplify_gen_subreg (word_mode, op, mode, byte); >-} >- >-/* Return true if a 64-bit move from SRC to DEST should be split into two. */ >- >-bool >-riscv_split_64bit_move_p (rtx dest, rtx src) >-{ >- if (TARGET_64BIT) >- return false; >- >- /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case >- of zeroing an FPR with FCVT.D.W. */ >- if (TARGET_DOUBLE_FLOAT >- && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest)) >- || (FP_REG_RTX_P (dest) && MEM_P (src)) >- || (FP_REG_RTX_P (src) && MEM_P (dest)) >- || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src))))) >- return false; >- >- return true; >-} >- >-/* Split a doubleword move from SRC to DEST. On 32-bit targets, >- this function handles 64-bit moves for which riscv_split_64bit_move_p >- holds. For 64-bit targets, this function handles 128-bit moves. */ >- >-void >-riscv_split_doubleword_move (rtx dest, rtx src) >-{ >- rtx low_dest; >- >- /* The operation can be split into two normal moves. Decide in >- which order to do them. */ >- low_dest = riscv_subword (dest, false); >- if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src)) >- { >- riscv_emit_move (riscv_subword (dest, true), riscv_subword (src, true)); >- riscv_emit_move (low_dest, riscv_subword (src, false)); >- } >- else >- { >- riscv_emit_move (low_dest, riscv_subword (src, false)); >- riscv_emit_move (riscv_subword (dest, true), riscv_subword (src, true)); >- } >-} >- >-/* Return the appropriate instructions to move SRC into DEST. Assume >- that SRC is operand 1 and DEST is operand 0. */ >- >-const char * >-riscv_output_move (rtx dest, rtx src) >-{ >- enum rtx_code dest_code, src_code; >- machine_mode mode; >- bool dbl_p; >- >- dest_code = GET_CODE (dest); >- src_code = GET_CODE (src); >- mode = GET_MODE (dest); >- dbl_p = (GET_MODE_SIZE (mode) == 8); >- >- if (dbl_p && riscv_split_64bit_move_p (dest, src)) >- return "#"; >- >- if (dest_code == REG && GP_REG_P (REGNO (dest))) >- { >- if (src_code == REG && FP_REG_P (REGNO (src))) >- return dbl_p ? "fmv.x.d\t%0,%1" : "fmv.x.w\t%0,%1"; >- >- if (src_code == MEM) >- switch (GET_MODE_SIZE (mode)) >- { >- case 1: return "lbu\t%0,%1"; >- case 2: return "lhu\t%0,%1"; >- case 4: return "lw\t%0,%1"; >- case 8: return "ld\t%0,%1"; >- } >- >- if (src_code == CONST_INT) >- return "li\t%0,%1"; >- >- if (src_code == HIGH) >- return "lui\t%0,%h1"; >- >- if (symbolic_operand (src, VOIDmode)) >- switch (riscv_classify_symbolic_expression (src)) >- { >- case SYMBOL_GOT_DISP: return "la\t%0,%1"; >- case SYMBOL_ABSOLUTE: return "lla\t%0,%1"; >- case SYMBOL_PCREL: return "lla\t%0,%1"; >- default: gcc_unreachable (); >- } >- } >- if ((src_code == REG && GP_REG_P (REGNO (src))) >- || (src == CONST0_RTX (mode))) >- { >- if (dest_code == REG) >- { >- if (GP_REG_P (REGNO (dest))) >- return "mv\t%0,%z1"; >- >- if (FP_REG_P (REGNO (dest))) >- { >- if (!dbl_p) >- return "fmv.w.x\t%0,%z1"; >- if (TARGET_64BIT) >- return "fmv.d.x\t%0,%z1"; >- /* in RV32, we can emulate fmv.d.x %0, x0 using fcvt.d.w */ >- gcc_assert (src == CONST0_RTX (mode)); >- return "fcvt.d.w\t%0,x0"; >- } >- } >- if (dest_code == MEM) >- switch (GET_MODE_SIZE (mode)) >- { >- case 1: return "sb\t%z1,%0"; >- case 2: return "sh\t%z1,%0"; >- case 4: return "sw\t%z1,%0"; >- case 8: return "sd\t%z1,%0"; >- } >- } >- if (src_code == REG && FP_REG_P (REGNO (src))) >- { >- if (dest_code == REG && FP_REG_P (REGNO (dest))) >- return dbl_p ? "fmv.d\t%0,%1" : "fmv.s\t%0,%1"; >- >- if (dest_code == MEM) >- return dbl_p ? "fsd\t%1,%0" : "fsw\t%1,%0"; >- } >- if (dest_code == REG && FP_REG_P (REGNO (dest))) >- { >- if (src_code == MEM) >- return dbl_p ? "fld\t%0,%1" : "flw\t%0,%1"; >- } >- gcc_unreachable (); >-} >- >-const char * >-riscv_output_return () >-{ >- if (cfun->machine->naked_p) >- return ""; >- >- return "ret"; >-} >- >- >-/* Return true if CMP1 is a suitable second operand for integer ordering >- test CODE. See also the *sCC patterns in riscv.md. */ >- >-static bool >-riscv_int_order_operand_ok_p (enum rtx_code code, rtx cmp1) >-{ >- switch (code) >- { >- case GT: >- case GTU: >- return reg_or_0_operand (cmp1, VOIDmode); >- >- case GE: >- case GEU: >- return cmp1 == const1_rtx; >- >- case LT: >- case LTU: >- return arith_operand (cmp1, VOIDmode); >- >- case LE: >- return sle_operand (cmp1, VOIDmode); >- >- case LEU: >- return sleu_operand (cmp1, VOIDmode); >- >- default: >- gcc_unreachable (); >- } >-} >- >-/* Return true if *CMP1 (of mode MODE) is a valid second operand for >- integer ordering test *CODE, or if an equivalent combination can >- be formed by adjusting *CODE and *CMP1. When returning true, update >- *CODE and *CMP1 with the chosen code and operand, otherwise leave >- them alone. */ >- >-static bool >-riscv_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1, >- machine_mode mode) >-{ >- HOST_WIDE_INT plus_one; >- >- if (riscv_int_order_operand_ok_p (*code, *cmp1)) >- return true; >- >- if (CONST_INT_P (*cmp1)) >- switch (*code) >- { >- case LE: >- plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); >- if (INTVAL (*cmp1) < plus_one) >- { >- *code = LT; >- *cmp1 = force_reg (mode, GEN_INT (plus_one)); >- return true; >- } >- break; >- >- case LEU: >- plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); >- if (plus_one != 0) >- { >- *code = LTU; >- *cmp1 = force_reg (mode, GEN_INT (plus_one)); >- return true; >- } >- break; >- >- default: >- break; >- } >- return false; >-} >- >-/* Compare CMP0 and CMP1 using ordering test CODE and store the result >- in TARGET. CMP0 and TARGET are register_operands. If INVERT_PTR >- is nonnull, it's OK to set TARGET to the inverse of the result and >- flip *INVERT_PTR instead. */ >- >-static void >-riscv_emit_int_order_test (enum rtx_code code, bool *invert_ptr, >- rtx target, rtx cmp0, rtx cmp1) >-{ >- machine_mode mode; >- >- /* First see if there is a RISCV instruction that can do this operation. >- If not, try doing the same for the inverse operation. If that also >- fails, force CMP1 into a register and try again. */ >- mode = GET_MODE (cmp0); >- if (riscv_canonicalize_int_order_test (&code, &cmp1, mode)) >- riscv_emit_binary (code, target, cmp0, cmp1); >- else >- { >- enum rtx_code inv_code = reverse_condition (code); >- if (!riscv_canonicalize_int_order_test (&inv_code, &cmp1, mode)) >- { >- cmp1 = force_reg (mode, cmp1); >- riscv_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1); >- } >- else if (invert_ptr == 0) >- { >- rtx inv_target = riscv_force_binary (GET_MODE (target), >- inv_code, cmp0, cmp1); >- riscv_emit_binary (XOR, target, inv_target, const1_rtx); >- } >- else >- { >- *invert_ptr = !*invert_ptr; >- riscv_emit_binary (inv_code, target, cmp0, cmp1); >- } >- } >-} >- >-/* Return a register that is zero iff CMP0 and CMP1 are equal. >- The register will have the same mode as CMP0. */ >- >-static rtx >-riscv_zero_if_equal (rtx cmp0, rtx cmp1) >-{ >- if (cmp1 == const0_rtx) >- return cmp0; >- >- return expand_binop (GET_MODE (cmp0), sub_optab, >- cmp0, cmp1, 0, 0, OPTAB_DIRECT); >-} >- >-/* Sign- or zero-extend OP0 and OP1 for integer comparisons. */ >- >-static void >-riscv_extend_comparands (rtx_code code, rtx *op0, rtx *op1) >-{ >- /* Comparisons consider all XLEN bits, so extend sub-XLEN values. */ >- if (GET_MODE_SIZE (word_mode) > GET_MODE_SIZE (GET_MODE (*op0))) >- { >- /* It is more profitable to zero-extend QImode values. But not if the >- first operand has already been sign-extended, and the second one is >- is a constant or has already been sign-extended also. */ >- if (unsigned_condition (code) == code >- && (GET_MODE (*op0) == QImode >- && ! (GET_CODE (*op0) == SUBREG >- && SUBREG_PROMOTED_VAR_P (*op0) >- && SUBREG_PROMOTED_SIGNED_P (*op0) >- && (CONST_INT_P (*op1) >- || (GET_CODE (*op1) == SUBREG >- && SUBREG_PROMOTED_VAR_P (*op1) >- && SUBREG_PROMOTED_SIGNED_P (*op1)))))) >- { >- *op0 = gen_rtx_ZERO_EXTEND (word_mode, *op0); >- if (CONST_INT_P (*op1)) >- *op1 = GEN_INT ((uint8_t) INTVAL (*op1)); >- else >- *op1 = gen_rtx_ZERO_EXTEND (word_mode, *op1); >- } >- else >- { >- *op0 = gen_rtx_SIGN_EXTEND (word_mode, *op0); >- if (*op1 != const0_rtx) >- *op1 = gen_rtx_SIGN_EXTEND (word_mode, *op1); >- } >- } >-} >- >-/* Convert a comparison into something that can be used in a branch. On >- entry, *OP0 and *OP1 are the values being compared and *CODE is the code >- used to compare them. Update them to describe the final comparison. */ >- >-static void >-riscv_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1) >-{ >- if (splittable_const_int_operand (*op1, VOIDmode)) >- { >- HOST_WIDE_INT rhs = INTVAL (*op1); >- >- if (*code == EQ || *code == NE) >- { >- /* Convert e.g. OP0 == 2048 into OP0 - 2048 == 0. */ >- if (SMALL_OPERAND (-rhs)) >- { >- *op0 = riscv_force_binary (GET_MODE (*op0), PLUS, *op0, >- GEN_INT (-rhs)); >- *op1 = const0_rtx; >- } >- } >- else >- { >- static const enum rtx_code mag_comparisons[][2] = { >- {LEU, LTU}, {GTU, GEU}, {LE, LT}, {GT, GE} >- }; >- >- /* Convert e.g. (OP0 <= 0xFFF) into (OP0 < 0x1000). */ >- for (size_t i = 0; i < ARRAY_SIZE (mag_comparisons); i++) >- { >- HOST_WIDE_INT new_rhs; >- bool increment = *code == mag_comparisons[i][0]; >- bool decrement = *code == mag_comparisons[i][1]; >- if (!increment && !decrement) >- continue; >- >- new_rhs = rhs + (increment ? 1 : -1); >- if (riscv_integer_cost (new_rhs) < riscv_integer_cost (rhs) >- && (rhs < 0) == (new_rhs < 0)) >- { >- *op1 = GEN_INT (new_rhs); >- *code = mag_comparisons[i][increment]; >- } >- break; >- } >- } >- } >- >- riscv_extend_comparands (*code, op0, op1); >- >- *op0 = force_reg (word_mode, *op0); >- if (*op1 != const0_rtx) >- *op1 = force_reg (word_mode, *op1); >-} >- >-/* Like riscv_emit_int_compare, but for floating-point comparisons. */ >- >-static void >-riscv_emit_float_compare (enum rtx_code *code, rtx *op0, rtx *op1) >-{ >- rtx tmp0, tmp1, cmp_op0 = *op0, cmp_op1 = *op1; >- enum rtx_code fp_code = *code; >- *code = NE; >- >- switch (fp_code) >- { >- case UNORDERED: >- *code = EQ; >- /* Fall through. */ >- >- case ORDERED: >- /* a == a && b == b */ >- tmp0 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op0); >- tmp1 = riscv_force_binary (word_mode, EQ, cmp_op1, cmp_op1); >- *op0 = riscv_force_binary (word_mode, AND, tmp0, tmp1); >- *op1 = const0_rtx; >- break; >- >- case UNEQ: >- /* ordered(a, b) > (a == b) */ >- *code = EQ; >- tmp0 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op0); >- tmp1 = riscv_force_binary (word_mode, EQ, cmp_op1, cmp_op1); >- *op0 = riscv_force_binary (word_mode, AND, tmp0, tmp1); >- *op1 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op1); >- break; >- >-#define UNORDERED_COMPARISON(CODE, CMP) \ >- case CODE: \ >- *code = EQ; \ >- *op0 = gen_reg_rtx (word_mode); \ >- if (GET_MODE (cmp_op0) == SFmode && TARGET_64BIT) \ >- emit_insn (gen_f##CMP##_quietsfdi4 (*op0, cmp_op0, cmp_op1)); \ >- else if (GET_MODE (cmp_op0) == SFmode) \ >- emit_insn (gen_f##CMP##_quietsfsi4 (*op0, cmp_op0, cmp_op1)); \ >- else if (GET_MODE (cmp_op0) == DFmode && TARGET_64BIT) \ >- emit_insn (gen_f##CMP##_quietdfdi4 (*op0, cmp_op0, cmp_op1)); \ >- else if (GET_MODE (cmp_op0) == DFmode) \ >- emit_insn (gen_f##CMP##_quietdfsi4 (*op0, cmp_op0, cmp_op1)); \ >- else \ >- gcc_unreachable (); \ >- *op1 = const0_rtx; \ >- break; >- >- case UNLT: >- std::swap (cmp_op0, cmp_op1); >- gcc_fallthrough (); >- >- UNORDERED_COMPARISON(UNGT, le) >- >- case UNLE: >- std::swap (cmp_op0, cmp_op1); >- gcc_fallthrough (); >- >- UNORDERED_COMPARISON(UNGE, lt) >-#undef UNORDERED_COMPARISON >- >- case NE: >- fp_code = EQ; >- *code = EQ; >- /* Fall through. */ >- >- case EQ: >- case LE: >- case LT: >- case GE: >- case GT: >- /* We have instructions for these cases. */ >- *op0 = riscv_force_binary (word_mode, fp_code, cmp_op0, cmp_op1); >- *op1 = const0_rtx; >- break; >- >- case LTGT: >- /* (a < b) | (a > b) */ >- tmp0 = riscv_force_binary (word_mode, LT, cmp_op0, cmp_op1); >- tmp1 = riscv_force_binary (word_mode, GT, cmp_op0, cmp_op1); >- *op0 = riscv_force_binary (word_mode, IOR, tmp0, tmp1); >- *op1 = const0_rtx; >- break; >- >- default: >- gcc_unreachable (); >- } >-} >- >-/* CODE-compare OP0 and OP1. Store the result in TARGET. */ >- >-void >-riscv_expand_int_scc (rtx target, enum rtx_code code, rtx op0, rtx op1) >-{ >- riscv_extend_comparands (code, &op0, &op1); >- op0 = force_reg (word_mode, op0); >- >- if (code == EQ || code == NE) >- { >- rtx zie = riscv_zero_if_equal (op0, op1); >- riscv_emit_binary (code, target, zie, const0_rtx); >- } >- else >- riscv_emit_int_order_test (code, 0, target, op0, op1); >-} >- >-/* Like riscv_expand_int_scc, but for floating-point comparisons. */ >- >-void >-riscv_expand_float_scc (rtx target, enum rtx_code code, rtx op0, rtx op1) >-{ >- riscv_emit_float_compare (&code, &op0, &op1); >- >- rtx cmp = riscv_force_binary (word_mode, code, op0, op1); >- riscv_emit_set (target, lowpart_subreg (SImode, cmp, word_mode)); >-} >- >-/* Jump to LABEL if (CODE OP0 OP1) holds. */ >- >-void >-riscv_expand_conditional_branch (rtx label, rtx_code code, rtx op0, rtx op1) >-{ >- if (FLOAT_MODE_P (GET_MODE (op1))) >- riscv_emit_float_compare (&code, &op0, &op1); >- else >- riscv_emit_int_compare (&code, &op0, &op1); >- >- rtx condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1); >- emit_jump_insn (gen_condjump (condition, label)); >-} >- >-/* If (CODE OP0 OP1) holds, move CONS to DEST; else move ALT to DEST. */ >- >-void >-riscv_expand_conditional_move (rtx dest, rtx cons, rtx alt, rtx_code code, >- rtx op0, rtx op1) >-{ >- riscv_emit_int_compare (&code, &op0, &op1); >- rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1); >- emit_insn (gen_rtx_SET (dest, gen_rtx_IF_THEN_ELSE (GET_MODE (dest), cond, >- cons, alt))); >-} >- >-/* Implement TARGET_FUNCTION_ARG_BOUNDARY. Every parameter gets at >- least PARM_BOUNDARY bits of alignment, but will be given anything up >- to PREFERRED_STACK_BOUNDARY bits if the type requires it. */ >- >-static unsigned int >-riscv_function_arg_boundary (machine_mode mode, const_tree type) >-{ >- unsigned int alignment; >- >- /* Use natural alignment if the type is not aggregate data. */ >- if (type && !AGGREGATE_TYPE_P (type)) >- alignment = TYPE_ALIGN (TYPE_MAIN_VARIANT (type)); >- else >- alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode); >- >- return MIN (PREFERRED_STACK_BOUNDARY, MAX (PARM_BOUNDARY, alignment)); >-} >- >-/* If MODE represents an argument that can be passed or returned in >- floating-point registers, return the number of registers, else 0. */ >- >-static unsigned >-riscv_pass_mode_in_fpr_p (machine_mode mode) >-{ >- if (GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FP_ARG) >- { >- if (GET_MODE_CLASS (mode) == MODE_FLOAT) >- return 1; >- >- if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) >- return 2; >- } >- >- return 0; >-} >- >-typedef struct { >- const_tree type; >- HOST_WIDE_INT offset; >-} riscv_aggregate_field; >- >-/* Identify subfields of aggregates that are candidates for passing in >- floating-point registers. */ >- >-static int >-riscv_flatten_aggregate_field (const_tree type, >- riscv_aggregate_field fields[2], >- int n, HOST_WIDE_INT offset, >- bool ignore_zero_width_bit_field_p) >-{ >- switch (TREE_CODE (type)) >- { >- case RECORD_TYPE: >- /* Can't handle incomplete types nor sizes that are not fixed. */ >- if (!COMPLETE_TYPE_P (type) >- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST >- || !tree_fits_uhwi_p (TYPE_SIZE (type))) >- return -1; >- >- for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f)) >- if (TREE_CODE (f) == FIELD_DECL) >- { >- if (!TYPE_P (TREE_TYPE (f))) >- return -1; >- >- /* The C++ front end strips zero-length bit-fields from structs. >- So we need to ignore them in the C front end to make C code >- compatible with C++ code. */ >- if (ignore_zero_width_bit_field_p >- && DECL_BIT_FIELD (f) >- && (DECL_SIZE (f) == NULL_TREE >- || integer_zerop (DECL_SIZE (f)))) >- ; >- else >- { >- HOST_WIDE_INT pos = offset + int_byte_position (f); >- n = riscv_flatten_aggregate_field (TREE_TYPE (f), >- fields, n, pos, >- ignore_zero_width_bit_field_p); >- } >- if (n < 0) >- return -1; >- } >- return n; >- >- case ARRAY_TYPE: >- { >- HOST_WIDE_INT n_elts; >- riscv_aggregate_field subfields[2]; >- tree index = TYPE_DOMAIN (type); >- tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type)); >- int n_subfields = riscv_flatten_aggregate_field (TREE_TYPE (type), >- subfields, 0, offset, >- ignore_zero_width_bit_field_p); >- >- /* Can't handle incomplete types nor sizes that are not fixed. */ >- if (n_subfields <= 0 >- || !COMPLETE_TYPE_P (type) >- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST >- || !index >- || !TYPE_MAX_VALUE (index) >- || !tree_fits_uhwi_p (TYPE_MAX_VALUE (index)) >- || !TYPE_MIN_VALUE (index) >- || !tree_fits_uhwi_p (TYPE_MIN_VALUE (index)) >- || !tree_fits_uhwi_p (elt_size)) >- return -1; >- >- n_elts = 1 + tree_to_uhwi (TYPE_MAX_VALUE (index)) >- - tree_to_uhwi (TYPE_MIN_VALUE (index)); >- gcc_assert (n_elts >= 0); >- >- for (HOST_WIDE_INT i = 0; i < n_elts; i++) >- for (int j = 0; j < n_subfields; j++) >- { >- if (n >= 2) >- return -1; >- >- fields[n] = subfields[j]; >- fields[n++].offset += i * tree_to_uhwi (elt_size); >- } >- >- return n; >- } >- >- case COMPLEX_TYPE: >- { >- /* Complex type need consume 2 field, so n must be 0. */ >- if (n != 0) >- return -1; >- >- HOST_WIDE_INT elt_size = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (type))); >- >- if (elt_size <= UNITS_PER_FP_ARG) >- { >- fields[0].type = TREE_TYPE (type); >- fields[0].offset = offset; >- fields[1].type = TREE_TYPE (type); >- fields[1].offset = offset + elt_size; >- >- return 2; >- } >- >- return -1; >- } >- >- default: >- if (n < 2 >- && ((SCALAR_FLOAT_TYPE_P (type) >- && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FP_ARG) >- || (INTEGRAL_TYPE_P (type) >- && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_WORD))) >- { >- fields[n].type = type; >- fields[n].offset = offset; >- return n + 1; >- } >- else >- return -1; >- } >-} >- >-/* Identify candidate aggregates for passing in floating-point registers. >- Candidates have at most two fields after flattening. */ >- >-static int >-riscv_flatten_aggregate_argument (const_tree type, >- riscv_aggregate_field fields[2], >- bool ignore_zero_width_bit_field_p) >-{ >- if (!type || TREE_CODE (type) != RECORD_TYPE) >- return -1; >- >- return riscv_flatten_aggregate_field (type, fields, 0, 0, >- ignore_zero_width_bit_field_p); >-} >- >-/* See whether TYPE is a record whose fields should be returned in one or >- two floating-point registers. If so, populate FIELDS accordingly. */ >- >-static unsigned >-riscv_pass_aggregate_in_fpr_pair_p (const_tree type, >- riscv_aggregate_field fields[2]) >-{ >- static int warned = 0; >- >- /* This is the old ABI, which differs for C++ and C. */ >- int n_old = riscv_flatten_aggregate_argument (type, fields, false); >- for (int i = 0; i < n_old; i++) >- if (!SCALAR_FLOAT_TYPE_P (fields[i].type)) >- { >- n_old = -1; >- break; >- } >- >- /* This is the new ABI, which is the same for C++ and C. */ >- int n_new = riscv_flatten_aggregate_argument (type, fields, true); >- for (int i = 0; i < n_new; i++) >- if (!SCALAR_FLOAT_TYPE_P (fields[i].type)) >- { >- n_new = -1; >- break; >- } >- >- if ((n_old != n_new) && (warned == 0)) >- { >- warning (0, "ABI for flattened struct with zero-length bit-fields " >- "changed in GCC 10"); >- warned = 1; >- } >- >- return n_new > 0 ? n_new : 0; >-} >- >-/* See whether TYPE is a record whose fields should be returned in one or >- floating-point register and one integer register. If so, populate >- FIELDS accordingly. */ >- >-static bool >-riscv_pass_aggregate_in_fpr_and_gpr_p (const_tree type, >- riscv_aggregate_field fields[2]) >-{ >- static int warned = 0; >- >- /* This is the old ABI, which differs for C++ and C. */ >- unsigned num_int_old = 0, num_float_old = 0; >- int n_old = riscv_flatten_aggregate_argument (type, fields, false); >- for (int i = 0; i < n_old; i++) >- { >- num_float_old += SCALAR_FLOAT_TYPE_P (fields[i].type); >- num_int_old += INTEGRAL_TYPE_P (fields[i].type); >- } >- >- /* This is the new ABI, which is the same for C++ and C. */ >- unsigned num_int_new = 0, num_float_new = 0; >- int n_new = riscv_flatten_aggregate_argument (type, fields, true); >- for (int i = 0; i < n_new; i++) >- { >- num_float_new += SCALAR_FLOAT_TYPE_P (fields[i].type); >- num_int_new += INTEGRAL_TYPE_P (fields[i].type); >- } >- >- if (((num_int_old == 1 && num_float_old == 1 >- && (num_int_old != num_int_new || num_float_old != num_float_new)) >- || (num_int_new == 1 && num_float_new == 1 >- && (num_int_old != num_int_new || num_float_old != num_float_new))) >- && (warned == 0)) >- { >- warning (0, "ABI for flattened struct with zero-length bit-fields " >- "changed in GCC 10"); >- warned = 1; >- } >- >- return num_int_new == 1 && num_float_new == 1; >-} >- >-/* Return the representation of an argument passed or returned in an FPR >- when the value has mode VALUE_MODE and the type has TYPE_MODE. The >- two modes may be different for structures like: >- >- struct __attribute__((packed)) foo { float f; } >- >- where the SFmode value "f" is passed in REGNO but the struct itself >- has mode BLKmode. */ >- >-static rtx >-riscv_pass_fpr_single (machine_mode type_mode, unsigned regno, >- machine_mode value_mode, >- HOST_WIDE_INT offset) >-{ >- rtx x = gen_rtx_REG (value_mode, regno); >- >- if (type_mode != value_mode) >- { >- x = gen_rtx_EXPR_LIST (VOIDmode, x, GEN_INT (offset)); >- x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x)); >- } >- return x; >-} >- >-/* Pass or return a composite value in the FPR pair REGNO and REGNO + 1. >- MODE is the mode of the composite. MODE1 and OFFSET1 are the mode and >- byte offset for the first value, likewise MODE2 and OFFSET2 for the >- second value. */ >- >-static rtx >-riscv_pass_fpr_pair (machine_mode mode, unsigned regno1, >- machine_mode mode1, HOST_WIDE_INT offset1, >- unsigned regno2, machine_mode mode2, >- HOST_WIDE_INT offset2) >-{ >- return gen_rtx_PARALLEL >- (mode, >- gen_rtvec (2, >- gen_rtx_EXPR_LIST (VOIDmode, >- gen_rtx_REG (mode1, regno1), >- GEN_INT (offset1)), >- gen_rtx_EXPR_LIST (VOIDmode, >- gen_rtx_REG (mode2, regno2), >- GEN_INT (offset2)))); >-} >- >-/* Fill INFO with information about a single argument, and return an >- RTL pattern to pass or return the argument. CUM is the cumulative >- state for earlier arguments. MODE is the mode of this argument and >- TYPE is its type (if known). NAMED is true if this is a named >- (fixed) argument rather than a variable one. RETURN_P is true if >- returning the argument, or false if passing the argument. */ >- >-static rtx >-riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum, >- machine_mode mode, const_tree type, bool named, >- bool return_p) >-{ >- unsigned num_bytes, num_words; >- unsigned fpr_base = return_p ? FP_RETURN : FP_ARG_FIRST; >- unsigned gpr_base = return_p ? GP_RETURN : GP_ARG_FIRST; >- unsigned alignment = riscv_function_arg_boundary (mode, type); >- >- memset (info, 0, sizeof (*info)); >- info->gpr_offset = cum->num_gprs; >- info->fpr_offset = cum->num_fprs; >- >- if (named) >- { >- riscv_aggregate_field fields[2]; >- unsigned fregno = fpr_base + info->fpr_offset; >- unsigned gregno = gpr_base + info->gpr_offset; >- >- /* Pass one- or two-element floating-point aggregates in FPRs. */ >- if ((info->num_fprs = riscv_pass_aggregate_in_fpr_pair_p (type, fields)) >- && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) >- switch (info->num_fprs) >- { >- case 1: >- return riscv_pass_fpr_single (mode, fregno, >- TYPE_MODE (fields[0].type), >- fields[0].offset); >- >- case 2: >- return riscv_pass_fpr_pair (mode, fregno, >- TYPE_MODE (fields[0].type), >- fields[0].offset, >- fregno + 1, >- TYPE_MODE (fields[1].type), >- fields[1].offset); >- >- default: >- gcc_unreachable (); >- } >- >- /* Pass real and complex floating-point numbers in FPRs. */ >- if ((info->num_fprs = riscv_pass_mode_in_fpr_p (mode)) >- && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) >- switch (GET_MODE_CLASS (mode)) >- { >- case MODE_FLOAT: >- return gen_rtx_REG (mode, fregno); >- >- case MODE_COMPLEX_FLOAT: >- return riscv_pass_fpr_pair (mode, fregno, GET_MODE_INNER (mode), 0, >- fregno + 1, GET_MODE_INNER (mode), >- GET_MODE_UNIT_SIZE (mode)); >- >- default: >- gcc_unreachable (); >- } >- >- /* Pass structs with one float and one integer in an FPR and a GPR. */ >- if (riscv_pass_aggregate_in_fpr_and_gpr_p (type, fields) >- && info->gpr_offset < MAX_ARGS_IN_REGISTERS >- && info->fpr_offset < MAX_ARGS_IN_REGISTERS) >- { >- info->num_gprs = 1; >- info->num_fprs = 1; >- >- if (!SCALAR_FLOAT_TYPE_P (fields[0].type)) >- std::swap (fregno, gregno); >- >- return riscv_pass_fpr_pair (mode, fregno, TYPE_MODE (fields[0].type), >- fields[0].offset, >- gregno, TYPE_MODE (fields[1].type), >- fields[1].offset); >- } >- } >- >- /* Work out the size of the argument. */ >- num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); >- num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >- >- /* Doubleword-aligned varargs start on an even register boundary. */ >- if (!named && num_bytes != 0 && alignment > BITS_PER_WORD) >- info->gpr_offset += info->gpr_offset & 1; >- >- /* Partition the argument between registers and stack. */ >- info->num_fprs = 0; >- info->num_gprs = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset); >- info->stack_p = (num_words - info->num_gprs) != 0; >- >- if (info->num_gprs || return_p) >- return gen_rtx_REG (mode, gpr_base + info->gpr_offset); >- >- return NULL_RTX; >-} >- >-/* Implement TARGET_FUNCTION_ARG. */ >- >-static rtx >-riscv_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) >-{ >- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >- struct riscv_arg_info info; >- >- if (arg.end_marker_p ()) >- return NULL; >- >- return riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >-} >- >-/* Implement TARGET_FUNCTION_ARG_ADVANCE. */ >- >-static void >-riscv_function_arg_advance (cumulative_args_t cum_v, >- const function_arg_info &arg) >-{ >- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >- struct riscv_arg_info info; >- >- riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >- >- /* Advance the register count. This has the effect of setting >- num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned >- argument required us to skip the final GPR and pass the whole >- argument on the stack. */ >- cum->num_fprs = info.fpr_offset + info.num_fprs; >- cum->num_gprs = info.gpr_offset + info.num_gprs; >-} >- >-/* Implement TARGET_ARG_PARTIAL_BYTES. */ >- >-static int >-riscv_arg_partial_bytes (cumulative_args_t cum, >- const function_arg_info &generic_arg) >-{ >- struct riscv_arg_info arg; >- >- riscv_get_arg_info (&arg, get_cumulative_args (cum), generic_arg.mode, >- generic_arg.type, generic_arg.named, false); >- return arg.stack_p ? arg.num_gprs * UNITS_PER_WORD : 0; >-} >- >-/* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, >- VALTYPE is the return type and MODE is VOIDmode. For libcalls, >- VALTYPE is null and MODE is the mode of the return value. */ >- >-rtx >-riscv_function_value (const_tree type, const_tree func, machine_mode mode) >-{ >- struct riscv_arg_info info; >- CUMULATIVE_ARGS args; >- >- if (type) >- { >- int unsigned_p = TYPE_UNSIGNED (type); >- >- mode = TYPE_MODE (type); >- >- /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes, >- return values, promote the mode here too. */ >- mode = promote_function_mode (type, mode, &unsigned_p, func, 1); >- } >- >- memset (&args, 0, sizeof args); >- return riscv_get_arg_info (&info, &args, mode, type, true, true); >-} >- >-/* Implement TARGET_PASS_BY_REFERENCE. */ >- >-static bool >-riscv_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg) >-{ >- HOST_WIDE_INT size = arg.type_size_in_bytes (); >- struct riscv_arg_info info; >- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >- >- /* ??? std_gimplify_va_arg_expr passes NULL for cum. Fortunately, we >- never pass variadic arguments in floating-point registers, so we can >- avoid the call to riscv_get_arg_info in this case. */ >- if (cum != NULL) >- { >- /* Don't pass by reference if we can use a floating-point register. */ >- riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >- if (info.num_fprs) >- return false; >- } >- >- /* Pass by reference if the data do not fit in two integer registers. */ >- return !IN_RANGE (size, 0, 2 * UNITS_PER_WORD); >-} >- >-/* Implement TARGET_RETURN_IN_MEMORY. */ >- >-static bool >-riscv_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED) >-{ >- CUMULATIVE_ARGS args; >- cumulative_args_t cum = pack_cumulative_args (&args); >- >- /* The rules for returning in memory are the same as for passing the >- first named argument by reference. */ >- memset (&args, 0, sizeof args); >- function_arg_info arg (const_cast<tree> (type), /*named=*/true); >- return riscv_pass_by_reference (cum, arg); >-} >- >-/* Implement TARGET_SETUP_INCOMING_VARARGS. */ >- >-static void >-riscv_setup_incoming_varargs (cumulative_args_t cum, >- const function_arg_info &arg, >- int *pretend_size ATTRIBUTE_UNUSED, int no_rtl) >-{ >- CUMULATIVE_ARGS local_cum; >- int gp_saved; >- >- /* The caller has advanced CUM up to, but not beyond, the last named >- argument. Advance a local copy of CUM past the last "real" named >- argument, to find out how many registers are left over. */ >- local_cum = *get_cumulative_args (cum); >- riscv_function_arg_advance (pack_cumulative_args (&local_cum), arg); >- >- /* Found out how many registers we need to save. */ >- gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs; >- >- if (!no_rtl && gp_saved > 0) >- { >- rtx ptr = plus_constant (Pmode, virtual_incoming_args_rtx, >- REG_PARM_STACK_SPACE (cfun->decl) >- - gp_saved * UNITS_PER_WORD); >- rtx mem = gen_frame_mem (BLKmode, ptr); >- set_mem_alias_set (mem, get_varargs_alias_set ()); >- >- move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, >- mem, gp_saved); >- } >- if (REG_PARM_STACK_SPACE (cfun->decl) == 0) >- cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD; >-} >- >-/* Handle an attribute requiring a FUNCTION_DECL; >- arguments as in struct attribute_spec.handler. */ >-static tree >-riscv_handle_fndecl_attribute (tree *node, tree name, >- tree args ATTRIBUTE_UNUSED, >- int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) >-{ >- if (TREE_CODE (*node) != FUNCTION_DECL) >- { >- warning (OPT_Wattributes, "%qE attribute only applies to functions", >- name); >- *no_add_attrs = true; >- } >- >- return NULL_TREE; >-} >- >-/* Verify type based attributes. NODE is the what the attribute is being >- applied to. NAME is the attribute name. ARGS are the attribute args. >- FLAGS gives info about the context. NO_ADD_ATTRS should be set to true if >- the attribute should be ignored. */ >- >-static tree >-riscv_handle_type_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args, >- int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) >-{ >- /* Check for an argument. */ >- if (is_attribute_p ("interrupt", name)) >- { >- if (args) >- { >- tree cst = TREE_VALUE (args); >- const char *string; >- >- if (TREE_CODE (cst) != STRING_CST) >- { >- warning (OPT_Wattributes, >- "%qE attribute requires a string argument", >- name); >- *no_add_attrs = true; >- return NULL_TREE; >- } >- >- string = TREE_STRING_POINTER (cst); >- if (strcmp (string, "user") && strcmp (string, "supervisor") >- && strcmp (string, "machine")) >- { >- warning (OPT_Wattributes, >- "argument to %qE attribute is not \"user\", \"supervisor\", or \"machine\"", >- name); >- *no_add_attrs = true; >- } >- } >- } >- >- return NULL_TREE; >-} >- >-/* Return true if function TYPE is an interrupt function. */ >-static bool >-riscv_interrupt_type_p (tree type) >-{ >- return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL; >-} >- >-/* Return true if FUNC is a naked function. */ >-static bool >-riscv_naked_function_p (tree func) >-{ >- tree func_decl = func; >- if (func == NULL_TREE) >- func_decl = current_function_decl; >- return NULL_TREE != lookup_attribute ("naked", DECL_ATTRIBUTES (func_decl)); >-} >- >-/* Implement TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS. */ >-static bool >-riscv_allocate_stack_slots_for_args () >-{ >- /* Naked functions should not allocate stack slots for arguments. */ >- return !riscv_naked_function_p (current_function_decl); >-} >- >-/* Implement TARGET_WARN_FUNC_RETURN. */ >-static bool >-riscv_warn_func_return (tree decl) >-{ >- /* Naked functions are implemented entirely in assembly, including the >- return sequence, so suppress warnings about this. */ >- return !riscv_naked_function_p (decl); >-} >- >-/* Implement TARGET_EXPAND_BUILTIN_VA_START. */ >- >-static void >-riscv_va_start (tree valist, rtx nextarg) >-{ >- nextarg = plus_constant (Pmode, nextarg, -cfun->machine->varargs_size); >- std_expand_builtin_va_start (valist, nextarg); >-} >- >-/* Make ADDR suitable for use as a call or sibcall target. */ >- >-rtx >-riscv_legitimize_call_address (rtx addr) >-{ >- if (!call_insn_operand (addr, VOIDmode)) >- { >- rtx reg = RISCV_PROLOGUE_TEMP (Pmode); >- riscv_emit_move (reg, addr); >- return reg; >- } >- return addr; >-} >- >-/* Emit straight-line code to move LENGTH bytes from SRC to DEST. >- Assume that the areas do not overlap. */ >- >-static void >-riscv_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length) >-{ >- HOST_WIDE_INT offset, delta; >- unsigned HOST_WIDE_INT bits; >- int i; >- enum machine_mode mode; >- rtx *regs; >- >- bits = MAX (BITS_PER_UNIT, >- MIN (BITS_PER_WORD, MIN (MEM_ALIGN (src), MEM_ALIGN (dest)))); >- >- mode = mode_for_size (bits, MODE_INT, 0).require (); >- delta = bits / BITS_PER_UNIT; >- >- /* Allocate a buffer for the temporary registers. */ >- regs = XALLOCAVEC (rtx, length / delta); >- >- /* Load as many BITS-sized chunks as possible. Use a normal load if >- the source has enough alignment, otherwise use left/right pairs. */ >- for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) >- { >- regs[i] = gen_reg_rtx (mode); >- riscv_emit_move (regs[i], adjust_address (src, mode, offset)); >- } >- >- /* Copy the chunks to the destination. */ >- for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) >- riscv_emit_move (adjust_address (dest, mode, offset), regs[i]); >- >- /* Mop up any left-over bytes. */ >- if (offset < length) >- { >- src = adjust_address (src, BLKmode, offset); >- dest = adjust_address (dest, BLKmode, offset); >- move_by_pieces (dest, src, length - offset, >- MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), RETURN_BEGIN); >- } >-} >- >-/* Helper function for doing a loop-based block operation on memory >- reference MEM. Each iteration of the loop will operate on LENGTH >- bytes of MEM. >- >- Create a new base register for use within the loop and point it to >- the start of MEM. Create a new memory reference that uses this >- register. Store them in *LOOP_REG and *LOOP_MEM respectively. */ >- >-static void >-riscv_adjust_block_mem (rtx mem, HOST_WIDE_INT length, >- rtx *loop_reg, rtx *loop_mem) >-{ >- *loop_reg = copy_addr_to_reg (XEXP (mem, 0)); >- >- /* Although the new mem does not refer to a known location, >- it does keep up to LENGTH bytes of alignment. */ >- *loop_mem = change_address (mem, BLKmode, *loop_reg); >- set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT)); >-} >- >-/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER >- bytes at a time. LENGTH must be at least BYTES_PER_ITER. Assume that >- the memory regions do not overlap. */ >- >-static void >-riscv_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, >- HOST_WIDE_INT bytes_per_iter) >-{ >- rtx label, src_reg, dest_reg, final_src, test; >- HOST_WIDE_INT leftover; >- >- leftover = length % bytes_per_iter; >- length -= leftover; >- >- /* Create registers and memory references for use within the loop. */ >- riscv_adjust_block_mem (src, bytes_per_iter, &src_reg, &src); >- riscv_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest); >- >- /* Calculate the value that SRC_REG should have after the last iteration >- of the loop. */ >- final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length), >- 0, 0, OPTAB_WIDEN); >- >- /* Emit the start of the loop. */ >- label = gen_label_rtx (); >- emit_label (label); >- >- /* Emit the loop body. */ >- riscv_block_move_straight (dest, src, bytes_per_iter); >- >- /* Move on to the next block. */ >- riscv_emit_move (src_reg, plus_constant (Pmode, src_reg, bytes_per_iter)); >- riscv_emit_move (dest_reg, plus_constant (Pmode, dest_reg, bytes_per_iter)); >- >- /* Emit the loop condition. */ >- test = gen_rtx_NE (VOIDmode, src_reg, final_src); >- if (Pmode == DImode) >- emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label)); >- else >- emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label)); >- >- /* Mop up any left-over bytes. */ >- if (leftover) >- riscv_block_move_straight (dest, src, leftover); >- else >- emit_insn(gen_nop ()); >-} >- >-/* Expand a cpymemsi instruction, which copies LENGTH bytes from >- memory reference SRC to memory reference DEST. */ >- >-bool >-riscv_expand_block_move (rtx dest, rtx src, rtx length) >-{ >- if (CONST_INT_P (length)) >- { >- HOST_WIDE_INT factor, align; >- >- align = MIN (MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), BITS_PER_WORD); >- factor = BITS_PER_WORD / align; >- >- if (optimize_function_for_size_p (cfun) >- && INTVAL (length) * factor * UNITS_PER_WORD > MOVE_RATIO (false)) >- return false; >- >- if (INTVAL (length) <= RISCV_MAX_MOVE_BYTES_STRAIGHT / factor) >- { >- riscv_block_move_straight (dest, src, INTVAL (length)); >- return true; >- } >- else if (optimize && align >= BITS_PER_WORD) >- { >- unsigned min_iter_words >- = RISCV_MAX_MOVE_BYTES_PER_LOOP_ITER / UNITS_PER_WORD; >- unsigned iter_words = min_iter_words; >- HOST_WIDE_INT bytes = INTVAL (length), words = bytes / UNITS_PER_WORD; >- >- /* Lengthen the loop body if it shortens the tail. */ >- for (unsigned i = min_iter_words; i < min_iter_words * 2 - 1; i++) >- { >- unsigned cur_cost = iter_words + words % iter_words; >- unsigned new_cost = i + words % i; >- if (new_cost <= cur_cost) >- iter_words = i; >- } >- >- riscv_block_move_loop (dest, src, bytes, iter_words * UNITS_PER_WORD); >- return true; >- } >- } >- return false; >-} >- >-/* Print symbolic operand OP, which is part of a HIGH or LO_SUM >- in context CONTEXT. HI_RELOC indicates a high-part reloc. */ >- >-static void >-riscv_print_operand_reloc (FILE *file, rtx op, bool hi_reloc) >-{ >- const char *reloc; >- >- switch (riscv_classify_symbolic_expression (op)) >- { >- case SYMBOL_ABSOLUTE: >- reloc = hi_reloc ? "%hi" : "%lo"; >- break; >- >- case SYMBOL_PCREL: >- reloc = hi_reloc ? "%pcrel_hi" : "%pcrel_lo"; >- break; >- >- case SYMBOL_TLS_LE: >- reloc = hi_reloc ? "%tprel_hi" : "%tprel_lo"; >- break; >- >- default: >- output_operand_lossage ("invalid use of '%%%c'", hi_reloc ? 'h' : 'R'); >- return; >- } >- >- fprintf (file, "%s(", reloc); >- output_addr_const (file, riscv_strip_unspec_address (op)); >- fputc (')', file); >-} >- >-/* Return true if the .AQ suffix should be added to an AMO to implement the >- acquire portion of memory model MODEL. */ >- >-static bool >-riscv_memmodel_needs_amo_acquire (enum memmodel model) >-{ >- switch (model) >- { >- case MEMMODEL_ACQ_REL: >- case MEMMODEL_SEQ_CST: >- case MEMMODEL_SYNC_SEQ_CST: >- case MEMMODEL_ACQUIRE: >- case MEMMODEL_CONSUME: >- case MEMMODEL_SYNC_ACQUIRE: >- return true; >- >- case MEMMODEL_RELEASE: >- case MEMMODEL_SYNC_RELEASE: >- case MEMMODEL_RELAXED: >- return false; >- >- default: >- gcc_unreachable (); >- } >-} >- >-/* Return true if a FENCE should be emitted to before a memory access to >- implement the release portion of memory model MODEL. */ >- >-static bool >-riscv_memmodel_needs_release_fence (enum memmodel model) >-{ >- switch (model) >- { >- case MEMMODEL_ACQ_REL: >- case MEMMODEL_SEQ_CST: >- case MEMMODEL_SYNC_SEQ_CST: >- case MEMMODEL_RELEASE: >- case MEMMODEL_SYNC_RELEASE: >- return true; >- >- case MEMMODEL_ACQUIRE: >- case MEMMODEL_CONSUME: >- case MEMMODEL_SYNC_ACQUIRE: >- case MEMMODEL_RELAXED: >- return false; >- >- default: >- gcc_unreachable (); >- } >-} >- >-/* Implement TARGET_PRINT_OPERAND. The RISCV-specific operand codes are: >- >- 'h' Print the high-part relocation associated with OP, after stripping >- any outermost HIGH. >- 'R' Print the low-part relocation associated with OP. >- 'C' Print the integer branch condition for comparison OP. >- 'A' Print the atomic operation suffix for memory model OP. >- 'F' Print a FENCE if the memory model requires a release. >- 'z' Print x0 if OP is zero, otherwise print OP normally. >- 'i' Print i if the operand is not a register. */ >- >-static void >-riscv_print_operand (FILE *file, rtx op, int letter) >-{ >- machine_mode mode = GET_MODE (op); >- enum rtx_code code = GET_CODE (op); >- >- switch (letter) >- { >- case 'h': >- if (code == HIGH) >- op = XEXP (op, 0); >- riscv_print_operand_reloc (file, op, true); >- break; >- >- case 'R': >- riscv_print_operand_reloc (file, op, false); >- break; >- >- case 'C': >- /* The RTL names match the instruction names. */ >- fputs (GET_RTX_NAME (code), file); >- break; >- >- case 'A': >- if (riscv_memmodel_needs_amo_acquire ((enum memmodel) INTVAL (op))) >- fputs (".aq", file); >- break; >- >- case 'F': >- if (riscv_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) >- fputs ("fence iorw,ow; ", file); >- break; >- >- case 'i': >- if (code != REG) >- fputs ("i", file); >- break; >- >- default: >- switch (code) >- { >- case REG: >- if (letter && letter != 'z') >- output_operand_lossage ("invalid use of '%%%c'", letter); >- fprintf (file, "%s", reg_names[REGNO (op)]); >- break; >- >- case MEM: >- if (letter && letter != 'z') >- output_operand_lossage ("invalid use of '%%%c'", letter); >- else >- output_address (mode, XEXP (op, 0)); >- break; >- >- default: >- if (letter == 'z' && op == CONST0_RTX (GET_MODE (op))) >- fputs (reg_names[GP_REG_FIRST], file); >- else if (letter && letter != 'z') >- output_operand_lossage ("invalid use of '%%%c'", letter); >- else >- output_addr_const (file, riscv_strip_unspec_address (op)); >- break; >- } >- } >-} >- >-/* Implement TARGET_PRINT_OPERAND_ADDRESS. */ >- >-static void >-riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx x) >-{ >- struct riscv_address_info addr; >- >- if (riscv_classify_address (&addr, x, word_mode, true)) >- switch (addr.type) >- { >- case ADDRESS_REG: >- riscv_print_operand (file, addr.offset, 0); >- fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); >- return; >- >- case ADDRESS_LO_SUM: >- riscv_print_operand_reloc (file, addr.offset, false); >- fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); >- return; >- >- case ADDRESS_CONST_INT: >- output_addr_const (file, x); >- fprintf (file, "(%s)", reg_names[GP_REG_FIRST]); >- return; >- >- case ADDRESS_SYMBOLIC: >- output_addr_const (file, riscv_strip_unspec_address (x)); >- return; >- } >- gcc_unreachable (); >-} >- >-static bool >-riscv_size_ok_for_small_data_p (int size) >-{ >- return g_switch_value && IN_RANGE (size, 1, g_switch_value); >-} >- >-/* Return true if EXP should be placed in the small data section. */ >- >-static bool >-riscv_in_small_data_p (const_tree x) >-{ >- if (TREE_CODE (x) == STRING_CST || TREE_CODE (x) == FUNCTION_DECL) >- return false; >- >- if (TREE_CODE (x) == VAR_DECL && DECL_SECTION_NAME (x)) >- { >- const char *sec = DECL_SECTION_NAME (x); >- return strcmp (sec, ".sdata") == 0 || strcmp (sec, ".sbss") == 0; >- } >- >- return riscv_size_ok_for_small_data_p (int_size_in_bytes (TREE_TYPE (x))); >-} >- >-/* Switch to the appropriate section for output of DECL. */ >- >-static section * >-riscv_select_section (tree decl, int reloc, >- unsigned HOST_WIDE_INT align) >-{ >- switch (categorize_decl_for_section (decl, reloc)) >- { >- case SECCAT_SRODATA: >- return get_named_section (decl, ".srodata", reloc); >- >- default: >- return default_elf_select_section (decl, reloc, align); >- } >-} >- >-/* Switch to the appropriate section for output of DECL. */ >- >-static void >-riscv_unique_section (tree decl, int reloc) >-{ >- const char *prefix = NULL; >- bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP; >- >- switch (categorize_decl_for_section (decl, reloc)) >- { >- case SECCAT_SRODATA: >- prefix = one_only ? ".sr" : ".srodata"; >- break; >- >- default: >- break; >- } >- if (prefix) >- { >- const char *name, *linkonce; >- char *string; >- >- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); >- name = targetm.strip_name_encoding (name); >- >- /* If we're using one_only, then there needs to be a .gnu.linkonce >- prefix to the section name. */ >- linkonce = one_only ? ".gnu.linkonce" : ""; >- >- string = ACONCAT ((linkonce, prefix, ".", name, NULL)); >- >- set_decl_section_name (decl, string); >- return; >- } >- default_unique_section (decl, reloc); >-} >- >-/* Return a section for X, handling small data. */ >- >-static section * >-riscv_elf_select_rtx_section (machine_mode mode, rtx x, >- unsigned HOST_WIDE_INT align) >-{ >- section *s = default_elf_select_rtx_section (mode, x, align); >- >- if (riscv_size_ok_for_small_data_p (GET_MODE_SIZE (mode))) >- { >- if (strncmp (s->named.name, ".rodata.cst", strlen (".rodata.cst")) == 0) >- { >- /* Rename .rodata.cst* to .srodata.cst*. */ >- char *name = (char *) alloca (strlen (s->named.name) + 2); >- sprintf (name, ".s%s", s->named.name + 1); >- return get_section (name, s->named.common.flags, NULL); >- } >- >- if (s == data_section) >- return sdata_section; >- } >- >- return s; >-} >- >-/* Make the last instruction frame-related and note that it performs >- the operation described by FRAME_PATTERN. */ >- >-static void >-riscv_set_frame_expr (rtx frame_pattern) >-{ >- rtx insn; >- >- insn = get_last_insn (); >- RTX_FRAME_RELATED_P (insn) = 1; >- REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, >- frame_pattern, >- REG_NOTES (insn)); >-} >- >-/* Return a frame-related rtx that stores REG at MEM. >- REG must be a single register. */ >- >-static rtx >-riscv_frame_set (rtx mem, rtx reg) >-{ >- rtx set = gen_rtx_SET (mem, reg); >- RTX_FRAME_RELATED_P (set) = 1; >- return set; >-} >- >-/* Return true if the current function must save register REGNO. */ >- >-static bool >-riscv_save_reg_p (unsigned int regno) >-{ >- bool call_saved = !global_regs[regno] && !call_used_or_fixed_reg_p (regno); >- bool might_clobber = crtl->saves_all_registers >- || df_regs_ever_live_p (regno); >- >- if (call_saved && might_clobber) >- return true; >- >- if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) >- return true; >- >- if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return) >- return true; >- >- /* If this is an interrupt handler, then must save extra registers. */ >- if (cfun->machine->interrupt_handler_p) >- { >- /* zero register is always zero. */ >- if (regno == GP_REG_FIRST) >- return false; >- >- /* The function will return the stack pointer to its original value. */ >- if (regno == STACK_POINTER_REGNUM) >- return false; >- >- /* By convention, we assume that gp and tp are safe. */ >- if (regno == GP_REGNUM || regno == THREAD_POINTER_REGNUM) >- return false; >- >- /* We must save every register used in this function. If this is not a >- leaf function, then we must save all temporary registers. */ >- if (df_regs_ever_live_p (regno) >- || (!crtl->is_leaf && call_used_or_fixed_reg_p (regno))) >- return true; >- } >- >- return false; >-} >- >-/* Determine whether to call GPR save/restore routines. */ >-static bool >-riscv_use_save_libcall (const struct riscv_frame_info *frame) >-{ >- if (!TARGET_SAVE_RESTORE || crtl->calls_eh_return || frame_pointer_needed >- || cfun->machine->interrupt_handler_p) >- return false; >- >- return frame->save_libcall_adjustment != 0; >-} >- >-/* Determine which GPR save/restore routine to call. */ >- >-static unsigned >-riscv_save_libcall_count (unsigned mask) >-{ >- for (unsigned n = GP_REG_LAST; n > GP_REG_FIRST; n--) >- if (BITSET_P (mask, n)) >- return CALLEE_SAVED_REG_NUMBER (n) + 1; >- abort (); >-} >- >-/* Populate the current function's riscv_frame_info structure. >- >- RISC-V stack frames grown downward. High addresses are at the top. >- >- +-------------------------------+ >- | | >- | incoming stack arguments | >- | | >- +-------------------------------+ <-- incoming stack pointer >- | | >- | callee-allocated save area | >- | for arguments that are | >- | split between registers and | >- | the stack | >- | | >- +-------------------------------+ <-- arg_pointer_rtx >- | | >- | callee-allocated save area | >- | for register varargs | >- | | >- +-------------------------------+ <-- hard_frame_pointer_rtx; >- | | stack_pointer_rtx + gp_sp_offset >- | GPR save area | + UNITS_PER_WORD >- | | >- +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset >- | | + UNITS_PER_HWVALUE >- | FPR save area | >- | | >- +-------------------------------+ <-- frame_pointer_rtx (virtual) >- | | >- | local variables | >- | | >- P +-------------------------------+ >- | | >- | outgoing stack arguments | >- | | >- +-------------------------------+ <-- stack_pointer_rtx >- >- Dynamic stack allocations such as alloca insert data at point P. >- They decrease stack_pointer_rtx but leave frame_pointer_rtx and >- hard_frame_pointer_rtx unchanged. */ >- >-static HOST_WIDE_INT riscv_first_stack_step (struct riscv_frame_info *frame); >- >-static void >-riscv_compute_frame_info (void) >-{ >- struct riscv_frame_info *frame; >- HOST_WIDE_INT offset; >- bool interrupt_save_t1 = false; >- unsigned int regno, i, num_x_saved = 0, num_f_saved = 0; >- >- frame = &cfun->machine->frame; >- >- /* In an interrupt function, if we have a large frame, then we need to >- save/restore t1. We check for this before clearing the frame struct. */ >- if (cfun->machine->interrupt_handler_p) >- { >- HOST_WIDE_INT step1 = riscv_first_stack_step (frame); >- if (! SMALL_OPERAND (frame->total_size - step1)) >- interrupt_save_t1 = true; >- } >- >- memset (frame, 0, sizeof (*frame)); >- >- if (!cfun->machine->naked_p) >- { >- /* Find out which GPRs we need to save. */ >- for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >- if (riscv_save_reg_p (regno) >- || (interrupt_save_t1 && (regno == T1_REGNUM))) >- frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; >- >- /* If this function calls eh_return, we must also save and restore the >- EH data registers. */ >- if (crtl->calls_eh_return) >- for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++) >- frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; >- >- /* Find out which FPRs we need to save. This loop must iterate over >- the same space as its companion in riscv_for_each_saved_reg. */ >- if (TARGET_HARD_FLOAT) >- for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >- if (riscv_save_reg_p (regno)) >- frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++; >- } >- >- /* At the bottom of the frame are any outgoing stack arguments. */ >- offset = RISCV_STACK_ALIGN (crtl->outgoing_args_size); >- /* Next are local stack variables. */ >- offset += RISCV_STACK_ALIGN (get_frame_size ()); >- /* The virtual frame pointer points above the local variables. */ >- frame->frame_pointer_offset = offset; >- /* Next are the callee-saved FPRs. */ >- if (frame->fmask) >- offset += RISCV_STACK_ALIGN (num_f_saved * UNITS_PER_FP_REG); >- frame->fp_sp_offset = offset - UNITS_PER_FP_REG; >- /* Next are the callee-saved GPRs. */ >- if (frame->mask) >- { >- unsigned x_save_size = RISCV_STACK_ALIGN (num_x_saved * UNITS_PER_WORD); >- unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask); >- >- /* Only use save/restore routines if they don't alter the stack size. */ >- if (RISCV_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) == x_save_size) >- { >- /* Libcall saves/restores 3 registers at once, so we need to >- allocate 12 bytes for callee-saved register. */ >- if (TARGET_RVE) >- x_save_size = 3 * UNITS_PER_WORD; >- >- frame->save_libcall_adjustment = x_save_size; >- } >- >- offset += x_save_size; >- } >- frame->gp_sp_offset = offset - UNITS_PER_WORD; >- /* The hard frame pointer points above the callee-saved GPRs. */ >- frame->hard_frame_pointer_offset = offset; >- /* Above the hard frame pointer is the callee-allocated varags save area. */ >- offset += RISCV_STACK_ALIGN (cfun->machine->varargs_size); >- /* Next is the callee-allocated area for pretend stack arguments. */ >- offset += RISCV_STACK_ALIGN (crtl->args.pretend_args_size); >- /* Arg pointer must be below pretend args, but must be above alignment >- padding. */ >- frame->arg_pointer_offset = offset - crtl->args.pretend_args_size; >- frame->total_size = offset; >- /* Next points the incoming stack pointer and any incoming arguments. */ >- >- /* Only use save/restore routines when the GPRs are atop the frame. */ >- if (frame->hard_frame_pointer_offset != frame->total_size) >- frame->save_libcall_adjustment = 0; >-} >- >-/* Make sure that we're not trying to eliminate to the wrong hard frame >- pointer. */ >- >-static bool >-riscv_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) >-{ >- return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM); >-} >- >-/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer >- or argument pointer. TO is either the stack pointer or hard frame >- pointer. */ >- >-HOST_WIDE_INT >-riscv_initial_elimination_offset (int from, int to) >-{ >- HOST_WIDE_INT src, dest; >- >- riscv_compute_frame_info (); >- >- if (to == HARD_FRAME_POINTER_REGNUM) >- dest = cfun->machine->frame.hard_frame_pointer_offset; >- else if (to == STACK_POINTER_REGNUM) >- dest = 0; /* The stack pointer is the base of all offsets, hence 0. */ >- else >- gcc_unreachable (); >- >- if (from == FRAME_POINTER_REGNUM) >- src = cfun->machine->frame.frame_pointer_offset; >- else if (from == ARG_POINTER_REGNUM) >- src = cfun->machine->frame.arg_pointer_offset; >- else >- gcc_unreachable (); >- >- return src - dest; >-} >- >-/* Implement RETURN_ADDR_RTX. We do not support moving back to a >- previous frame. */ >- >-rtx >-riscv_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) >-{ >- if (count != 0) >- return const0_rtx; >- >- return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM); >-} >- >-/* Emit code to change the current function's return address to >- ADDRESS. SCRATCH is available as a scratch register, if needed. >- ADDRESS and SCRATCH are both word-mode GPRs. */ >- >-void >-riscv_set_return_address (rtx address, rtx scratch) >-{ >- rtx slot_address; >- >- gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)); >- slot_address = riscv_add_offset (scratch, stack_pointer_rtx, >- cfun->machine->frame.gp_sp_offset); >- riscv_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address); >-} >- >-/* A function to save or store a register. The first argument is the >- register and the second is the stack slot. */ >-typedef void (*riscv_save_restore_fn) (rtx, rtx); >- >-/* Use FN to save or restore register REGNO. MODE is the register's >- mode and OFFSET is the offset of its save slot from the current >- stack pointer. */ >- >-static void >-riscv_save_restore_reg (machine_mode mode, int regno, >- HOST_WIDE_INT offset, riscv_save_restore_fn fn) >-{ >- rtx mem; >- >- mem = gen_frame_mem (mode, plus_constant (Pmode, stack_pointer_rtx, offset)); >- fn (gen_rtx_REG (mode, regno), mem); >-} >- >-/* Call FN for each register that is saved by the current function. >- SP_OFFSET is the offset of the current stack pointer from the start >- of the frame. */ >- >-static void >-riscv_for_each_saved_reg (HOST_WIDE_INT sp_offset, riscv_save_restore_fn fn, >- bool epilogue, bool maybe_eh_return) >-{ >- HOST_WIDE_INT offset; >- >- /* Save the link register and s-registers. */ >- offset = cfun->machine->frame.gp_sp_offset - sp_offset; >- for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >- if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >- { >- bool handle_reg = TRUE; >- >- /* If this is a normal return in a function that calls the eh_return >- builtin, then do not restore the eh return data registers as that >- would clobber the return value. But we do still need to save them >- in the prologue, and restore them for an exception return, so we >- need special handling here. */ >- if (epilogue && !maybe_eh_return && crtl->calls_eh_return) >- { >- unsigned int i, regnum; >- >- for (i = 0; (regnum = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; >- i++) >- if (regno == regnum) >- { >- handle_reg = FALSE; >- break; >- } >- } >- >- if (handle_reg) >- riscv_save_restore_reg (word_mode, regno, offset, fn); >- offset -= UNITS_PER_WORD; >- } >- >- /* This loop must iterate over the same space as its companion in >- riscv_compute_frame_info. */ >- offset = cfun->machine->frame.fp_sp_offset - sp_offset; >- for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >- if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) >- { >- machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode; >- >- riscv_save_restore_reg (mode, regno, offset, fn); >- offset -= GET_MODE_SIZE (mode); >- } >-} >- >-/* Save register REG to MEM. Make the instruction frame-related. */ >- >-static void >-riscv_save_reg (rtx reg, rtx mem) >-{ >- riscv_emit_move (mem, reg); >- riscv_set_frame_expr (riscv_frame_set (mem, reg)); >-} >- >-/* Restore register REG from MEM. */ >- >-static void >-riscv_restore_reg (rtx reg, rtx mem) >-{ >- rtx insn = riscv_emit_move (reg, mem); >- rtx dwarf = NULL_RTX; >- dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); >- >- if (epilogue_cfa_sp_offset && REGNO (reg) == HARD_FRAME_POINTER_REGNUM) >- { >- rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >- GEN_INT (epilogue_cfa_sp_offset)); >- dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >- } >- >- REG_NOTES (insn) = dwarf; >- RTX_FRAME_RELATED_P (insn) = 1; >-} >- >-/* For stack frames that can't be allocated with a single ADDI instruction, >- compute the best value to initially allocate. It must at a minimum >- allocate enough space to spill the callee-saved registers. If TARGET_RVC, >- try to pick a value that will allow compression of the register saves >- without adding extra instructions. */ >- >-static HOST_WIDE_INT >-riscv_first_stack_step (struct riscv_frame_info *frame) >-{ >- if (SMALL_OPERAND (frame->total_size)) >- return frame->total_size; >- >- HOST_WIDE_INT min_first_step = >- RISCV_STACK_ALIGN (frame->total_size - frame->fp_sp_offset); >- HOST_WIDE_INT max_first_step = IMM_REACH / 2 - PREFERRED_STACK_BOUNDARY / 8; >- HOST_WIDE_INT min_second_step = frame->total_size - max_first_step; >- gcc_assert (min_first_step <= max_first_step); >- >- /* As an optimization, use the least-significant bits of the total frame >- size, so that the second adjustment step is just LUI + ADD. */ >- if (!SMALL_OPERAND (min_second_step) >- && frame->total_size % IMM_REACH < IMM_REACH / 2 >- && frame->total_size % IMM_REACH >= min_first_step) >- return frame->total_size % IMM_REACH; >- >- if (TARGET_RVC) >- { >- /* If we need two subtracts, and one is small enough to allow compressed >- loads and stores, then put that one first. */ >- if (IN_RANGE (min_second_step, 0, >- (TARGET_64BIT ? SDSP_REACH : SWSP_REACH))) >- return MAX (min_second_step, min_first_step); >- >- /* If we need LUI + ADDI + ADD for the second adjustment step, then start >- with the minimum first step, so that we can get compressed loads and >- stores. */ >- else if (!SMALL_OPERAND (min_second_step)) >- return min_first_step; >- } >- >- return max_first_step; >-} >- >-static rtx >-riscv_adjust_libcall_cfi_prologue () >-{ >- rtx dwarf = NULL_RTX; >- rtx adjust_sp_rtx, reg, mem, insn; >- int saved_size = cfun->machine->frame.save_libcall_adjustment; >- int offset; >- >- for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >- if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >- { >- /* The save order is ra, s0, s1, s2 to s11. */ >- if (regno == RETURN_ADDR_REGNUM) >- offset = saved_size - UNITS_PER_WORD; >- else if (regno == S0_REGNUM) >- offset = saved_size - UNITS_PER_WORD * 2; >- else if (regno == S1_REGNUM) >- offset = saved_size - UNITS_PER_WORD * 3; >- else >- offset = saved_size - ((regno - S2_REGNUM + 4) * UNITS_PER_WORD); >- >- reg = gen_rtx_REG (SImode, regno); >- mem = gen_frame_mem (SImode, plus_constant (Pmode, >- stack_pointer_rtx, >- offset)); >- >- insn = gen_rtx_SET (mem, reg); >- dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf); >- } >- >- /* Debug info for adjust sp. */ >- adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx, >- stack_pointer_rtx, GEN_INT (-saved_size)); >- dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, >- dwarf); >- return dwarf; >-} >- >-static void >-riscv_emit_stack_tie (void) >-{ >- if (Pmode == SImode) >- emit_insn (gen_stack_tiesi (stack_pointer_rtx, hard_frame_pointer_rtx)); >- else >- emit_insn (gen_stack_tiedi (stack_pointer_rtx, hard_frame_pointer_rtx)); >-} >- >-/* Expand the "prologue" pattern. */ >- >-void >-riscv_expand_prologue (void) >-{ >- struct riscv_frame_info *frame = &cfun->machine->frame; >- HOST_WIDE_INT size = frame->total_size; >- unsigned mask = frame->mask; >- rtx insn; >- >- if (flag_stack_usage_info) >- current_function_static_stack_size = size; >- >- if (cfun->machine->naked_p) >- return; >- >- /* When optimizing for size, call a subroutine to save the registers. */ >- if (riscv_use_save_libcall (frame)) >- { >- rtx dwarf = NULL_RTX; >- dwarf = riscv_adjust_libcall_cfi_prologue (); >- >- size -= frame->save_libcall_adjustment; >- insn = emit_insn (riscv_gen_gpr_save_insn (frame)); >- frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ >- >- RTX_FRAME_RELATED_P (insn) = 1; >- REG_NOTES (insn) = dwarf; >- } >- >- /* Save the registers. */ >- if ((frame->mask | frame->fmask) != 0) >- { >- HOST_WIDE_INT step1 = MIN (size, riscv_first_stack_step (frame)); >- >- insn = gen_add3_insn (stack_pointer_rtx, >- stack_pointer_rtx, >- GEN_INT (-step1)); >- RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >- size -= step1; >- riscv_for_each_saved_reg (size, riscv_save_reg, false, false); >- } >- >- frame->mask = mask; /* Undo the above fib. */ >- >- /* Set up the frame pointer, if we're using one. */ >- if (frame_pointer_needed) >- { >- insn = gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, >- GEN_INT (frame->hard_frame_pointer_offset - size)); >- RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >- >- riscv_emit_stack_tie (); >- } >- >- /* Allocate the rest of the frame. */ >- if (size > 0) >- { >- if (SMALL_OPERAND (-size)) >- { >- insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >- GEN_INT (-size)); >- RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >- } >- else >- { >- riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), GEN_INT (-size)); >- emit_insn (gen_add3_insn (stack_pointer_rtx, >- stack_pointer_rtx, >- RISCV_PROLOGUE_TEMP (Pmode))); >- >- /* Describe the effect of the previous instructions. */ >- insn = plus_constant (Pmode, stack_pointer_rtx, -size); >- insn = gen_rtx_SET (stack_pointer_rtx, insn); >- riscv_set_frame_expr (insn); >- } >- } >-} >- >-static rtx >-riscv_adjust_libcall_cfi_epilogue () >-{ >- rtx dwarf = NULL_RTX; >- rtx adjust_sp_rtx, reg; >- int saved_size = cfun->machine->frame.save_libcall_adjustment; >- >- /* Debug info for adjust sp. */ >- adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx, >- stack_pointer_rtx, GEN_INT (saved_size)); >- dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, >- dwarf); >- >- for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >- if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >- { >- reg = gen_rtx_REG (SImode, regno); >- dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); >- } >- >- return dwarf; >-} >- >-/* Expand an "epilogue", "sibcall_epilogue", or "eh_return_internal" pattern; >- style says which. */ >- >-void >-riscv_expand_epilogue (int style) >-{ >- /* Split the frame into two. STEP1 is the amount of stack we should >- deallocate before restoring the registers. STEP2 is the amount we >- should deallocate afterwards. >- >- Start off by assuming that no registers need to be restored. */ >- struct riscv_frame_info *frame = &cfun->machine->frame; >- unsigned mask = frame->mask; >- HOST_WIDE_INT step1 = frame->total_size; >- HOST_WIDE_INT step2 = 0; >- bool use_restore_libcall = ((style == NORMAL_RETURN) >- && riscv_use_save_libcall (frame)); >- rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); >- rtx insn; >- >- /* We need to add memory barrier to prevent read from deallocated stack. */ >- bool need_barrier_p = (get_frame_size () >- + cfun->machine->frame.arg_pointer_offset) != 0; >- >- if (cfun->machine->naked_p) >- { >- gcc_assert (style == NORMAL_RETURN); >- >- emit_jump_insn (gen_return ()); >- >- return; >- } >- >- if ((style == NORMAL_RETURN) && riscv_can_use_return_insn ()) >- { >- emit_jump_insn (gen_return ()); >- return; >- } >- >- /* Reset the epilogue cfa info before starting to emit the epilogue. */ >- epilogue_cfa_sp_offset = 0; >- >- /* Move past any dynamic stack allocations. */ >- if (cfun->calls_alloca) >- { >- /* Emit a barrier to prevent loads from a deallocated stack. */ >- riscv_emit_stack_tie (); >- need_barrier_p = false; >- >- rtx adjust = GEN_INT (-frame->hard_frame_pointer_offset); >- if (!SMALL_OPERAND (INTVAL (adjust))) >- { >- riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), adjust); >- adjust = RISCV_PROLOGUE_TEMP (Pmode); >- } >- >- insn = emit_insn ( >- gen_add3_insn (stack_pointer_rtx, hard_frame_pointer_rtx, >- adjust)); >- >- rtx dwarf = NULL_RTX; >- rtx cfa_adjust_value = gen_rtx_PLUS ( >- Pmode, hard_frame_pointer_rtx, >- GEN_INT (-frame->hard_frame_pointer_offset)); >- rtx cfa_adjust_rtx = gen_rtx_SET (stack_pointer_rtx, cfa_adjust_value); >- dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, cfa_adjust_rtx, dwarf); >- RTX_FRAME_RELATED_P (insn) = 1; >- >- REG_NOTES (insn) = dwarf; >- } >- >- /* If we need to restore registers, deallocate as much stack as >- possible in the second step without going out of range. */ >- if ((frame->mask | frame->fmask) != 0) >- { >- step2 = riscv_first_stack_step (frame); >- step1 -= step2; >- } >- >- /* Set TARGET to BASE + STEP1. */ >- if (step1 > 0) >- { >- /* Emit a barrier to prevent loads from a deallocated stack. */ >- riscv_emit_stack_tie (); >- need_barrier_p = false; >- >- /* Get an rtx for STEP1 that we can add to BASE. */ >- rtx adjust = GEN_INT (step1); >- if (!SMALL_OPERAND (step1)) >- { >- riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), adjust); >- adjust = RISCV_PROLOGUE_TEMP (Pmode); >- } >- >- insn = emit_insn ( >- gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, adjust)); >- >- rtx dwarf = NULL_RTX; >- rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >- GEN_INT (step2)); >- >- dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >- RTX_FRAME_RELATED_P (insn) = 1; >- >- REG_NOTES (insn) = dwarf; >- } >- else if (frame_pointer_needed) >- { >- /* Tell riscv_restore_reg to emit dwarf to redefine CFA when restoring >- old value of FP. */ >- epilogue_cfa_sp_offset = step2; >- } >- >- if (use_restore_libcall) >- frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ >- >- /* Restore the registers. */ >- riscv_for_each_saved_reg (frame->total_size - step2, riscv_restore_reg, >- true, style == EXCEPTION_RETURN); >- >- if (use_restore_libcall) >- { >- frame->mask = mask; /* Undo the above fib. */ >- gcc_assert (step2 >= frame->save_libcall_adjustment); >- step2 -= frame->save_libcall_adjustment; >- } >- >- if (need_barrier_p) >- riscv_emit_stack_tie (); >- >- /* Deallocate the final bit of the frame. */ >- if (step2 > 0) >- { >- insn = emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >- GEN_INT (step2))); >- >- rtx dwarf = NULL_RTX; >- rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >- const0_rtx); >- dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >- RTX_FRAME_RELATED_P (insn) = 1; >- >- REG_NOTES (insn) = dwarf; >- } >- >- if (use_restore_libcall) >- { >- rtx dwarf = riscv_adjust_libcall_cfi_epilogue (); >- insn = emit_insn (gen_gpr_restore (GEN_INT (riscv_save_libcall_count (mask)))); >- RTX_FRAME_RELATED_P (insn) = 1; >- REG_NOTES (insn) = dwarf; >- >- emit_jump_insn (gen_gpr_restore_return (ra)); >- return; >- } >- >- /* Add in the __builtin_eh_return stack adjustment. */ >- if ((style == EXCEPTION_RETURN) && crtl->calls_eh_return) >- emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >- EH_RETURN_STACKADJ_RTX)); >- >- /* Return from interrupt. */ >- if (cfun->machine->interrupt_handler_p) >- { >- enum riscv_privilege_levels mode = cfun->machine->interrupt_mode; >- >- gcc_assert (mode != UNKNOWN_MODE); >- >- if (mode == MACHINE_MODE) >- emit_jump_insn (gen_riscv_mret ()); >- else if (mode == SUPERVISOR_MODE) >- emit_jump_insn (gen_riscv_sret ()); >- else >- emit_jump_insn (gen_riscv_uret ()); >- } >- else if (style != SIBCALL_RETURN) >- emit_jump_insn (gen_simple_return_internal (ra)); >-} >- >-/* Implement EPILOGUE_USES. */ >- >-bool >-riscv_epilogue_uses (unsigned int regno) >-{ >- if (regno == RETURN_ADDR_REGNUM) >- return true; >- >- if (epilogue_completed && cfun->machine->interrupt_handler_p) >- { >- /* An interrupt function restores temp regs, so we must indicate that >- they are live at function end. */ >- if (df_regs_ever_live_p (regno) >- || (!crtl->is_leaf && call_used_or_fixed_reg_p (regno))) >- return true; >- } >- >- return false; >-} >- >-/* Return nonzero if this function is known to have a null epilogue. >- This allows the optimizer to omit jumps to jumps if no stack >- was created. */ >- >-bool >-riscv_can_use_return_insn (void) >-{ >- return (reload_completed && cfun->machine->frame.total_size == 0 >- && ! cfun->machine->interrupt_handler_p); >-} >- >-/* Given that there exists at least one variable that is set (produced) >- by OUT_INSN and read (consumed) by IN_INSN, return true iff >- IN_INSN represents one or more memory store operations and none of >- the variables set by OUT_INSN is used by IN_INSN as the address of a >- store operation. If either IN_INSN or OUT_INSN does not represent >- a "single" RTL SET expression (as loosely defined by the >- implementation of the single_set function) or a PARALLEL with only >- SETs, CLOBBERs, and USEs inside, this function returns false. >- >- Borrowed from rs6000, riscv_store_data_bypass_p checks for certain >- conditions that result in assertion failures in the generic >- store_data_bypass_p function and returns FALSE in such cases. >- >- This is required to make -msave-restore work with the sifive-7 >- pipeline description. */ >- >-bool >-riscv_store_data_bypass_p (rtx_insn *out_insn, rtx_insn *in_insn) >-{ >- rtx out_set, in_set; >- rtx out_pat, in_pat; >- rtx out_exp, in_exp; >- int i, j; >- >- in_set = single_set (in_insn); >- if (in_set) >- { >- if (MEM_P (SET_DEST (in_set))) >- { >- out_set = single_set (out_insn); >- if (!out_set) >- { >- out_pat = PATTERN (out_insn); >- if (GET_CODE (out_pat) == PARALLEL) >- { >- for (i = 0; i < XVECLEN (out_pat, 0); i++) >- { >- out_exp = XVECEXP (out_pat, 0, i); >- if ((GET_CODE (out_exp) == CLOBBER) >- || (GET_CODE (out_exp) == USE)) >- continue; >- else if (GET_CODE (out_exp) != SET) >- return false; >- } >- } >- } >- } >- } >- else >- { >- in_pat = PATTERN (in_insn); >- if (GET_CODE (in_pat) != PARALLEL) >- return false; >- >- for (i = 0; i < XVECLEN (in_pat, 0); i++) >- { >- in_exp = XVECEXP (in_pat, 0, i); >- if ((GET_CODE (in_exp) == CLOBBER) || (GET_CODE (in_exp) == USE)) >- continue; >- else if (GET_CODE (in_exp) != SET) >- return false; >- >- if (MEM_P (SET_DEST (in_exp))) >- { >- out_set = single_set (out_insn); >- if (!out_set) >- { >- out_pat = PATTERN (out_insn); >- if (GET_CODE (out_pat) != PARALLEL) >- return false; >- for (j = 0; j < XVECLEN (out_pat, 0); j++) >- { >- out_exp = XVECEXP (out_pat, 0, j); >- if ((GET_CODE (out_exp) == CLOBBER) >- || (GET_CODE (out_exp) == USE)) >- continue; >- else if (GET_CODE (out_exp) != SET) >- return false; >- } >- } >- } >- } >- } >- >- return store_data_bypass_p (out_insn, in_insn); >-} >- >-/* Implement TARGET_SECONDARY_MEMORY_NEEDED. >- >- When floating-point registers are wider than integer ones, moves between >- them must go through memory. */ >- >-static bool >-riscv_secondary_memory_needed (machine_mode mode, reg_class_t class1, >- reg_class_t class2) >-{ >- return (GET_MODE_SIZE (mode) > UNITS_PER_WORD >- && (class1 == FP_REGS) != (class2 == FP_REGS)); >-} >- >-/* Implement TARGET_REGISTER_MOVE_COST. */ >- >-static int >-riscv_register_move_cost (machine_mode mode, >- reg_class_t from, reg_class_t to) >-{ >- return riscv_secondary_memory_needed (mode, from, to) ? 8 : 2; >-} >- >-/* Implement TARGET_HARD_REGNO_NREGS. */ >- >-static unsigned int >-riscv_hard_regno_nregs (unsigned int regno, machine_mode mode) >-{ >- if (FP_REG_P (regno)) >- return (GET_MODE_SIZE (mode) + UNITS_PER_FP_REG - 1) / UNITS_PER_FP_REG; >- >- /* All other registers are word-sized. */ >- return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >-} >- >-/* Implement TARGET_HARD_REGNO_MODE_OK. */ >- >-static bool >-riscv_hard_regno_mode_ok (unsigned int regno, machine_mode mode) >-{ >- unsigned int nregs = riscv_hard_regno_nregs (regno, mode); >- >- if (GP_REG_P (regno)) >- { >- if (!GP_REG_P (regno + nregs - 1)) >- return false; >- } >- else if (FP_REG_P (regno)) >- { >- if (!FP_REG_P (regno + nregs - 1)) >- return false; >- >- if (GET_MODE_CLASS (mode) != MODE_FLOAT >- && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) >- return false; >- >- /* Only use callee-saved registers if a potential callee is guaranteed >- to spill the requisite width. */ >- if (GET_MODE_UNIT_SIZE (mode) > UNITS_PER_FP_REG >- || (!call_used_or_fixed_reg_p (regno) >- && GET_MODE_UNIT_SIZE (mode) > UNITS_PER_FP_ARG)) >- return false; >- } >- else >- return false; >- >- /* Require same callee-savedness for all registers. */ >- for (unsigned i = 1; i < nregs; i++) >- if (call_used_or_fixed_reg_p (regno) >- != call_used_or_fixed_reg_p (regno + i)) >- return false; >- >- return true; >-} >- >-/* Implement TARGET_MODES_TIEABLE_P. >- >- Don't allow floating-point modes to be tied, since type punning of >- single-precision and double-precision is implementation defined. */ >- >-static bool >-riscv_modes_tieable_p (machine_mode mode1, machine_mode mode2) >-{ >- return (mode1 == mode2 >- || !(GET_MODE_CLASS (mode1) == MODE_FLOAT >- && GET_MODE_CLASS (mode2) == MODE_FLOAT)); >-} >- >-/* Implement CLASS_MAX_NREGS. */ >- >-static unsigned char >-riscv_class_max_nregs (reg_class_t rclass, machine_mode mode) >-{ >- if (reg_class_subset_p (FP_REGS, rclass)) >- return riscv_hard_regno_nregs (FP_REG_FIRST, mode); >- >- if (reg_class_subset_p (GR_REGS, rclass)) >- return riscv_hard_regno_nregs (GP_REG_FIRST, mode); >- >- return 0; >-} >- >-/* Implement TARGET_MEMORY_MOVE_COST. */ >- >-static int >-riscv_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in) >-{ >- return (tune_info->memory_cost >- + memory_move_secondary_cost (mode, rclass, in)); >-} >- >-/* Return the number of instructions that can be issued per cycle. */ >- >-static int >-riscv_issue_rate (void) >-{ >- return tune_info->issue_rate; >-} >- >-/* Auxiliary function to emit RISC-V ELF attribute. */ >-static void >-riscv_emit_attribute () >-{ >- fprintf (asm_out_file, "\t.attribute arch, \"%s\"\n", >- riscv_arch_str ().c_str ()); >- >- fprintf (asm_out_file, "\t.attribute unaligned_access, %d\n", >- TARGET_STRICT_ALIGN ? 0 : 1); >- >- fprintf (asm_out_file, "\t.attribute stack_align, %d\n", >- riscv_stack_boundary / 8); >-} >- >-/* Implement TARGET_ASM_FILE_START. */ >- >-static void >-riscv_file_start (void) >-{ >- default_file_start (); >- >- /* Instruct GAS to generate position-[in]dependent code. */ >- fprintf (asm_out_file, "\t.option %spic\n", (flag_pic ? "" : "no")); >- >- /* If the user specifies "-mno-relax" on the command line then disable linker >- relaxation in the assembler. */ >- if (! riscv_mrelax) >- fprintf (asm_out_file, "\t.option norelax\n"); >- >- if (riscv_emit_attribute_p) >- riscv_emit_attribute (); >-} >- >-/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text >- in order to avoid duplicating too much logic from elsewhere. */ >- >-static void >-riscv_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, >- HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, >- tree function) >-{ >- const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl)); >- rtx this_rtx, temp1, temp2, fnaddr; >- rtx_insn *insn; >- >- /* Pretend to be a post-reload pass while generating rtl. */ >- reload_completed = 1; >- >- /* Mark the end of the (empty) prologue. */ >- emit_note (NOTE_INSN_PROLOGUE_END); >- >- /* Determine if we can use a sibcall to call FUNCTION directly. */ >- fnaddr = gen_rtx_MEM (FUNCTION_MODE, XEXP (DECL_RTL (function), 0)); >- >- /* We need two temporary registers in some cases. */ >- temp1 = gen_rtx_REG (Pmode, RISCV_PROLOGUE_TEMP_REGNUM); >- temp2 = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM); >- >- /* Find out which register contains the "this" pointer. */ >- if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) >- this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); >- else >- this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST); >- >- /* Add DELTA to THIS_RTX. */ >- if (delta != 0) >- { >- rtx offset = GEN_INT (delta); >- if (!SMALL_OPERAND (delta)) >- { >- riscv_emit_move (temp1, offset); >- offset = temp1; >- } >- emit_insn (gen_add3_insn (this_rtx, this_rtx, offset)); >- } >- >- /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */ >- if (vcall_offset != 0) >- { >- rtx addr; >- >- /* Set TEMP1 to *THIS_RTX. */ >- riscv_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx)); >- >- /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */ >- addr = riscv_add_offset (temp2, temp1, vcall_offset); >- >- /* Load the offset and add it to THIS_RTX. */ >- riscv_emit_move (temp1, gen_rtx_MEM (Pmode, addr)); >- emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1)); >- } >- >- /* Jump to the target function. */ >- insn = emit_call_insn (gen_sibcall (fnaddr, const0_rtx, NULL, const0_rtx)); >- SIBLING_CALL_P (insn) = 1; >- >- /* Run just enough of rest_of_compilation. This sequence was >- "borrowed" from alpha.c. */ >- insn = get_insns (); >- split_all_insns_noflow (); >- shorten_branches (insn); >- assemble_start_function (thunk_fndecl, fnname); >- final_start_function (insn, file, 1); >- final (insn, file, 1); >- final_end_function (); >- assemble_end_function (thunk_fndecl, fnname); >- >- /* Clean up the vars set above. Note that final_end_function resets >- the global pointer for us. */ >- reload_completed = 0; >-} >- >-/* Allocate a chunk of memory for per-function machine-dependent data. */ >- >-static struct machine_function * >-riscv_init_machine_status (void) >-{ >- return ggc_cleared_alloc<machine_function> (); >-} >- >-/* Implement TARGET_OPTION_OVERRIDE. */ >- >-static void >-riscv_option_override (void) >-{ >- const struct riscv_cpu_info *cpu; >- >-#ifdef SUBTARGET_OVERRIDE_OPTIONS >- SUBTARGET_OVERRIDE_OPTIONS; >-#endif >- >- flag_pcc_struct_return = 0; >- >- if (flag_pic) >- g_switch_value = 0; >- >- /* The presence of the M extension implies that division instructions >- are present, so include them unless explicitly disabled. */ >- if (TARGET_MUL && (target_flags_explicit & MASK_DIV) == 0) >- target_flags |= MASK_DIV; >- else if (!TARGET_MUL && TARGET_DIV) >- error ("%<-mdiv%> requires %<-march%> to subsume the %<M%> extension"); >- >- /* Likewise floating-point division and square root. */ >- if (TARGET_HARD_FLOAT && (target_flags_explicit & MASK_FDIV) == 0) >- target_flags |= MASK_FDIV; >- >- /* Handle -mtune. */ >- cpu = riscv_parse_cpu (riscv_tune_string ? riscv_tune_string : >- RISCV_TUNE_STRING_DEFAULT); >- riscv_microarchitecture = cpu->microarchitecture; >- tune_info = optimize_size ? &optimize_size_tune_info : cpu->tune_info; >- >- /* Use -mtune's setting for slow_unaligned_access, even when optimizing >- for size. For architectures that trap and emulate unaligned accesses, >- the performance cost is too great, even for -Os. Similarly, if >- -m[no-]strict-align is left unspecified, heed -mtune's advice. */ >- riscv_slow_unaligned_access_p = (cpu->tune_info->slow_unaligned_access >- || TARGET_STRICT_ALIGN); >- if ((target_flags_explicit & MASK_STRICT_ALIGN) == 0 >- && cpu->tune_info->slow_unaligned_access) >- target_flags |= MASK_STRICT_ALIGN; >- >- /* If the user hasn't specified a branch cost, use the processor's >- default. */ >- if (riscv_branch_cost == 0) >- riscv_branch_cost = tune_info->branch_cost; >- >- /* Function to allocate machine-dependent function status. */ >- init_machine_status = &riscv_init_machine_status; >- >- if (flag_pic) >- riscv_cmodel = CM_PIC; >- >- /* We get better code with explicit relocs for CM_MEDLOW, but >- worse code for the others (for now). Pick the best default. */ >- if ((target_flags_explicit & MASK_EXPLICIT_RELOCS) == 0) >- if (riscv_cmodel == CM_MEDLOW) >- target_flags |= MASK_EXPLICIT_RELOCS; >- >- /* Require that the ISA supports the requested floating-point ABI. */ >- if (UNITS_PER_FP_ARG > (TARGET_HARD_FLOAT ? UNITS_PER_FP_REG : 0)) >- error ("requested ABI requires %<-march%> to subsume the %qc extension", >- UNITS_PER_FP_ARG > 8 ? 'Q' : (UNITS_PER_FP_ARG > 4 ? 'D' : 'F')); >- >- if (TARGET_RVE && riscv_abi != ABI_ILP32E) >- error ("rv32e requires ilp32e ABI"); >- >- /* We do not yet support ILP32 on RV64. */ >- if (BITS_PER_WORD != POINTER_SIZE) >- error ("ABI requires %<-march=rv%d%>", POINTER_SIZE); >- >- /* Validate -mpreferred-stack-boundary= value. */ >- riscv_stack_boundary = ABI_STACK_BOUNDARY; >- if (riscv_preferred_stack_boundary_arg) >- { >- int min = ctz_hwi (STACK_BOUNDARY / 8); >- int max = 8; >- >- if (!IN_RANGE (riscv_preferred_stack_boundary_arg, min, max)) >- error ("%<-mpreferred-stack-boundary=%d%> must be between %d and %d", >- riscv_preferred_stack_boundary_arg, min, max); >- >- riscv_stack_boundary = 8 << riscv_preferred_stack_boundary_arg; >- } >- >- if (riscv_emit_attribute_p < 0) >-#ifdef HAVE_AS_RISCV_ATTRIBUTE >- riscv_emit_attribute_p = TARGET_RISCV_ATTRIBUTE; >-#else >- riscv_emit_attribute_p = 0; >- >- if (riscv_emit_attribute_p) >- error ("%<-mriscv-attribute%> RISC-V ELF attribute requires GNU as 2.32" >- " [%<-mriscv-attribute%>]"); >-#endif >- >-} >- >-/* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */ >- >-static void >-riscv_conditional_register_usage (void) >-{ >- /* We have only x0~x15 on RV32E. */ >- if (TARGET_RVE) >- { >- for (int r = 16; r <= 31; r++) >- fixed_regs[r] = 1; >- } >- >- if (riscv_abi == ABI_ILP32E) >- { >- for (int r = 16; r <= 31; r++) >- call_used_regs[r] = 1; >- } >- >- if (!TARGET_HARD_FLOAT) >- { >- for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >- fixed_regs[regno] = call_used_regs[regno] = 1; >- } >- >- /* In the soft-float ABI, there are no callee-saved FP registers. */ >- if (UNITS_PER_FP_ARG == 0) >- { >- for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >- call_used_regs[regno] = 1; >- } >-} >- >-/* Return a register priority for hard reg REGNO. */ >- >-static int >-riscv_register_priority (int regno) >-{ >- /* Favor compressed registers to improve the odds of RVC instruction >- selection. */ >- if (riscv_compressed_reg_p (regno)) >- return 1; >- >- return 0; >-} >- >-/* Implement TARGET_TRAMPOLINE_INIT. */ >- >-static void >-riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) >-{ >- rtx addr, end_addr, mem; >- uint32_t trampoline[4]; >- unsigned int i; >- HOST_WIDE_INT static_chain_offset, target_function_offset; >- >- /* Work out the offsets of the pointers from the start of the >- trampoline code. */ >- gcc_assert (ARRAY_SIZE (trampoline) * 4 == TRAMPOLINE_CODE_SIZE); >- >- /* Get pointers to the beginning and end of the code block. */ >- addr = force_reg (Pmode, XEXP (m_tramp, 0)); >- end_addr = riscv_force_binary (Pmode, PLUS, addr, >- GEN_INT (TRAMPOLINE_CODE_SIZE)); >- >- >- if (Pmode == SImode) >- { >- chain_value = force_reg (Pmode, chain_value); >- >- rtx target_function = force_reg (Pmode, XEXP (DECL_RTL (fndecl), 0)); >- /* lui t2, hi(chain) >- lui t1, hi(func) >- addi t2, t2, lo(chain) >- jr r1, lo(func) >- */ >- unsigned HOST_WIDE_INT lui_hi_chain_code, lui_hi_func_code; >- unsigned HOST_WIDE_INT lo_chain_code, lo_func_code; >- >- rtx uimm_mask = force_reg (SImode, gen_int_mode (-IMM_REACH, SImode)); >- >- /* 0xfff. */ >- rtx imm12_mask = gen_reg_rtx (SImode); >- emit_insn (gen_one_cmplsi2 (imm12_mask, uimm_mask)); >- >- rtx fixup_value = force_reg (SImode, gen_int_mode (IMM_REACH/2, SImode)); >- >- /* Gen lui t2, hi(chain). */ >- rtx hi_chain = riscv_force_binary (SImode, PLUS, chain_value, >- fixup_value); >- hi_chain = riscv_force_binary (SImode, AND, hi_chain, >- uimm_mask); >- lui_hi_chain_code = OPCODE_LUI | (STATIC_CHAIN_REGNUM << SHIFT_RD); >- rtx lui_hi_chain = riscv_force_binary (SImode, IOR, hi_chain, >- gen_int_mode (lui_hi_chain_code, SImode)); >- >- mem = adjust_address (m_tramp, SImode, 0); >- riscv_emit_move (mem, lui_hi_chain); >- >- /* Gen lui t1, hi(func). */ >- rtx hi_func = riscv_force_binary (SImode, PLUS, target_function, >- fixup_value); >- hi_func = riscv_force_binary (SImode, AND, hi_func, >- uimm_mask); >- lui_hi_func_code = OPCODE_LUI | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD); >- rtx lui_hi_func = riscv_force_binary (SImode, IOR, hi_func, >- gen_int_mode (lui_hi_func_code, SImode)); >- >- mem = adjust_address (m_tramp, SImode, 1 * GET_MODE_SIZE (SImode)); >- riscv_emit_move (mem, lui_hi_func); >- >- /* Gen addi t2, t2, lo(chain). */ >- rtx lo_chain = riscv_force_binary (SImode, AND, chain_value, >- imm12_mask); >- lo_chain = riscv_force_binary (SImode, ASHIFT, lo_chain, GEN_INT (20)); >- >- lo_chain_code = OPCODE_ADDI >- | (STATIC_CHAIN_REGNUM << SHIFT_RD) >- | (STATIC_CHAIN_REGNUM << SHIFT_RS1); >- >- rtx addi_lo_chain = riscv_force_binary (SImode, IOR, lo_chain, >- force_reg (SImode, GEN_INT (lo_chain_code))); >- >- mem = adjust_address (m_tramp, SImode, 2 * GET_MODE_SIZE (SImode)); >- riscv_emit_move (mem, addi_lo_chain); >- >- /* Gen jr r1, lo(func). */ >- rtx lo_func = riscv_force_binary (SImode, AND, target_function, >- imm12_mask); >- lo_func = riscv_force_binary (SImode, ASHIFT, lo_func, GEN_INT (20)); >- >- lo_func_code = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1); >- >- rtx jr_lo_func = riscv_force_binary (SImode, IOR, lo_func, >- force_reg (SImode, GEN_INT (lo_func_code))); >- >- mem = adjust_address (m_tramp, SImode, 3 * GET_MODE_SIZE (SImode)); >- riscv_emit_move (mem, jr_lo_func); >- } >- else >- { >- static_chain_offset = TRAMPOLINE_CODE_SIZE; >- target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode); >- >- /* auipc t2, 0 >- l[wd] t1, target_function_offset(t2) >- l[wd] t2, static_chain_offset(t2) >- jr t1 >- */ >- trampoline[0] = OPCODE_AUIPC | (STATIC_CHAIN_REGNUM << SHIFT_RD); >- trampoline[1] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW) >- | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD) >- | (STATIC_CHAIN_REGNUM << SHIFT_RS1) >- | (target_function_offset << SHIFT_IMM); >- trampoline[2] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW) >- | (STATIC_CHAIN_REGNUM << SHIFT_RD) >- | (STATIC_CHAIN_REGNUM << SHIFT_RS1) >- | (static_chain_offset << SHIFT_IMM); >- trampoline[3] = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1); >- >- /* Copy the trampoline code. */ >- for (i = 0; i < ARRAY_SIZE (trampoline); i++) >- { >- mem = adjust_address (m_tramp, SImode, i * GET_MODE_SIZE (SImode)); >- riscv_emit_move (mem, gen_int_mode (trampoline[i], SImode)); >- } >- >- /* Set up the static chain pointer field. */ >- mem = adjust_address (m_tramp, ptr_mode, static_chain_offset); >- riscv_emit_move (mem, chain_value); >- >- /* Set up the target function field. */ >- mem = adjust_address (m_tramp, ptr_mode, target_function_offset); >- riscv_emit_move (mem, XEXP (DECL_RTL (fndecl), 0)); >- } >- >- /* Flush the code part of the trampoline. */ >- emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE))); >- emit_insn (gen_clear_cache (addr, end_addr)); >-} >- >-/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ >- >-static bool >-riscv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, >- tree exp ATTRIBUTE_UNUSED) >-{ >- /* Don't use sibcalls when use save-restore routine. */ >- if (TARGET_SAVE_RESTORE) >- return false; >- >- /* Don't use sibcall for naked functions. */ >- if (cfun->machine->naked_p) >- return false; >- >- /* Don't use sibcall for interrupt functions. */ >- if (cfun->machine->interrupt_handler_p) >- return false; >- >- return true; >-} >- >-/* Get the interrupt type, return UNKNOWN_MODE if it's not >- interrupt function. */ >-static enum riscv_privilege_levels >-riscv_get_interrupt_type (tree decl) >-{ >- gcc_assert (decl != NULL_TREE); >- >- if ((TREE_CODE(decl) != FUNCTION_DECL) >- || (!riscv_interrupt_type_p (TREE_TYPE (decl)))) >- return UNKNOWN_MODE; >- >- tree attr_args >- = TREE_VALUE (lookup_attribute ("interrupt", >- TYPE_ATTRIBUTES (TREE_TYPE (decl)))); >- >- if (attr_args && TREE_CODE (TREE_VALUE (attr_args)) != VOID_TYPE) >- { >- const char *string = TREE_STRING_POINTER (TREE_VALUE (attr_args)); >- >- if (!strcmp (string, "user")) >- return USER_MODE; >- else if (!strcmp (string, "supervisor")) >- return SUPERVISOR_MODE; >- else /* Must be "machine". */ >- return MACHINE_MODE; >- } >- else >- /* Interrupt attributes are machine mode by default. */ >- return MACHINE_MODE; >-} >- >-/* Implement `TARGET_SET_CURRENT_FUNCTION'. */ >-/* Sanity cheching for above function attributes. */ >-static void >-riscv_set_current_function (tree decl) >-{ >- if (decl == NULL_TREE >- || current_function_decl == NULL_TREE >- || current_function_decl == error_mark_node >- || ! cfun->machine >- || cfun->machine->attributes_checked_p) >- return; >- >- cfun->machine->naked_p = riscv_naked_function_p (decl); >- cfun->machine->interrupt_handler_p >- = riscv_interrupt_type_p (TREE_TYPE (decl)); >- >- if (cfun->machine->naked_p && cfun->machine->interrupt_handler_p) >- error ("function attributes %qs and %qs are mutually exclusive", >- "interrupt", "naked"); >- >- if (cfun->machine->interrupt_handler_p) >- { >- tree ret = TREE_TYPE (TREE_TYPE (decl)); >- tree args = TYPE_ARG_TYPES (TREE_TYPE (decl)); >- >- if (TREE_CODE (ret) != VOID_TYPE) >- error ("%qs function cannot return a value", "interrupt"); >- >- if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE) >- error ("%qs function cannot have arguments", "interrupt"); >- >- cfun->machine->interrupt_mode = riscv_get_interrupt_type (decl); >- >- gcc_assert (cfun->machine->interrupt_mode != UNKNOWN_MODE); >- } >- >- /* Don't print the above diagnostics more than once. */ >- cfun->machine->attributes_checked_p = 1; >-} >- >-/* Implement TARGET_MERGE_DECL_ATTRIBUTES. */ >-static tree >-riscv_merge_decl_attributes (tree olddecl, tree newdecl) >-{ >- tree combined_attrs; >- >- enum riscv_privilege_levels old_interrupt_type >- = riscv_get_interrupt_type (olddecl); >- enum riscv_privilege_levels new_interrupt_type >- = riscv_get_interrupt_type (newdecl); >- >- /* Check old and new has same interrupt type. */ >- if ((old_interrupt_type != UNKNOWN_MODE) >- && (new_interrupt_type != UNKNOWN_MODE) >- && (old_interrupt_type != new_interrupt_type)) >- error ("%qs function cannot have different interrupt type", "interrupt"); >- >- /* Create combined attributes. */ >- combined_attrs = merge_attributes (DECL_ATTRIBUTES (olddecl), >- DECL_ATTRIBUTES (newdecl)); >- >- return combined_attrs; >-} >- >-/* Implement TARGET_CANNOT_COPY_INSN_P. */ >- >-static bool >-riscv_cannot_copy_insn_p (rtx_insn *insn) >-{ >- return recog_memoized (insn) >= 0 && get_attr_cannot_copy (insn); >-} >- >-/* Implement TARGET_SLOW_UNALIGNED_ACCESS. */ >- >-static bool >-riscv_slow_unaligned_access (machine_mode, unsigned int) >-{ >- return riscv_slow_unaligned_access_p; >-} >- >-/* Implement TARGET_CAN_CHANGE_MODE_CLASS. */ >- >-static bool >-riscv_can_change_mode_class (machine_mode, machine_mode, reg_class_t rclass) >-{ >- return !reg_classes_intersect_p (FP_REGS, rclass); >-} >- >- >-/* Implement TARGET_CONSTANT_ALIGNMENT. */ >- >-static HOST_WIDE_INT >-riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align) >-{ >- if ((TREE_CODE (exp) == STRING_CST || TREE_CODE (exp) == CONSTRUCTOR) >- && (riscv_align_data_type == riscv_align_data_type_xlen)) >- return MAX (align, BITS_PER_WORD); >- return align; >-} >- >-/* Implement TARGET_PROMOTE_FUNCTION_MODE. */ >- >-/* This function is equivalent to default_promote_function_mode_always_promote >- except that it returns a promoted mode even if type is NULL_TREE. This is >- needed by libcalls which have no type (only a mode) such as fixed conversion >- routines that take a signed or unsigned char/short/int argument and convert >- it to a fixed type. */ >- >-static machine_mode >-riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, >- machine_mode mode, >- int *punsignedp ATTRIBUTE_UNUSED, >- const_tree fntype ATTRIBUTE_UNUSED, >- int for_return ATTRIBUTE_UNUSED) >-{ >- int unsignedp; >- >- if (type != NULL_TREE) >- return promote_mode (type, mode, punsignedp); >- >- unsignedp = *punsignedp; >- PROMOTE_MODE (mode, unsignedp, type); >- *punsignedp = unsignedp; >- return mode; >-} >- >-/* Implement TARGET_MACHINE_DEPENDENT_REORG. */ >- >-static void >-riscv_reorg (void) >-{ >- /* Do nothing unless we have -msave-restore */ >- if (TARGET_SAVE_RESTORE) >- riscv_remove_unneeded_save_restore_calls (); >-} >- >-/* Return nonzero if register FROM_REGNO can be renamed to register >- TO_REGNO. */ >- >-bool >-riscv_hard_regno_rename_ok (unsigned from_regno ATTRIBUTE_UNUSED, >- unsigned to_regno) >-{ >- /* Interrupt functions can only use registers that have already been >- saved by the prologue, even if they would normally be >- call-clobbered. */ >- return !cfun->machine->interrupt_handler_p || df_regs_ever_live_p (to_regno); >-} >- >- >-/* Helper function for generating gpr_save pattern. */ >- >-rtx >-riscv_gen_gpr_save_insn (struct riscv_frame_info *frame) >-{ >- unsigned count = riscv_save_libcall_count (frame->mask); >- /* 1 for unspec 2 for clobber t0/t1 and 1 for ra. */ >- unsigned veclen = 1 + 2 + 1 + count; >- rtvec vec = rtvec_alloc (veclen); >- >- gcc_assert (veclen <= ARRAY_SIZE (gpr_save_reg_order)); >- >- RTVEC_ELT (vec, 0) = >- gen_rtx_UNSPEC_VOLATILE (VOIDmode, >- gen_rtvec (1, GEN_INT (count)), UNSPECV_GPR_SAVE); >- >- for (unsigned i = 1; i < veclen; ++i) >- { >- unsigned regno = gpr_save_reg_order[i]; >- rtx reg = gen_rtx_REG (Pmode, regno); >- rtx elt; >- >- /* t0 and t1 are CLOBBERs, others are USEs. */ >- if (i < 3) >- elt = gen_rtx_CLOBBER (Pmode, reg); >- else >- elt = gen_rtx_USE (Pmode, reg); >- >- RTVEC_ELT (vec, i) = elt; >- } >- >- /* Largest number of caller-save register must set in mask if we are >- not using __riscv_save_0. */ >- gcc_assert ((count == 0) || >- BITSET_P (frame->mask, gpr_save_reg_order[veclen - 1])); >- >- return gen_rtx_PARALLEL (VOIDmode, vec); >-} >- >-/* Return true if it's valid gpr_save pattern. */ >- >-bool >-riscv_gpr_save_operation_p (rtx op) >-{ >- unsigned len = XVECLEN (op, 0); >- >- if (len > ARRAY_SIZE (gpr_save_reg_order)) >- return false; >- >- for (unsigned i = 0; i < len; i++) >- { >- rtx elt = XVECEXP (op, 0, i); >- if (i == 0) >- { >- /* First element in parallel is unspec. */ >- if (GET_CODE (elt) != UNSPEC_VOLATILE >- || GET_CODE (XVECEXP (elt, 0, 0)) != CONST_INT >- || XINT (elt, 1) != UNSPECV_GPR_SAVE) >- return false; >- } >- else >- { >- /* Two CLOBBER and USEs, must check the order. */ >- unsigned expect_code = i < 3 ? CLOBBER : USE; >- if (GET_CODE (elt) != expect_code >- || !REG_P (XEXP (elt, 1)) >- || (REGNO (XEXP (elt, 1)) != gpr_save_reg_order[i])) >- return false; >- } >- break; >- } >- return true; >-} >- >-/* Implement TARGET_NEW_ADDRESS_PROFITABLE_P. */ >- >-bool >-riscv_new_address_profitable_p (rtx memref, rtx_insn *insn, rtx new_addr) >-{ >- /* Prefer old address if it is less expensive. */ >- addr_space_t as = MEM_ADDR_SPACE (memref); >- bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); >- int old_cost = address_cost (XEXP (memref, 0), GET_MODE (memref), as, speed); >- int new_cost = address_cost (new_addr, GET_MODE (memref), as, speed); >- return new_cost <= old_cost; >-} >- >-/* Initialize the GCC target structure. */ >-#undef TARGET_ASM_ALIGNED_HI_OP >-#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" >-#undef TARGET_ASM_ALIGNED_SI_OP >-#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" >-#undef TARGET_ASM_ALIGNED_DI_OP >-#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t" >- >-#undef TARGET_OPTION_OVERRIDE >-#define TARGET_OPTION_OVERRIDE riscv_option_override >- >-#undef TARGET_LEGITIMIZE_ADDRESS >-#define TARGET_LEGITIMIZE_ADDRESS riscv_legitimize_address >- >-#undef TARGET_SCHED_ISSUE_RATE >-#define TARGET_SCHED_ISSUE_RATE riscv_issue_rate >- >-#undef TARGET_FUNCTION_OK_FOR_SIBCALL >-#define TARGET_FUNCTION_OK_FOR_SIBCALL riscv_function_ok_for_sibcall >- >-#undef TARGET_SET_CURRENT_FUNCTION >-#define TARGET_SET_CURRENT_FUNCTION riscv_set_current_function >- >-#undef TARGET_REGISTER_MOVE_COST >-#define TARGET_REGISTER_MOVE_COST riscv_register_move_cost >-#undef TARGET_MEMORY_MOVE_COST >-#define TARGET_MEMORY_MOVE_COST riscv_memory_move_cost >-#undef TARGET_RTX_COSTS >-#define TARGET_RTX_COSTS riscv_rtx_costs >-#undef TARGET_ADDRESS_COST >-#define TARGET_ADDRESS_COST riscv_address_cost >- >-#undef TARGET_ASM_FILE_START >-#define TARGET_ASM_FILE_START riscv_file_start >-#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE >-#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true >- >-#undef TARGET_EXPAND_BUILTIN_VA_START >-#define TARGET_EXPAND_BUILTIN_VA_START riscv_va_start >- >-#undef TARGET_PROMOTE_FUNCTION_MODE >-#define TARGET_PROMOTE_FUNCTION_MODE riscv_promote_function_mode >- >-#undef TARGET_RETURN_IN_MEMORY >-#define TARGET_RETURN_IN_MEMORY riscv_return_in_memory >- >-#undef TARGET_ASM_OUTPUT_MI_THUNK >-#define TARGET_ASM_OUTPUT_MI_THUNK riscv_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_PRINT_OPERAND >-#define TARGET_PRINT_OPERAND riscv_print_operand >-#undef TARGET_PRINT_OPERAND_ADDRESS >-#define TARGET_PRINT_OPERAND_ADDRESS riscv_print_operand_address >- >-#undef TARGET_SETUP_INCOMING_VARARGS >-#define TARGET_SETUP_INCOMING_VARARGS riscv_setup_incoming_varargs >-#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS >-#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS riscv_allocate_stack_slots_for_args >-#undef TARGET_STRICT_ARGUMENT_NAMING >-#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true >-#undef TARGET_MUST_PASS_IN_STACK >-#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size >-#undef TARGET_PASS_BY_REFERENCE >-#define TARGET_PASS_BY_REFERENCE riscv_pass_by_reference >-#undef TARGET_ARG_PARTIAL_BYTES >-#define TARGET_ARG_PARTIAL_BYTES riscv_arg_partial_bytes >-#undef TARGET_FUNCTION_ARG >-#define TARGET_FUNCTION_ARG riscv_function_arg >-#undef TARGET_FUNCTION_ARG_ADVANCE >-#define TARGET_FUNCTION_ARG_ADVANCE riscv_function_arg_advance >-#undef TARGET_FUNCTION_ARG_BOUNDARY >-#define TARGET_FUNCTION_ARG_BOUNDARY riscv_function_arg_boundary >- >-/* The generic ELF target does not always have TLS support. */ >-#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 riscv_cannot_force_const_mem >- >-#undef TARGET_LEGITIMATE_CONSTANT_P >-#define TARGET_LEGITIMATE_CONSTANT_P riscv_legitimate_constant_p >- >-#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P >-#define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_true >- >-#undef TARGET_LEGITIMATE_ADDRESS_P >-#define TARGET_LEGITIMATE_ADDRESS_P riscv_legitimate_address_p >- >-#undef TARGET_CAN_ELIMINATE >-#define TARGET_CAN_ELIMINATE riscv_can_eliminate >- >-#undef TARGET_CONDITIONAL_REGISTER_USAGE >-#define TARGET_CONDITIONAL_REGISTER_USAGE riscv_conditional_register_usage >- >-#undef TARGET_CLASS_MAX_NREGS >-#define TARGET_CLASS_MAX_NREGS riscv_class_max_nregs >- >-#undef TARGET_TRAMPOLINE_INIT >-#define TARGET_TRAMPOLINE_INIT riscv_trampoline_init >- >-#undef TARGET_IN_SMALL_DATA_P >-#define TARGET_IN_SMALL_DATA_P riscv_in_small_data_p >- >-#undef TARGET_HAVE_SRODATA_SECTION >-#define TARGET_HAVE_SRODATA_SECTION true >- >-#undef TARGET_ASM_SELECT_SECTION >-#define TARGET_ASM_SELECT_SECTION riscv_select_section >- >-#undef TARGET_ASM_UNIQUE_SECTION >-#define TARGET_ASM_UNIQUE_SECTION riscv_unique_section >- >-#undef TARGET_ASM_SELECT_RTX_SECTION >-#define TARGET_ASM_SELECT_RTX_SECTION riscv_elf_select_rtx_section >- >-#undef TARGET_MIN_ANCHOR_OFFSET >-#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2) >- >-#undef TARGET_MAX_ANCHOR_OFFSET >-#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1) >- >-#undef TARGET_REGISTER_PRIORITY >-#define TARGET_REGISTER_PRIORITY riscv_register_priority >- >-#undef TARGET_CANNOT_COPY_INSN_P >-#define TARGET_CANNOT_COPY_INSN_P riscv_cannot_copy_insn_p >- >-#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV >-#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV riscv_atomic_assign_expand_fenv >- >-#undef TARGET_INIT_BUILTINS >-#define TARGET_INIT_BUILTINS riscv_init_builtins >- >-#undef TARGET_BUILTIN_DECL >-#define TARGET_BUILTIN_DECL riscv_builtin_decl >- >-#undef TARGET_EXPAND_BUILTIN >-#define TARGET_EXPAND_BUILTIN riscv_expand_builtin >- >-#undef TARGET_HARD_REGNO_NREGS >-#define TARGET_HARD_REGNO_NREGS riscv_hard_regno_nregs >-#undef TARGET_HARD_REGNO_MODE_OK >-#define TARGET_HARD_REGNO_MODE_OK riscv_hard_regno_mode_ok >- >-#undef TARGET_MODES_TIEABLE_P >-#define TARGET_MODES_TIEABLE_P riscv_modes_tieable_p >- >-#undef TARGET_SLOW_UNALIGNED_ACCESS >-#define TARGET_SLOW_UNALIGNED_ACCESS riscv_slow_unaligned_access >- >-#undef TARGET_SECONDARY_MEMORY_NEEDED >-#define TARGET_SECONDARY_MEMORY_NEEDED riscv_secondary_memory_needed >- >-#undef TARGET_CAN_CHANGE_MODE_CLASS >-#define TARGET_CAN_CHANGE_MODE_CLASS riscv_can_change_mode_class >- >-#undef TARGET_CONSTANT_ALIGNMENT >-#define TARGET_CONSTANT_ALIGNMENT riscv_constant_alignment >- >-#undef TARGET_MERGE_DECL_ATTRIBUTES >-#define TARGET_MERGE_DECL_ATTRIBUTES riscv_merge_decl_attributes >- >-#undef TARGET_ATTRIBUTE_TABLE >-#define TARGET_ATTRIBUTE_TABLE riscv_attribute_table >- >-#undef TARGET_WARN_FUNC_RETURN >-#define TARGET_WARN_FUNC_RETURN riscv_warn_func_return >- >-/* The low bit is ignored by jump instructions so is safe to use. */ >-#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS >-#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 >- >-#undef TARGET_MACHINE_DEPENDENT_REORG >-#define TARGET_MACHINE_DEPENDENT_REORG riscv_reorg >- >-#undef TARGET_NEW_ADDRESS_PROFITABLE_P >-#define TARGET_NEW_ADDRESS_PROFITABLE_P riscv_new_address_profitable_p >- >-struct gcc_target targetm = TARGET_INITIALIZER; >- >-#include "gt-riscv.h" >+/* Subroutines used for code generation for RISC-V. >+ Copyright (C) 2011-2020 Free Software Foundation, Inc. >+ Contributed by Andrew Waterman (andrew@sifive.com). >+ Based on MIPS target for GNU compiler. >+ >+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 >+ >+#define INCLUDE_STRING >+#include "config.h" >+#include "system.h" >+#include "coretypes.h" >+#include "tm.h" >+#include "rtl.h" >+#include "regs.h" >+#include "insn-config.h" >+#include "insn-attr.h" >+#include "recog.h" >+#include "output.h" >+#include "alias.h" >+#include "tree.h" >+#include "stringpool.h" >+#include "attribs.h" >+#include "varasm.h" >+#include "stor-layout.h" >+#include "calls.h" >+#include "function.h" >+#include "explow.h" >+#include "memmodel.h" >+#include "emit-rtl.h" >+#include "reload.h" >+#include "tm_p.h" >+#include "target.h" >+#include "target-def.h" >+#include "basic-block.h" >+#include "expr.h" >+#include "optabs.h" >+#include "bitmap.h" >+#include "df.h" >+#include "diagnostic.h" >+#include "builtins.h" >+#include "predict.h" >+#include "tree-pass.h" >+ >+/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */ >+#define UNSPEC_ADDRESS_P(X) \ >+ (GET_CODE (X) == UNSPEC \ >+ && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \ >+ && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES) >+ >+/* Extract the symbol or label from UNSPEC wrapper X. */ >+#define UNSPEC_ADDRESS(X) \ >+ XVECEXP (X, 0, 0) >+ >+/* Extract the symbol type from UNSPEC wrapper X. */ >+#define UNSPEC_ADDRESS_TYPE(X) \ >+ ((enum riscv_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST)) >+ >+/* True if bit BIT is set in VALUE. */ >+#define BITSET_P(VALUE, BIT) (((VALUE) & (1ULL << (BIT))) != 0) >+ >+/* Classifies an address. >+ >+ ADDRESS_REG >+ A natural register + offset address. The register satisfies >+ riscv_valid_base_register_p and the offset is a const_arith_operand. >+ >+ ADDRESS_LO_SUM >+ A LO_SUM rtx. The first operand is a valid base register and >+ the second operand is a symbolic address. >+ >+ ADDRESS_CONST_INT >+ A signed 16-bit constant address. >+ >+ ADDRESS_SYMBOLIC: >+ A constant symbolic address. */ >+enum riscv_address_type { >+ ADDRESS_REG, >+ ADDRESS_LO_SUM, >+ ADDRESS_CONST_INT, >+ ADDRESS_SYMBOLIC >+}; >+ >+/* Information about a function's frame layout. */ >+struct GTY(()) riscv_frame_info { >+ /* The size of the frame in bytes. */ >+ HOST_WIDE_INT total_size; >+ >+ /* Bit X is set if the function saves or restores GPR X. */ >+ unsigned int mask; >+ >+ /* Likewise FPR X. */ >+ unsigned int fmask; >+ >+ /* How much the GPR save/restore routines adjust sp (or 0 if unused). */ >+ unsigned save_libcall_adjustment; >+ >+ /* Offsets of fixed-point and floating-point save areas from frame bottom */ >+ HOST_WIDE_INT gp_sp_offset; >+ HOST_WIDE_INT fp_sp_offset; >+ >+ /* Offset of virtual frame pointer from stack pointer/frame bottom */ >+ HOST_WIDE_INT frame_pointer_offset; >+ >+ /* Offset of hard frame pointer from stack pointer/frame bottom */ >+ HOST_WIDE_INT hard_frame_pointer_offset; >+ >+ /* The offset of arg_pointer_rtx from the bottom of the frame. */ >+ HOST_WIDE_INT arg_pointer_offset; >+}; >+ >+enum riscv_privilege_levels { >+ UNKNOWN_MODE, USER_MODE, SUPERVISOR_MODE, MACHINE_MODE >+}; >+ >+struct GTY(()) machine_function { >+ /* The number of extra stack bytes taken up by register varargs. >+ This area is allocated by the callee at the very top of the frame. */ >+ int varargs_size; >+ >+ /* True if current function is a naked function. */ >+ bool naked_p; >+ >+ /* True if current function is an interrupt function. */ >+ bool interrupt_handler_p; >+ /* For an interrupt handler, indicates the privilege level. */ >+ enum riscv_privilege_levels interrupt_mode; >+ >+ /* True if attributes on current function have been checked. */ >+ bool attributes_checked_p; >+ >+ /* The current frame information, calculated by riscv_compute_frame_info. */ >+ struct riscv_frame_info frame; >+}; >+ >+/* Information about a single argument. */ >+struct riscv_arg_info { >+ /* True if the argument is at least partially passed on the stack. */ >+ bool stack_p; >+ >+ /* The number of integer registers allocated to this argument. */ >+ unsigned int num_gprs; >+ >+ /* The offset of the first register used, provided num_gprs is nonzero. >+ If passed entirely on the stack, the value is MAX_ARGS_IN_REGISTERS. */ >+ unsigned int gpr_offset; >+ >+ /* The number of floating-point registers allocated to this argument. */ >+ unsigned int num_fprs; >+ >+ /* The offset of the first register used, provided num_fprs is nonzero. */ >+ unsigned int fpr_offset; >+}; >+ >+/* Information about an address described by riscv_address_type. >+ >+ ADDRESS_CONST_INT >+ No fields are used. >+ >+ ADDRESS_REG >+ REG is the base register and OFFSET is the constant offset. >+ >+ ADDRESS_LO_SUM >+ REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE >+ is the type of symbol it references. >+ >+ ADDRESS_SYMBOLIC >+ SYMBOL_TYPE is the type of symbol that the address references. */ >+struct riscv_address_info { >+ enum riscv_address_type type; >+ rtx reg; >+ rtx offset; >+ enum riscv_symbol_type symbol_type; >+}; >+ >+/* One stage in a constant building sequence. These sequences have >+ the form: >+ >+ A = VALUE[0] >+ A = A CODE[1] VALUE[1] >+ A = A CODE[2] VALUE[2] >+ ... >+ >+ where A is an accumulator, each CODE[i] is a binary rtl operation >+ and each VALUE[i] is a constant integer. CODE[0] is undefined. */ >+struct riscv_integer_op { >+ enum rtx_code code; >+ unsigned HOST_WIDE_INT value; >+}; >+ >+/* The largest number of operations needed to load an integer constant. >+ The worst case is LUI, ADDI, SLLI, ADDI, SLLI, ADDI, SLLI, ADDI. */ >+#define RISCV_MAX_INTEGER_OPS 8 >+ >+/* Costs of various operations on the different architectures. */ >+ >+struct riscv_tune_info >+{ >+ unsigned short fp_add[2]; >+ unsigned short fp_mul[2]; >+ unsigned short fp_div[2]; >+ unsigned short int_mul[2]; >+ unsigned short int_div[2]; >+ unsigned short issue_rate; >+ unsigned short branch_cost; >+ unsigned short memory_cost; >+ bool slow_unaligned_access; >+}; >+ >+/* Information about one CPU we know about. */ >+struct riscv_cpu_info { >+ /* This CPU's canonical name. */ >+ const char *name; >+ >+ /* Which automaton to use for tuning. */ >+ enum riscv_microarchitecture_type microarchitecture; >+ >+ /* Tuning parameters for this CPU. */ >+ const struct riscv_tune_info *tune_info; >+}; >+ >+/* Global variables for machine-dependent things. */ >+ >+/* Whether unaligned accesses execute very slowly. */ >+bool riscv_slow_unaligned_access_p; >+ >+/* Stack alignment to assume/maintain. */ >+unsigned riscv_stack_boundary; >+ >+/* If non-zero, this is an offset to be added to SP to redefine the CFA >+ when restoring the FP register from the stack. Only valid when generating >+ the epilogue. */ >+static int epilogue_cfa_sp_offset; >+ >+/* Which tuning parameters to use. */ >+static const struct riscv_tune_info *tune_info; >+ >+/* Which automaton to use for tuning. */ >+enum riscv_microarchitecture_type riscv_microarchitecture; >+ >+/* Index R is the smallest register class that contains register R. */ >+const enum reg_class riscv_regno_to_class[FIRST_PSEUDO_REGISTER] = { >+ GR_REGS, GR_REGS, GR_REGS, GR_REGS, >+ GR_REGS, GR_REGS, SIBCALL_REGS, SIBCALL_REGS, >+ JALR_REGS, JALR_REGS, SIBCALL_REGS, SIBCALL_REGS, >+ SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, >+ SIBCALL_REGS, SIBCALL_REGS, JALR_REGS, JALR_REGS, >+ JALR_REGS, JALR_REGS, JALR_REGS, JALR_REGS, >+ JALR_REGS, JALR_REGS, JALR_REGS, JALR_REGS, >+ SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FP_REGS, FP_REGS, FP_REGS, FP_REGS, >+ FRAME_REGS, FRAME_REGS, >+}; >+ >+/* Costs to use when optimizing for rocket. */ >+static const struct riscv_tune_info rocket_tune_info = { >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ >+ {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ >+ {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ >+ 1, /* issue_rate */ >+ 3, /* branch_cost */ >+ 5, /* memory_cost */ >+ true, /* slow_unaligned_access */ >+}; >+ >+/* Costs to use when optimizing for Sifive 7 Series. */ >+static const struct riscv_tune_info sifive_7_tune_info = { >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ >+ {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ >+ {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ >+ {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ >+ 2, /* issue_rate */ >+ 4, /* branch_cost */ >+ 3, /* memory_cost */ >+ true, /* slow_unaligned_access */ >+}; >+ >+/* Costs to use when optimizing for size. */ >+static const struct riscv_tune_info optimize_size_tune_info = { >+ {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_add */ >+ {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_mul */ >+ {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_div */ >+ {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* int_mul */ >+ {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* int_div */ >+ 1, /* issue_rate */ >+ 1, /* branch_cost */ >+ 2, /* memory_cost */ >+ false, /* slow_unaligned_access */ >+}; >+ >+static tree riscv_handle_fndecl_attribute (tree *, tree, tree, int, bool *); >+static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *); >+ >+/* Defining target-specific uses of __attribute__. */ >+static const struct attribute_spec riscv_attribute_table[] = >+{ >+ /* Syntax: { name, min_len, max_len, decl_required, type_required, >+ function_type_required, affects_type_identity, handler, >+ exclude } */ >+ >+ /* The attribute telling no prologue/epilogue. */ >+ { "naked", 0, 0, true, false, false, false, >+ riscv_handle_fndecl_attribute, NULL }, >+ /* This attribute generates prologue/epilogue for interrupt handlers. */ >+ { "interrupt", 0, 1, false, true, true, false, >+ riscv_handle_type_attribute, NULL }, >+ >+ /* The last attribute spec is set to be NULL. */ >+ { NULL, 0, 0, false, false, false, false, NULL, NULL } >+}; >+ >+/* Order for the CLOBBERs/USEs of gpr_save. */ >+static const unsigned gpr_save_reg_order[] = { >+ INVALID_REGNUM, T0_REGNUM, T1_REGNUM, RETURN_ADDR_REGNUM, >+ S0_REGNUM, S1_REGNUM, S2_REGNUM, S3_REGNUM, S4_REGNUM, >+ S5_REGNUM, S6_REGNUM, S7_REGNUM, S8_REGNUM, S9_REGNUM, >+ S10_REGNUM, S11_REGNUM >+}; >+ >+/* A table describing all the processors GCC knows about. */ >+static const struct riscv_cpu_info riscv_cpu_info_table[] = { >+ { "rocket", generic, &rocket_tune_info }, >+ { "sifive-3-series", generic, &rocket_tune_info }, >+ { "sifive-5-series", generic, &rocket_tune_info }, >+ { "sifive-7-series", sifive_7, &sifive_7_tune_info }, >+ { "size", generic, &optimize_size_tune_info }, >+}; >+ >+/* Return the riscv_cpu_info entry for the given name string. */ >+ >+static const struct riscv_cpu_info * >+riscv_parse_cpu (const char *cpu_string) >+{ >+ for (unsigned i = 0; i < ARRAY_SIZE (riscv_cpu_info_table); i++) >+ if (strcmp (riscv_cpu_info_table[i].name, cpu_string) == 0) >+ return riscv_cpu_info_table + i; >+ >+ error ("unknown cpu %qs for %<-mtune%>", cpu_string); >+ return riscv_cpu_info_table; >+} >+ >+/* Helper function for riscv_build_integer; arguments are as for >+ riscv_build_integer. */ >+ >+static int >+riscv_build_integer_1 (struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS], >+ HOST_WIDE_INT value, machine_mode mode) >+{ >+ HOST_WIDE_INT low_part = CONST_LOW_PART (value); >+ int cost = RISCV_MAX_INTEGER_OPS + 1, alt_cost; >+ struct riscv_integer_op alt_codes[RISCV_MAX_INTEGER_OPS]; >+ >+ if (SMALL_OPERAND (value) || LUI_OPERAND (value)) >+ { >+ /* Simply ADDI or LUI. */ >+ codes[0].code = UNKNOWN; >+ codes[0].value = value; >+ return 1; >+ } >+ >+ /* End with ADDI. When constructing HImode constants, do not generate any >+ intermediate value that is not itself a valid HImode constant. The >+ XORI case below will handle those remaining HImode constants. */ >+ if (low_part != 0 >+ && (mode != HImode >+ || value - low_part <= ((1 << (GET_MODE_BITSIZE (HImode) - 1)) - 1))) >+ { >+ alt_cost = 1 + riscv_build_integer_1 (alt_codes, value - low_part, mode); >+ if (alt_cost < cost) >+ { >+ alt_codes[alt_cost-1].code = PLUS; >+ alt_codes[alt_cost-1].value = low_part; >+ memcpy (codes, alt_codes, sizeof (alt_codes)); >+ cost = alt_cost; >+ } >+ } >+ >+ /* End with XORI. */ >+ if (cost > 2 && (low_part < 0 || mode == HImode)) >+ { >+ alt_cost = 1 + riscv_build_integer_1 (alt_codes, value ^ low_part, mode); >+ if (alt_cost < cost) >+ { >+ alt_codes[alt_cost-1].code = XOR; >+ alt_codes[alt_cost-1].value = low_part; >+ memcpy (codes, alt_codes, sizeof (alt_codes)); >+ cost = alt_cost; >+ } >+ } >+ >+ /* Eliminate trailing zeros and end with SLLI. */ >+ if (cost > 2 && (value & 1) == 0) >+ { >+ int shift = ctz_hwi (value); >+ unsigned HOST_WIDE_INT x = value; >+ x = sext_hwi (x >> shift, HOST_BITS_PER_WIDE_INT - shift); >+ >+ /* Don't eliminate the lower 12 bits if LUI might apply. */ >+ if (shift > IMM_BITS && !SMALL_OPERAND (x) && LUI_OPERAND (x << IMM_BITS)) >+ shift -= IMM_BITS, x <<= IMM_BITS; >+ >+ alt_cost = 1 + riscv_build_integer_1 (alt_codes, x, mode); >+ if (alt_cost < cost) >+ { >+ alt_codes[alt_cost-1].code = ASHIFT; >+ alt_codes[alt_cost-1].value = shift; >+ memcpy (codes, alt_codes, sizeof (alt_codes)); >+ cost = alt_cost; >+ } >+ } >+ >+ gcc_assert (cost <= RISCV_MAX_INTEGER_OPS); >+ return cost; >+} >+ >+/* Fill CODES with a sequence of rtl operations to load VALUE. >+ Return the number of operations needed. */ >+ >+static int >+riscv_build_integer (struct riscv_integer_op *codes, HOST_WIDE_INT value, >+ machine_mode mode) >+{ >+ int cost = riscv_build_integer_1 (codes, value, mode); >+ >+ /* Eliminate leading zeros and end with SRLI. */ >+ if (value > 0 && cost > 2) >+ { >+ struct riscv_integer_op alt_codes[RISCV_MAX_INTEGER_OPS]; >+ int alt_cost, shift = clz_hwi (value); >+ HOST_WIDE_INT shifted_val; >+ >+ /* Try filling trailing bits with 1s. */ >+ shifted_val = (value << shift) | ((((HOST_WIDE_INT) 1) << shift) - 1); >+ alt_cost = 1 + riscv_build_integer_1 (alt_codes, shifted_val, mode); >+ if (alt_cost < cost) >+ { >+ alt_codes[alt_cost-1].code = LSHIFTRT; >+ alt_codes[alt_cost-1].value = shift; >+ memcpy (codes, alt_codes, sizeof (alt_codes)); >+ cost = alt_cost; >+ } >+ >+ /* Try filling trailing bits with 0s. */ >+ shifted_val = value << shift; >+ alt_cost = 1 + riscv_build_integer_1 (alt_codes, shifted_val, mode); >+ if (alt_cost < cost) >+ { >+ alt_codes[alt_cost-1].code = LSHIFTRT; >+ alt_codes[alt_cost-1].value = shift; >+ memcpy (codes, alt_codes, sizeof (alt_codes)); >+ cost = alt_cost; >+ } >+ } >+ >+ return cost; >+} >+ >+/* Return the cost of constructing VAL in the event that a scratch >+ register is available. */ >+ >+static int >+riscv_split_integer_cost (HOST_WIDE_INT val) >+{ >+ int cost; >+ unsigned HOST_WIDE_INT loval = sext_hwi (val, 32); >+ unsigned HOST_WIDE_INT hival = sext_hwi ((val - loval) >> 32, 32); >+ struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >+ >+ cost = 2 + riscv_build_integer (codes, loval, VOIDmode); >+ if (loval != hival) >+ cost += riscv_build_integer (codes, hival, VOIDmode); >+ >+ return cost; >+} >+ >+/* Return the cost of constructing the integer constant VAL. */ >+ >+static int >+riscv_integer_cost (HOST_WIDE_INT val) >+{ >+ struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >+ return MIN (riscv_build_integer (codes, val, VOIDmode), >+ riscv_split_integer_cost (val)); >+} >+ >+/* Try to split a 64b integer into 32b parts, then reassemble. */ >+ >+static rtx >+riscv_split_integer (HOST_WIDE_INT val, machine_mode mode) >+{ >+ unsigned HOST_WIDE_INT loval = sext_hwi (val, 32); >+ unsigned HOST_WIDE_INT hival = sext_hwi ((val - loval) >> 32, 32); >+ rtx hi = gen_reg_rtx (mode), lo = gen_reg_rtx (mode); >+ >+ riscv_move_integer (hi, hi, hival, mode, FALSE); >+ riscv_move_integer (lo, lo, loval, mode, FALSE); >+ >+ hi = gen_rtx_fmt_ee (ASHIFT, mode, hi, GEN_INT (32)); >+ hi = force_reg (mode, hi); >+ >+ return gen_rtx_fmt_ee (PLUS, mode, hi, lo); >+} >+ >+/* Return true if X is a thread-local symbol. */ >+ >+static bool >+riscv_tls_symbol_p (const_rtx x) >+{ >+ return SYMBOL_REF_P (x) && SYMBOL_REF_TLS_MODEL (x) != 0; >+} >+ >+/* Return true if symbol X binds locally. */ >+ >+static bool >+riscv_symbol_binds_local_p (const_rtx x) >+{ >+ if (SYMBOL_REF_P (x)) >+ return (SYMBOL_REF_DECL (x) >+ ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) >+ : SYMBOL_REF_LOCAL_P (x)); >+ else >+ return false; >+} >+ >+/* Return the method that should be used to access SYMBOL_REF or >+ LABEL_REF X. */ >+ >+static enum riscv_symbol_type >+riscv_classify_symbol (const_rtx x) >+{ >+ if (riscv_tls_symbol_p (x)) >+ return SYMBOL_TLS; >+ >+ if (GET_CODE (x) == SYMBOL_REF && flag_pic && !riscv_symbol_binds_local_p (x)) >+ return SYMBOL_GOT_DISP; >+ >+ return riscv_cmodel == CM_MEDLOW ? SYMBOL_ABSOLUTE : SYMBOL_PCREL; >+} >+ >+/* Classify the base of symbolic expression X. */ >+ >+enum riscv_symbol_type >+riscv_classify_symbolic_expression (rtx x) >+{ >+ rtx offset; >+ >+ split_const (x, &x, &offset); >+ if (UNSPEC_ADDRESS_P (x)) >+ return UNSPEC_ADDRESS_TYPE (x); >+ >+ return riscv_classify_symbol (x); >+} >+ >+/* Return true if X is a symbolic constant. If it is, store the type of >+ the symbol in *SYMBOL_TYPE. */ >+ >+bool >+riscv_symbolic_constant_p (rtx x, enum riscv_symbol_type *symbol_type) >+{ >+ rtx offset; >+ >+ split_const (x, &x, &offset); >+ if (UNSPEC_ADDRESS_P (x)) >+ { >+ *symbol_type = UNSPEC_ADDRESS_TYPE (x); >+ x = UNSPEC_ADDRESS (x); >+ } >+ else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) >+ *symbol_type = riscv_classify_symbol (x); >+ else >+ return false; >+ >+ if (offset == const0_rtx) >+ return true; >+ >+ /* Nonzero offsets are only valid for references that don't use the GOT. */ >+ switch (*symbol_type) >+ { >+ case SYMBOL_ABSOLUTE: >+ case SYMBOL_PCREL: >+ case SYMBOL_TLS_LE: >+ /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */ >+ return sext_hwi (INTVAL (offset), 32) == INTVAL (offset); >+ >+ default: >+ return false; >+ } >+} >+ >+/* Returns the number of instructions necessary to reference a symbol. */ >+ >+static int riscv_symbol_insns (enum riscv_symbol_type type) >+{ >+ switch (type) >+ { >+ case SYMBOL_TLS: return 0; /* Depends on the TLS model. */ >+ case SYMBOL_ABSOLUTE: return 2; /* LUI + the reference. */ >+ case SYMBOL_PCREL: return 2; /* AUIPC + the reference. */ >+ case SYMBOL_TLS_LE: return 3; /* LUI + ADD TP + the reference. */ >+ case SYMBOL_GOT_DISP: return 3; /* AUIPC + LD GOT + the reference. */ >+ default: gcc_unreachable (); >+ } >+} >+ >+/* Implement TARGET_LEGITIMATE_CONSTANT_P. */ >+ >+static bool >+riscv_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x) >+{ >+ return riscv_const_insns (x) > 0; >+} >+ >+/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */ >+ >+static bool >+riscv_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x) >+{ >+ enum riscv_symbol_type type; >+ rtx base, offset; >+ >+ /* There is no assembler syntax for expressing an address-sized >+ high part. */ >+ if (GET_CODE (x) == HIGH) >+ return true; >+ >+ split_const (x, &base, &offset); >+ if (riscv_symbolic_constant_p (base, &type)) >+ { >+ /* As an optimization, don't spill symbolic constants that are as >+ cheap to rematerialize as to access in the constant pool. */ >+ if (SMALL_OPERAND (INTVAL (offset)) && riscv_symbol_insns (type) > 0) >+ return true; >+ >+ /* As an optimization, avoid needlessly generate dynamic relocations. */ >+ if (flag_pic) >+ return true; >+ } >+ >+ /* TLS symbols must be computed by riscv_legitimize_move. */ >+ if (tls_referenced_p (x)) >+ return true; >+ >+ return false; >+} >+ >+/* Return true if register REGNO is a valid base register for mode MODE. >+ STRICT_P is true if REG_OK_STRICT is in effect. */ >+ >+int >+riscv_regno_mode_ok_for_base_p (int regno, >+ machine_mode mode ATTRIBUTE_UNUSED, >+ bool strict_p) >+{ >+ if (!HARD_REGISTER_NUM_P (regno)) >+ { >+ if (!strict_p) >+ return true; >+ regno = reg_renumber[regno]; >+ } >+ >+ /* These fake registers will be eliminated to either the stack or >+ hard frame pointer, both of which are usually valid base registers. >+ Reload deals with the cases where the eliminated form isn't valid. */ >+ if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM) >+ return true; >+ >+ return GP_REG_P (regno); >+} >+ >+/* Return true if X is a valid base register for mode MODE. >+ STRICT_P is true if REG_OK_STRICT is in effect. */ >+ >+static bool >+riscv_valid_base_register_p (rtx x, machine_mode mode, bool strict_p) >+{ >+ if (!strict_p && GET_CODE (x) == SUBREG) >+ x = SUBREG_REG (x); >+ >+ return (REG_P (x) >+ && riscv_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p)); >+} >+ >+/* Return true if, for every base register BASE_REG, (plus BASE_REG X) >+ can address a value of mode MODE. */ >+ >+static bool >+riscv_valid_offset_p (rtx x, machine_mode mode) >+{ >+ /* Check that X is a signed 12-bit number. */ >+ if (!const_arith_operand (x, Pmode)) >+ return false; >+ >+ /* We may need to split multiword moves, so make sure that every word >+ is accessible. */ >+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD >+ && !SMALL_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD)) >+ return false; >+ >+ return true; >+} >+ >+/* Should a symbol of type SYMBOL_TYPE should be split in two? */ >+ >+bool >+riscv_split_symbol_type (enum riscv_symbol_type symbol_type) >+{ >+ if (symbol_type == SYMBOL_TLS_LE) >+ return true; >+ >+ if (!TARGET_EXPLICIT_RELOCS) >+ return false; >+ >+ return symbol_type == SYMBOL_ABSOLUTE || symbol_type == SYMBOL_PCREL; >+} >+ >+/* Return true if a LO_SUM can address a value of mode MODE when the >+ LO_SUM symbol has type SYM_TYPE. X is the LO_SUM second operand, which >+ is used when the mode is BLKmode. */ >+ >+static bool >+riscv_valid_lo_sum_p (enum riscv_symbol_type sym_type, machine_mode mode, >+ rtx x) >+{ >+ int align, size; >+ >+ /* Check that symbols of type SYMBOL_TYPE can be used to access values >+ of mode MODE. */ >+ if (riscv_symbol_insns (sym_type) == 0) >+ return false; >+ >+ /* Check that there is a known low-part relocation. */ >+ if (!riscv_split_symbol_type (sym_type)) >+ return false; >+ >+ /* We can't tell size or alignment when we have BLKmode, so try extracing a >+ decl from the symbol if possible. */ >+ if (mode == BLKmode) >+ { >+ rtx offset; >+ >+ /* Extract the symbol from the LO_SUM operand, if any. */ >+ split_const (x, &x, &offset); >+ >+ /* Might be a CODE_LABEL. We can compute align but not size for that, >+ so don't bother trying to handle it. */ >+ if (!SYMBOL_REF_P (x)) >+ return false; >+ >+ /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ >+ align = (SYMBOL_REF_DECL (x) >+ ? DECL_ALIGN (SYMBOL_REF_DECL (x)) >+ : 1); >+ size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) >+ ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) >+ : 2*BITS_PER_WORD); >+ } >+ else >+ { >+ align = GET_MODE_ALIGNMENT (mode); >+ size = GET_MODE_BITSIZE (mode); >+ } >+ >+ /* We may need to split multiword moves, so make sure that each word >+ can be accessed without inducing a carry. */ >+ if (size > BITS_PER_WORD >+ && (!TARGET_STRICT_ALIGN || size > align)) >+ return false; >+ >+ return true; >+} >+ >+/* Return true if X is a valid address for machine mode MODE. If it is, >+ fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in >+ effect. */ >+ >+static bool >+riscv_classify_address (struct riscv_address_info *info, rtx x, >+ machine_mode mode, bool strict_p) >+{ >+ switch (GET_CODE (x)) >+ { >+ case REG: >+ case SUBREG: >+ info->type = ADDRESS_REG; >+ info->reg = x; >+ info->offset = const0_rtx; >+ return riscv_valid_base_register_p (info->reg, mode, strict_p); >+ >+ case PLUS: >+ info->type = ADDRESS_REG; >+ info->reg = XEXP (x, 0); >+ info->offset = XEXP (x, 1); >+ return (riscv_valid_base_register_p (info->reg, mode, strict_p) >+ && riscv_valid_offset_p (info->offset, mode)); >+ >+ case LO_SUM: >+ info->type = ADDRESS_LO_SUM; >+ info->reg = XEXP (x, 0); >+ info->offset = XEXP (x, 1); >+ /* We have to trust the creator of the LO_SUM to do something vaguely >+ sane. Target-independent code that creates a LO_SUM should also >+ create and verify the matching HIGH. Target-independent code that >+ adds an offset to a LO_SUM must prove that the offset will not >+ induce a carry. Failure to do either of these things would be >+ a bug, and we are not required to check for it here. The RISC-V >+ backend itself should only create LO_SUMs for valid symbolic >+ constants, with the high part being either a HIGH or a copy >+ of _gp. */ >+ info->symbol_type >+ = riscv_classify_symbolic_expression (info->offset); >+ return (riscv_valid_base_register_p (info->reg, mode, strict_p) >+ && riscv_valid_lo_sum_p (info->symbol_type, mode, info->offset)); >+ >+ case CONST_INT: >+ /* Small-integer addresses don't occur very often, but they >+ are legitimate if x0 is a valid base register. */ >+ info->type = ADDRESS_CONST_INT; >+ return SMALL_OPERAND (INTVAL (x)); >+ >+ default: >+ return false; >+ } >+} >+ >+/* Implement TARGET_LEGITIMATE_ADDRESS_P. */ >+ >+static bool >+riscv_legitimate_address_p (machine_mode mode, rtx x, bool strict_p) >+{ >+ struct riscv_address_info addr; >+ >+ return riscv_classify_address (&addr, x, mode, strict_p); >+} >+ >+/* Return true if hard reg REGNO can be used in compressed instructions. */ >+ >+static bool >+riscv_compressed_reg_p (int regno) >+{ >+ /* x8-x15/f8-f15 are compressible registers. */ >+ return (TARGET_RVC && (IN_RANGE (regno, GP_REG_FIRST + 8, GP_REG_FIRST + 15) >+ || IN_RANGE (regno, FP_REG_FIRST + 8, FP_REG_FIRST + 15))); >+} >+ >+/* Return true if x is an unsigned 5-bit immediate scaled by 4. */ >+ >+static bool >+riscv_compressed_lw_offset_p (rtx x) >+{ >+ return (CONST_INT_P (x) >+ && (INTVAL (x) & 3) == 0 >+ && IN_RANGE (INTVAL (x), 0, CSW_MAX_OFFSET)); >+} >+ >+/* Return true if load/store from/to address x can be compressed. */ >+ >+static bool >+riscv_compressed_lw_address_p (rtx x) >+{ >+ struct riscv_address_info addr; >+ bool result = riscv_classify_address (&addr, x, GET_MODE (x), >+ reload_completed); >+ >+ /* Before reload, assuming all load/stores of valid addresses get compressed >+ gives better code size than checking if the address is reg + small_offset >+ early on. */ >+ if (result && !reload_completed) >+ return true; >+ >+ /* Return false if address is not compressed_reg + small_offset. */ >+ if (!result >+ || addr.type != ADDRESS_REG >+ || (!riscv_compressed_reg_p (REGNO (addr.reg)) >+ && addr.reg != stack_pointer_rtx) >+ || !riscv_compressed_lw_offset_p (addr.offset)) >+ return false; >+ >+ return result; >+} >+ >+/* Return the number of instructions needed to load or store a value >+ of mode MODE at address X. Return 0 if X isn't valid for MODE. >+ Assume that multiword moves may need to be split into word moves >+ if MIGHT_SPLIT_P, otherwise assume that a single load or store is >+ enough. */ >+ >+int >+riscv_address_insns (rtx x, machine_mode mode, bool might_split_p) >+{ >+ struct riscv_address_info addr = {}; >+ int n = 1; >+ >+ if (!riscv_classify_address (&addr, x, mode, false)) >+ { >+ /* This could be a pattern from the pic.md file. In which case we want >+ this address to always have a cost of 3 to make it as expensive as the >+ most expensive symbol. This prevents constant propagation from >+ preferring symbols over register plus offset. */ >+ return 3; >+ } >+ >+ /* BLKmode is used for single unaligned loads and stores and should >+ not count as a multiword mode. */ >+ if (mode != BLKmode && might_split_p) >+ n += (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >+ >+ if (addr.type == ADDRESS_LO_SUM) >+ n += riscv_symbol_insns (addr.symbol_type) - 1; >+ >+ return n; >+} >+ >+/* Return the number of instructions needed to load constant X. >+ Return 0 if X isn't a valid constant. */ >+ >+int >+riscv_const_insns (rtx x) >+{ >+ enum riscv_symbol_type symbol_type; >+ rtx offset; >+ >+ switch (GET_CODE (x)) >+ { >+ case HIGH: >+ if (!riscv_symbolic_constant_p (XEXP (x, 0), &symbol_type) >+ || !riscv_split_symbol_type (symbol_type)) >+ return 0; >+ >+ /* This is simply an LUI. */ >+ return 1; >+ >+ case CONST_INT: >+ { >+ int cost = riscv_integer_cost (INTVAL (x)); >+ /* Force complicated constants to memory. */ >+ return cost < 4 ? cost : 0; >+ } >+ >+ case CONST_DOUBLE: >+ case CONST_VECTOR: >+ /* We can use x0 to load floating-point zero. */ >+ return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0; >+ >+ case CONST: >+ /* See if we can refer to X directly. */ >+ if (riscv_symbolic_constant_p (x, &symbol_type)) >+ return riscv_symbol_insns (symbol_type); >+ >+ /* Otherwise try splitting the constant into a base and offset. */ >+ split_const (x, &x, &offset); >+ if (offset != 0) >+ { >+ int n = riscv_const_insns (x); >+ if (n != 0) >+ return n + riscv_integer_cost (INTVAL (offset)); >+ } >+ return 0; >+ >+ case SYMBOL_REF: >+ case LABEL_REF: >+ return riscv_symbol_insns (riscv_classify_symbol (x)); >+ >+ default: >+ return 0; >+ } >+} >+ >+/* X is a doubleword constant that can be handled by splitting it into >+ two words and loading each word separately. Return the number of >+ instructions required to do this. */ >+ >+int >+riscv_split_const_insns (rtx x) >+{ >+ unsigned int low, high; >+ >+ low = riscv_const_insns (riscv_subword (x, false)); >+ high = riscv_const_insns (riscv_subword (x, true)); >+ gcc_assert (low > 0 && high > 0); >+ return low + high; >+} >+ >+/* Return the number of instructions needed to implement INSN, >+ given that it loads from or stores to MEM. */ >+ >+int >+riscv_load_store_insns (rtx mem, rtx_insn *insn) >+{ >+ machine_mode mode; >+ bool might_split_p; >+ rtx set; >+ >+ gcc_assert (MEM_P (mem)); >+ mode = GET_MODE (mem); >+ >+ /* Try to prove that INSN does not need to be split. */ >+ might_split_p = true; >+ if (GET_MODE_BITSIZE (mode) <= 32) >+ might_split_p = false; >+ else if (GET_MODE_BITSIZE (mode) == 64) >+ { >+ set = single_set (insn); >+ if (set && !riscv_split_64bit_move_p (SET_DEST (set), SET_SRC (set))) >+ might_split_p = false; >+ } >+ >+ return riscv_address_insns (XEXP (mem, 0), mode, might_split_p); >+} >+ >+/* Emit a move from SRC to DEST. Assume that the move expanders can >+ handle all moves if !can_create_pseudo_p (). The distinction is >+ important because, unlike emit_move_insn, the move expanders know >+ how to force Pmode objects into the constant pool even when the >+ constant pool address is not itself legitimate. */ >+ >+rtx >+riscv_emit_move (rtx dest, rtx src) >+{ >+ return (can_create_pseudo_p () >+ ? emit_move_insn (dest, src) >+ : emit_move_insn_1 (dest, src)); >+} >+ >+/* Emit an instruction of the form (set TARGET SRC). */ >+ >+static rtx >+riscv_emit_set (rtx target, rtx src) >+{ >+ emit_insn (gen_rtx_SET (target, src)); >+ return target; >+} >+ >+/* Emit an instruction of the form (set DEST (CODE X Y)). */ >+ >+static rtx >+riscv_emit_binary (enum rtx_code code, rtx dest, rtx x, rtx y) >+{ >+ return riscv_emit_set (dest, gen_rtx_fmt_ee (code, GET_MODE (dest), x, y)); >+} >+ >+/* Compute (CODE X Y) and store the result in a new register >+ of mode MODE. Return that new register. */ >+ >+static rtx >+riscv_force_binary (machine_mode mode, enum rtx_code code, rtx x, rtx y) >+{ >+ return riscv_emit_binary (code, gen_reg_rtx (mode), x, y); >+} >+ >+/* Copy VALUE to a register and return that register. If new pseudos >+ are allowed, copy it into a new register, otherwise use DEST. */ >+ >+static rtx >+riscv_force_temporary (rtx dest, rtx value, bool in_splitter) >+{ >+ /* We can't call gen_reg_rtx from a splitter, because this might realloc >+ the regno_reg_rtx array, which would invalidate reg rtx pointers in the >+ combine undo buffer. */ >+ if (can_create_pseudo_p () && !in_splitter) >+ return force_reg (Pmode, value); >+ else >+ { >+ riscv_emit_move (dest, value); >+ return dest; >+ } >+} >+ >+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE, >+ then add CONST_INT OFFSET to the result. */ >+ >+static rtx >+riscv_unspec_address_offset (rtx base, rtx offset, >+ enum riscv_symbol_type symbol_type) >+{ >+ base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), >+ UNSPEC_ADDRESS_FIRST + symbol_type); >+ if (offset != const0_rtx) >+ base = gen_rtx_PLUS (Pmode, base, offset); >+ return gen_rtx_CONST (Pmode, base); >+} >+ >+/* Return an UNSPEC address with underlying address ADDRESS and symbol >+ type SYMBOL_TYPE. */ >+ >+rtx >+riscv_unspec_address (rtx address, enum riscv_symbol_type symbol_type) >+{ >+ rtx base, offset; >+ >+ split_const (address, &base, &offset); >+ return riscv_unspec_address_offset (base, offset, symbol_type); >+} >+ >+/* If OP is an UNSPEC address, return the address to which it refers, >+ otherwise return OP itself. */ >+ >+static rtx >+riscv_strip_unspec_address (rtx op) >+{ >+ rtx base, offset; >+ >+ split_const (op, &base, &offset); >+ if (UNSPEC_ADDRESS_P (base)) >+ op = plus_constant (Pmode, UNSPEC_ADDRESS (base), INTVAL (offset)); >+ return op; >+} >+ >+/* If riscv_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the >+ high part to BASE and return the result. Just return BASE otherwise. >+ TEMP is as for riscv_force_temporary. >+ >+ The returned expression can be used as the first operand to a LO_SUM. */ >+ >+static rtx >+riscv_unspec_offset_high (rtx temp, rtx addr, enum riscv_symbol_type symbol_type) >+{ >+ addr = gen_rtx_HIGH (Pmode, riscv_unspec_address (addr, symbol_type)); >+ return riscv_force_temporary (temp, addr, FALSE); >+} >+ >+/* Load an entry from the GOT for a TLS GD access. */ >+ >+static rtx riscv_got_load_tls_gd (rtx dest, rtx sym) >+{ >+ if (Pmode == DImode) >+ return gen_got_load_tls_gddi (dest, sym); >+ else >+ return gen_got_load_tls_gdsi (dest, sym); >+} >+ >+/* Load an entry from the GOT for a TLS IE access. */ >+ >+static rtx riscv_got_load_tls_ie (rtx dest, rtx sym) >+{ >+ if (Pmode == DImode) >+ return gen_got_load_tls_iedi (dest, sym); >+ else >+ return gen_got_load_tls_iesi (dest, sym); >+} >+ >+/* Add in the thread pointer for a TLS LE access. */ >+ >+static rtx riscv_tls_add_tp_le (rtx dest, rtx base, rtx sym) >+{ >+ rtx tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); >+ if (Pmode == DImode) >+ return gen_tls_add_tp_ledi (dest, base, tp, sym); >+ else >+ return gen_tls_add_tp_lesi (dest, base, tp, sym); >+} >+ >+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise >+ it appears in a MEM of that mode. Return true if ADDR is a legitimate >+ constant in that context and can be split into high and low parts. >+ If so, and if LOW_OUT is nonnull, emit the high part and store the >+ low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise. >+ >+ TEMP is as for riscv_force_temporary and is used to load the high >+ part into a register. >+ >+ When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be >+ a legitimize SET_SRC for an .md pattern, otherwise the low part >+ is guaranteed to be a legitimate address for mode MODE. */ >+ >+bool >+riscv_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out, >+ bool in_splitter) >+{ >+ enum riscv_symbol_type symbol_type; >+ >+ if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE) >+ || !riscv_symbolic_constant_p (addr, &symbol_type) >+ || riscv_symbol_insns (symbol_type) == 0 >+ || !riscv_split_symbol_type (symbol_type)) >+ return false; >+ >+ if (low_out) >+ switch (symbol_type) >+ { >+ case SYMBOL_ABSOLUTE: >+ { >+ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (addr)); >+ high = riscv_force_temporary (temp, high, in_splitter); >+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr); >+ } >+ break; >+ >+ case SYMBOL_PCREL: >+ { >+ static unsigned seqno; >+ char buf[32]; >+ rtx label; >+ >+ ssize_t bytes = snprintf (buf, sizeof (buf), ".LA%u", seqno); >+ gcc_assert ((size_t) bytes < sizeof (buf)); >+ >+ label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); >+ SYMBOL_REF_FLAGS (label) |= SYMBOL_FLAG_LOCAL; >+ /* ??? Ugly hack to make weak symbols work. May need to change the >+ RTL for the auipc and/or low patterns to get a better fix for >+ this. */ >+ if (! nonzero_address_p (addr)) >+ SYMBOL_REF_WEAK (label) = 1; >+ >+ if (temp == NULL) >+ temp = gen_reg_rtx (Pmode); >+ >+ if (Pmode == DImode) >+ emit_insn (gen_auipcdi (temp, copy_rtx (addr), GEN_INT (seqno))); >+ else >+ emit_insn (gen_auipcsi (temp, copy_rtx (addr), GEN_INT (seqno))); >+ >+ *low_out = gen_rtx_LO_SUM (Pmode, temp, label); >+ >+ seqno++; >+ } >+ break; >+ >+ default: >+ gcc_unreachable (); >+ } >+ >+ return true; >+} >+ >+/* Return a legitimate address for REG + OFFSET. TEMP is as for >+ riscv_force_temporary; it is only needed when OFFSET is not a >+ SMALL_OPERAND. */ >+ >+static rtx >+riscv_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset) >+{ >+ if (!SMALL_OPERAND (offset)) >+ { >+ rtx high; >+ >+ /* Leave OFFSET as a 16-bit offset and put the excess in HIGH. >+ The addition inside the macro CONST_HIGH_PART may cause an >+ overflow, so we need to force a sign-extension check. */ >+ high = gen_int_mode (CONST_HIGH_PART (offset), Pmode); >+ offset = CONST_LOW_PART (offset); >+ high = riscv_force_temporary (temp, high, FALSE); >+ reg = riscv_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg), >+ FALSE); >+ } >+ return plus_constant (Pmode, reg, offset); >+} >+ >+/* The __tls_get_attr symbol. */ >+static GTY(()) rtx riscv_tls_symbol; >+ >+/* Return an instruction sequence that calls __tls_get_addr. SYM is >+ the TLS symbol we are referencing and TYPE is the symbol type to use >+ (either global dynamic or local dynamic). RESULT is an RTX for the >+ return value location. */ >+ >+static rtx_insn * >+riscv_call_tls_get_addr (rtx sym, rtx result) >+{ >+ rtx a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST), func; >+ rtx_insn *insn; >+ >+ if (!riscv_tls_symbol) >+ riscv_tls_symbol = init_one_libfunc ("__tls_get_addr"); >+ func = gen_rtx_MEM (FUNCTION_MODE, riscv_tls_symbol); >+ >+ start_sequence (); >+ >+ emit_insn (riscv_got_load_tls_gd (a0, sym)); >+ insn = emit_call_insn (gen_call_value (result, func, const0_rtx, NULL)); >+ RTL_CONST_CALL_P (insn) = 1; >+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); >+ insn = get_insns (); >+ >+ end_sequence (); >+ >+ return insn; >+} >+ >+/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return >+ its address. The return value will be both a valid address and a valid >+ SET_SRC (either a REG or a LO_SUM). */ >+ >+static rtx >+riscv_legitimize_tls_address (rtx loc) >+{ >+ rtx dest, tp, tmp; >+ enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); >+ >+#if 0 >+ /* TLS copy relocs are now deprecated and should not be used. */ >+ /* Since we support TLS copy relocs, non-PIC TLS accesses may all use LE. */ >+ if (!flag_pic) >+ model = TLS_MODEL_LOCAL_EXEC; >+#endif >+ >+ switch (model) >+ { >+ case TLS_MODEL_LOCAL_DYNAMIC: >+ /* Rely on section anchors for the optimization that LDM TLS >+ provides. The anchor's address is loaded with GD TLS. */ >+ case TLS_MODEL_GLOBAL_DYNAMIC: >+ tmp = gen_rtx_REG (Pmode, GP_RETURN); >+ dest = gen_reg_rtx (Pmode); >+ emit_libcall_block (riscv_call_tls_get_addr (loc, tmp), dest, tmp, loc); >+ break; >+ >+ case TLS_MODEL_INITIAL_EXEC: >+ /* la.tls.ie; tp-relative add */ >+ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); >+ tmp = gen_reg_rtx (Pmode); >+ emit_insn (riscv_got_load_tls_ie (tmp, loc)); >+ dest = gen_reg_rtx (Pmode); >+ emit_insn (gen_add3_insn (dest, tmp, tp)); >+ break; >+ >+ case TLS_MODEL_LOCAL_EXEC: >+ tmp = riscv_unspec_offset_high (NULL, loc, SYMBOL_TLS_LE); >+ dest = gen_reg_rtx (Pmode); >+ emit_insn (riscv_tls_add_tp_le (dest, tmp, loc)); >+ dest = gen_rtx_LO_SUM (Pmode, dest, >+ riscv_unspec_address (loc, SYMBOL_TLS_LE)); >+ break; >+ >+ default: >+ gcc_unreachable (); >+ } >+ return dest; >+} >+ >+/* If X is not a valid address for mode MODE, force it into a register. */ >+ >+static rtx >+riscv_force_address (rtx x, machine_mode mode) >+{ >+ if (!riscv_legitimate_address_p (mode, x, false)) >+ x = force_reg (Pmode, x); >+ return x; >+} >+ >+/* Modify base + offset so that offset fits within a compressed load/store insn >+ and the excess is added to base. */ >+ >+static rtx >+riscv_shorten_lw_offset (rtx base, HOST_WIDE_INT offset) >+{ >+ rtx addr, high; >+ /* Leave OFFSET as an unsigned 5-bit offset scaled by 4 and put the excess >+ into HIGH. */ >+ high = GEN_INT (offset & ~CSW_MAX_OFFSET); >+ offset &= CSW_MAX_OFFSET; >+ if (!SMALL_OPERAND (INTVAL (high))) >+ high = force_reg (Pmode, high); >+ base = force_reg (Pmode, gen_rtx_PLUS (Pmode, high, base)); >+ addr = plus_constant (Pmode, base, offset); >+ return addr; >+} >+ >+/* This function is used to implement LEGITIMIZE_ADDRESS. If X can >+ be legitimized in a way that the generic machinery might not expect, >+ return a new address, otherwise return NULL. MODE is the mode of >+ the memory being accessed. */ >+ >+static rtx >+riscv_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, >+ machine_mode mode) >+{ >+ rtx addr; >+ >+ if (riscv_tls_symbol_p (x)) >+ return riscv_legitimize_tls_address (x); >+ >+ /* See if the address can split into a high part and a LO_SUM. */ >+ if (riscv_split_symbol (NULL, x, mode, &addr, FALSE)) >+ return riscv_force_address (addr, mode); >+ >+ /* Handle BASE + OFFSET. */ >+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)) >+ && INTVAL (XEXP (x, 1)) != 0) >+ { >+ rtx base = XEXP (x, 0); >+ HOST_WIDE_INT offset = INTVAL (XEXP (x, 1)); >+ >+ if (!riscv_valid_base_register_p (base, mode, false)) >+ base = copy_to_mode_reg (Pmode, base); >+ if (optimize_function_for_size_p (cfun) >+ && (strcmp (current_pass->name, "shorten_memrefs") == 0) >+ && mode == SImode) >+ /* Convert BASE + LARGE_OFFSET into NEW_BASE + SMALL_OFFSET to allow >+ possible compressed load/store. */ >+ addr = riscv_shorten_lw_offset (base, offset); >+ else >+ addr = riscv_add_offset (NULL, base, offset); >+ return riscv_force_address (addr, mode); >+ } >+ >+ return x; >+} >+ >+/* Load VALUE into DEST. TEMP is as for riscv_force_temporary. ORIG_MODE >+ is the original src mode before promotion. */ >+ >+void >+riscv_move_integer (rtx temp, rtx dest, HOST_WIDE_INT value, >+ machine_mode orig_mode, bool in_splitter) >+{ >+ struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS]; >+ machine_mode mode; >+ int i, num_ops; >+ rtx x; >+ >+ /* We can't call gen_reg_rtx from a splitter, because this might realloc >+ the regno_reg_rtx array, which would invalidate reg rtx pointers in the >+ combine undo buffer. */ >+ bool can_create_pseudo = can_create_pseudo_p () && ! in_splitter; >+ >+ mode = GET_MODE (dest); >+ /* We use the original mode for the riscv_build_integer call, because HImode >+ values are given special treatment. */ >+ num_ops = riscv_build_integer (codes, value, orig_mode); >+ >+ if (can_create_pseudo && num_ops > 2 /* not a simple constant */ >+ && num_ops >= riscv_split_integer_cost (value)) >+ x = riscv_split_integer (value, mode); >+ else >+ { >+ /* Apply each binary operation to X. */ >+ x = GEN_INT (codes[0].value); >+ >+ for (i = 1; i < num_ops; i++) >+ { >+ if (!can_create_pseudo) >+ x = riscv_emit_set (temp, x); >+ else >+ x = force_reg (mode, x); >+ >+ x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value)); >+ } >+ } >+ >+ riscv_emit_set (dest, x); >+} >+ >+/* Subroutine of riscv_legitimize_move. Move constant SRC into register >+ DEST given that SRC satisfies immediate_operand but doesn't satisfy >+ move_operand. */ >+ >+static void >+riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src) >+{ >+ rtx base, offset; >+ >+ /* Split moves of big integers into smaller pieces. */ >+ if (splittable_const_int_operand (src, mode)) >+ { >+ riscv_move_integer (dest, dest, INTVAL (src), mode, FALSE); >+ return; >+ } >+ >+ /* Split moves of symbolic constants into high/low pairs. */ >+ if (riscv_split_symbol (dest, src, MAX_MACHINE_MODE, &src, FALSE)) >+ { >+ riscv_emit_set (dest, src); >+ return; >+ } >+ >+ /* Generate the appropriate access sequences for TLS symbols. */ >+ if (riscv_tls_symbol_p (src)) >+ { >+ riscv_emit_move (dest, riscv_legitimize_tls_address (src)); >+ return; >+ } >+ >+ /* If we have (const (plus symbol offset)), and that expression cannot >+ be forced into memory, load the symbol first and add in the offset. Also >+ prefer to do this even if the constant _can_ be forced into memory, as it >+ usually produces better code. */ >+ split_const (src, &base, &offset); >+ if (offset != const0_rtx >+ && (targetm.cannot_force_const_mem (mode, src) || can_create_pseudo_p ())) >+ { >+ base = riscv_force_temporary (dest, base, FALSE); >+ riscv_emit_move (dest, riscv_add_offset (NULL, base, INTVAL (offset))); >+ return; >+ } >+ >+ src = force_const_mem (mode, src); >+ >+ /* When using explicit relocs, constant pool references are sometimes >+ not legitimate addresses. */ >+ riscv_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0), FALSE); >+ riscv_emit_move (dest, src); >+} >+ >+/* If (set DEST SRC) is not a valid move instruction, emit an equivalent >+ sequence that is valid. */ >+ >+bool >+riscv_legitimize_move (machine_mode mode, rtx dest, rtx src) >+{ >+ /* Look for qimode loads from memory where the dest is reg */ >+ if(mode == QImode && MEM_P (src) && REG_P (dest) && can_create_pseudo_p()) >+ { >+ rtx temp_reg; >+ >+ if (TARGET_64BIT) >+ { >+ temp_reg = gen_reg_rtx (DImode); >+ emit_insn(gen_zero_extendqidi2(temp_reg, src)); >+ } >+ else >+ { >+ temp_reg = gen_reg_rtx (SImode); >+ emit_insn(gen_zero_extendqisi2(temp_reg, src)); >+ } >+ >+ riscv_emit_move(dest, gen_lowpart(QImode,temp_reg)); >+ return true; >+ } >+ >+ if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode)) >+ { >+ rtx reg; >+ >+ if (GET_CODE (src) == CONST_INT) >+ { >+ /* Apply the equivalent of PROMOTE_MODE here for constants to >+ improve cse. */ >+ machine_mode promoted_mode = mode; >+ if (GET_MODE_CLASS (mode) == MODE_INT >+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD) >+ promoted_mode = word_mode; >+ >+ if (splittable_const_int_operand (src, mode)) >+ { >+ reg = gen_reg_rtx (promoted_mode); >+ riscv_move_integer (reg, reg, INTVAL (src), mode, FALSE); >+ } >+ else >+ reg = force_reg (promoted_mode, src); >+ >+ if (promoted_mode != mode) >+ reg = gen_lowpart (mode, reg); >+ } >+ else >+ reg = force_reg (mode, src); >+ riscv_emit_move (dest, reg); >+ return true; >+ } >+ >+ /* We need to deal with constants that would be legitimate >+ immediate_operands but aren't legitimate move_operands. */ >+ if (CONSTANT_P (src) && !move_operand (src, mode)) >+ { >+ riscv_legitimize_const_move (mode, dest, src); >+ set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src)); >+ return true; >+ } >+ >+ /* RISC-V GCC may generate non-legitimate address due to we provide some >+ pattern for optimize access PIC local symbol and it's make GCC generate >+ unrecognizable instruction during optmizing. */ >+ >+ if (MEM_P (dest) && !riscv_legitimate_address_p (mode, XEXP (dest, 0), >+ reload_completed)) >+ { >+ XEXP (dest, 0) = riscv_force_address (XEXP (dest, 0), mode); >+ } >+ >+ if (MEM_P (src) && !riscv_legitimate_address_p (mode, XEXP (src, 0), >+ reload_completed)) >+ { >+ XEXP (src, 0) = riscv_force_address (XEXP (src, 0), mode); >+ } >+ >+ return false; >+} >+ >+/* Return true if there is an instruction that implements CODE and accepts >+ X as an immediate operand. */ >+ >+static int >+riscv_immediate_operand_p (int code, HOST_WIDE_INT x) >+{ >+ switch (code) >+ { >+ case ASHIFT: >+ case ASHIFTRT: >+ case LSHIFTRT: >+ /* All shift counts are truncated to a valid constant. */ >+ return true; >+ >+ case AND: >+ case IOR: >+ case XOR: >+ case PLUS: >+ case LT: >+ case LTU: >+ /* These instructions take 12-bit signed immediates. */ >+ return SMALL_OPERAND (x); >+ >+ case LE: >+ /* We add 1 to the immediate and use SLT. */ >+ return SMALL_OPERAND (x + 1); >+ >+ case LEU: >+ /* Likewise SLTU, but reject the always-true case. */ >+ return SMALL_OPERAND (x + 1) && x + 1 != 0; >+ >+ case GE: >+ case GEU: >+ /* We can emulate an immediate of 1 by using GT/GTU against x0. */ >+ return x == 1; >+ >+ default: >+ /* By default assume that x0 can be used for 0. */ >+ return x == 0; >+ } >+} >+ >+/* Return the cost of binary operation X, given that the instruction >+ sequence for a word-sized or smaller operation takes SIGNLE_INSNS >+ instructions and that the sequence of a double-word operation takes >+ DOUBLE_INSNS instructions. */ >+ >+static int >+riscv_binary_cost (rtx x, int single_insns, int double_insns) >+{ >+ if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2) >+ return COSTS_N_INSNS (double_insns); >+ return COSTS_N_INSNS (single_insns); >+} >+ >+/* Return the cost of sign- or zero-extending OP. */ >+ >+static int >+riscv_extend_cost (rtx op, bool unsigned_p) >+{ >+ if (MEM_P (op)) >+ return 0; >+ >+ if (unsigned_p && GET_MODE (op) == QImode) >+ /* We can use ANDI. */ >+ return COSTS_N_INSNS (1); >+ >+ if (!unsigned_p && GET_MODE (op) == SImode) >+ /* We can use SEXT.W. */ >+ return COSTS_N_INSNS (1); >+ >+ /* We need to use a shift left and a shift right. */ >+ return COSTS_N_INSNS (2); >+} >+ >+/* Implement TARGET_RTX_COSTS. */ >+ >+#define SINGLE_SHIFT_COST 1 >+ >+static bool >+riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UNUSED, >+ int *total, bool speed) >+{ >+ bool float_mode_p = FLOAT_MODE_P (mode); >+ int cost; >+ >+ switch (GET_CODE (x)) >+ { >+ case CONST_INT: >+ if (riscv_immediate_operand_p (outer_code, INTVAL (x))) >+ { >+ *total = 0; >+ return true; >+ } >+ /* Fall through. */ >+ >+ case SYMBOL_REF: >+ case LABEL_REF: >+ case CONST_DOUBLE: >+ case CONST: >+ if ((cost = riscv_const_insns (x)) > 0) >+ { >+ /* If the constant is likely to be stored in a GPR, SETs of >+ single-insn constants are as cheap as register sets; we >+ never want to CSE them. */ >+ if (cost == 1 && outer_code == SET) >+ *total = 0; >+ /* When we load a constant more than once, it usually is better >+ to duplicate the last operation in the sequence than to CSE >+ the constant itself. */ >+ else if (outer_code == SET || GET_MODE (x) == VOIDmode) >+ *total = COSTS_N_INSNS (1); >+ } >+ else /* The instruction will be fetched from the constant pool. */ >+ *total = COSTS_N_INSNS (riscv_symbol_insns (SYMBOL_ABSOLUTE)); >+ return true; >+ >+ case MEM: >+ /* If the address is legitimate, return the number of >+ instructions it needs. */ >+ if ((cost = riscv_address_insns (XEXP (x, 0), mode, true)) > 0) >+ { >+ *total = COSTS_N_INSNS (cost + tune_info->memory_cost); >+ return true; >+ } >+ /* Otherwise use the default handling. */ >+ return false; >+ >+ case NOT: >+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1); >+ return false; >+ >+ case AND: >+ case IOR: >+ case XOR: >+ /* Double-word operations use two single-word operations. */ >+ *total = riscv_binary_cost (x, 1, 2); >+ return false; >+ >+ case ZERO_EXTRACT: >+ /* This is an SImode shift. */ >+ if (outer_code == SET >+ && CONST_INT_P (XEXP (x, 1)) >+ && CONST_INT_P (XEXP (x, 2)) >+ && (INTVAL (XEXP (x, 2)) > 0) >+ && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (x, 2)) == 32)) >+ { >+ *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >+ return true; >+ } >+ return false; >+ >+ case ASHIFT: >+ case ASHIFTRT: >+ case LSHIFTRT: >+ *total = riscv_binary_cost (x, SINGLE_SHIFT_COST, >+ CONSTANT_P (XEXP (x, 1)) ? 4 : 9); >+ return false; >+ >+ case ABS: >+ *total = COSTS_N_INSNS (float_mode_p ? 1 : 3); >+ return false; >+ >+ case LO_SUM: >+ *total = set_src_cost (XEXP (x, 0), mode, speed); >+ return true; >+ >+ case LT: >+ /* This is an SImode shift. */ >+ if (outer_code == SET && GET_MODE (x) == DImode >+ && GET_MODE (XEXP (x, 0)) == SImode) >+ { >+ *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >+ return true; >+ } >+ /* Fall through. */ >+ case LTU: >+ case LE: >+ case LEU: >+ case GT: >+ case GTU: >+ case GE: >+ case GEU: >+ case EQ: >+ case NE: >+ /* Branch comparisons have VOIDmode, so use the first operand's >+ mode instead. */ >+ mode = GET_MODE (XEXP (x, 0)); >+ if (float_mode_p) >+ *total = tune_info->fp_add[mode == DFmode]; >+ else >+ *total = riscv_binary_cost (x, 1, 3); >+ return false; >+ >+ case UNORDERED: >+ case ORDERED: >+ /* (FEQ(A, A) & FEQ(B, B)) compared against 0. */ >+ mode = GET_MODE (XEXP (x, 0)); >+ *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2); >+ return false; >+ >+ case UNEQ: >+ /* (FEQ(A, A) & FEQ(B, B)) compared against FEQ(A, B). */ >+ mode = GET_MODE (XEXP (x, 0)); >+ *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (3); >+ return false; >+ >+ case LTGT: >+ /* (FLT(A, A) || FGT(B, B)). */ >+ mode = GET_MODE (XEXP (x, 0)); >+ *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (2); >+ return false; >+ >+ case UNGE: >+ case UNGT: >+ case UNLE: >+ case UNLT: >+ /* FLT or FLE, but guarded by an FFLAGS read and write. */ >+ mode = GET_MODE (XEXP (x, 0)); >+ *total = tune_info->fp_add[mode == DFmode] + COSTS_N_INSNS (4); >+ return false; >+ >+ case MINUS: >+ case PLUS: >+ if (float_mode_p) >+ *total = tune_info->fp_add[mode == DFmode]; >+ else >+ *total = riscv_binary_cost (x, 1, 4); >+ return false; >+ >+ case NEG: >+ { >+ rtx op = XEXP (x, 0); >+ if (GET_CODE (op) == FMA && !HONOR_SIGNED_ZEROS (mode)) >+ { >+ *total = (tune_info->fp_mul[mode == DFmode] >+ + set_src_cost (XEXP (op, 0), mode, speed) >+ + set_src_cost (XEXP (op, 1), mode, speed) >+ + set_src_cost (XEXP (op, 2), mode, speed)); >+ return true; >+ } >+ } >+ >+ if (float_mode_p) >+ *total = tune_info->fp_add[mode == DFmode]; >+ else >+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1); >+ return false; >+ >+ case MULT: >+ if (float_mode_p) >+ *total = tune_info->fp_mul[mode == DFmode]; >+ else if (!TARGET_MUL) >+ /* Estimate the cost of a library call. */ >+ *total = COSTS_N_INSNS (speed ? 32 : 6); >+ else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) >+ *total = 3 * tune_info->int_mul[0] + COSTS_N_INSNS (2); >+ else if (!speed) >+ *total = COSTS_N_INSNS (1); >+ else >+ *total = tune_info->int_mul[mode == DImode]; >+ return false; >+ >+ case DIV: >+ case SQRT: >+ case MOD: >+ if (float_mode_p) >+ { >+ *total = tune_info->fp_div[mode == DFmode]; >+ return false; >+ } >+ /* Fall through. */ >+ >+ case UDIV: >+ case UMOD: >+ if (!TARGET_DIV) >+ /* Estimate the cost of a library call. */ >+ *total = COSTS_N_INSNS (speed ? 32 : 6); >+ else if (speed) >+ *total = tune_info->int_div[mode == DImode]; >+ else >+ *total = COSTS_N_INSNS (1); >+ return false; >+ >+ case ZERO_EXTEND: >+ /* This is an SImode shift. */ >+ if (GET_CODE (XEXP (x, 0)) == LSHIFTRT) >+ { >+ *total = COSTS_N_INSNS (SINGLE_SHIFT_COST); >+ return true; >+ } >+ /* Fall through. */ >+ case SIGN_EXTEND: >+ *total = riscv_extend_cost (XEXP (x, 0), GET_CODE (x) == ZERO_EXTEND); >+ return false; >+ >+ case FLOAT: >+ case UNSIGNED_FLOAT: >+ case FIX: >+ case FLOAT_EXTEND: >+ case FLOAT_TRUNCATE: >+ *total = tune_info->fp_add[mode == DFmode]; >+ return false; >+ >+ case FMA: >+ *total = (tune_info->fp_mul[mode == DFmode] >+ + set_src_cost (XEXP (x, 0), mode, speed) >+ + set_src_cost (XEXP (x, 1), mode, speed) >+ + set_src_cost (XEXP (x, 2), mode, speed)); >+ return true; >+ >+ case UNSPEC: >+ if (XINT (x, 1) == UNSPEC_AUIPC) >+ { >+ /* Make AUIPC cheap to avoid spilling its result to the stack. */ >+ *total = 1; >+ return true; >+ } >+ return false; >+ >+ default: >+ return false; >+ } >+} >+ >+/* Implement TARGET_ADDRESS_COST. */ >+ >+static int >+riscv_address_cost (rtx addr, machine_mode mode, >+ addr_space_t as ATTRIBUTE_UNUSED, >+ bool speed ATTRIBUTE_UNUSED) >+{ >+ /* When optimizing for size, make uncompressible 32-bit addresses more >+ * expensive so that compressible 32-bit addresses are preferred. */ >+ if (TARGET_RVC && !speed && riscv_mshorten_memrefs && mode == SImode >+ && !riscv_compressed_lw_address_p (addr)) >+ return riscv_address_insns (addr, mode, false) + 1; >+ return riscv_address_insns (addr, mode, false); >+} >+ >+/* Return one word of double-word value OP. HIGH_P is true to select the >+ high part or false to select the low part. */ >+ >+rtx >+riscv_subword (rtx op, bool high_p) >+{ >+ unsigned int byte = high_p ? UNITS_PER_WORD : 0; >+ machine_mode mode = GET_MODE (op); >+ >+ if (mode == VOIDmode) >+ mode = TARGET_64BIT ? TImode : DImode; >+ >+ if (MEM_P (op)) >+ return adjust_address (op, word_mode, byte); >+ >+ if (REG_P (op)) >+ gcc_assert (!FP_REG_RTX_P (op)); >+ >+ return simplify_gen_subreg (word_mode, op, mode, byte); >+} >+ >+/* Return true if a 64-bit move from SRC to DEST should be split into two. */ >+ >+bool >+riscv_split_64bit_move_p (rtx dest, rtx src) >+{ >+ if (TARGET_64BIT) >+ return false; >+ >+ /* Allow FPR <-> FPR and FPR <-> MEM moves, and permit the special case >+ of zeroing an FPR with FCVT.D.W. */ >+ if (TARGET_DOUBLE_FLOAT >+ && ((FP_REG_RTX_P (src) && FP_REG_RTX_P (dest)) >+ || (FP_REG_RTX_P (dest) && MEM_P (src)) >+ || (FP_REG_RTX_P (src) && MEM_P (dest)) >+ || (FP_REG_RTX_P (dest) && src == CONST0_RTX (GET_MODE (src))))) >+ return false; >+ >+ return true; >+} >+ >+/* Split a doubleword move from SRC to DEST. On 32-bit targets, >+ this function handles 64-bit moves for which riscv_split_64bit_move_p >+ holds. For 64-bit targets, this function handles 128-bit moves. */ >+ >+void >+riscv_split_doubleword_move (rtx dest, rtx src) >+{ >+ rtx low_dest; >+ >+ /* The operation can be split into two normal moves. Decide in >+ which order to do them. */ >+ low_dest = riscv_subword (dest, false); >+ if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src)) >+ { >+ riscv_emit_move (riscv_subword (dest, true), riscv_subword (src, true)); >+ riscv_emit_move (low_dest, riscv_subword (src, false)); >+ } >+ else >+ { >+ riscv_emit_move (low_dest, riscv_subword (src, false)); >+ riscv_emit_move (riscv_subword (dest, true), riscv_subword (src, true)); >+ } >+} >+ >+/* Return the appropriate instructions to move SRC into DEST. Assume >+ that SRC is operand 1 and DEST is operand 0. */ >+ >+const char * >+riscv_output_move (rtx dest, rtx src) >+{ >+ enum rtx_code dest_code, src_code; >+ machine_mode mode; >+ bool dbl_p; >+ >+ dest_code = GET_CODE (dest); >+ src_code = GET_CODE (src); >+ mode = GET_MODE (dest); >+ dbl_p = (GET_MODE_SIZE (mode) == 8); >+ >+ if (dbl_p && riscv_split_64bit_move_p (dest, src)) >+ return "#"; >+ >+ if (dest_code == REG && GP_REG_P (REGNO (dest))) >+ { >+ if (src_code == REG && FP_REG_P (REGNO (src))) >+ return dbl_p ? "fmv.x.d\t%0,%1" : "fmv.x.w\t%0,%1"; >+ >+ if (src_code == MEM) >+ switch (GET_MODE_SIZE (mode)) >+ { >+ case 1: return "lbu\t%0,%1"; >+ case 2: return "lhu\t%0,%1"; >+ case 4: return "lw\t%0,%1"; >+ case 8: return "ld\t%0,%1"; >+ } >+ >+ if (src_code == CONST_INT) >+ return "li\t%0,%1"; >+ >+ if (src_code == HIGH) >+ return "lui\t%0,%h1"; >+ >+ if (symbolic_operand (src, VOIDmode)) >+ switch (riscv_classify_symbolic_expression (src)) >+ { >+ case SYMBOL_GOT_DISP: return "la\t%0,%1"; >+ case SYMBOL_ABSOLUTE: return "lla\t%0,%1"; >+ case SYMBOL_PCREL: return "lla\t%0,%1"; >+ default: gcc_unreachable (); >+ } >+ } >+ if ((src_code == REG && GP_REG_P (REGNO (src))) >+ || (src == CONST0_RTX (mode))) >+ { >+ if (dest_code == REG) >+ { >+ if (GP_REG_P (REGNO (dest))) >+ return "mv\t%0,%z1"; >+ >+ if (FP_REG_P (REGNO (dest))) >+ { >+ if (!dbl_p) >+ return "fmv.w.x\t%0,%z1"; >+ if (TARGET_64BIT) >+ return "fmv.d.x\t%0,%z1"; >+ /* in RV32, we can emulate fmv.d.x %0, x0 using fcvt.d.w */ >+ gcc_assert (src == CONST0_RTX (mode)); >+ return "fcvt.d.w\t%0,x0"; >+ } >+ } >+ if (dest_code == MEM) >+ switch (GET_MODE_SIZE (mode)) >+ { >+ case 1: return "sb\t%z1,%0"; >+ case 2: return "sh\t%z1,%0"; >+ case 4: return "sw\t%z1,%0"; >+ case 8: return "sd\t%z1,%0"; >+ } >+ } >+ if (src_code == REG && FP_REG_P (REGNO (src))) >+ { >+ if (dest_code == REG && FP_REG_P (REGNO (dest))) >+ return dbl_p ? "fmv.d\t%0,%1" : "fmv.s\t%0,%1"; >+ >+ if (dest_code == MEM) >+ return dbl_p ? "fsd\t%1,%0" : "fsw\t%1,%0"; >+ } >+ if (dest_code == REG && FP_REG_P (REGNO (dest))) >+ { >+ if (src_code == MEM) >+ return dbl_p ? "fld\t%0,%1" : "flw\t%0,%1"; >+ } >+ gcc_unreachable (); >+} >+ >+const char * >+riscv_output_return () >+{ >+ if (cfun->machine->naked_p) >+ return ""; >+ >+ return "ret"; >+} >+ >+ >+/* Return true if CMP1 is a suitable second operand for integer ordering >+ test CODE. See also the *sCC patterns in riscv.md. */ >+ >+static bool >+riscv_int_order_operand_ok_p (enum rtx_code code, rtx cmp1) >+{ >+ switch (code) >+ { >+ case GT: >+ case GTU: >+ return reg_or_0_operand (cmp1, VOIDmode); >+ >+ case GE: >+ case GEU: >+ return cmp1 == const1_rtx; >+ >+ case LT: >+ case LTU: >+ return arith_operand (cmp1, VOIDmode); >+ >+ case LE: >+ return sle_operand (cmp1, VOIDmode); >+ >+ case LEU: >+ return sleu_operand (cmp1, VOIDmode); >+ >+ default: >+ gcc_unreachable (); >+ } >+} >+ >+/* Return true if *CMP1 (of mode MODE) is a valid second operand for >+ integer ordering test *CODE, or if an equivalent combination can >+ be formed by adjusting *CODE and *CMP1. When returning true, update >+ *CODE and *CMP1 with the chosen code and operand, otherwise leave >+ them alone. */ >+ >+static bool >+riscv_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1, >+ machine_mode mode) >+{ >+ HOST_WIDE_INT plus_one; >+ >+ if (riscv_int_order_operand_ok_p (*code, *cmp1)) >+ return true; >+ >+ if (CONST_INT_P (*cmp1)) >+ switch (*code) >+ { >+ case LE: >+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); >+ if (INTVAL (*cmp1) < plus_one) >+ { >+ *code = LT; >+ *cmp1 = force_reg (mode, GEN_INT (plus_one)); >+ return true; >+ } >+ break; >+ >+ case LEU: >+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); >+ if (plus_one != 0) >+ { >+ *code = LTU; >+ *cmp1 = force_reg (mode, GEN_INT (plus_one)); >+ return true; >+ } >+ break; >+ >+ default: >+ break; >+ } >+ return false; >+} >+ >+/* Compare CMP0 and CMP1 using ordering test CODE and store the result >+ in TARGET. CMP0 and TARGET are register_operands. If INVERT_PTR >+ is nonnull, it's OK to set TARGET to the inverse of the result and >+ flip *INVERT_PTR instead. */ >+ >+static void >+riscv_emit_int_order_test (enum rtx_code code, bool *invert_ptr, >+ rtx target, rtx cmp0, rtx cmp1) >+{ >+ machine_mode mode; >+ >+ /* First see if there is a RISCV instruction that can do this operation. >+ If not, try doing the same for the inverse operation. If that also >+ fails, force CMP1 into a register and try again. */ >+ mode = GET_MODE (cmp0); >+ if (riscv_canonicalize_int_order_test (&code, &cmp1, mode)) >+ riscv_emit_binary (code, target, cmp0, cmp1); >+ else >+ { >+ enum rtx_code inv_code = reverse_condition (code); >+ if (!riscv_canonicalize_int_order_test (&inv_code, &cmp1, mode)) >+ { >+ cmp1 = force_reg (mode, cmp1); >+ riscv_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1); >+ } >+ else if (invert_ptr == 0) >+ { >+ rtx inv_target = riscv_force_binary (GET_MODE (target), >+ inv_code, cmp0, cmp1); >+ riscv_emit_binary (XOR, target, inv_target, const1_rtx); >+ } >+ else >+ { >+ *invert_ptr = !*invert_ptr; >+ riscv_emit_binary (inv_code, target, cmp0, cmp1); >+ } >+ } >+} >+ >+/* Return a register that is zero iff CMP0 and CMP1 are equal. >+ The register will have the same mode as CMP0. */ >+ >+static rtx >+riscv_zero_if_equal (rtx cmp0, rtx cmp1) >+{ >+ if (cmp1 == const0_rtx) >+ return cmp0; >+ >+ return expand_binop (GET_MODE (cmp0), sub_optab, >+ cmp0, cmp1, 0, 0, OPTAB_DIRECT); >+} >+ >+/* Sign- or zero-extend OP0 and OP1 for integer comparisons. */ >+ >+static void >+riscv_extend_comparands (rtx_code code, rtx *op0, rtx *op1) >+{ >+ /* Comparisons consider all XLEN bits, so extend sub-XLEN values. */ >+ if (GET_MODE_SIZE (word_mode) > GET_MODE_SIZE (GET_MODE (*op0))) >+ { >+ /* It is more profitable to zero-extend QImode values. But not if the >+ first operand has already been sign-extended, and the second one is >+ is a constant or has already been sign-extended also. */ >+ if (unsigned_condition (code) == code >+ && (GET_MODE (*op0) == QImode >+ && ! (GET_CODE (*op0) == SUBREG >+ && SUBREG_PROMOTED_VAR_P (*op0) >+ && SUBREG_PROMOTED_SIGNED_P (*op0) >+ && (CONST_INT_P (*op1) >+ || (GET_CODE (*op1) == SUBREG >+ && SUBREG_PROMOTED_VAR_P (*op1) >+ && SUBREG_PROMOTED_SIGNED_P (*op1)))))) >+ { >+ *op0 = gen_rtx_ZERO_EXTEND (word_mode, *op0); >+ if (CONST_INT_P (*op1)) >+ *op1 = GEN_INT ((uint8_t) INTVAL (*op1)); >+ else >+ *op1 = gen_rtx_ZERO_EXTEND (word_mode, *op1); >+ } >+ else >+ { >+ *op0 = gen_rtx_SIGN_EXTEND (word_mode, *op0); >+ if (*op1 != const0_rtx) >+ *op1 = gen_rtx_SIGN_EXTEND (word_mode, *op1); >+ } >+ } >+} >+ >+/* Convert a comparison into something that can be used in a branch. On >+ entry, *OP0 and *OP1 are the values being compared and *CODE is the code >+ used to compare them. Update them to describe the final comparison. */ >+ >+static void >+riscv_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1) >+{ >+ if (splittable_const_int_operand (*op1, VOIDmode)) >+ { >+ HOST_WIDE_INT rhs = INTVAL (*op1); >+ >+ if (*code == EQ || *code == NE) >+ { >+ /* Convert e.g. OP0 == 2048 into OP0 - 2048 == 0. */ >+ if (SMALL_OPERAND (-rhs)) >+ { >+ *op0 = riscv_force_binary (GET_MODE (*op0), PLUS, *op0, >+ GEN_INT (-rhs)); >+ *op1 = const0_rtx; >+ } >+ } >+ else >+ { >+ static const enum rtx_code mag_comparisons[][2] = { >+ {LEU, LTU}, {GTU, GEU}, {LE, LT}, {GT, GE} >+ }; >+ >+ /* Convert e.g. (OP0 <= 0xFFF) into (OP0 < 0x1000). */ >+ for (size_t i = 0; i < ARRAY_SIZE (mag_comparisons); i++) >+ { >+ HOST_WIDE_INT new_rhs; >+ bool increment = *code == mag_comparisons[i][0]; >+ bool decrement = *code == mag_comparisons[i][1]; >+ if (!increment && !decrement) >+ continue; >+ >+ new_rhs = rhs + (increment ? 1 : -1); >+ if (riscv_integer_cost (new_rhs) < riscv_integer_cost (rhs) >+ && (rhs < 0) == (new_rhs < 0)) >+ { >+ *op1 = GEN_INT (new_rhs); >+ *code = mag_comparisons[i][increment]; >+ } >+ break; >+ } >+ } >+ } >+ >+ riscv_extend_comparands (*code, op0, op1); >+ >+ *op0 = force_reg (word_mode, *op0); >+ if (*op1 != const0_rtx) >+ *op1 = force_reg (word_mode, *op1); >+} >+ >+/* Like riscv_emit_int_compare, but for floating-point comparisons. */ >+ >+static void >+riscv_emit_float_compare (enum rtx_code *code, rtx *op0, rtx *op1) >+{ >+ rtx tmp0, tmp1, cmp_op0 = *op0, cmp_op1 = *op1; >+ enum rtx_code fp_code = *code; >+ *code = NE; >+ >+ switch (fp_code) >+ { >+ case UNORDERED: >+ *code = EQ; >+ /* Fall through. */ >+ >+ case ORDERED: >+ /* a == a && b == b */ >+ tmp0 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op0); >+ tmp1 = riscv_force_binary (word_mode, EQ, cmp_op1, cmp_op1); >+ *op0 = riscv_force_binary (word_mode, AND, tmp0, tmp1); >+ *op1 = const0_rtx; >+ break; >+ >+ case UNEQ: >+ /* ordered(a, b) > (a == b) */ >+ *code = EQ; >+ tmp0 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op0); >+ tmp1 = riscv_force_binary (word_mode, EQ, cmp_op1, cmp_op1); >+ *op0 = riscv_force_binary (word_mode, AND, tmp0, tmp1); >+ *op1 = riscv_force_binary (word_mode, EQ, cmp_op0, cmp_op1); >+ break; >+ >+#define UNORDERED_COMPARISON(CODE, CMP) \ >+ case CODE: \ >+ *code = EQ; \ >+ *op0 = gen_reg_rtx (word_mode); \ >+ if (GET_MODE (cmp_op0) == SFmode && TARGET_64BIT) \ >+ emit_insn (gen_f##CMP##_quietsfdi4 (*op0, cmp_op0, cmp_op1)); \ >+ else if (GET_MODE (cmp_op0) == SFmode) \ >+ emit_insn (gen_f##CMP##_quietsfsi4 (*op0, cmp_op0, cmp_op1)); \ >+ else if (GET_MODE (cmp_op0) == DFmode && TARGET_64BIT) \ >+ emit_insn (gen_f##CMP##_quietdfdi4 (*op0, cmp_op0, cmp_op1)); \ >+ else if (GET_MODE (cmp_op0) == DFmode) \ >+ emit_insn (gen_f##CMP##_quietdfsi4 (*op0, cmp_op0, cmp_op1)); \ >+ else \ >+ gcc_unreachable (); \ >+ *op1 = const0_rtx; \ >+ break; >+ >+ case UNLT: >+ std::swap (cmp_op0, cmp_op1); >+ gcc_fallthrough (); >+ >+ UNORDERED_COMPARISON(UNGT, le) >+ >+ case UNLE: >+ std::swap (cmp_op0, cmp_op1); >+ gcc_fallthrough (); >+ >+ UNORDERED_COMPARISON(UNGE, lt) >+#undef UNORDERED_COMPARISON >+ >+ case NE: >+ fp_code = EQ; >+ *code = EQ; >+ /* Fall through. */ >+ >+ case EQ: >+ case LE: >+ case LT: >+ case GE: >+ case GT: >+ /* We have instructions for these cases. */ >+ *op0 = riscv_force_binary (word_mode, fp_code, cmp_op0, cmp_op1); >+ *op1 = const0_rtx; >+ break; >+ >+ case LTGT: >+ /* (a < b) | (a > b) */ >+ tmp0 = riscv_force_binary (word_mode, LT, cmp_op0, cmp_op1); >+ tmp1 = riscv_force_binary (word_mode, GT, cmp_op0, cmp_op1); >+ *op0 = riscv_force_binary (word_mode, IOR, tmp0, tmp1); >+ *op1 = const0_rtx; >+ break; >+ >+ default: >+ gcc_unreachable (); >+ } >+} >+ >+/* CODE-compare OP0 and OP1. Store the result in TARGET. */ >+ >+void >+riscv_expand_int_scc (rtx target, enum rtx_code code, rtx op0, rtx op1) >+{ >+ riscv_extend_comparands (code, &op0, &op1); >+ op0 = force_reg (word_mode, op0); >+ >+ if (code == EQ || code == NE) >+ { >+ rtx zie = riscv_zero_if_equal (op0, op1); >+ riscv_emit_binary (code, target, zie, const0_rtx); >+ } >+ else >+ riscv_emit_int_order_test (code, 0, target, op0, op1); >+} >+ >+/* Like riscv_expand_int_scc, but for floating-point comparisons. */ >+ >+void >+riscv_expand_float_scc (rtx target, enum rtx_code code, rtx op0, rtx op1) >+{ >+ riscv_emit_float_compare (&code, &op0, &op1); >+ >+ rtx cmp = riscv_force_binary (word_mode, code, op0, op1); >+ riscv_emit_set (target, lowpart_subreg (SImode, cmp, word_mode)); >+} >+ >+/* Jump to LABEL if (CODE OP0 OP1) holds. */ >+ >+void >+riscv_expand_conditional_branch (rtx label, rtx_code code, rtx op0, rtx op1) >+{ >+ if (FLOAT_MODE_P (GET_MODE (op1))) >+ riscv_emit_float_compare (&code, &op0, &op1); >+ else >+ riscv_emit_int_compare (&code, &op0, &op1); >+ >+ rtx condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1); >+ emit_jump_insn (gen_condjump (condition, label)); >+} >+ >+/* If (CODE OP0 OP1) holds, move CONS to DEST; else move ALT to DEST. */ >+ >+void >+riscv_expand_conditional_move (rtx dest, rtx cons, rtx alt, rtx_code code, >+ rtx op0, rtx op1) >+{ >+ riscv_emit_int_compare (&code, &op0, &op1); >+ rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1); >+ emit_insn (gen_rtx_SET (dest, gen_rtx_IF_THEN_ELSE (GET_MODE (dest), cond, >+ cons, alt))); >+} >+ >+/* Implement TARGET_FUNCTION_ARG_BOUNDARY. Every parameter gets at >+ least PARM_BOUNDARY bits of alignment, but will be given anything up >+ to PREFERRED_STACK_BOUNDARY bits if the type requires it. */ >+ >+static unsigned int >+riscv_function_arg_boundary (machine_mode mode, const_tree type) >+{ >+ unsigned int alignment; >+ >+ /* Use natural alignment if the type is not aggregate data. */ >+ if (type && !AGGREGATE_TYPE_P (type)) >+ alignment = TYPE_ALIGN (TYPE_MAIN_VARIANT (type)); >+ else >+ alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode); >+ >+ return MIN (PREFERRED_STACK_BOUNDARY, MAX (PARM_BOUNDARY, alignment)); >+} >+ >+/* If MODE represents an argument that can be passed or returned in >+ floating-point registers, return the number of registers, else 0. */ >+ >+static unsigned >+riscv_pass_mode_in_fpr_p (machine_mode mode) >+{ >+ if (GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FP_ARG) >+ { >+ if (GET_MODE_CLASS (mode) == MODE_FLOAT) >+ return 1; >+ >+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) >+ return 2; >+ } >+ >+ return 0; >+} >+ >+typedef struct { >+ const_tree type; >+ HOST_WIDE_INT offset; >+} riscv_aggregate_field; >+ >+/* Identify subfields of aggregates that are candidates for passing in >+ floating-point registers. */ >+ >+static int >+riscv_flatten_aggregate_field (const_tree type, >+ riscv_aggregate_field fields[2], >+ int n, HOST_WIDE_INT offset, >+ bool ignore_zero_width_bit_field_p) >+{ >+ switch (TREE_CODE (type)) >+ { >+ case RECORD_TYPE: >+ /* Can't handle incomplete types nor sizes that are not fixed. */ >+ if (!COMPLETE_TYPE_P (type) >+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST >+ || !tree_fits_uhwi_p (TYPE_SIZE (type))) >+ return -1; >+ >+ for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f)) >+ if (TREE_CODE (f) == FIELD_DECL) >+ { >+ if (!TYPE_P (TREE_TYPE (f))) >+ return -1; >+ >+ /* The C++ front end strips zero-length bit-fields from structs. >+ So we need to ignore them in the C front end to make C code >+ compatible with C++ code. */ >+ if (ignore_zero_width_bit_field_p >+ && DECL_BIT_FIELD (f) >+ && (DECL_SIZE (f) == NULL_TREE >+ || integer_zerop (DECL_SIZE (f)))) >+ ; >+ else >+ { >+ HOST_WIDE_INT pos = offset + int_byte_position (f); >+ n = riscv_flatten_aggregate_field (TREE_TYPE (f), >+ fields, n, pos, >+ ignore_zero_width_bit_field_p); >+ } >+ if (n < 0) >+ return -1; >+ } >+ return n; >+ >+ case ARRAY_TYPE: >+ { >+ HOST_WIDE_INT n_elts; >+ riscv_aggregate_field subfields[2]; >+ tree index = TYPE_DOMAIN (type); >+ tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type)); >+ int n_subfields = riscv_flatten_aggregate_field (TREE_TYPE (type), >+ subfields, 0, offset, >+ ignore_zero_width_bit_field_p); >+ >+ /* Can't handle incomplete types nor sizes that are not fixed. */ >+ if (n_subfields <= 0 >+ || !COMPLETE_TYPE_P (type) >+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST >+ || !index >+ || !TYPE_MAX_VALUE (index) >+ || !tree_fits_uhwi_p (TYPE_MAX_VALUE (index)) >+ || !TYPE_MIN_VALUE (index) >+ || !tree_fits_uhwi_p (TYPE_MIN_VALUE (index)) >+ || !tree_fits_uhwi_p (elt_size)) >+ return -1; >+ >+ n_elts = 1 + tree_to_uhwi (TYPE_MAX_VALUE (index)) >+ - tree_to_uhwi (TYPE_MIN_VALUE (index)); >+ gcc_assert (n_elts >= 0); >+ >+ for (HOST_WIDE_INT i = 0; i < n_elts; i++) >+ for (int j = 0; j < n_subfields; j++) >+ { >+ if (n >= 2) >+ return -1; >+ >+ fields[n] = subfields[j]; >+ fields[n++].offset += i * tree_to_uhwi (elt_size); >+ } >+ >+ return n; >+ } >+ >+ case COMPLEX_TYPE: >+ { >+ /* Complex type need consume 2 field, so n must be 0. */ >+ if (n != 0) >+ return -1; >+ >+ HOST_WIDE_INT elt_size = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (type))); >+ >+ if (elt_size <= UNITS_PER_FP_ARG) >+ { >+ fields[0].type = TREE_TYPE (type); >+ fields[0].offset = offset; >+ fields[1].type = TREE_TYPE (type); >+ fields[1].offset = offset + elt_size; >+ >+ return 2; >+ } >+ >+ return -1; >+ } >+ >+ default: >+ if (n < 2 >+ && ((SCALAR_FLOAT_TYPE_P (type) >+ && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FP_ARG) >+ || (INTEGRAL_TYPE_P (type) >+ && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_WORD))) >+ { >+ fields[n].type = type; >+ fields[n].offset = offset; >+ return n + 1; >+ } >+ else >+ return -1; >+ } >+} >+ >+/* Identify candidate aggregates for passing in floating-point registers. >+ Candidates have at most two fields after flattening. */ >+ >+static int >+riscv_flatten_aggregate_argument (const_tree type, >+ riscv_aggregate_field fields[2], >+ bool ignore_zero_width_bit_field_p) >+{ >+ if (!type || TREE_CODE (type) != RECORD_TYPE) >+ return -1; >+ >+ return riscv_flatten_aggregate_field (type, fields, 0, 0, >+ ignore_zero_width_bit_field_p); >+} >+ >+/* See whether TYPE is a record whose fields should be returned in one or >+ two floating-point registers. If so, populate FIELDS accordingly. */ >+ >+static unsigned >+riscv_pass_aggregate_in_fpr_pair_p (const_tree type, >+ riscv_aggregate_field fields[2]) >+{ >+ static int warned = 0; >+ >+ /* This is the old ABI, which differs for C++ and C. */ >+ int n_old = riscv_flatten_aggregate_argument (type, fields, false); >+ for (int i = 0; i < n_old; i++) >+ if (!SCALAR_FLOAT_TYPE_P (fields[i].type)) >+ { >+ n_old = -1; >+ break; >+ } >+ >+ /* This is the new ABI, which is the same for C++ and C. */ >+ int n_new = riscv_flatten_aggregate_argument (type, fields, true); >+ for (int i = 0; i < n_new; i++) >+ if (!SCALAR_FLOAT_TYPE_P (fields[i].type)) >+ { >+ n_new = -1; >+ break; >+ } >+ >+ if ((n_old != n_new) && (warned == 0)) >+ { >+ warning (0, "ABI for flattened struct with zero-length bit-fields " >+ "changed in GCC 10"); >+ warned = 1; >+ } >+ >+ return n_new > 0 ? n_new : 0; >+} >+ >+/* See whether TYPE is a record whose fields should be returned in one or >+ floating-point register and one integer register. If so, populate >+ FIELDS accordingly. */ >+ >+static bool >+riscv_pass_aggregate_in_fpr_and_gpr_p (const_tree type, >+ riscv_aggregate_field fields[2]) >+{ >+ static int warned = 0; >+ >+ /* This is the old ABI, which differs for C++ and C. */ >+ unsigned num_int_old = 0, num_float_old = 0; >+ int n_old = riscv_flatten_aggregate_argument (type, fields, false); >+ for (int i = 0; i < n_old; i++) >+ { >+ num_float_old += SCALAR_FLOAT_TYPE_P (fields[i].type); >+ num_int_old += INTEGRAL_TYPE_P (fields[i].type); >+ } >+ >+ /* This is the new ABI, which is the same for C++ and C. */ >+ unsigned num_int_new = 0, num_float_new = 0; >+ int n_new = riscv_flatten_aggregate_argument (type, fields, true); >+ for (int i = 0; i < n_new; i++) >+ { >+ num_float_new += SCALAR_FLOAT_TYPE_P (fields[i].type); >+ num_int_new += INTEGRAL_TYPE_P (fields[i].type); >+ } >+ >+ if (((num_int_old == 1 && num_float_old == 1 >+ && (num_int_old != num_int_new || num_float_old != num_float_new)) >+ || (num_int_new == 1 && num_float_new == 1 >+ && (num_int_old != num_int_new || num_float_old != num_float_new))) >+ && (warned == 0)) >+ { >+ warning (0, "ABI for flattened struct with zero-length bit-fields " >+ "changed in GCC 10"); >+ warned = 1; >+ } >+ >+ return num_int_new == 1 && num_float_new == 1; >+} >+ >+/* Return the representation of an argument passed or returned in an FPR >+ when the value has mode VALUE_MODE and the type has TYPE_MODE. The >+ two modes may be different for structures like: >+ >+ struct __attribute__((packed)) foo { float f; } >+ >+ where the SFmode value "f" is passed in REGNO but the struct itself >+ has mode BLKmode. */ >+ >+static rtx >+riscv_pass_fpr_single (machine_mode type_mode, unsigned regno, >+ machine_mode value_mode, >+ HOST_WIDE_INT offset) >+{ >+ rtx x = gen_rtx_REG (value_mode, regno); >+ >+ if (type_mode != value_mode) >+ { >+ x = gen_rtx_EXPR_LIST (VOIDmode, x, GEN_INT (offset)); >+ x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x)); >+ } >+ return x; >+} >+ >+/* Pass or return a composite value in the FPR pair REGNO and REGNO + 1. >+ MODE is the mode of the composite. MODE1 and OFFSET1 are the mode and >+ byte offset for the first value, likewise MODE2 and OFFSET2 for the >+ second value. */ >+ >+static rtx >+riscv_pass_fpr_pair (machine_mode mode, unsigned regno1, >+ machine_mode mode1, HOST_WIDE_INT offset1, >+ unsigned regno2, machine_mode mode2, >+ HOST_WIDE_INT offset2) >+{ >+ return gen_rtx_PARALLEL >+ (mode, >+ gen_rtvec (2, >+ gen_rtx_EXPR_LIST (VOIDmode, >+ gen_rtx_REG (mode1, regno1), >+ GEN_INT (offset1)), >+ gen_rtx_EXPR_LIST (VOIDmode, >+ gen_rtx_REG (mode2, regno2), >+ GEN_INT (offset2)))); >+} >+ >+/* Fill INFO with information about a single argument, and return an >+ RTL pattern to pass or return the argument. CUM is the cumulative >+ state for earlier arguments. MODE is the mode of this argument and >+ TYPE is its type (if known). NAMED is true if this is a named >+ (fixed) argument rather than a variable one. RETURN_P is true if >+ returning the argument, or false if passing the argument. */ >+ >+static rtx >+riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum, >+ machine_mode mode, const_tree type, bool named, >+ bool return_p) >+{ >+ unsigned num_bytes, num_words; >+ unsigned fpr_base = return_p ? FP_RETURN : FP_ARG_FIRST; >+ unsigned gpr_base = return_p ? GP_RETURN : GP_ARG_FIRST; >+ unsigned alignment = riscv_function_arg_boundary (mode, type); >+ >+ memset (info, 0, sizeof (*info)); >+ info->gpr_offset = cum->num_gprs; >+ info->fpr_offset = cum->num_fprs; >+ >+ if (named) >+ { >+ riscv_aggregate_field fields[2]; >+ unsigned fregno = fpr_base + info->fpr_offset; >+ unsigned gregno = gpr_base + info->gpr_offset; >+ >+ /* Pass one- or two-element floating-point aggregates in FPRs. */ >+ if ((info->num_fprs = riscv_pass_aggregate_in_fpr_pair_p (type, fields)) >+ && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) >+ switch (info->num_fprs) >+ { >+ case 1: >+ return riscv_pass_fpr_single (mode, fregno, >+ TYPE_MODE (fields[0].type), >+ fields[0].offset); >+ >+ case 2: >+ return riscv_pass_fpr_pair (mode, fregno, >+ TYPE_MODE (fields[0].type), >+ fields[0].offset, >+ fregno + 1, >+ TYPE_MODE (fields[1].type), >+ fields[1].offset); >+ >+ default: >+ gcc_unreachable (); >+ } >+ >+ /* Pass real and complex floating-point numbers in FPRs. */ >+ if ((info->num_fprs = riscv_pass_mode_in_fpr_p (mode)) >+ && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) >+ switch (GET_MODE_CLASS (mode)) >+ { >+ case MODE_FLOAT: >+ return gen_rtx_REG (mode, fregno); >+ >+ case MODE_COMPLEX_FLOAT: >+ return riscv_pass_fpr_pair (mode, fregno, GET_MODE_INNER (mode), 0, >+ fregno + 1, GET_MODE_INNER (mode), >+ GET_MODE_UNIT_SIZE (mode)); >+ >+ default: >+ gcc_unreachable (); >+ } >+ >+ /* Pass structs with one float and one integer in an FPR and a GPR. */ >+ if (riscv_pass_aggregate_in_fpr_and_gpr_p (type, fields) >+ && info->gpr_offset < MAX_ARGS_IN_REGISTERS >+ && info->fpr_offset < MAX_ARGS_IN_REGISTERS) >+ { >+ info->num_gprs = 1; >+ info->num_fprs = 1; >+ >+ if (!SCALAR_FLOAT_TYPE_P (fields[0].type)) >+ std::swap (fregno, gregno); >+ >+ return riscv_pass_fpr_pair (mode, fregno, TYPE_MODE (fields[0].type), >+ fields[0].offset, >+ gregno, TYPE_MODE (fields[1].type), >+ fields[1].offset); >+ } >+ } >+ >+ /* Work out the size of the argument. */ >+ num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); >+ num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >+ >+ /* Doubleword-aligned varargs start on an even register boundary. */ >+ if (!named && num_bytes != 0 && alignment > BITS_PER_WORD) >+ info->gpr_offset += info->gpr_offset & 1; >+ >+ /* Partition the argument between registers and stack. */ >+ info->num_fprs = 0; >+ info->num_gprs = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset); >+ info->stack_p = (num_words - info->num_gprs) != 0; >+ >+ if (info->num_gprs || return_p) >+ return gen_rtx_REG (mode, gpr_base + info->gpr_offset); >+ >+ return NULL_RTX; >+} >+ >+/* Implement TARGET_FUNCTION_ARG. */ >+ >+static rtx >+riscv_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) >+{ >+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >+ struct riscv_arg_info info; >+ >+ if (arg.end_marker_p ()) >+ return NULL; >+ >+ return riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >+} >+ >+/* Implement TARGET_FUNCTION_ARG_ADVANCE. */ >+ >+static void >+riscv_function_arg_advance (cumulative_args_t cum_v, >+ const function_arg_info &arg) >+{ >+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >+ struct riscv_arg_info info; >+ >+ riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >+ >+ /* Advance the register count. This has the effect of setting >+ num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned >+ argument required us to skip the final GPR and pass the whole >+ argument on the stack. */ >+ cum->num_fprs = info.fpr_offset + info.num_fprs; >+ cum->num_gprs = info.gpr_offset + info.num_gprs; >+} >+ >+/* Implement TARGET_ARG_PARTIAL_BYTES. */ >+ >+static int >+riscv_arg_partial_bytes (cumulative_args_t cum, >+ const function_arg_info &generic_arg) >+{ >+ struct riscv_arg_info arg; >+ >+ riscv_get_arg_info (&arg, get_cumulative_args (cum), generic_arg.mode, >+ generic_arg.type, generic_arg.named, false); >+ return arg.stack_p ? arg.num_gprs * UNITS_PER_WORD : 0; >+} >+ >+/* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, >+ VALTYPE is the return type and MODE is VOIDmode. For libcalls, >+ VALTYPE is null and MODE is the mode of the return value. */ >+ >+rtx >+riscv_function_value (const_tree type, const_tree func, machine_mode mode) >+{ >+ struct riscv_arg_info info; >+ CUMULATIVE_ARGS args; >+ >+ if (type) >+ { >+ int unsigned_p = TYPE_UNSIGNED (type); >+ >+ mode = TYPE_MODE (type); >+ >+ /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes, >+ return values, promote the mode here too. */ >+ mode = promote_function_mode (type, mode, &unsigned_p, func, 1); >+ } >+ >+ memset (&args, 0, sizeof args); >+ return riscv_get_arg_info (&info, &args, mode, type, true, true); >+} >+ >+/* Implement TARGET_PASS_BY_REFERENCE. */ >+ >+static bool >+riscv_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg) >+{ >+ HOST_WIDE_INT size = arg.type_size_in_bytes (); >+ struct riscv_arg_info info; >+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); >+ >+ /* ??? std_gimplify_va_arg_expr passes NULL for cum. Fortunately, we >+ never pass variadic arguments in floating-point registers, so we can >+ avoid the call to riscv_get_arg_info in this case. */ >+ if (cum != NULL) >+ { >+ /* Don't pass by reference if we can use a floating-point register. */ >+ riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); >+ if (info.num_fprs) >+ return false; >+ } >+ >+ /* Pass by reference if the data do not fit in two integer registers. */ >+ return !IN_RANGE (size, 0, 2 * UNITS_PER_WORD); >+} >+ >+/* Implement TARGET_RETURN_IN_MEMORY. */ >+ >+static bool >+riscv_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED) >+{ >+ CUMULATIVE_ARGS args; >+ cumulative_args_t cum = pack_cumulative_args (&args); >+ >+ /* The rules for returning in memory are the same as for passing the >+ first named argument by reference. */ >+ memset (&args, 0, sizeof args); >+ function_arg_info arg (const_cast<tree> (type), /*named=*/true); >+ return riscv_pass_by_reference (cum, arg); >+} >+ >+/* Implement TARGET_SETUP_INCOMING_VARARGS. */ >+ >+static void >+riscv_setup_incoming_varargs (cumulative_args_t cum, >+ const function_arg_info &arg, >+ int *pretend_size ATTRIBUTE_UNUSED, int no_rtl) >+{ >+ CUMULATIVE_ARGS local_cum; >+ int gp_saved; >+ >+ /* The caller has advanced CUM up to, but not beyond, the last named >+ argument. Advance a local copy of CUM past the last "real" named >+ argument, to find out how many registers are left over. */ >+ local_cum = *get_cumulative_args (cum); >+ riscv_function_arg_advance (pack_cumulative_args (&local_cum), arg); >+ >+ /* Found out how many registers we need to save. */ >+ gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs; >+ >+ if (!no_rtl && gp_saved > 0) >+ { >+ rtx ptr = plus_constant (Pmode, virtual_incoming_args_rtx, >+ REG_PARM_STACK_SPACE (cfun->decl) >+ - gp_saved * UNITS_PER_WORD); >+ rtx mem = gen_frame_mem (BLKmode, ptr); >+ set_mem_alias_set (mem, get_varargs_alias_set ()); >+ >+ move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, >+ mem, gp_saved); >+ } >+ if (REG_PARM_STACK_SPACE (cfun->decl) == 0) >+ cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD; >+} >+ >+/* Handle an attribute requiring a FUNCTION_DECL; >+ arguments as in struct attribute_spec.handler. */ >+static tree >+riscv_handle_fndecl_attribute (tree *node, tree name, >+ tree args ATTRIBUTE_UNUSED, >+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) >+{ >+ if (TREE_CODE (*node) != FUNCTION_DECL) >+ { >+ warning (OPT_Wattributes, "%qE attribute only applies to functions", >+ name); >+ *no_add_attrs = true; >+ } >+ >+ return NULL_TREE; >+} >+ >+/* Verify type based attributes. NODE is the what the attribute is being >+ applied to. NAME is the attribute name. ARGS are the attribute args. >+ FLAGS gives info about the context. NO_ADD_ATTRS should be set to true if >+ the attribute should be ignored. */ >+ >+static tree >+riscv_handle_type_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args, >+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) >+{ >+ /* Check for an argument. */ >+ if (is_attribute_p ("interrupt", name)) >+ { >+ if (args) >+ { >+ tree cst = TREE_VALUE (args); >+ const char *string; >+ >+ if (TREE_CODE (cst) != STRING_CST) >+ { >+ warning (OPT_Wattributes, >+ "%qE attribute requires a string argument", >+ name); >+ *no_add_attrs = true; >+ return NULL_TREE; >+ } >+ >+ string = TREE_STRING_POINTER (cst); >+ if (strcmp (string, "user") && strcmp (string, "supervisor") >+ && strcmp (string, "machine")) >+ { >+ warning (OPT_Wattributes, >+ "argument to %qE attribute is not \"user\", \"supervisor\", or \"machine\"", >+ name); >+ *no_add_attrs = true; >+ } >+ } >+ } >+ >+ return NULL_TREE; >+} >+ >+/* Return true if function TYPE is an interrupt function. */ >+static bool >+riscv_interrupt_type_p (tree type) >+{ >+ return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL; >+} >+ >+/* Return true if FUNC is a naked function. */ >+static bool >+riscv_naked_function_p (tree func) >+{ >+ tree func_decl = func; >+ if (func == NULL_TREE) >+ func_decl = current_function_decl; >+ return NULL_TREE != lookup_attribute ("naked", DECL_ATTRIBUTES (func_decl)); >+} >+ >+/* Implement TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS. */ >+static bool >+riscv_allocate_stack_slots_for_args () >+{ >+ /* Naked functions should not allocate stack slots for arguments. */ >+ return !riscv_naked_function_p (current_function_decl); >+} >+ >+/* Implement TARGET_WARN_FUNC_RETURN. */ >+static bool >+riscv_warn_func_return (tree decl) >+{ >+ /* Naked functions are implemented entirely in assembly, including the >+ return sequence, so suppress warnings about this. */ >+ return !riscv_naked_function_p (decl); >+} >+ >+/* Implement TARGET_EXPAND_BUILTIN_VA_START. */ >+ >+static void >+riscv_va_start (tree valist, rtx nextarg) >+{ >+ nextarg = plus_constant (Pmode, nextarg, -cfun->machine->varargs_size); >+ std_expand_builtin_va_start (valist, nextarg); >+} >+ >+/* Make ADDR suitable for use as a call or sibcall target. */ >+ >+rtx >+riscv_legitimize_call_address (rtx addr) >+{ >+ if (!call_insn_operand (addr, VOIDmode)) >+ { >+ rtx reg = RISCV_PROLOGUE_TEMP (Pmode); >+ riscv_emit_move (reg, addr); >+ return reg; >+ } >+ return addr; >+} >+ >+/* Emit straight-line code to move LENGTH bytes from SRC to DEST. >+ Assume that the areas do not overlap. */ >+ >+static void >+riscv_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length) >+{ >+ HOST_WIDE_INT offset, delta; >+ unsigned HOST_WIDE_INT bits; >+ int i; >+ enum machine_mode mode; >+ rtx *regs; >+ >+ bits = MAX (BITS_PER_UNIT, >+ MIN (BITS_PER_WORD, MIN (MEM_ALIGN (src), MEM_ALIGN (dest)))); >+ >+ mode = mode_for_size (bits, MODE_INT, 0).require (); >+ delta = bits / BITS_PER_UNIT; >+ >+ /* Allocate a buffer for the temporary registers. */ >+ regs = XALLOCAVEC (rtx, length / delta); >+ >+ /* Load as many BITS-sized chunks as possible. Use a normal load if >+ the source has enough alignment, otherwise use left/right pairs. */ >+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) >+ { >+ regs[i] = gen_reg_rtx (mode); >+ riscv_emit_move (regs[i], adjust_address (src, mode, offset)); >+ } >+ >+ /* Copy the chunks to the destination. */ >+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) >+ riscv_emit_move (adjust_address (dest, mode, offset), regs[i]); >+ >+ /* Mop up any left-over bytes. */ >+ if (offset < length) >+ { >+ src = adjust_address (src, BLKmode, offset); >+ dest = adjust_address (dest, BLKmode, offset); >+ move_by_pieces (dest, src, length - offset, >+ MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), RETURN_BEGIN); >+ } >+} >+ >+/* Helper function for doing a loop-based block operation on memory >+ reference MEM. Each iteration of the loop will operate on LENGTH >+ bytes of MEM. >+ >+ Create a new base register for use within the loop and point it to >+ the start of MEM. Create a new memory reference that uses this >+ register. Store them in *LOOP_REG and *LOOP_MEM respectively. */ >+ >+static void >+riscv_adjust_block_mem (rtx mem, HOST_WIDE_INT length, >+ rtx *loop_reg, rtx *loop_mem) >+{ >+ *loop_reg = copy_addr_to_reg (XEXP (mem, 0)); >+ >+ /* Although the new mem does not refer to a known location, >+ it does keep up to LENGTH bytes of alignment. */ >+ *loop_mem = change_address (mem, BLKmode, *loop_reg); >+ set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT)); >+} >+ >+/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER >+ bytes at a time. LENGTH must be at least BYTES_PER_ITER. Assume that >+ the memory regions do not overlap. */ >+ >+static void >+riscv_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, >+ HOST_WIDE_INT bytes_per_iter) >+{ >+ rtx label, src_reg, dest_reg, final_src, test; >+ HOST_WIDE_INT leftover; >+ >+ leftover = length % bytes_per_iter; >+ length -= leftover; >+ >+ /* Create registers and memory references for use within the loop. */ >+ riscv_adjust_block_mem (src, bytes_per_iter, &src_reg, &src); >+ riscv_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest); >+ >+ /* Calculate the value that SRC_REG should have after the last iteration >+ of the loop. */ >+ final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length), >+ 0, 0, OPTAB_WIDEN); >+ >+ /* Emit the start of the loop. */ >+ label = gen_label_rtx (); >+ emit_label (label); >+ >+ /* Emit the loop body. */ >+ riscv_block_move_straight (dest, src, bytes_per_iter); >+ >+ /* Move on to the next block. */ >+ riscv_emit_move (src_reg, plus_constant (Pmode, src_reg, bytes_per_iter)); >+ riscv_emit_move (dest_reg, plus_constant (Pmode, dest_reg, bytes_per_iter)); >+ >+ /* Emit the loop condition. */ >+ test = gen_rtx_NE (VOIDmode, src_reg, final_src); >+ if (Pmode == DImode) >+ emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label)); >+ else >+ emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label)); >+ >+ /* Mop up any left-over bytes. */ >+ if (leftover) >+ riscv_block_move_straight (dest, src, leftover); >+ else >+ emit_insn(gen_nop ()); >+} >+ >+/* Expand a cpymemsi instruction, which copies LENGTH bytes from >+ memory reference SRC to memory reference DEST. */ >+ >+bool >+riscv_expand_block_move (rtx dest, rtx src, rtx length) >+{ >+ if (CONST_INT_P (length)) >+ { >+ HOST_WIDE_INT factor, align; >+ >+ align = MIN (MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), BITS_PER_WORD); >+ factor = BITS_PER_WORD / align; >+ >+ if (optimize_function_for_size_p (cfun) >+ && INTVAL (length) * factor * UNITS_PER_WORD > MOVE_RATIO (false)) >+ return false; >+ >+ if (INTVAL (length) <= RISCV_MAX_MOVE_BYTES_STRAIGHT / factor) >+ { >+ riscv_block_move_straight (dest, src, INTVAL (length)); >+ return true; >+ } >+ else if (optimize && align >= BITS_PER_WORD) >+ { >+ unsigned min_iter_words >+ = RISCV_MAX_MOVE_BYTES_PER_LOOP_ITER / UNITS_PER_WORD; >+ unsigned iter_words = min_iter_words; >+ HOST_WIDE_INT bytes = INTVAL (length), words = bytes / UNITS_PER_WORD; >+ >+ /* Lengthen the loop body if it shortens the tail. */ >+ for (unsigned i = min_iter_words; i < min_iter_words * 2 - 1; i++) >+ { >+ unsigned cur_cost = iter_words + words % iter_words; >+ unsigned new_cost = i + words % i; >+ if (new_cost <= cur_cost) >+ iter_words = i; >+ } >+ >+ riscv_block_move_loop (dest, src, bytes, iter_words * UNITS_PER_WORD); >+ return true; >+ } >+ } >+ return false; >+} >+ >+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM >+ in context CONTEXT. HI_RELOC indicates a high-part reloc. */ >+ >+static void >+riscv_print_operand_reloc (FILE *file, rtx op, bool hi_reloc) >+{ >+ const char *reloc; >+ >+ switch (riscv_classify_symbolic_expression (op)) >+ { >+ case SYMBOL_ABSOLUTE: >+ reloc = hi_reloc ? "%hi" : "%lo"; >+ break; >+ >+ case SYMBOL_PCREL: >+ reloc = hi_reloc ? "%pcrel_hi" : "%pcrel_lo"; >+ break; >+ >+ case SYMBOL_TLS_LE: >+ reloc = hi_reloc ? "%tprel_hi" : "%tprel_lo"; >+ break; >+ >+ default: >+ output_operand_lossage ("invalid use of '%%%c'", hi_reloc ? 'h' : 'R'); >+ return; >+ } >+ >+ fprintf (file, "%s(", reloc); >+ output_addr_const (file, riscv_strip_unspec_address (op)); >+ fputc (')', file); >+} >+ >+/* Return true if the .AQ suffix should be added to an AMO to implement the >+ acquire portion of memory model MODEL. */ >+ >+static bool >+riscv_memmodel_needs_amo_acquire (enum memmodel model) >+{ >+ switch (model) >+ { >+ case MEMMODEL_ACQ_REL: >+ case MEMMODEL_SEQ_CST: >+ case MEMMODEL_SYNC_SEQ_CST: >+ case MEMMODEL_ACQUIRE: >+ case MEMMODEL_CONSUME: >+ case MEMMODEL_SYNC_ACQUIRE: >+ return true; >+ >+ case MEMMODEL_RELEASE: >+ case MEMMODEL_SYNC_RELEASE: >+ case MEMMODEL_RELAXED: >+ return false; >+ >+ default: >+ gcc_unreachable (); >+ } >+} >+ >+/* Return true if a FENCE should be emitted to before a memory access to >+ implement the release portion of memory model MODEL. */ >+ >+static bool >+riscv_memmodel_needs_release_fence (enum memmodel model) >+{ >+ switch (model) >+ { >+ case MEMMODEL_ACQ_REL: >+ case MEMMODEL_SEQ_CST: >+ case MEMMODEL_SYNC_SEQ_CST: >+ case MEMMODEL_RELEASE: >+ case MEMMODEL_SYNC_RELEASE: >+ return true; >+ >+ case MEMMODEL_ACQUIRE: >+ case MEMMODEL_CONSUME: >+ case MEMMODEL_SYNC_ACQUIRE: >+ case MEMMODEL_RELAXED: >+ return false; >+ >+ default: >+ gcc_unreachable (); >+ } >+} >+ >+/* Implement TARGET_PRINT_OPERAND. The RISCV-specific operand codes are: >+ >+ 'h' Print the high-part relocation associated with OP, after stripping >+ any outermost HIGH. >+ 'R' Print the low-part relocation associated with OP. >+ 'C' Print the integer branch condition for comparison OP. >+ 'A' Print the atomic operation suffix for memory model OP. >+ 'F' Print a FENCE if the memory model requires a release. >+ 'z' Print x0 if OP is zero, otherwise print OP normally. >+ 'i' Print i if the operand is not a register. */ >+ >+static void >+riscv_print_operand (FILE *file, rtx op, int letter) >+{ >+ machine_mode mode = GET_MODE (op); >+ enum rtx_code code = GET_CODE (op); >+ >+ switch (letter) >+ { >+ case 'h': >+ if (code == HIGH) >+ op = XEXP (op, 0); >+ riscv_print_operand_reloc (file, op, true); >+ break; >+ >+ case 'R': >+ riscv_print_operand_reloc (file, op, false); >+ break; >+ >+ case 'C': >+ /* The RTL names match the instruction names. */ >+ fputs (GET_RTX_NAME (code), file); >+ break; >+ >+ case 'A': >+ if (riscv_memmodel_needs_amo_acquire ((enum memmodel) INTVAL (op))) >+ fputs (".aq", file); >+ break; >+ >+ case 'F': >+ if (riscv_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) >+ fputs ("fence iorw,ow; ", file); >+ break; >+ >+ case 'i': >+ if (code != REG) >+ fputs ("i", file); >+ break; >+ >+ default: >+ switch (code) >+ { >+ case REG: >+ if (letter && letter != 'z') >+ output_operand_lossage ("invalid use of '%%%c'", letter); >+ fprintf (file, "%s", reg_names[REGNO (op)]); >+ break; >+ >+ case MEM: >+ if (letter && letter != 'z') >+ output_operand_lossage ("invalid use of '%%%c'", letter); >+ else >+ output_address (mode, XEXP (op, 0)); >+ break; >+ >+ default: >+ if (letter == 'z' && op == CONST0_RTX (GET_MODE (op))) >+ fputs (reg_names[GP_REG_FIRST], file); >+ else if (letter && letter != 'z') >+ output_operand_lossage ("invalid use of '%%%c'", letter); >+ else >+ output_addr_const (file, riscv_strip_unspec_address (op)); >+ break; >+ } >+ } >+} >+ >+/* Implement TARGET_PRINT_OPERAND_ADDRESS. */ >+ >+static void >+riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx x) >+{ >+ struct riscv_address_info addr; >+ >+ if (riscv_classify_address (&addr, x, word_mode, true)) >+ switch (addr.type) >+ { >+ case ADDRESS_REG: >+ riscv_print_operand (file, addr.offset, 0); >+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); >+ return; >+ >+ case ADDRESS_LO_SUM: >+ riscv_print_operand_reloc (file, addr.offset, false); >+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); >+ return; >+ >+ case ADDRESS_CONST_INT: >+ output_addr_const (file, x); >+ fprintf (file, "(%s)", reg_names[GP_REG_FIRST]); >+ return; >+ >+ case ADDRESS_SYMBOLIC: >+ output_addr_const (file, riscv_strip_unspec_address (x)); >+ return; >+ } >+ gcc_unreachable (); >+} >+ >+static bool >+riscv_size_ok_for_small_data_p (int size) >+{ >+ return g_switch_value && IN_RANGE (size, 1, g_switch_value); >+} >+ >+/* Return true if EXP should be placed in the small data section. */ >+ >+static bool >+riscv_in_small_data_p (const_tree x) >+{ >+ if (TREE_CODE (x) == STRING_CST || TREE_CODE (x) == FUNCTION_DECL) >+ return false; >+ >+ if (TREE_CODE (x) == VAR_DECL && DECL_SECTION_NAME (x)) >+ { >+ const char *sec = DECL_SECTION_NAME (x); >+ return strcmp (sec, ".sdata") == 0 || strcmp (sec, ".sbss") == 0; >+ } >+ >+ return riscv_size_ok_for_small_data_p (int_size_in_bytes (TREE_TYPE (x))); >+} >+ >+/* Switch to the appropriate section for output of DECL. */ >+ >+static section * >+riscv_select_section (tree decl, int reloc, >+ unsigned HOST_WIDE_INT align) >+{ >+ switch (categorize_decl_for_section (decl, reloc)) >+ { >+ case SECCAT_SRODATA: >+ return get_named_section (decl, ".srodata", reloc); >+ >+ default: >+ return default_elf_select_section (decl, reloc, align); >+ } >+} >+ >+/* Switch to the appropriate section for output of DECL. */ >+ >+static void >+riscv_unique_section (tree decl, int reloc) >+{ >+ const char *prefix = NULL; >+ bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP; >+ >+ switch (categorize_decl_for_section (decl, reloc)) >+ { >+ case SECCAT_SRODATA: >+ prefix = one_only ? ".sr" : ".srodata"; >+ break; >+ >+ default: >+ break; >+ } >+ if (prefix) >+ { >+ const char *name, *linkonce; >+ char *string; >+ >+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); >+ name = targetm.strip_name_encoding (name); >+ >+ /* If we're using one_only, then there needs to be a .gnu.linkonce >+ prefix to the section name. */ >+ linkonce = one_only ? ".gnu.linkonce" : ""; >+ >+ string = ACONCAT ((linkonce, prefix, ".", name, NULL)); >+ >+ set_decl_section_name (decl, string); >+ return; >+ } >+ default_unique_section (decl, reloc); >+} >+ >+/* Return a section for X, handling small data. */ >+ >+static section * >+riscv_elf_select_rtx_section (machine_mode mode, rtx x, >+ unsigned HOST_WIDE_INT align) >+{ >+ section *s = default_elf_select_rtx_section (mode, x, align); >+ >+ if (riscv_size_ok_for_small_data_p (GET_MODE_SIZE (mode))) >+ { >+ if (strncmp (s->named.name, ".rodata.cst", strlen (".rodata.cst")) == 0) >+ { >+ /* Rename .rodata.cst* to .srodata.cst*. */ >+ char *name = (char *) alloca (strlen (s->named.name) + 2); >+ sprintf (name, ".s%s", s->named.name + 1); >+ return get_section (name, s->named.common.flags, NULL); >+ } >+ >+ if (s == data_section) >+ return sdata_section; >+ } >+ >+ return s; >+} >+ >+/* Make the last instruction frame-related and note that it performs >+ the operation described by FRAME_PATTERN. */ >+ >+static void >+riscv_set_frame_expr (rtx frame_pattern) >+{ >+ rtx insn; >+ >+ insn = get_last_insn (); >+ RTX_FRAME_RELATED_P (insn) = 1; >+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, >+ frame_pattern, >+ REG_NOTES (insn)); >+} >+ >+/* Return a frame-related rtx that stores REG at MEM. >+ REG must be a single register. */ >+ >+static rtx >+riscv_frame_set (rtx mem, rtx reg) >+{ >+ rtx set = gen_rtx_SET (mem, reg); >+ RTX_FRAME_RELATED_P (set) = 1; >+ return set; >+} >+ >+/* Return true if the current function must save register REGNO. */ >+ >+static bool >+riscv_save_reg_p (unsigned int regno) >+{ >+ bool call_saved = !global_regs[regno] && !call_used_or_fixed_reg_p (regno); >+ bool might_clobber = crtl->saves_all_registers >+ || df_regs_ever_live_p (regno); >+ >+ if (call_saved && might_clobber) >+ return true; >+ >+ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) >+ return true; >+ >+ if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return) >+ return true; >+ >+ /* If this is an interrupt handler, then must save extra registers. */ >+ if (cfun->machine->interrupt_handler_p) >+ { >+ /* zero register is always zero. */ >+ if (regno == GP_REG_FIRST) >+ return false; >+ >+ /* The function will return the stack pointer to its original value. */ >+ if (regno == STACK_POINTER_REGNUM) >+ return false; >+ >+ /* By convention, we assume that gp and tp are safe. */ >+ if (regno == GP_REGNUM || regno == THREAD_POINTER_REGNUM) >+ return false; >+ >+ /* We must save every register used in this function. If this is not a >+ leaf function, then we must save all temporary registers. */ >+ if (df_regs_ever_live_p (regno) >+ || (!crtl->is_leaf && call_used_or_fixed_reg_p (regno))) >+ return true; >+ } >+ >+ return false; >+} >+ >+/* Determine whether to call GPR save/restore routines. */ >+static bool >+riscv_use_save_libcall (const struct riscv_frame_info *frame) >+{ >+ if (!TARGET_SAVE_RESTORE || crtl->calls_eh_return || frame_pointer_needed >+ || cfun->machine->interrupt_handler_p) >+ return false; >+ >+ return frame->save_libcall_adjustment != 0; >+} >+ >+/* Determine which GPR save/restore routine to call. */ >+ >+static unsigned >+riscv_save_libcall_count (unsigned mask) >+{ >+ for (unsigned n = GP_REG_LAST; n > GP_REG_FIRST; n--) >+ if (BITSET_P (mask, n)) >+ return CALLEE_SAVED_REG_NUMBER (n) + 1; >+ abort (); >+} >+ >+/* Populate the current function's riscv_frame_info structure. >+ >+ RISC-V stack frames grown downward. High addresses are at the top. >+ >+ +-------------------------------+ >+ | | >+ | incoming stack arguments | >+ | | >+ +-------------------------------+ <-- incoming stack pointer >+ | | >+ | callee-allocated save area | >+ | for arguments that are | >+ | split between registers and | >+ | the stack | >+ | | >+ +-------------------------------+ <-- arg_pointer_rtx >+ | | >+ | callee-allocated save area | >+ | for register varargs | >+ | | >+ +-------------------------------+ <-- hard_frame_pointer_rtx; >+ | | stack_pointer_rtx + gp_sp_offset >+ | GPR save area | + UNITS_PER_WORD >+ | | >+ +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset >+ | | + UNITS_PER_HWVALUE >+ | FPR save area | >+ | | >+ +-------------------------------+ <-- frame_pointer_rtx (virtual) >+ | | >+ | local variables | >+ | | >+ P +-------------------------------+ >+ | | >+ | outgoing stack arguments | >+ | | >+ +-------------------------------+ <-- stack_pointer_rtx >+ >+ Dynamic stack allocations such as alloca insert data at point P. >+ They decrease stack_pointer_rtx but leave frame_pointer_rtx and >+ hard_frame_pointer_rtx unchanged. */ >+ >+static HOST_WIDE_INT riscv_first_stack_step (struct riscv_frame_info *frame); >+ >+static void >+riscv_compute_frame_info (void) >+{ >+ struct riscv_frame_info *frame; >+ HOST_WIDE_INT offset; >+ bool interrupt_save_t1 = false; >+ unsigned int regno, i, num_x_saved = 0, num_f_saved = 0; >+ >+ frame = &cfun->machine->frame; >+ >+ /* In an interrupt function, if we have a large frame, then we need to >+ save/restore t1. We check for this before clearing the frame struct. */ >+ if (cfun->machine->interrupt_handler_p) >+ { >+ HOST_WIDE_INT step1 = riscv_first_stack_step (frame); >+ if (! SMALL_OPERAND (frame->total_size - step1)) >+ interrupt_save_t1 = true; >+ } >+ >+ memset (frame, 0, sizeof (*frame)); >+ >+ if (!cfun->machine->naked_p) >+ { >+ /* Find out which GPRs we need to save. */ >+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >+ if (riscv_save_reg_p (regno) >+ || (interrupt_save_t1 && (regno == T1_REGNUM))) >+ frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; >+ >+ /* If this function calls eh_return, we must also save and restore the >+ EH data registers. */ >+ if (crtl->calls_eh_return) >+ for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++) >+ frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; >+ >+ /* Find out which FPRs we need to save. This loop must iterate over >+ the same space as its companion in riscv_for_each_saved_reg. */ >+ if (TARGET_HARD_FLOAT) >+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >+ if (riscv_save_reg_p (regno)) >+ frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++; >+ } >+ >+ /* At the bottom of the frame are any outgoing stack arguments. */ >+ offset = RISCV_STACK_ALIGN (crtl->outgoing_args_size); >+ /* Next are local stack variables. */ >+ offset += RISCV_STACK_ALIGN (get_frame_size ()); >+ /* The virtual frame pointer points above the local variables. */ >+ frame->frame_pointer_offset = offset; >+ /* Next are the callee-saved FPRs. */ >+ if (frame->fmask) >+ offset += RISCV_STACK_ALIGN (num_f_saved * UNITS_PER_FP_REG); >+ frame->fp_sp_offset = offset - UNITS_PER_FP_REG; >+ /* Next are the callee-saved GPRs. */ >+ if (frame->mask) >+ { >+ unsigned x_save_size = RISCV_STACK_ALIGN (num_x_saved * UNITS_PER_WORD); >+ unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask); >+ >+ /* Only use save/restore routines if they don't alter the stack size. */ >+ if (RISCV_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) == x_save_size) >+ { >+ /* Libcall saves/restores 3 registers at once, so we need to >+ allocate 12 bytes for callee-saved register. */ >+ if (TARGET_RVE) >+ x_save_size = 3 * UNITS_PER_WORD; >+ >+ frame->save_libcall_adjustment = x_save_size; >+ } >+ >+ offset += x_save_size; >+ } >+ frame->gp_sp_offset = offset - UNITS_PER_WORD; >+ /* The hard frame pointer points above the callee-saved GPRs. */ >+ frame->hard_frame_pointer_offset = offset; >+ /* Above the hard frame pointer is the callee-allocated varags save area. */ >+ offset += RISCV_STACK_ALIGN (cfun->machine->varargs_size); >+ /* Next is the callee-allocated area for pretend stack arguments. */ >+ offset += RISCV_STACK_ALIGN (crtl->args.pretend_args_size); >+ /* Arg pointer must be below pretend args, but must be above alignment >+ padding. */ >+ frame->arg_pointer_offset = offset - crtl->args.pretend_args_size; >+ frame->total_size = offset; >+ /* Next points the incoming stack pointer and any incoming arguments. */ >+ >+ /* Only use save/restore routines when the GPRs are atop the frame. */ >+ if (frame->hard_frame_pointer_offset != frame->total_size) >+ frame->save_libcall_adjustment = 0; >+} >+ >+/* Make sure that we're not trying to eliminate to the wrong hard frame >+ pointer. */ >+ >+static bool >+riscv_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) >+{ >+ return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM); >+} >+ >+/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer >+ or argument pointer. TO is either the stack pointer or hard frame >+ pointer. */ >+ >+HOST_WIDE_INT >+riscv_initial_elimination_offset (int from, int to) >+{ >+ HOST_WIDE_INT src, dest; >+ >+ riscv_compute_frame_info (); >+ >+ if (to == HARD_FRAME_POINTER_REGNUM) >+ dest = cfun->machine->frame.hard_frame_pointer_offset; >+ else if (to == STACK_POINTER_REGNUM) >+ dest = 0; /* The stack pointer is the base of all offsets, hence 0. */ >+ else >+ gcc_unreachable (); >+ >+ if (from == FRAME_POINTER_REGNUM) >+ src = cfun->machine->frame.frame_pointer_offset; >+ else if (from == ARG_POINTER_REGNUM) >+ src = cfun->machine->frame.arg_pointer_offset; >+ else >+ gcc_unreachable (); >+ >+ return src - dest; >+} >+ >+/* Implement RETURN_ADDR_RTX. We do not support moving back to a >+ previous frame. */ >+ >+rtx >+riscv_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) >+{ >+ if (count != 0) >+ return const0_rtx; >+ >+ return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM); >+} >+ >+/* Emit code to change the current function's return address to >+ ADDRESS. SCRATCH is available as a scratch register, if needed. >+ ADDRESS and SCRATCH are both word-mode GPRs. */ >+ >+void >+riscv_set_return_address (rtx address, rtx scratch) >+{ >+ rtx slot_address; >+ >+ gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)); >+ slot_address = riscv_add_offset (scratch, stack_pointer_rtx, >+ cfun->machine->frame.gp_sp_offset); >+ riscv_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address); >+} >+ >+/* A function to save or store a register. The first argument is the >+ register and the second is the stack slot. */ >+typedef void (*riscv_save_restore_fn) (rtx, rtx); >+ >+/* Use FN to save or restore register REGNO. MODE is the register's >+ mode and OFFSET is the offset of its save slot from the current >+ stack pointer. */ >+ >+static void >+riscv_save_restore_reg (machine_mode mode, int regno, >+ HOST_WIDE_INT offset, riscv_save_restore_fn fn) >+{ >+ rtx mem; >+ >+ mem = gen_frame_mem (mode, plus_constant (Pmode, stack_pointer_rtx, offset)); >+ fn (gen_rtx_REG (mode, regno), mem); >+} >+ >+/* Call FN for each register that is saved by the current function. >+ SP_OFFSET is the offset of the current stack pointer from the start >+ of the frame. */ >+ >+static void >+riscv_for_each_saved_reg (HOST_WIDE_INT sp_offset, riscv_save_restore_fn fn, >+ bool epilogue, bool maybe_eh_return) >+{ >+ HOST_WIDE_INT offset; >+ >+ /* Save the link register and s-registers. */ >+ offset = cfun->machine->frame.gp_sp_offset - sp_offset; >+ for (unsigned int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >+ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >+ { >+ bool handle_reg = TRUE; >+ >+ /* If this is a normal return in a function that calls the eh_return >+ builtin, then do not restore the eh return data registers as that >+ would clobber the return value. But we do still need to save them >+ in the prologue, and restore them for an exception return, so we >+ need special handling here. */ >+ if (epilogue && !maybe_eh_return && crtl->calls_eh_return) >+ { >+ unsigned int i, regnum; >+ >+ for (i = 0; (regnum = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; >+ i++) >+ if (regno == regnum) >+ { >+ handle_reg = FALSE; >+ break; >+ } >+ } >+ >+ if (handle_reg) >+ riscv_save_restore_reg (word_mode, regno, offset, fn); >+ offset -= UNITS_PER_WORD; >+ } >+ >+ /* This loop must iterate over the same space as its companion in >+ riscv_compute_frame_info. */ >+ offset = cfun->machine->frame.fp_sp_offset - sp_offset; >+ for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >+ if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) >+ { >+ machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode; >+ >+ riscv_save_restore_reg (mode, regno, offset, fn); >+ offset -= GET_MODE_SIZE (mode); >+ } >+} >+ >+/* Save register REG to MEM. Make the instruction frame-related. */ >+ >+static void >+riscv_save_reg (rtx reg, rtx mem) >+{ >+ riscv_emit_move (mem, reg); >+ riscv_set_frame_expr (riscv_frame_set (mem, reg)); >+} >+ >+/* Restore register REG from MEM. */ >+ >+static void >+riscv_restore_reg (rtx reg, rtx mem) >+{ >+ rtx insn = riscv_emit_move (reg, mem); >+ rtx dwarf = NULL_RTX; >+ dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); >+ >+ if (epilogue_cfa_sp_offset && REGNO (reg) == HARD_FRAME_POINTER_REGNUM) >+ { >+ rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >+ GEN_INT (epilogue_cfa_sp_offset)); >+ dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >+ } >+ >+ REG_NOTES (insn) = dwarf; >+ RTX_FRAME_RELATED_P (insn) = 1; >+} >+ >+/* For stack frames that can't be allocated with a single ADDI instruction, >+ compute the best value to initially allocate. It must at a minimum >+ allocate enough space to spill the callee-saved registers. If TARGET_RVC, >+ try to pick a value that will allow compression of the register saves >+ without adding extra instructions. */ >+ >+static HOST_WIDE_INT >+riscv_first_stack_step (struct riscv_frame_info *frame) >+{ >+ if (SMALL_OPERAND (frame->total_size)) >+ return frame->total_size; >+ >+ HOST_WIDE_INT min_first_step = >+ RISCV_STACK_ALIGN (frame->total_size - frame->fp_sp_offset); >+ HOST_WIDE_INT max_first_step = IMM_REACH / 2 - PREFERRED_STACK_BOUNDARY / 8; >+ HOST_WIDE_INT min_second_step = frame->total_size - max_first_step; >+ gcc_assert (min_first_step <= max_first_step); >+ >+ /* As an optimization, use the least-significant bits of the total frame >+ size, so that the second adjustment step is just LUI + ADD. */ >+ if (!SMALL_OPERAND (min_second_step) >+ && frame->total_size % IMM_REACH < IMM_REACH / 2 >+ && frame->total_size % IMM_REACH >= min_first_step) >+ return frame->total_size % IMM_REACH; >+ >+ if (TARGET_RVC) >+ { >+ /* If we need two subtracts, and one is small enough to allow compressed >+ loads and stores, then put that one first. */ >+ if (IN_RANGE (min_second_step, 0, >+ (TARGET_64BIT ? SDSP_REACH : SWSP_REACH))) >+ return MAX (min_second_step, min_first_step); >+ >+ /* If we need LUI + ADDI + ADD for the second adjustment step, then start >+ with the minimum first step, so that we can get compressed loads and >+ stores. */ >+ else if (!SMALL_OPERAND (min_second_step)) >+ return min_first_step; >+ } >+ >+ return max_first_step; >+} >+ >+static rtx >+riscv_adjust_libcall_cfi_prologue () >+{ >+ rtx dwarf = NULL_RTX; >+ rtx adjust_sp_rtx, reg, mem, insn; >+ int saved_size = cfun->machine->frame.save_libcall_adjustment; >+ int offset; >+ >+ for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >+ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >+ { >+ /* The save order is ra, s0, s1, s2 to s11. */ >+ if (regno == RETURN_ADDR_REGNUM) >+ offset = saved_size - UNITS_PER_WORD; >+ else if (regno == S0_REGNUM) >+ offset = saved_size - UNITS_PER_WORD * 2; >+ else if (regno == S1_REGNUM) >+ offset = saved_size - UNITS_PER_WORD * 3; >+ else >+ offset = saved_size - ((regno - S2_REGNUM + 4) * UNITS_PER_WORD); >+ >+ reg = gen_rtx_REG (SImode, regno); >+ mem = gen_frame_mem (SImode, plus_constant (Pmode, >+ stack_pointer_rtx, >+ offset)); >+ >+ insn = gen_rtx_SET (mem, reg); >+ dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf); >+ } >+ >+ /* Debug info for adjust sp. */ >+ adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx, >+ stack_pointer_rtx, GEN_INT (-saved_size)); >+ dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, >+ dwarf); >+ return dwarf; >+} >+ >+static void >+riscv_emit_stack_tie (void) >+{ >+ if (Pmode == SImode) >+ emit_insn (gen_stack_tiesi (stack_pointer_rtx, hard_frame_pointer_rtx)); >+ else >+ emit_insn (gen_stack_tiedi (stack_pointer_rtx, hard_frame_pointer_rtx)); >+} >+ >+/* Expand the "prologue" pattern. */ >+ >+void >+riscv_expand_prologue (void) >+{ >+ struct riscv_frame_info *frame = &cfun->machine->frame; >+ HOST_WIDE_INT size = frame->total_size; >+ unsigned mask = frame->mask; >+ rtx insn; >+ >+ if (flag_stack_usage_info) >+ current_function_static_stack_size = size; >+ >+ if (cfun->machine->naked_p) >+ return; >+ >+ /* When optimizing for size, call a subroutine to save the registers. */ >+ if (riscv_use_save_libcall (frame)) >+ { >+ rtx dwarf = NULL_RTX; >+ dwarf = riscv_adjust_libcall_cfi_prologue (); >+ >+ size -= frame->save_libcall_adjustment; >+ insn = emit_insn (riscv_gen_gpr_save_insn (frame)); >+ frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ >+ >+ RTX_FRAME_RELATED_P (insn) = 1; >+ REG_NOTES (insn) = dwarf; >+ } >+ >+ /* Save the registers. */ >+ if ((frame->mask | frame->fmask) != 0) >+ { >+ HOST_WIDE_INT step1 = MIN (size, riscv_first_stack_step (frame)); >+ >+ insn = gen_add3_insn (stack_pointer_rtx, >+ stack_pointer_rtx, >+ GEN_INT (-step1)); >+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >+ size -= step1; >+ riscv_for_each_saved_reg (size, riscv_save_reg, false, false); >+ } >+ >+ frame->mask = mask; /* Undo the above fib. */ >+ >+ /* Set up the frame pointer, if we're using one. */ >+ if (frame_pointer_needed) >+ { >+ insn = gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, >+ GEN_INT (frame->hard_frame_pointer_offset - size)); >+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >+ >+ riscv_emit_stack_tie (); >+ } >+ >+ /* Allocate the rest of the frame. */ >+ if (size > 0) >+ { >+ if (SMALL_OPERAND (-size)) >+ { >+ insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >+ GEN_INT (-size)); >+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; >+ } >+ else >+ { >+ riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), GEN_INT (-size)); >+ emit_insn (gen_add3_insn (stack_pointer_rtx, >+ stack_pointer_rtx, >+ RISCV_PROLOGUE_TEMP (Pmode))); >+ >+ /* Describe the effect of the previous instructions. */ >+ insn = plus_constant (Pmode, stack_pointer_rtx, -size); >+ insn = gen_rtx_SET (stack_pointer_rtx, insn); >+ riscv_set_frame_expr (insn); >+ } >+ } >+} >+ >+static rtx >+riscv_adjust_libcall_cfi_epilogue () >+{ >+ rtx dwarf = NULL_RTX; >+ rtx adjust_sp_rtx, reg; >+ int saved_size = cfun->machine->frame.save_libcall_adjustment; >+ >+ /* Debug info for adjust sp. */ >+ adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx, >+ stack_pointer_rtx, GEN_INT (saved_size)); >+ dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, >+ dwarf); >+ >+ for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) >+ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) >+ { >+ reg = gen_rtx_REG (SImode, regno); >+ dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); >+ } >+ >+ return dwarf; >+} >+ >+/* Expand an "epilogue", "sibcall_epilogue", or "eh_return_internal" pattern; >+ style says which. */ >+ >+void >+riscv_expand_epilogue (int style) >+{ >+ /* Split the frame into two. STEP1 is the amount of stack we should >+ deallocate before restoring the registers. STEP2 is the amount we >+ should deallocate afterwards. >+ >+ Start off by assuming that no registers need to be restored. */ >+ struct riscv_frame_info *frame = &cfun->machine->frame; >+ unsigned mask = frame->mask; >+ HOST_WIDE_INT step1 = frame->total_size; >+ HOST_WIDE_INT step2 = 0; >+ bool use_restore_libcall = ((style == NORMAL_RETURN) >+ && riscv_use_save_libcall (frame)); >+ rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); >+ rtx insn; >+ >+ /* We need to add memory barrier to prevent read from deallocated stack. */ >+ bool need_barrier_p = (get_frame_size () >+ + cfun->machine->frame.arg_pointer_offset) != 0; >+ >+ if (cfun->machine->naked_p) >+ { >+ gcc_assert (style == NORMAL_RETURN); >+ >+ emit_jump_insn (gen_return ()); >+ >+ return; >+ } >+ >+ if ((style == NORMAL_RETURN) && riscv_can_use_return_insn ()) >+ { >+ emit_jump_insn (gen_return ()); >+ return; >+ } >+ >+ /* Reset the epilogue cfa info before starting to emit the epilogue. */ >+ epilogue_cfa_sp_offset = 0; >+ >+ /* Move past any dynamic stack allocations. */ >+ if (cfun->calls_alloca) >+ { >+ /* Emit a barrier to prevent loads from a deallocated stack. */ >+ riscv_emit_stack_tie (); >+ need_barrier_p = false; >+ >+ rtx adjust = GEN_INT (-frame->hard_frame_pointer_offset); >+ if (!SMALL_OPERAND (INTVAL (adjust))) >+ { >+ riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), adjust); >+ adjust = RISCV_PROLOGUE_TEMP (Pmode); >+ } >+ >+ insn = emit_insn ( >+ gen_add3_insn (stack_pointer_rtx, hard_frame_pointer_rtx, >+ adjust)); >+ >+ rtx dwarf = NULL_RTX; >+ rtx cfa_adjust_value = gen_rtx_PLUS ( >+ Pmode, hard_frame_pointer_rtx, >+ GEN_INT (-frame->hard_frame_pointer_offset)); >+ rtx cfa_adjust_rtx = gen_rtx_SET (stack_pointer_rtx, cfa_adjust_value); >+ dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, cfa_adjust_rtx, dwarf); >+ RTX_FRAME_RELATED_P (insn) = 1; >+ >+ REG_NOTES (insn) = dwarf; >+ } >+ >+ /* If we need to restore registers, deallocate as much stack as >+ possible in the second step without going out of range. */ >+ if ((frame->mask | frame->fmask) != 0) >+ { >+ step2 = riscv_first_stack_step (frame); >+ step1 -= step2; >+ } >+ >+ /* Set TARGET to BASE + STEP1. */ >+ if (step1 > 0) >+ { >+ /* Emit a barrier to prevent loads from a deallocated stack. */ >+ riscv_emit_stack_tie (); >+ need_barrier_p = false; >+ >+ /* Get an rtx for STEP1 that we can add to BASE. */ >+ rtx adjust = GEN_INT (step1); >+ if (!SMALL_OPERAND (step1)) >+ { >+ riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), adjust); >+ adjust = RISCV_PROLOGUE_TEMP (Pmode); >+ } >+ >+ insn = emit_insn ( >+ gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, adjust)); >+ >+ rtx dwarf = NULL_RTX; >+ rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >+ GEN_INT (step2)); >+ >+ dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >+ RTX_FRAME_RELATED_P (insn) = 1; >+ >+ REG_NOTES (insn) = dwarf; >+ } >+ else if (frame_pointer_needed) >+ { >+ /* Tell riscv_restore_reg to emit dwarf to redefine CFA when restoring >+ old value of FP. */ >+ epilogue_cfa_sp_offset = step2; >+ } >+ >+ if (use_restore_libcall) >+ frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ >+ >+ /* Restore the registers. */ >+ riscv_for_each_saved_reg (frame->total_size - step2, riscv_restore_reg, >+ true, style == EXCEPTION_RETURN); >+ >+ if (use_restore_libcall) >+ { >+ frame->mask = mask; /* Undo the above fib. */ >+ gcc_assert (step2 >= frame->save_libcall_adjustment); >+ step2 -= frame->save_libcall_adjustment; >+ } >+ >+ if (need_barrier_p) >+ riscv_emit_stack_tie (); >+ >+ /* Deallocate the final bit of the frame. */ >+ if (step2 > 0) >+ { >+ insn = emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >+ GEN_INT (step2))); >+ >+ rtx dwarf = NULL_RTX; >+ rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, >+ const0_rtx); >+ dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); >+ RTX_FRAME_RELATED_P (insn) = 1; >+ >+ REG_NOTES (insn) = dwarf; >+ } >+ >+ if (use_restore_libcall) >+ { >+ rtx dwarf = riscv_adjust_libcall_cfi_epilogue (); >+ insn = emit_insn (gen_gpr_restore (GEN_INT (riscv_save_libcall_count (mask)))); >+ RTX_FRAME_RELATED_P (insn) = 1; >+ REG_NOTES (insn) = dwarf; >+ >+ emit_jump_insn (gen_gpr_restore_return (ra)); >+ return; >+ } >+ >+ /* Add in the __builtin_eh_return stack adjustment. */ >+ if ((style == EXCEPTION_RETURN) && crtl->calls_eh_return) >+ emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, >+ EH_RETURN_STACKADJ_RTX)); >+ >+ /* Return from interrupt. */ >+ if (cfun->machine->interrupt_handler_p) >+ { >+ enum riscv_privilege_levels mode = cfun->machine->interrupt_mode; >+ >+ gcc_assert (mode != UNKNOWN_MODE); >+ >+ if (mode == MACHINE_MODE) >+ emit_jump_insn (gen_riscv_mret ()); >+ else if (mode == SUPERVISOR_MODE) >+ emit_jump_insn (gen_riscv_sret ()); >+ else >+ emit_jump_insn (gen_riscv_uret ()); >+ } >+ else if (style != SIBCALL_RETURN) >+ emit_jump_insn (gen_simple_return_internal (ra)); >+} >+ >+/* Implement EPILOGUE_USES. */ >+ >+bool >+riscv_epilogue_uses (unsigned int regno) >+{ >+ if (regno == RETURN_ADDR_REGNUM) >+ return true; >+ >+ if (epilogue_completed && cfun->machine->interrupt_handler_p) >+ { >+ /* An interrupt function restores temp regs, so we must indicate that >+ they are live at function end. */ >+ if (df_regs_ever_live_p (regno) >+ || (!crtl->is_leaf && call_used_or_fixed_reg_p (regno))) >+ return true; >+ } >+ >+ return false; >+} >+ >+/* Return nonzero if this function is known to have a null epilogue. >+ This allows the optimizer to omit jumps to jumps if no stack >+ was created. */ >+ >+bool >+riscv_can_use_return_insn (void) >+{ >+ return (reload_completed && cfun->machine->frame.total_size == 0 >+ && ! cfun->machine->interrupt_handler_p); >+} >+ >+/* Given that there exists at least one variable that is set (produced) >+ by OUT_INSN and read (consumed) by IN_INSN, return true iff >+ IN_INSN represents one or more memory store operations and none of >+ the variables set by OUT_INSN is used by IN_INSN as the address of a >+ store operation. If either IN_INSN or OUT_INSN does not represent >+ a "single" RTL SET expression (as loosely defined by the >+ implementation of the single_set function) or a PARALLEL with only >+ SETs, CLOBBERs, and USEs inside, this function returns false. >+ >+ Borrowed from rs6000, riscv_store_data_bypass_p checks for certain >+ conditions that result in assertion failures in the generic >+ store_data_bypass_p function and returns FALSE in such cases. >+ >+ This is required to make -msave-restore work with the sifive-7 >+ pipeline description. */ >+ >+bool >+riscv_store_data_bypass_p (rtx_insn *out_insn, rtx_insn *in_insn) >+{ >+ rtx out_set, in_set; >+ rtx out_pat, in_pat; >+ rtx out_exp, in_exp; >+ int i, j; >+ >+ in_set = single_set (in_insn); >+ if (in_set) >+ { >+ if (MEM_P (SET_DEST (in_set))) >+ { >+ out_set = single_set (out_insn); >+ if (!out_set) >+ { >+ out_pat = PATTERN (out_insn); >+ if (GET_CODE (out_pat) == PARALLEL) >+ { >+ for (i = 0; i < XVECLEN (out_pat, 0); i++) >+ { >+ out_exp = XVECEXP (out_pat, 0, i); >+ if ((GET_CODE (out_exp) == CLOBBER) >+ || (GET_CODE (out_exp) == USE)) >+ continue; >+ else if (GET_CODE (out_exp) != SET) >+ return false; >+ } >+ } >+ } >+ } >+ } >+ else >+ { >+ in_pat = PATTERN (in_insn); >+ if (GET_CODE (in_pat) != PARALLEL) >+ return false; >+ >+ for (i = 0; i < XVECLEN (in_pat, 0); i++) >+ { >+ in_exp = XVECEXP (in_pat, 0, i); >+ if ((GET_CODE (in_exp) == CLOBBER) || (GET_CODE (in_exp) == USE)) >+ continue; >+ else if (GET_CODE (in_exp) != SET) >+ return false; >+ >+ if (MEM_P (SET_DEST (in_exp))) >+ { >+ out_set = single_set (out_insn); >+ if (!out_set) >+ { >+ out_pat = PATTERN (out_insn); >+ if (GET_CODE (out_pat) != PARALLEL) >+ return false; >+ for (j = 0; j < XVECLEN (out_pat, 0); j++) >+ { >+ out_exp = XVECEXP (out_pat, 0, j); >+ if ((GET_CODE (out_exp) == CLOBBER) >+ || (GET_CODE (out_exp) == USE)) >+ continue; >+ else if (GET_CODE (out_exp) != SET) >+ return false; >+ } >+ } >+ } >+ } >+ } >+ >+ return store_data_bypass_p (out_insn, in_insn); >+} >+ >+/* Implement TARGET_SECONDARY_MEMORY_NEEDED. >+ >+ When floating-point registers are wider than integer ones, moves between >+ them must go through memory. */ >+ >+static bool >+riscv_secondary_memory_needed (machine_mode mode, reg_class_t class1, >+ reg_class_t class2) >+{ >+ return (GET_MODE_SIZE (mode) > UNITS_PER_WORD >+ && (class1 == FP_REGS) != (class2 == FP_REGS)); >+} >+ >+/* Implement TARGET_REGISTER_MOVE_COST. */ >+ >+static int >+riscv_register_move_cost (machine_mode mode, >+ reg_class_t from, reg_class_t to) >+{ >+ return riscv_secondary_memory_needed (mode, from, to) ? 8 : 2; >+} >+ >+/* Implement TARGET_HARD_REGNO_NREGS. */ >+ >+static unsigned int >+riscv_hard_regno_nregs (unsigned int regno, machine_mode mode) >+{ >+ if (FP_REG_P (regno)) >+ return (GET_MODE_SIZE (mode) + UNITS_PER_FP_REG - 1) / UNITS_PER_FP_REG; >+ >+ /* All other registers are word-sized. */ >+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; >+} >+ >+/* Implement TARGET_HARD_REGNO_MODE_OK. */ >+ >+static bool >+riscv_hard_regno_mode_ok (unsigned int regno, machine_mode mode) >+{ >+ unsigned int nregs = riscv_hard_regno_nregs (regno, mode); >+ >+ if (GP_REG_P (regno)) >+ { >+ if (!GP_REG_P (regno + nregs - 1)) >+ return false; >+ } >+ else if (FP_REG_P (regno)) >+ { >+ if (!FP_REG_P (regno + nregs - 1)) >+ return false; >+ >+ if (GET_MODE_CLASS (mode) != MODE_FLOAT >+ && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) >+ return false; >+ >+ /* Only use callee-saved registers if a potential callee is guaranteed >+ to spill the requisite width. */ >+ if (GET_MODE_UNIT_SIZE (mode) > UNITS_PER_FP_REG >+ || (!call_used_or_fixed_reg_p (regno) >+ && GET_MODE_UNIT_SIZE (mode) > UNITS_PER_FP_ARG)) >+ return false; >+ } >+ else >+ return false; >+ >+ /* Require same callee-savedness for all registers. */ >+ for (unsigned i = 1; i < nregs; i++) >+ if (call_used_or_fixed_reg_p (regno) >+ != call_used_or_fixed_reg_p (regno + i)) >+ return false; >+ >+ return true; >+} >+ >+/* Implement TARGET_MODES_TIEABLE_P. >+ >+ Don't allow floating-point modes to be tied, since type punning of >+ single-precision and double-precision is implementation defined. */ >+ >+static bool >+riscv_modes_tieable_p (machine_mode mode1, machine_mode mode2) >+{ >+ return (mode1 == mode2 >+ || !(GET_MODE_CLASS (mode1) == MODE_FLOAT >+ && GET_MODE_CLASS (mode2) == MODE_FLOAT)); >+} >+ >+/* Implement CLASS_MAX_NREGS. */ >+ >+static unsigned char >+riscv_class_max_nregs (reg_class_t rclass, machine_mode mode) >+{ >+ if (reg_class_subset_p (FP_REGS, rclass)) >+ return riscv_hard_regno_nregs (FP_REG_FIRST, mode); >+ >+ if (reg_class_subset_p (GR_REGS, rclass)) >+ return riscv_hard_regno_nregs (GP_REG_FIRST, mode); >+ >+ return 0; >+} >+ >+/* Implement TARGET_MEMORY_MOVE_COST. */ >+ >+static int >+riscv_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in) >+{ >+ return (tune_info->memory_cost >+ + memory_move_secondary_cost (mode, rclass, in)); >+} >+ >+/* Return the number of instructions that can be issued per cycle. */ >+ >+static int >+riscv_issue_rate (void) >+{ >+ return tune_info->issue_rate; >+} >+ >+/* Auxiliary function to emit RISC-V ELF attribute. */ >+static void >+riscv_emit_attribute () >+{ >+ fprintf (asm_out_file, "\t.attribute arch, \"%s\"\n", >+ riscv_arch_str ().c_str ()); >+ >+ fprintf (asm_out_file, "\t.attribute unaligned_access, %d\n", >+ TARGET_STRICT_ALIGN ? 0 : 1); >+ >+ fprintf (asm_out_file, "\t.attribute stack_align, %d\n", >+ riscv_stack_boundary / 8); >+} >+ >+/* Implement TARGET_ASM_FILE_START. */ >+ >+static void >+riscv_file_start (void) >+{ >+ default_file_start (); >+ >+ /* Instruct GAS to generate position-[in]dependent code. */ >+ fprintf (asm_out_file, "\t.option %spic\n", (flag_pic ? "" : "no")); >+ >+ /* If the user specifies "-mno-relax" on the command line then disable linker >+ relaxation in the assembler. */ >+ if (! riscv_mrelax) >+ fprintf (asm_out_file, "\t.option norelax\n"); >+ >+ if (riscv_emit_attribute_p) >+ riscv_emit_attribute (); >+} >+ >+/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text >+ in order to avoid duplicating too much logic from elsewhere. */ >+ >+static void >+riscv_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, >+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, >+ tree function) >+{ >+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl)); >+ rtx this_rtx, temp1, temp2, fnaddr; >+ rtx_insn *insn; >+ >+ /* Pretend to be a post-reload pass while generating rtl. */ >+ reload_completed = 1; >+ >+ /* Mark the end of the (empty) prologue. */ >+ emit_note (NOTE_INSN_PROLOGUE_END); >+ >+ /* Determine if we can use a sibcall to call FUNCTION directly. */ >+ fnaddr = gen_rtx_MEM (FUNCTION_MODE, XEXP (DECL_RTL (function), 0)); >+ >+ /* We need two temporary registers in some cases. */ >+ temp1 = gen_rtx_REG (Pmode, RISCV_PROLOGUE_TEMP_REGNUM); >+ temp2 = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM); >+ >+ /* Find out which register contains the "this" pointer. */ >+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) >+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); >+ else >+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST); >+ >+ /* Add DELTA to THIS_RTX. */ >+ if (delta != 0) >+ { >+ rtx offset = GEN_INT (delta); >+ if (!SMALL_OPERAND (delta)) >+ { >+ riscv_emit_move (temp1, offset); >+ offset = temp1; >+ } >+ emit_insn (gen_add3_insn (this_rtx, this_rtx, offset)); >+ } >+ >+ /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */ >+ if (vcall_offset != 0) >+ { >+ rtx addr; >+ >+ /* Set TEMP1 to *THIS_RTX. */ >+ riscv_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx)); >+ >+ /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */ >+ addr = riscv_add_offset (temp2, temp1, vcall_offset); >+ >+ /* Load the offset and add it to THIS_RTX. */ >+ riscv_emit_move (temp1, gen_rtx_MEM (Pmode, addr)); >+ emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1)); >+ } >+ >+ /* Jump to the target function. */ >+ insn = emit_call_insn (gen_sibcall (fnaddr, const0_rtx, NULL, const0_rtx)); >+ SIBLING_CALL_P (insn) = 1; >+ >+ /* Run just enough of rest_of_compilation. This sequence was >+ "borrowed" from alpha.c. */ >+ insn = get_insns (); >+ split_all_insns_noflow (); >+ shorten_branches (insn); >+ assemble_start_function (thunk_fndecl, fnname); >+ final_start_function (insn, file, 1); >+ final (insn, file, 1); >+ final_end_function (); >+ assemble_end_function (thunk_fndecl, fnname); >+ >+ /* Clean up the vars set above. Note that final_end_function resets >+ the global pointer for us. */ >+ reload_completed = 0; >+} >+ >+/* Allocate a chunk of memory for per-function machine-dependent data. */ >+ >+static struct machine_function * >+riscv_init_machine_status (void) >+{ >+ return ggc_cleared_alloc<machine_function> (); >+} >+ >+/* Implement TARGET_OPTION_OVERRIDE. */ >+ >+static void >+riscv_option_override (void) >+{ >+ const struct riscv_cpu_info *cpu; >+ >+#ifdef SUBTARGET_OVERRIDE_OPTIONS >+ SUBTARGET_OVERRIDE_OPTIONS; >+#endif >+ >+ flag_pcc_struct_return = 0; >+ >+ if (flag_pic) >+ g_switch_value = 0; >+ >+ /* The presence of the M extension implies that division instructions >+ are present, so include them unless explicitly disabled. */ >+ if (TARGET_MUL && (target_flags_explicit & MASK_DIV) == 0) >+ target_flags |= MASK_DIV; >+ else if (!TARGET_MUL && TARGET_DIV) >+ error ("%<-mdiv%> requires %<-march%> to subsume the %<M%> extension"); >+ >+ /* Likewise floating-point division and square root. */ >+ if (TARGET_HARD_FLOAT && (target_flags_explicit & MASK_FDIV) == 0) >+ target_flags |= MASK_FDIV; >+ >+ /* Handle -mtune. */ >+ cpu = riscv_parse_cpu (riscv_tune_string ? riscv_tune_string : >+ RISCV_TUNE_STRING_DEFAULT); >+ riscv_microarchitecture = cpu->microarchitecture; >+ tune_info = optimize_size ? &optimize_size_tune_info : cpu->tune_info; >+ >+ /* Use -mtune's setting for slow_unaligned_access, even when optimizing >+ for size. For architectures that trap and emulate unaligned accesses, >+ the performance cost is too great, even for -Os. Similarly, if >+ -m[no-]strict-align is left unspecified, heed -mtune's advice. */ >+ riscv_slow_unaligned_access_p = (cpu->tune_info->slow_unaligned_access >+ || TARGET_STRICT_ALIGN); >+ if ((target_flags_explicit & MASK_STRICT_ALIGN) == 0 >+ && cpu->tune_info->slow_unaligned_access) >+ target_flags |= MASK_STRICT_ALIGN; >+ >+ /* If the user hasn't specified a branch cost, use the processor's >+ default. */ >+ if (riscv_branch_cost == 0) >+ riscv_branch_cost = tune_info->branch_cost; >+ >+ /* Function to allocate machine-dependent function status. */ >+ init_machine_status = &riscv_init_machine_status; >+ >+ if (flag_pic) >+ riscv_cmodel = CM_PIC; >+ >+ /* We get better code with explicit relocs for CM_MEDLOW, but >+ worse code for the others (for now). Pick the best default. */ >+ if ((target_flags_explicit & MASK_EXPLICIT_RELOCS) == 0) >+ if (riscv_cmodel == CM_MEDLOW) >+ target_flags |= MASK_EXPLICIT_RELOCS; >+ >+ /* Require that the ISA supports the requested floating-point ABI. */ >+ if (UNITS_PER_FP_ARG > (TARGET_HARD_FLOAT ? UNITS_PER_FP_REG : 0)) >+ error ("requested ABI requires %<-march%> to subsume the %qc extension", >+ UNITS_PER_FP_ARG > 8 ? 'Q' : (UNITS_PER_FP_ARG > 4 ? 'D' : 'F')); >+ >+ if (TARGET_RVE && riscv_abi != ABI_ILP32E) >+ error ("rv32e requires ilp32e ABI"); >+ >+ /* We do not yet support ILP32 on RV64. */ >+ if (BITS_PER_WORD != POINTER_SIZE) >+ error ("ABI requires %<-march=rv%d%>", POINTER_SIZE); >+ >+ /* Validate -mpreferred-stack-boundary= value. */ >+ riscv_stack_boundary = ABI_STACK_BOUNDARY; >+ if (riscv_preferred_stack_boundary_arg) >+ { >+ int min = ctz_hwi (STACK_BOUNDARY / 8); >+ int max = 8; >+ >+ if (!IN_RANGE (riscv_preferred_stack_boundary_arg, min, max)) >+ error ("%<-mpreferred-stack-boundary=%d%> must be between %d and %d", >+ riscv_preferred_stack_boundary_arg, min, max); >+ >+ riscv_stack_boundary = 8 << riscv_preferred_stack_boundary_arg; >+ } >+ >+ if (riscv_emit_attribute_p < 0) >+#ifdef HAVE_AS_RISCV_ATTRIBUTE >+ riscv_emit_attribute_p = TARGET_RISCV_ATTRIBUTE; >+#else >+ riscv_emit_attribute_p = 0; >+ >+ if (riscv_emit_attribute_p) >+ error ("%<-mriscv-attribute%> RISC-V ELF attribute requires GNU as 2.32" >+ " [%<-mriscv-attribute%>]"); >+#endif >+ >+} >+ >+/* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */ >+ >+static void >+riscv_conditional_register_usage (void) >+{ >+ /* We have only x0~x15 on RV32E. */ >+ if (TARGET_RVE) >+ { >+ for (int r = 16; r <= 31; r++) >+ fixed_regs[r] = 1; >+ } >+ >+ if (riscv_abi == ABI_ILP32E) >+ { >+ for (int r = 16; r <= 31; r++) >+ call_used_regs[r] = 1; >+ } >+ >+ if (!TARGET_HARD_FLOAT) >+ { >+ for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >+ fixed_regs[regno] = call_used_regs[regno] = 1; >+ } >+ >+ /* In the soft-float ABI, there are no callee-saved FP registers. */ >+ if (UNITS_PER_FP_ARG == 0) >+ { >+ for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) >+ call_used_regs[regno] = 1; >+ } >+} >+ >+/* Return a register priority for hard reg REGNO. */ >+ >+static int >+riscv_register_priority (int regno) >+{ >+ /* Favor compressed registers to improve the odds of RVC instruction >+ selection. */ >+ if (riscv_compressed_reg_p (regno)) >+ return 1; >+ >+ return 0; >+} >+ >+/* Implement TARGET_TRAMPOLINE_INIT. */ >+ >+static void >+riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) >+{ >+ rtx addr, end_addr, mem; >+ uint32_t trampoline[4]; >+ unsigned int i; >+ HOST_WIDE_INT static_chain_offset, target_function_offset; >+ >+ /* Work out the offsets of the pointers from the start of the >+ trampoline code. */ >+ gcc_assert (ARRAY_SIZE (trampoline) * 4 == TRAMPOLINE_CODE_SIZE); >+ >+ /* Get pointers to the beginning and end of the code block. */ >+ addr = force_reg (Pmode, XEXP (m_tramp, 0)); >+ end_addr = riscv_force_binary (Pmode, PLUS, addr, >+ GEN_INT (TRAMPOLINE_CODE_SIZE)); >+ >+ >+ if (Pmode == SImode) >+ { >+ chain_value = force_reg (Pmode, chain_value); >+ >+ rtx target_function = force_reg (Pmode, XEXP (DECL_RTL (fndecl), 0)); >+ /* lui t2, hi(chain) >+ lui t1, hi(func) >+ addi t2, t2, lo(chain) >+ jr r1, lo(func) >+ */ >+ unsigned HOST_WIDE_INT lui_hi_chain_code, lui_hi_func_code; >+ unsigned HOST_WIDE_INT lo_chain_code, lo_func_code; >+ >+ rtx uimm_mask = force_reg (SImode, gen_int_mode (-IMM_REACH, SImode)); >+ >+ /* 0xfff. */ >+ rtx imm12_mask = gen_reg_rtx (SImode); >+ emit_insn (gen_one_cmplsi2 (imm12_mask, uimm_mask)); >+ >+ rtx fixup_value = force_reg (SImode, gen_int_mode (IMM_REACH/2, SImode)); >+ >+ /* Gen lui t2, hi(chain). */ >+ rtx hi_chain = riscv_force_binary (SImode, PLUS, chain_value, >+ fixup_value); >+ hi_chain = riscv_force_binary (SImode, AND, hi_chain, >+ uimm_mask); >+ lui_hi_chain_code = OPCODE_LUI | (STATIC_CHAIN_REGNUM << SHIFT_RD); >+ rtx lui_hi_chain = riscv_force_binary (SImode, IOR, hi_chain, >+ gen_int_mode (lui_hi_chain_code, SImode)); >+ >+ mem = adjust_address (m_tramp, SImode, 0); >+ riscv_emit_move (mem, lui_hi_chain); >+ >+ /* Gen lui t1, hi(func). */ >+ rtx hi_func = riscv_force_binary (SImode, PLUS, target_function, >+ fixup_value); >+ hi_func = riscv_force_binary (SImode, AND, hi_func, >+ uimm_mask); >+ lui_hi_func_code = OPCODE_LUI | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD); >+ rtx lui_hi_func = riscv_force_binary (SImode, IOR, hi_func, >+ gen_int_mode (lui_hi_func_code, SImode)); >+ >+ mem = adjust_address (m_tramp, SImode, 1 * GET_MODE_SIZE (SImode)); >+ riscv_emit_move (mem, lui_hi_func); >+ >+ /* Gen addi t2, t2, lo(chain). */ >+ rtx lo_chain = riscv_force_binary (SImode, AND, chain_value, >+ imm12_mask); >+ lo_chain = riscv_force_binary (SImode, ASHIFT, lo_chain, GEN_INT (20)); >+ >+ lo_chain_code = OPCODE_ADDI >+ | (STATIC_CHAIN_REGNUM << SHIFT_RD) >+ | (STATIC_CHAIN_REGNUM << SHIFT_RS1); >+ >+ rtx addi_lo_chain = riscv_force_binary (SImode, IOR, lo_chain, >+ force_reg (SImode, GEN_INT (lo_chain_code))); >+ >+ mem = adjust_address (m_tramp, SImode, 2 * GET_MODE_SIZE (SImode)); >+ riscv_emit_move (mem, addi_lo_chain); >+ >+ /* Gen jr r1, lo(func). */ >+ rtx lo_func = riscv_force_binary (SImode, AND, target_function, >+ imm12_mask); >+ lo_func = riscv_force_binary (SImode, ASHIFT, lo_func, GEN_INT (20)); >+ >+ lo_func_code = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1); >+ >+ rtx jr_lo_func = riscv_force_binary (SImode, IOR, lo_func, >+ force_reg (SImode, GEN_INT (lo_func_code))); >+ >+ mem = adjust_address (m_tramp, SImode, 3 * GET_MODE_SIZE (SImode)); >+ riscv_emit_move (mem, jr_lo_func); >+ } >+ else >+ { >+ static_chain_offset = TRAMPOLINE_CODE_SIZE; >+ target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode); >+ >+ /* auipc t2, 0 >+ l[wd] t1, target_function_offset(t2) >+ l[wd] t2, static_chain_offset(t2) >+ jr t1 >+ */ >+ trampoline[0] = OPCODE_AUIPC | (STATIC_CHAIN_REGNUM << SHIFT_RD); >+ trampoline[1] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW) >+ | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD) >+ | (STATIC_CHAIN_REGNUM << SHIFT_RS1) >+ | (target_function_offset << SHIFT_IMM); >+ trampoline[2] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW) >+ | (STATIC_CHAIN_REGNUM << SHIFT_RD) >+ | (STATIC_CHAIN_REGNUM << SHIFT_RS1) >+ | (static_chain_offset << SHIFT_IMM); >+ trampoline[3] = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1); >+ >+ /* Copy the trampoline code. */ >+ for (i = 0; i < ARRAY_SIZE (trampoline); i++) >+ { >+ mem = adjust_address (m_tramp, SImode, i * GET_MODE_SIZE (SImode)); >+ riscv_emit_move (mem, gen_int_mode (trampoline[i], SImode)); >+ } >+ >+ /* Set up the static chain pointer field. */ >+ mem = adjust_address (m_tramp, ptr_mode, static_chain_offset); >+ riscv_emit_move (mem, chain_value); >+ >+ /* Set up the target function field. */ >+ mem = adjust_address (m_tramp, ptr_mode, target_function_offset); >+ riscv_emit_move (mem, XEXP (DECL_RTL (fndecl), 0)); >+ } >+ >+ /* Flush the code part of the trampoline. */ >+ emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE))); >+ emit_insn (gen_clear_cache (addr, end_addr)); >+} >+ >+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ >+ >+static bool >+riscv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, >+ tree exp ATTRIBUTE_UNUSED) >+{ >+ /* Don't use sibcalls when use save-restore routine. */ >+ if (TARGET_SAVE_RESTORE) >+ return false; >+ >+ /* Don't use sibcall for naked functions. */ >+ if (cfun->machine->naked_p) >+ return false; >+ >+ /* Don't use sibcall for interrupt functions. */ >+ if (cfun->machine->interrupt_handler_p) >+ return false; >+ >+ return true; >+} >+ >+/* Get the interrupt type, return UNKNOWN_MODE if it's not >+ interrupt function. */ >+static enum riscv_privilege_levels >+riscv_get_interrupt_type (tree decl) >+{ >+ gcc_assert (decl != NULL_TREE); >+ >+ if ((TREE_CODE(decl) != FUNCTION_DECL) >+ || (!riscv_interrupt_type_p (TREE_TYPE (decl)))) >+ return UNKNOWN_MODE; >+ >+ tree attr_args >+ = TREE_VALUE (lookup_attribute ("interrupt", >+ TYPE_ATTRIBUTES (TREE_TYPE (decl)))); >+ >+ if (attr_args && TREE_CODE (TREE_VALUE (attr_args)) != VOID_TYPE) >+ { >+ const char *string = TREE_STRING_POINTER (TREE_VALUE (attr_args)); >+ >+ if (!strcmp (string, "user")) >+ return USER_MODE; >+ else if (!strcmp (string, "supervisor")) >+ return SUPERVISOR_MODE; >+ else /* Must be "machine". */ >+ return MACHINE_MODE; >+ } >+ else >+ /* Interrupt attributes are machine mode by default. */ >+ return MACHINE_MODE; >+} >+ >+/* Implement `TARGET_SET_CURRENT_FUNCTION'. */ >+/* Sanity cheching for above function attributes. */ >+static void >+riscv_set_current_function (tree decl) >+{ >+ if (decl == NULL_TREE >+ || current_function_decl == NULL_TREE >+ || current_function_decl == error_mark_node >+ || ! cfun->machine >+ || cfun->machine->attributes_checked_p) >+ return; >+ >+ cfun->machine->naked_p = riscv_naked_function_p (decl); >+ cfun->machine->interrupt_handler_p >+ = riscv_interrupt_type_p (TREE_TYPE (decl)); >+ >+ if (cfun->machine->naked_p && cfun->machine->interrupt_handler_p) >+ error ("function attributes %qs and %qs are mutually exclusive", >+ "interrupt", "naked"); >+ >+ if (cfun->machine->interrupt_handler_p) >+ { >+ tree ret = TREE_TYPE (TREE_TYPE (decl)); >+ tree args = TYPE_ARG_TYPES (TREE_TYPE (decl)); >+ >+ if (TREE_CODE (ret) != VOID_TYPE) >+ error ("%qs function cannot return a value", "interrupt"); >+ >+ if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE) >+ error ("%qs function cannot have arguments", "interrupt"); >+ >+ cfun->machine->interrupt_mode = riscv_get_interrupt_type (decl); >+ >+ gcc_assert (cfun->machine->interrupt_mode != UNKNOWN_MODE); >+ } >+ >+ /* Don't print the above diagnostics more than once. */ >+ cfun->machine->attributes_checked_p = 1; >+} >+ >+/* Implement TARGET_MERGE_DECL_ATTRIBUTES. */ >+static tree >+riscv_merge_decl_attributes (tree olddecl, tree newdecl) >+{ >+ tree combined_attrs; >+ >+ enum riscv_privilege_levels old_interrupt_type >+ = riscv_get_interrupt_type (olddecl); >+ enum riscv_privilege_levels new_interrupt_type >+ = riscv_get_interrupt_type (newdecl); >+ >+ /* Check old and new has same interrupt type. */ >+ if ((old_interrupt_type != UNKNOWN_MODE) >+ && (new_interrupt_type != UNKNOWN_MODE) >+ && (old_interrupt_type != new_interrupt_type)) >+ error ("%qs function cannot have different interrupt type", "interrupt"); >+ >+ /* Create combined attributes. */ >+ combined_attrs = merge_attributes (DECL_ATTRIBUTES (olddecl), >+ DECL_ATTRIBUTES (newdecl)); >+ >+ return combined_attrs; >+} >+ >+/* Implement TARGET_CANNOT_COPY_INSN_P. */ >+ >+static bool >+riscv_cannot_copy_insn_p (rtx_insn *insn) >+{ >+ return recog_memoized (insn) >= 0 && get_attr_cannot_copy (insn); >+} >+ >+/* Implement TARGET_SLOW_UNALIGNED_ACCESS. */ >+ >+static bool >+riscv_slow_unaligned_access (machine_mode, unsigned int) >+{ >+ return riscv_slow_unaligned_access_p; >+} >+ >+/* Implement TARGET_CAN_CHANGE_MODE_CLASS. */ >+ >+static bool >+riscv_can_change_mode_class (machine_mode, machine_mode, reg_class_t rclass) >+{ >+ return !reg_classes_intersect_p (FP_REGS, rclass); >+} >+ >+ >+/* Implement TARGET_CONSTANT_ALIGNMENT. */ >+ >+static HOST_WIDE_INT >+riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align) >+{ >+ if ((TREE_CODE (exp) == STRING_CST || TREE_CODE (exp) == CONSTRUCTOR) >+ && (riscv_align_data_type == riscv_align_data_type_xlen)) >+ return MAX (align, BITS_PER_WORD); >+ return align; >+} >+ >+/* Implement TARGET_PROMOTE_FUNCTION_MODE. */ >+ >+/* This function is equivalent to default_promote_function_mode_always_promote >+ except that it returns a promoted mode even if type is NULL_TREE. This is >+ needed by libcalls which have no type (only a mode) such as fixed conversion >+ routines that take a signed or unsigned char/short/int argument and convert >+ it to a fixed type. */ >+ >+static machine_mode >+riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, >+ machine_mode mode, >+ int *punsignedp ATTRIBUTE_UNUSED, >+ const_tree fntype ATTRIBUTE_UNUSED, >+ int for_return ATTRIBUTE_UNUSED) >+{ >+ int unsignedp; >+ >+ if (type != NULL_TREE) >+ return promote_mode (type, mode, punsignedp); >+ >+ unsignedp = *punsignedp; >+ PROMOTE_MODE (mode, unsignedp, type); >+ *punsignedp = unsignedp; >+ return mode; >+} >+ >+/* Implement TARGET_MACHINE_DEPENDENT_REORG. */ >+ >+static void >+riscv_reorg (void) >+{ >+ /* Do nothing unless we have -msave-restore */ >+ if (TARGET_SAVE_RESTORE) >+ riscv_remove_unneeded_save_restore_calls (); >+} >+ >+/* Return nonzero if register FROM_REGNO can be renamed to register >+ TO_REGNO. */ >+ >+bool >+riscv_hard_regno_rename_ok (unsigned from_regno ATTRIBUTE_UNUSED, >+ unsigned to_regno) >+{ >+ /* Interrupt functions can only use registers that have already been >+ saved by the prologue, even if they would normally be >+ call-clobbered. */ >+ return !cfun->machine->interrupt_handler_p || df_regs_ever_live_p (to_regno); >+} >+ >+ >+/* Helper function for generating gpr_save pattern. */ >+ >+rtx >+riscv_gen_gpr_save_insn (struct riscv_frame_info *frame) >+{ >+ unsigned count = riscv_save_libcall_count (frame->mask); >+ /* 1 for unspec 2 for clobber t0/t1 and 1 for ra. */ >+ unsigned veclen = 1 + 2 + 1 + count; >+ rtvec vec = rtvec_alloc (veclen); >+ >+ gcc_assert (veclen <= ARRAY_SIZE (gpr_save_reg_order)); >+ >+ RTVEC_ELT (vec, 0) = >+ gen_rtx_UNSPEC_VOLATILE (VOIDmode, >+ gen_rtvec (1, GEN_INT (count)), UNSPECV_GPR_SAVE); >+ >+ for (unsigned i = 1; i < veclen; ++i) >+ { >+ unsigned regno = gpr_save_reg_order[i]; >+ rtx reg = gen_rtx_REG (Pmode, regno); >+ rtx elt; >+ >+ /* t0 and t1 are CLOBBERs, others are USEs. */ >+ if (i < 3) >+ elt = gen_rtx_CLOBBER (Pmode, reg); >+ else >+ elt = gen_rtx_USE (Pmode, reg); >+ >+ RTVEC_ELT (vec, i) = elt; >+ } >+ >+ /* Largest number of caller-save register must set in mask if we are >+ not using __riscv_save_0. */ >+ gcc_assert ((count == 0) || >+ BITSET_P (frame->mask, gpr_save_reg_order[veclen - 1])); >+ >+ return gen_rtx_PARALLEL (VOIDmode, vec); >+} >+ >+/* Return true if it's valid gpr_save pattern. */ >+ >+bool >+riscv_gpr_save_operation_p (rtx op) >+{ >+ unsigned len = XVECLEN (op, 0); >+ >+ if (len > ARRAY_SIZE (gpr_save_reg_order)) >+ return false; >+ >+ for (unsigned i = 0; i < len; i++) >+ { >+ rtx elt = XVECEXP (op, 0, i); >+ if (i == 0) >+ { >+ /* First element in parallel is unspec. */ >+ if (GET_CODE (elt) != UNSPEC_VOLATILE >+ || GET_CODE (XVECEXP (elt, 0, 0)) != CONST_INT >+ || XINT (elt, 1) != UNSPECV_GPR_SAVE) >+ return false; >+ } >+ else >+ { >+ /* Two CLOBBER and USEs, must check the order. */ >+ unsigned expect_code = i < 3 ? CLOBBER : USE; >+ if (GET_CODE (elt) != expect_code >+ || !REG_P (XEXP (elt, 1)) >+ || (REGNO (XEXP (elt, 1)) != gpr_save_reg_order[i])) >+ return false; >+ } >+ break; >+ } >+ return true; >+} >+ >+/* Implement TARGET_NEW_ADDRESS_PROFITABLE_P. */ >+ >+bool >+riscv_new_address_profitable_p (rtx memref, rtx_insn *insn, rtx new_addr) >+{ >+ /* Prefer old address if it is less expensive. */ >+ addr_space_t as = MEM_ADDR_SPACE (memref); >+ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); >+ int old_cost = address_cost (XEXP (memref, 0), GET_MODE (memref), as, speed); >+ int new_cost = address_cost (new_addr, GET_MODE (memref), as, speed); >+ return new_cost <= old_cost; >+} >+ >+/* Initialize the GCC target structure. */ >+#undef TARGET_ASM_ALIGNED_HI_OP >+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" >+#undef TARGET_ASM_ALIGNED_SI_OP >+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" >+#undef TARGET_ASM_ALIGNED_DI_OP >+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t" >+ >+#undef TARGET_OPTION_OVERRIDE >+#define TARGET_OPTION_OVERRIDE riscv_option_override >+ >+#undef TARGET_LEGITIMIZE_ADDRESS >+#define TARGET_LEGITIMIZE_ADDRESS riscv_legitimize_address >+ >+#undef TARGET_SCHED_ISSUE_RATE >+#define TARGET_SCHED_ISSUE_RATE riscv_issue_rate >+ >+#undef TARGET_FUNCTION_OK_FOR_SIBCALL >+#define TARGET_FUNCTION_OK_FOR_SIBCALL riscv_function_ok_for_sibcall >+ >+#undef TARGET_SET_CURRENT_FUNCTION >+#define TARGET_SET_CURRENT_FUNCTION riscv_set_current_function >+ >+#undef TARGET_REGISTER_MOVE_COST >+#define TARGET_REGISTER_MOVE_COST riscv_register_move_cost >+#undef TARGET_MEMORY_MOVE_COST >+#define TARGET_MEMORY_MOVE_COST riscv_memory_move_cost >+#undef TARGET_RTX_COSTS >+#define TARGET_RTX_COSTS riscv_rtx_costs >+#undef TARGET_ADDRESS_COST >+#define TARGET_ADDRESS_COST riscv_address_cost >+ >+#undef TARGET_ASM_FILE_START >+#define TARGET_ASM_FILE_START riscv_file_start >+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE >+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true >+ >+#undef TARGET_EXPAND_BUILTIN_VA_START >+#define TARGET_EXPAND_BUILTIN_VA_START riscv_va_start >+ >+#undef TARGET_PROMOTE_FUNCTION_MODE >+#define TARGET_PROMOTE_FUNCTION_MODE riscv_promote_function_mode >+ >+#undef TARGET_RETURN_IN_MEMORY >+#define TARGET_RETURN_IN_MEMORY riscv_return_in_memory >+ >+#undef TARGET_ASM_OUTPUT_MI_THUNK >+#define TARGET_ASM_OUTPUT_MI_THUNK riscv_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_PRINT_OPERAND >+#define TARGET_PRINT_OPERAND riscv_print_operand >+#undef TARGET_PRINT_OPERAND_ADDRESS >+#define TARGET_PRINT_OPERAND_ADDRESS riscv_print_operand_address >+ >+#undef TARGET_SETUP_INCOMING_VARARGS >+#define TARGET_SETUP_INCOMING_VARARGS riscv_setup_incoming_varargs >+#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS >+#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS riscv_allocate_stack_slots_for_args >+#undef TARGET_STRICT_ARGUMENT_NAMING >+#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true >+#undef TARGET_MUST_PASS_IN_STACK >+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size >+#undef TARGET_PASS_BY_REFERENCE >+#define TARGET_PASS_BY_REFERENCE riscv_pass_by_reference >+#undef TARGET_ARG_PARTIAL_BYTES >+#define TARGET_ARG_PARTIAL_BYTES riscv_arg_partial_bytes >+#undef TARGET_FUNCTION_ARG >+#define TARGET_FUNCTION_ARG riscv_function_arg >+#undef TARGET_FUNCTION_ARG_ADVANCE >+#define TARGET_FUNCTION_ARG_ADVANCE riscv_function_arg_advance >+#undef TARGET_FUNCTION_ARG_BOUNDARY >+#define TARGET_FUNCTION_ARG_BOUNDARY riscv_function_arg_boundary >+ >+/* The generic ELF target does not always have TLS support. */ >+#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 riscv_cannot_force_const_mem >+ >+#undef TARGET_LEGITIMATE_CONSTANT_P >+#define TARGET_LEGITIMATE_CONSTANT_P riscv_legitimate_constant_p >+ >+#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P >+#define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_true >+ >+#undef TARGET_LEGITIMATE_ADDRESS_P >+#define TARGET_LEGITIMATE_ADDRESS_P riscv_legitimate_address_p >+ >+#undef TARGET_CAN_ELIMINATE >+#define TARGET_CAN_ELIMINATE riscv_can_eliminate >+ >+#undef TARGET_CONDITIONAL_REGISTER_USAGE >+#define TARGET_CONDITIONAL_REGISTER_USAGE riscv_conditional_register_usage >+ >+#undef TARGET_CLASS_MAX_NREGS >+#define TARGET_CLASS_MAX_NREGS riscv_class_max_nregs >+ >+#undef TARGET_TRAMPOLINE_INIT >+#define TARGET_TRAMPOLINE_INIT riscv_trampoline_init >+ >+#undef TARGET_IN_SMALL_DATA_P >+#define TARGET_IN_SMALL_DATA_P riscv_in_small_data_p >+ >+#undef TARGET_HAVE_SRODATA_SECTION >+#define TARGET_HAVE_SRODATA_SECTION true >+ >+#undef TARGET_ASM_SELECT_SECTION >+#define TARGET_ASM_SELECT_SECTION riscv_select_section >+ >+#undef TARGET_ASM_UNIQUE_SECTION >+#define TARGET_ASM_UNIQUE_SECTION riscv_unique_section >+ >+#undef TARGET_ASM_SELECT_RTX_SECTION >+#define TARGET_ASM_SELECT_RTX_SECTION riscv_elf_select_rtx_section >+ >+#undef TARGET_MIN_ANCHOR_OFFSET >+#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2) >+ >+#undef TARGET_MAX_ANCHOR_OFFSET >+#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1) >+ >+#undef TARGET_REGISTER_PRIORITY >+#define TARGET_REGISTER_PRIORITY riscv_register_priority >+ >+#undef TARGET_CANNOT_COPY_INSN_P >+#define TARGET_CANNOT_COPY_INSN_P riscv_cannot_copy_insn_p >+ >+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV >+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV riscv_atomic_assign_expand_fenv >+ >+#undef TARGET_INIT_BUILTINS >+#define TARGET_INIT_BUILTINS riscv_init_builtins >+ >+#undef TARGET_BUILTIN_DECL >+#define TARGET_BUILTIN_DECL riscv_builtin_decl >+ >+#undef TARGET_EXPAND_BUILTIN >+#define TARGET_EXPAND_BUILTIN riscv_expand_builtin >+ >+#undef TARGET_HARD_REGNO_NREGS >+#define TARGET_HARD_REGNO_NREGS riscv_hard_regno_nregs >+#undef TARGET_HARD_REGNO_MODE_OK >+#define TARGET_HARD_REGNO_MODE_OK riscv_hard_regno_mode_ok >+ >+#undef TARGET_MODES_TIEABLE_P >+#define TARGET_MODES_TIEABLE_P riscv_modes_tieable_p >+ >+#undef TARGET_SLOW_UNALIGNED_ACCESS >+#define TARGET_SLOW_UNALIGNED_ACCESS riscv_slow_unaligned_access >+ >+#undef TARGET_SECONDARY_MEMORY_NEEDED >+#define TARGET_SECONDARY_MEMORY_NEEDED riscv_secondary_memory_needed >+ >+#undef TARGET_CAN_CHANGE_MODE_CLASS >+#define TARGET_CAN_CHANGE_MODE_CLASS riscv_can_change_mode_class >+ >+#undef TARGET_CONSTANT_ALIGNMENT >+#define TARGET_CONSTANT_ALIGNMENT riscv_constant_alignment >+ >+#undef TARGET_MERGE_DECL_ATTRIBUTES >+#define TARGET_MERGE_DECL_ATTRIBUTES riscv_merge_decl_attributes >+ >+#undef TARGET_ATTRIBUTE_TABLE >+#define TARGET_ATTRIBUTE_TABLE riscv_attribute_table >+ >+#undef TARGET_WARN_FUNC_RETURN >+#define TARGET_WARN_FUNC_RETURN riscv_warn_func_return >+ >+/* The low bit is ignored by jump instructions so is safe to use. */ >+#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS >+#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 >+ >+#undef TARGET_MACHINE_DEPENDENT_REORG >+#define TARGET_MACHINE_DEPENDENT_REORG riscv_reorg >+ >+#undef TARGET_NEW_ADDRESS_PROFITABLE_P >+#define TARGET_NEW_ADDRESS_PROFITABLE_P riscv_new_address_profitable_p >+ >+struct gcc_target targetm = TARGET_INITIALIZER; >+ >+#include "gt-riscv.h"
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 97417
:
49470
|
49500
|
49524
|
49532
|
49533
|
49534
|
49536
|
49542
|
49543
|
49575
|
49757
|
49767
|
49773