| /* Target Code for ft32 |
| Copyright (C) 2015-2021 Free Software Foundation, Inc. |
| Contributed by FTDI <support@ftdi.com> |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published |
| by the Free Software Foundation; either version 3, or (at your |
| option) any later version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #define IN_TARGET_CODE 1 |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "df.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "regs.h" |
| #include "emit-rtl.h" |
| #include "diagnostic-core.h" |
| #include "output.h" |
| #include "stor-layout.h" |
| #include "calls.h" |
| #include "expr.h" |
| #include "builtins.h" |
| #include "print-tree.h" |
| |
| /* This file should be included last. */ |
| #include "target-def.h" |
| |
| #include <stdint.h> |
| |
| #define LOSE_AND_RETURN(msgid, x) \ |
| do \ |
| { \ |
| ft32_operand_lossage (msgid, x); \ |
| return; \ |
| } while (0) |
| |
| /* Worker function for TARGET_RETURN_IN_MEMORY. */ |
| |
| static bool |
| ft32_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) |
| { |
| const HOST_WIDE_INT size = int_size_in_bytes (type); |
| return (size == -1 || size > 2 * UNITS_PER_WORD); |
| } |
| |
| /* Define how to find the value returned by a function. |
| VALTYPE is the data type of the value (as a tree). |
| If the precise function being called is known, FUNC is its |
| FUNCTION_DECL; otherwise, FUNC is 0. |
| |
| We always return values in register $r0 for ft32. */ |
| |
| static rtx |
| ft32_function_value (const_tree valtype, |
| const_tree fntype_or_decl ATTRIBUTE_UNUSED, |
| bool outgoing ATTRIBUTE_UNUSED) |
| { |
| return gen_rtx_REG (TYPE_MODE (valtype), FT32_R0); |
| } |
| |
| /* Define how to find the value returned by a library function. |
| |
| We always return values in register $r0 for ft32. */ |
| |
| static rtx |
| ft32_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED) |
| { |
| return gen_rtx_REG (mode, FT32_R0); |
| } |
| |
| /* Handle TARGET_FUNCTION_VALUE_REGNO_P. |
| |
| We always return values in register $r0 for ft32. */ |
| |
| static bool |
| ft32_function_value_regno_p (const unsigned int regno) |
| { |
| return (regno == FT32_R0); |
| } |
| |
| /* Emit an error message when we're in an asm, and a fatal error for |
| "normal" insns. Formatted output isn't easily implemented, since we |
| use output_operand_lossage to output the actual message and handle the |
| categorization of the error. */ |
| |
| static void |
| ft32_operand_lossage (const char *msgid, rtx op) |
| { |
| debug_rtx (op); |
| output_operand_lossage ("%s", msgid); |
| } |
| |
| /* The PRINT_OPERAND_ADDRESS worker. */ |
| |
| void |
| ft32_print_operand_address (FILE * file, rtx x) |
| { |
| switch (GET_CODE (x)) |
| { |
| case REG: |
| fprintf (file, "%s,0", reg_names[REGNO (x)]); |
| break; |
| |
| case PLUS: |
| switch (GET_CODE (XEXP (x, 1))) |
| { |
| case CONST_INT: |
| fprintf (file, "%s,%ld", |
| reg_names[REGNO (XEXP (x, 0))], INTVAL (XEXP (x, 1))); |
| break; |
| case SYMBOL_REF: |
| output_addr_const (file, XEXP (x, 1)); |
| fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); |
| break; |
| case CONST: |
| { |
| rtx plus = XEXP (XEXP (x, 1), 0); |
| if (GET_CODE (XEXP (plus, 0)) == SYMBOL_REF |
| && CONST_INT_P (XEXP (plus, 1))) |
| { |
| output_addr_const (file, XEXP (plus, 0)); |
| fprintf (file, "+%ld(%s)", INTVAL (XEXP (plus, 1)), |
| reg_names[REGNO (XEXP (x, 0))]); |
| } |
| else |
| abort (); |
| } |
| break; |
| default: |
| abort (); |
| } |
| break; |
| |
| default: |
| output_addr_const (file, x); |
| break; |
| } |
| } |
| |
| /* The PRINT_OPERAND worker. */ |
| |
| void |
| ft32_print_operand (FILE * file, rtx x, int code) |
| { |
| rtx operand = x; |
| |
| /* New code entries should just be added to the switch below. If |
| handling is finished, just return. If handling was just a |
| modification of the operand, the modified operand should be put in |
| "operand", and then do a break to let default handling |
| (zero-modifier) output the operand. */ |
| |
| switch (code) |
| { |
| case 0: |
| /* No code, print as usual. */ |
| break; |
| |
| case 'h': |
| if (GET_CODE (operand) != REG) |
| internal_error ("%<h%> applied to non-register operand"); |
| fprintf (file, "%s", reg_names[REGNO (operand) + 1]); |
| return; |
| |
| case 'm': |
| fprintf (file, "%ld", (long) (- INTVAL(x))); |
| return; |
| |
| case 'd': // a DW spec, from an integer alignment (for BLKmode insns) |
| { |
| int i = INTVAL (x); |
| char dwspec; |
| switch (i) |
| { |
| case 1: |
| dwspec = 'b'; |
| break; |
| case 2: |
| dwspec = 's'; |
| break; |
| case 4: |
| dwspec = 'l'; |
| break; |
| default: |
| if ((i % 4) != 0) |
| internal_error ("bad alignment: %d", i); |
| else |
| dwspec = 'l'; |
| break; |
| } |
| fprintf (file, "%c", dwspec); |
| return; |
| } |
| |
| case 'f': |
| { |
| int bf = ft32_as_bitfield (INTVAL (x)); |
| fprintf (file, "512|(%d<<5)|%d", bf >> 5, bf & 31); |
| return; |
| } |
| |
| case 'g': |
| { |
| int bf = ft32_as_bitfield (0xffffffff ^ INTVAL (x)); |
| fprintf (file, "(%d<<5)|%d", bf >> 5, bf & 31); |
| return; |
| } |
| |
| case 'b': |
| { |
| ft32_print_operand (file, XEXP (x, 0), 0); |
| return; |
| } |
| |
| default: |
| LOSE_AND_RETURN ("invalid operand modifier letter", x); |
| } |
| |
| /* Print an operand as without a modifier letter. */ |
| switch (GET_CODE (operand)) |
| { |
| case REG: |
| fprintf (file, "%s", reg_names[REGNO (operand)]); |
| return; |
| |
| case MEM: |
| output_address (GET_MODE (XEXP (operand, 0)), XEXP (operand, 0)); |
| return; |
| |
| default: |
| /* No need to handle all strange variants, let output_addr_const |
| do it for us. */ |
| if (CONSTANT_P (operand)) |
| { |
| output_addr_const (file, operand); |
| return; |
| } |
| |
| LOSE_AND_RETURN ("unexpected operand", x); |
| } |
| } |
| |
| const char * |
| ft32_load_immediate (rtx dst, int32_t i) |
| { |
| char pattern[100]; |
| |
| if (i >= -524288 && i <= 524287) |
| { |
| sprintf (pattern, "ldk.l %%0,%d", i); |
| output_asm_insn (pattern, &dst); |
| } |
| else if (i >= -536870912 && i <= 536870911) |
| { |
| ft32_load_immediate (dst, i >> 10); |
| sprintf (pattern, "ldl.l %%0,%%0,%d", i & 1023); |
| output_asm_insn (pattern, &dst); |
| } |
| else |
| { |
| int rd; // rotate distance |
| uint32_t u = i; |
| for (rd = 1; rd < 32; rd++) |
| { |
| u = ((u >> 31) & 1) | (u << 1); |
| if ((int32_t) u >= -524288 && (int32_t) u <= 524287) |
| { |
| ft32_load_immediate (dst, (int32_t) u); |
| sprintf (pattern, "ror.l %%0,%%0,%d", rd); |
| output_asm_insn (pattern, &dst); |
| return ""; |
| } |
| } |
| ft32_load_immediate (dst, i >> 10); |
| sprintf (pattern, "ldl.l %%0,%%0,%d", i & 1023); |
| output_asm_insn (pattern, &dst); |
| } |
| |
| return ""; |
| } |
| |
| // x is a bit mask, for example: |
| // 00000000000000000000001111111110 |
| // If x contains a single bit mask, return the bitfield spec. |
| // in the above case it returns ((9 << 5) | 1) |
| // Otherwise return -1. |
| // |
| |
| #define NBITS(n) ((1U << (n)) - 1U) |
| |
| int |
| ft32_as_bitfield (unsigned int x) |
| { |
| int lobit, hibit; |
| |
| if (x == 0) |
| return -1; |
| |
| for (lobit = 0; lobit < 32; lobit++) |
| if (x & (1 << lobit)) |
| break; |
| for (hibit = 31; hibit >= 0; hibit--) |
| if (x & (1 << hibit)) |
| break; |
| |
| int width = 1 + hibit - lobit; |
| if (width > 16) |
| return -1; |
| |
| if (x != (NBITS (width) << lobit)) |
| return -1; // not a clean bitfield |
| |
| return ((width & 15) << 5) | lobit; |
| } |
| |
| /* Per-function machine data. */ |
| struct GTY (()) machine_function |
| { |
| /* Number of bytes saved on the stack for callee saved registers. */ |
| int callee_saved_reg_size; |
| |
| /* Number of bytes saved on the stack for local variables. */ |
| int local_vars_size; |
| |
| /* The sum of 2 sizes: locals vars and padding byte for saving the |
| * registers. Used in expand_prologue () and expand_epilogue (). */ |
| int size_for_adjusting_sp; |
| }; |
| |
| /* Zero initialization is OK for all current fields. */ |
| |
| static struct machine_function * |
| ft32_init_machine_status (void) |
| { |
| return ggc_cleared_alloc < machine_function > (); |
| } |
| |
| |
| /* The TARGET_OPTION_OVERRIDE worker. |
| All this curently does is set init_machine_status. */ |
| static void |
| ft32_option_override (void) |
| { |
| /* Set the per-function-data initializer. */ |
| init_machine_status = ft32_init_machine_status; |
| } |
| |
| /* Implement targetm.select_section. */ |
| static section * |
| ft32_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) |
| { |
| /* Variables and constants defined in the __ea address space |
| go into a special section named "._ea". */ |
| if (TREE_TYPE (decl) != error_mark_node |
| && TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_PM) |
| { |
| /* We might get called with string constants, but get_named_section |
| doesn't like them as they are not DECLs. Also, we need to set |
| flags in that case. */ |
| if (!DECL_P (decl)) |
| return get_section ("._pm", SECTION_WRITE | SECTION_DEBUG, NULL); |
| |
| return get_named_section (decl, "._pm", reloc); |
| } |
| |
| return default_elf_select_section (decl, reloc, align); |
| } |
| |
| /* Compute the size of the local area and the size to be adjusted by the |
| * prologue and epilogue. */ |
| |
| static void |
| ft32_compute_frame (void) |
| { |
| /* For aligning the local variables. */ |
| int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT; |
| int padding_locals; |
| int regno; |
| |
| /* Padding needed for each element of the frame. */ |
| cfun->machine->local_vars_size = get_frame_size (); |
| |
| /* Align to the stack alignment. */ |
| padding_locals = cfun->machine->local_vars_size % stack_alignment; |
| if (padding_locals) |
| padding_locals = stack_alignment - padding_locals; |
| |
| cfun->machine->local_vars_size += padding_locals; |
| |
| cfun->machine->callee_saved_reg_size = 0; |
| |
| /* Save callee-saved registers. */ |
| for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
| if (df_regs_ever_live_p (regno) && !call_used_or_fixed_reg_p (regno)) |
| cfun->machine->callee_saved_reg_size += 4; |
| |
| cfun->machine->size_for_adjusting_sp = |
| 0 // crtl->args.pretend_args_size |
| + cfun->machine->local_vars_size |
| + (ACCUMULATE_OUTGOING_ARGS |
| ? (HOST_WIDE_INT) crtl->outgoing_args_size : 0); |
| } |
| |
| // Must use LINK/UNLINK when... |
| // the frame is bigger than 512 bytes so cannot just "SUB" from SP |
| // the function actually uses $fp |
| |
| static int |
| must_link (void) |
| { |
| int bigframe = (cfun->machine->size_for_adjusting_sp >= 512); |
| return (bigframe || frame_pointer_needed || df_regs_ever_live_p (FT32_FP) |
| || df_regs_ever_live_p (FT32_FP)); |
| } |
| |
| void |
| ft32_expand_prologue (void) |
| { |
| int regno; |
| rtx insn; |
| |
| ft32_compute_frame (); |
| |
| int args_to_push = crtl->args.pretend_args_size; |
| if (args_to_push) |
| { |
| int i; |
| |
| insn = emit_insn (gen_movsi_pop ((gen_rtx_REG (Pmode, FT32_R29)))); |
| |
| for (i = 0; i < (args_to_push / 4); i++) |
| { |
| insn = |
| emit_insn (gen_movsi_push ((gen_rtx_REG (Pmode, FT32_R5 - i)))); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| |
| insn = emit_insn (gen_movsi_push ((gen_rtx_REG (Pmode, FT32_R29)))); |
| } |
| |
| if (flag_stack_usage_info) |
| current_function_static_stack_size = cfun->machine->size_for_adjusting_sp; |
| |
| if (!must_link () && (cfun->machine->callee_saved_reg_size == 4)) |
| { |
| insn = |
| emit_insn (gen_link |
| (gen_rtx_REG (Pmode, FT32_R13), |
| GEN_INT (-cfun->machine->size_for_adjusting_sp))); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| return; |
| } |
| /* Save callee-saved registers. */ |
| if (optimize_size) |
| { |
| for (regno = FIRST_PSEUDO_REGISTER; regno-- > 0;) |
| { |
| if (!call_used_or_fixed_reg_p (regno) |
| && df_regs_ever_live_p (regno)) |
| { |
| rtx preg = gen_rtx_REG (Pmode, regno); |
| emit_insn (gen_call_prolog (preg)); |
| break; |
| } |
| } |
| } |
| else |
| { |
| for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) |
| { |
| if (df_regs_ever_live_p (regno) |
| && !call_used_or_fixed_reg_p (regno)) |
| { |
| insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| } |
| } |
| |
| if (cfun->machine->size_for_adjusting_sp >= 65536) |
| { |
| error ("stack frame must be smaller than 64K"); |
| return; |
| } |
| if (must_link ()) |
| { |
| insn = |
| emit_insn (gen_link |
| (gen_rtx_REG (Pmode, FT32_FP), |
| GEN_INT (-cfun->machine->size_for_adjusting_sp))); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| else if (cfun->machine->size_for_adjusting_sp > 0) |
| { |
| int adj = cfun->machine->size_for_adjusting_sp; |
| insn = emit_insn (gen_addsi3 (gen_rtx_REG (SImode, FT32_SP), |
| gen_rtx_REG (SImode, FT32_SP), |
| GEN_INT (-adj))); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| } |
| |
| void |
| ft32_expand_epilogue (void) |
| { |
| int regno; |
| int pretend = crtl->args.pretend_args_size; |
| |
| if (!must_link () |
| && (cfun->machine->size_for_adjusting_sp == 24) |
| && (cfun->machine->callee_saved_reg_size == 0)) |
| { |
| emit_jump_insn (gen_returner24 ()); |
| return; |
| } |
| |
| // Set when the epilog code will also add 24 to $sp |
| int epilog24 = (!must_link () |
| && (cfun->machine->size_for_adjusting_sp == 24) |
| && optimize_size); |
| |
| if (must_link ()) |
| { |
| emit_insn (gen_unlink ()); |
| } |
| else if (!epilog24 && (cfun->machine->size_for_adjusting_sp > 0)) |
| { |
| emit_insn (gen_addsi3 (gen_rtx_REG (SImode, FT32_SP), |
| gen_rtx_REG (SImode, FT32_SP), |
| GEN_INT (cfun->machine->size_for_adjusting_sp))); |
| } |
| |
| if (cfun->machine->callee_saved_reg_size != 0) |
| { |
| for (regno = FIRST_PSEUDO_REGISTER; regno-- > 0;) |
| { |
| if (!call_used_or_fixed_reg_p (regno) |
| && df_regs_ever_live_p (regno)) |
| { |
| rtx preg = gen_rtx_REG (Pmode, regno); |
| if (optimize_size && (pretend == 0)) |
| { |
| if (epilog24) |
| emit_insn (gen_jump_epilog24 (preg)); |
| else |
| emit_insn (gen_jump_epilog (preg)); |
| return; |
| } |
| emit_insn (gen_movsi_pop (preg)); |
| } |
| } |
| } |
| |
| if (pretend != 0) |
| emit_jump_insn (gen_pretend_returner (GEN_INT (pretend))); |
| else |
| emit_jump_insn (gen_returner ()); |
| } |
| |
| #undef TARGET_FRAME_POINTER_REQUIRED |
| #define TARGET_FRAME_POINTER_REQUIRED ft32_frame_pointer_required |
| static bool |
| ft32_frame_pointer_required (void) |
| { |
| return cfun->calls_alloca; |
| } |
| |
| #undef TARGET_CAN_ELIMINATE |
| #define TARGET_CAN_ELIMINATE ft32_can_eliminate |
| |
| /* Return true if register FROM can be eliminated via register TO. */ |
| |
| static bool |
| ft32_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) |
| { |
| return 1; |
| return (to == FRAME_POINTER_REGNUM) || !ft32_frame_pointer_required (); |
| } |
| |
| /* Implements the macro INITIAL_ELIMINATION_OFFSET, return the OFFSET. */ |
| |
| int |
| ft32_initial_elimination_offset (int from, int to) |
| { |
| ft32_compute_frame (); |
| |
| if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) |
| { |
| return cfun->machine->callee_saved_reg_size + 2 * UNITS_PER_WORD; |
| } |
| |
| if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| { |
| int arg_offset; |
| arg_offset = must_link ()? 2 : 1; |
| return ((cfun->machine->callee_saved_reg_size |
| + arg_offset * UNITS_PER_WORD) |
| + cfun->machine->size_for_adjusting_sp); |
| } |
| |
| if ((from == FRAME_POINTER_REGNUM) && (to == STACK_POINTER_REGNUM)) |
| { |
| return cfun->machine->size_for_adjusting_sp; |
| } |
| |
| gcc_unreachable (); |
| } |
| |
| /* Worker function for TARGET_SETUP_INCOMING_VARARGS. */ |
| |
| static void |
| ft32_setup_incoming_varargs (cumulative_args_t cum_v, |
| const function_arg_info &arg, |
| int *pretend_size, int no_rtl ATTRIBUTE_UNUSED) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| int named_size = |
| GET_MODE_SIZE (SImode) * (*cum - FT32_R0) + GET_MODE_SIZE (arg.mode); |
| |
| if (named_size < 24) |
| *pretend_size = 24 - named_size; |
| else |
| *pretend_size = 0; |
| } |
| |
| /* Return the fixed registers used for condition codes. */ |
| |
| static bool |
| ft32_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) |
| { |
| *p1 = CC_REG; |
| *p2 = INVALID_REGNUM; |
| return true; |
| } |
| |
| /* Return the next register to be used to hold a function argument or |
| NULL_RTX if there's no more space. */ |
| |
| static rtx |
| ft32_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| |
| if (*cum < 8) |
| return gen_rtx_REG (arg.mode, *cum); |
| else |
| return NULL_RTX; |
| } |
| |
| #define FT32_FUNCTION_ARG_SIZE(MODE, TYPE) \ |
| ((MODE) != BLKmode ? GET_MODE_SIZE (MODE) \ |
| : (unsigned) int_size_in_bytes (TYPE)) |
| |
| static void |
| ft32_function_arg_advance (cumulative_args_t cum_v, |
| const function_arg_info &arg) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| |
| *cum = (*cum < FT32_R6 |
| ? *cum + ((3 + FT32_FUNCTION_ARG_SIZE (arg.mode, arg.type)) / 4) |
| : *cum); |
| } |
| |
| /* Return non-zero if the function argument described by ARG is to be |
| passed by reference. */ |
| |
| static bool |
| ft32_pass_by_reference (cumulative_args_t, const function_arg_info &arg) |
| { |
| if (arg.aggregate_type_p ()) |
| return true; |
| unsigned HOST_WIDE_INT size = arg.type_size_in_bytes (); |
| return size > 4 * 6; |
| } |
| |
| /* Some function arguments will only partially fit in the registers |
| that hold arguments. Given a new arg, return the number of bytes |
| that fit in argument passing registers. */ |
| |
| static int |
| ft32_arg_partial_bytes (cumulative_args_t cum_v, const function_arg_info &arg) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| int bytes_left, size; |
| |
| if (*cum >= 8) |
| return 0; |
| |
| if (ft32_pass_by_reference (cum_v, arg)) |
| size = 4; |
| else if (arg.type) |
| { |
| if (AGGREGATE_TYPE_P (arg.type)) |
| return 0; |
| size = int_size_in_bytes (arg.type); |
| } |
| else |
| size = GET_MODE_SIZE (arg.mode); |
| |
| bytes_left = (4 * 6) - ((*cum - 2) * 4); |
| |
| if (size > bytes_left) |
| return bytes_left; |
| else |
| return 0; |
| } |
| |
| /* Used by constraints.md to distinguish between GENERIC and PM |
| memory addresses. */ |
| |
| int |
| ft32_is_mem_pm (rtx o) |
| { |
| return (MEM_P (o) |
| && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (o))); |
| } |
| |
| /* The Global `targetm' Variable. */ |
| |
| /* Initialize the GCC target structure. */ |
| |
| #undef TARGET_PROMOTE_PROTOTYPES |
| #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true |
| |
| #undef TARGET_RETURN_IN_MEMORY |
| #define TARGET_RETURN_IN_MEMORY ft32_return_in_memory |
| #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 ft32_pass_by_reference |
| #undef TARGET_ARG_PARTIAL_BYTES |
| #define TARGET_ARG_PARTIAL_BYTES ft32_arg_partial_bytes |
| #undef TARGET_FUNCTION_ARG |
| #define TARGET_FUNCTION_ARG ft32_function_arg |
| #undef TARGET_FUNCTION_ARG_ADVANCE |
| #define TARGET_FUNCTION_ARG_ADVANCE ft32_function_arg_advance |
| |
| |
| #undef TARGET_SETUP_INCOMING_VARARGS |
| #define TARGET_SETUP_INCOMING_VARARGS ft32_setup_incoming_varargs |
| |
| #undef TARGET_FIXED_CONDITION_CODE_REGS |
| #define TARGET_FIXED_CONDITION_CODE_REGS ft32_fixed_condition_code_regs |
| |
| /* Define this to return an RTX representing the place where a |
| function returns or receives a value of data type RET_TYPE, a tree |
| node representing a data type. */ |
| #undef TARGET_FUNCTION_VALUE |
| #define TARGET_FUNCTION_VALUE ft32_function_value |
| #undef TARGET_LIBCALL_VALUE |
| #define TARGET_LIBCALL_VALUE ft32_libcall_value |
| #undef TARGET_FUNCTION_VALUE_REGNO_P |
| #define TARGET_FUNCTION_VALUE_REGNO_P ft32_function_value_regno_p |
| |
| #undef TARGET_OPTION_OVERRIDE |
| #define TARGET_OPTION_OVERRIDE ft32_option_override |
| |
| #undef TARGET_ASM_SELECT_SECTION |
| #define TARGET_ASM_SELECT_SECTION ft32_select_section |
| |
| #undef TARGET_VALID_POINTER_MODE |
| #define TARGET_VALID_POINTER_MODE ft32_valid_pointer_mode |
| static bool |
| ft32_valid_pointer_mode (scalar_int_mode mode) |
| { |
| if (mode == SImode) |
| return 1; |
| return 0; |
| } |
| |
| #undef TARGET_ADDR_SPACE_POINTER_MODE |
| #define TARGET_ADDR_SPACE_POINTER_MODE ft32_addr_space_pointer_mode |
| static scalar_int_mode |
| ft32_addr_space_pointer_mode (addr_space_t addrspace ATTRIBUTE_UNUSED) |
| { |
| return Pmode; |
| } |
| |
| #undef TARGET_ADDR_SPACE_ADDRESS_MODE |
| #define TARGET_ADDR_SPACE_ADDRESS_MODE ft32_addr_space_address_mode |
| static scalar_int_mode |
| ft32_addr_space_address_mode (addr_space_t addrspace ATTRIBUTE_UNUSED) |
| { |
| return Pmode; |
| } |
| |
| #undef TARGET_ADDR_SPACE_SUBSET_P |
| #define TARGET_ADDR_SPACE_SUBSET_P ft32_addr_space_subset_p |
| static bool |
| ft32_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED, |
| addr_space_t superset ATTRIBUTE_UNUSED) |
| { |
| return false; |
| } |
| |
| #undef TARGET_CASE_VALUES_THRESHOLD |
| #define TARGET_CASE_VALUES_THRESHOLD ft32_target_case_values_threshold |
| |
| static unsigned int |
| ft32_target_case_values_threshold (void) |
| { |
| return 4; |
| } |
| |
| #undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P |
| #define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \ |
| ft32_addr_space_legitimate_address_p |
| |
| |
| // Enabling LRA gives the infamous |
| // internal compiler error: Max. number of generated reload insns per insn is achieved (90) |
| // errors e.g. when compiling sieve.c |
| |
| static bool |
| ft32_lra_p (void) |
| { |
| return ft32_lra_flag; |
| } |
| |
| #undef TARGET_LRA_P |
| #define TARGET_LRA_P ft32_lra_p |
| |
| static bool |
| reg_ok_for_base_p (rtx r, bool strict) |
| { |
| int NUM = REGNO (r); |
| if (strict) |
| return (HARD_REGNO_OK_FOR_BASE_P (NUM) |
| || HARD_REGNO_OK_FOR_BASE_P (reg_renumber[(NUM)])); |
| else |
| return ((NUM) >= FIRST_PSEUDO_REGISTER || HARD_REGNO_OK_FOR_BASE_P (NUM)); |
| } |
| |
| static bool |
| ft32_addr_space_legitimate_address_p (machine_mode mode, rtx x, bool strict, |
| addr_space_t as ATTRIBUTE_UNUSED) |
| { |
| int max_offset = TARGET_FT32B ? 16384 : 128; |
| |
| if (mode != BLKmode) |
| { |
| if (GET_CODE (x) == PLUS) |
| { |
| rtx op1, op2; |
| op1 = XEXP (x, 0); |
| op2 = XEXP (x, 1); |
| if (GET_CODE (op1) == REG |
| && CONST_INT_P (op2) |
| && (-max_offset <= INTVAL (op2)) |
| && (INTVAL (op2) < max_offset) |
| && reg_ok_for_base_p (op1, strict)) |
| goto yes; |
| if (GET_CODE (op1) == SYMBOL_REF && CONST_INT_P (op2)) |
| goto yes; |
| } |
| if (REG_P (x) && reg_ok_for_base_p (x, strict)) |
| goto yes; |
| if (GET_CODE (x) == SYMBOL_REF |
| || GET_CODE (x) == LABEL_REF || CONST_INT_P (x)) |
| goto yes; |
| } |
| else |
| { |
| if (REG_P (x) && reg_ok_for_base_p (x, strict)) |
| goto yes; |
| } |
| |
| return 0; |
| yes: |
| return 1; |
| } |
| |
| #undef TARGET_ENCODE_SECTION_INFO |
| #define TARGET_ENCODE_SECTION_INFO ft32_elf_encode_section_info |
| |
| void |
| ft32_elf_encode_section_info (tree decl, rtx rtl, int first) |
| { |
| enum tree_code code; |
| rtx symbol; |
| |
| /* Careful not to prod global register variables. */ |
| if (!MEM_P (rtl)) |
| return; |
| symbol = XEXP (rtl, 0); |
| if (GET_CODE (symbol) != SYMBOL_REF) |
| return; |
| |
| default_encode_section_info (decl, rtl, first); |
| |
| code = TREE_CODE (decl); |
| switch (TREE_CODE_CLASS (code)) |
| { |
| case tcc_declaration: |
| { |
| tree type = TREE_TYPE (decl); |
| int is_flash = (type && TYPE_P (type) |
| && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))); |
| if ((code == VAR_DECL) && !is_flash) |
| SYMBOL_REF_FLAGS (symbol) |= 0x1000; |
| } |
| break; |
| |
| case tcc_constant: |
| case tcc_exceptional: |
| if (code == STRING_CST) |
| SYMBOL_REF_FLAGS (symbol) |= 0x1000; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| #undef TARGET_CONSTANT_ALIGNMENT |
| #define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings |
| |
| struct gcc_target targetm = TARGET_INITIALIZER; |
| |
| #include "gt-ft32.h" |