diff options
author | James Bowman <james.bowman@ftdichip.com> | 2015-05-16 23:49:08 +0000 |
---|---|---|
committer | James Bowman <jamesbowman@gcc.gnu.org> | 2015-05-16 23:49:08 +0000 |
commit | fef939d6a96bde0f924152e6b9e22701c28f8880 (patch) | |
tree | 5e4897f8e04c41bd78ca2cb6244476efaf07df39 /gcc/config/ft32 | |
parent | 9261aa4364af939b3fd91f20bf20c8e329434600 (diff) | |
download | gcc-fef939d6a96bde0f924152e6b9e22701c28f8880.zip gcc-fef939d6a96bde0f924152e6b9e22701c28f8880.tar.gz gcc-fef939d6a96bde0f924152e6b9e22701c28f8880.tar.bz2 |
FT32 target added. Approved by Jeff Law [law@redhat.com]
From-SVN: r223261
Diffstat (limited to 'gcc/config/ft32')
-rw-r--r-- | gcc/config/ft32/constraints.md | 113 | ||||
-rw-r--r-- | gcc/config/ft32/ft32-protos.h | 26 | ||||
-rw-r--r-- | gcc/config/ft32/ft32.c | 921 | ||||
-rw-r--r-- | gcc/config/ft32/ft32.h | 518 | ||||
-rw-r--r-- | gcc/config/ft32/ft32.md | 932 | ||||
-rw-r--r-- | gcc/config/ft32/ft32.opt | 27 | ||||
-rw-r--r-- | gcc/config/ft32/predicates.md | 85 | ||||
-rw-r--r-- | gcc/config/ft32/t-ft32 | 19 |
8 files changed, 2641 insertions, 0 deletions
diff --git a/gcc/config/ft32/constraints.md b/gcc/config/ft32/constraints.md new file mode 100644 index 0000000..8bd8389 --- /dev/null +++ b/gcc/config/ft32/constraints.md @@ -0,0 +1,113 @@ +;; Constraint definitions for FT32 +;; Copyright (C) 2015 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/>. + +;; ------------------------------------------------------------------------- +;; Constraints +;; ------------------------------------------------------------------------- + +(define_memory_constraint "A" + "An absolute address." + (and (match_code "mem") + (match_test "(!ft32_is_mem_pm(op))") + (ior (match_test "GET_CODE (XEXP (op, 0)) == SYMBOL_REF") + (match_test "GET_CODE (XEXP (op, 0)) == LABEL_REF") + (match_test "GET_CODE (XEXP (op, 0)) == CONST_INT") + (and (match_test "(GET_CODE (XEXP (op, 0)) == PLUS)") + (ior (match_test "GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF") + (match_test "GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF") + (match_test "GET_CODE (XEXP (XEXP (op, 0), 0)) == CONST_INT")) + (ior (match_test "GET_CODE (XEXP (XEXP (op, 0), 1)) == SYMBOL_REF") + (match_test "GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF") + (match_test "GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT")))))) + +(define_memory_constraint "B" + "An offset address." + (and (match_code "mem") + (match_test "(!ft32_is_mem_pm(op))") + (match_test "(GET_CODE (XEXP (op, 0)) == PLUS)"))) + +(define_memory_constraint "W" + "A register indirect memory operand." + (and (match_code "mem") + (match_test "!ft32_is_mem_pm(op) + && REG_P (XEXP (op, 0)) + && REGNO_OK_FOR_BASE_P (REGNO (XEXP (op, 0)))"))) + +(define_memory_constraint "e" + "An offset address." + (and (match_code "mem") + (match_test "ft32_is_mem_pm(op) && ( + (GET_CODE (XEXP (op, 0)) == SYMBOL_REF) || + (GET_CODE (XEXP (op, 0)) == LABEL_REF) || + (GET_CODE (XEXP (op, 0)) == CONST_INT) || + (GET_CODE (XEXP (op, 0)) == CONST))" + ))) + +(define_memory_constraint "f" + "An offset address." + (and (match_code "mem") + (match_test "ft32_is_mem_pm(op) && ( + ((GET_CODE (XEXP (op, 0)) == PLUS)) || + (GET_CODE (XEXP (op, 0)) == REG))" + ))) + +(define_constraint "O" + "The constant zero or one" + (and (match_code "const_int") + (match_test "((ival == 0) || (ival == 1))"))) + +(define_constraint "I" + "A 16-bit signed constant (-32768..32767)" + (and (match_code "const_int") + (match_test "ival >= -32768 && ival <= 32767"))) + +(define_constraint "w" + "A bitfield mask suitable for bext or bins" + (and (match_code "const_int") + (match_test "ft32_as_bitfield(ival) != -1"))) + +(define_constraint "x" + "An inverted bitfield mask suitable for bext or bins" + (and (match_code "const_int") + (match_test "ft32_as_bitfield(0xffffffff ^ ival) != -1"))) + +(define_constraint "L" + "A 16-bit unsigned constant, multiple of 4 (-65532..0)" + (and (match_code "const_int") + (match_test "-65532 <= ival && ival <= 0 && (ival & 3) == 0"))) + +(define_constraint "S" + "A 20-bit signed constant (-524288..524287)" + (ior + (and (match_code "const_int") + (match_test "ival >= -524288 && ival <= 524287")) + (match_test "GET_CODE (op) == LABEL_REF") + (match_test "GET_CODE (op) == SYMBOL_REF") + (match_test "GET_CODE (op) == CONST"))) + +(define_constraint "b" + "A constant for a bitfield width (1..16)" + (and (match_code "const_int") + (match_test "1 <= ival && ival <= 16"))) + +(define_constraint "KA" + "A 10-bit signed constant (-512..511)" + (and (match_code "const_int") + (match_test "-512 <= ival && ival <= 511"))) diff --git a/gcc/config/ft32/ft32-protos.h b/gcc/config/ft32/ft32-protos.h new file mode 100644 index 0000000..8e8e6da --- /dev/null +++ b/gcc/config/ft32/ft32-protos.h @@ -0,0 +1,26 @@ +/* Prototypes for ft32.c functions used in the md file & elsewhere. + Copyright (C) 2015 Free Software Foundation, Inc. + +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/>. */ + +extern void ft32_expand_prologue (void); +extern void ft32_expand_epilogue (void); +extern int ft32_initial_elimination_offset (int, int); +extern void ft32_print_operand (FILE *, rtx, int); +extern void ft32_print_operand_address (FILE *, rtx); +extern const char* ft32_load_immediate(rtx, int32_t i); +extern int ft32_as_bitfield(unsigned int x); diff --git a/gcc/config/ft32/ft32.c b/gcc/config/ft32/ft32.c new file mode 100644 index 0000000..7c7dbd5 --- /dev/null +++ b/gcc/config/ft32/ft32.c @@ -0,0 +1,921 @@ +/* Target Code for ft32 + Copyright (C) 2015 Free Software Foundation + 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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "recog.h" +#include "reload.h" +#include "diagnostic-core.h" +#include "obstack.h" +#include "hash-set.h" +#include "machmode.h" +#include "vec.h" +#include "double-int.h" +#include "input.h" +#include "alias.h" +#include "symtab.h" +#include "wide-int.h" +#include "inchash.h" +#include "tree.h" +#include "stor-layout.h" +#include "calls.h" +#include "expr.h" +#include "optabs.h" +#include "except.h" +#include "function.h" +#include "ggc.h" +#include "target.h" +#include "target-def.h" +#include "tm_p.h" +#include "langhooks.h" +#include "dominance.h" +#include "cfg.h" +#include "cfgrtl.h" +#include "cfganal.h" +#include "lcm.h" +#include "cfgbuild.h" +#include "cfgcleanup.h" +#include "predict.h" +#include "basic-block.h" +#include "df.h" +#include "builtins.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 (enum 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, "%d", -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 (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 ((-524288 <= i) && (i <= 524287)) + { + sprintf (pattern, "ldk.l %%0,%d", i); + output_asm_insn (pattern, &dst); + } + else if ((-536870912 <= i) && (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 ((-524288 <= (int32_t) u) && ((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_regs[regno])) + cfun->machine->callee_saved_reg_size += 4; + + cfun->machine->size_for_adjusting_sp = + crtl->args.pretend_args_size + + cfun->machine->local_vars_size + + (ACCUMULATE_OUTGOING_ARGS ? 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 (); + + 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 (!fixed_regs[regno] && !call_used_regs[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 (!fixed_regs[regno] && df_regs_ever_live_p (regno) + && !call_used_regs[regno]) + { + insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + } + + if (65536 <= cfun->machine->size_for_adjusting_sp) + { + 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) + { + insn = emit_insn (gen_addsi3 (gen_rtx_REG (SImode, FT32_SP), + gen_rtx_REG (SImode, FT32_SP), + GEN_INT (-(cfun->machine-> + size_for_adjusting_sp)))); + RTX_FRAME_RELATED_P (insn) = 1; + } +} + +void +ft32_expand_epilogue (void) +{ + int regno; + + 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 (!fixed_regs[regno] && !call_used_regs[regno] + && df_regs_ever_live_p (regno)) + { + rtx preg = gen_rtx_REG (Pmode, regno); + if (optimize_size) + { + if (epilog24) + emit_insn (gen_jump_epilog24 (preg)); + else + emit_insn (gen_jump_epilog (preg)); + return; + } + emit_insn (gen_movsi_pop (preg)); + } + } + } + + 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, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + int *pretend_size, int no_rtl) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int regno; + int regs = 8 - *cum; + + *pretend_size = regs < 0 ? 0 : GET_MODE_SIZE (SImode) * regs; + + if (no_rtl) + return; + + for (regno = *cum; regno < 8; regno++) + { + rtx reg = gen_rtx_REG (SImode, regno); + rtx slot = gen_rtx_PLUS (Pmode, + gen_rtx_REG (SImode, ARG_POINTER_REGNUM), + GEN_INT (UNITS_PER_WORD * (regno - FT32_R0))); + + emit_move_insn (gen_rtx_MEM (SImode, slot), reg); + } +} + + +/* 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, enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, + bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + + if (*cum < 8) + return gen_rtx_REG (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, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + + *cum = (*cum < FT32_R6 + ? *cum + ((3 + FT32_FUNCTION_ARG_SIZE (mode, type)) / 4) : *cum); +} + +/* Return non-zero if the function argument described by TYPE is to be + passed by reference. */ + +static bool +ft32_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + unsigned HOST_WIDE_INT size; + + if (type) + { + if (AGGREGATE_TYPE_P (type)) + return true; + size = int_size_in_bytes (type); + } + else + size = GET_MODE_SIZE (mode); + + 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, + enum machine_mode mode, tree type, bool named) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int bytes_left, size; + + if (*cum >= 8) + return 0; + + if (ft32_pass_by_reference (cum_v, mode, type, named)) + size = 4; + else if (type) + { + if (AGGREGATE_TYPE_P (type)) + return 0; + size = int_size_in_bytes (type); + } + else + size = GET_MODE_SIZE (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) +{ + if (GET_CODE (o) != MEM) + return false; + if (MEM_EXPR (o)) + return TYPE_ADDR_SPACE (TREE_TYPE (MEM_EXPR (o))) == ADDR_SPACE_PM; + else + return MEM_ADDR_SPACE (o) == ADDR_SPACE_PM; +} + +/* 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 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 (enum machine_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 enum machine_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 enum machine_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 (enum machine_mode mode, rtx x, + bool strict, + addr_space_t as ATTRIBUTE_UNUSED) +{ + 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) + && INTVAL (op2) >= -128 + && INTVAL (op2) < 128 && 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; +} + +struct gcc_target targetm = TARGET_INITIALIZER; + +#include "gt-ft32.h" diff --git a/gcc/config/ft32/ft32.h b/gcc/config/ft32/ft32.h new file mode 100644 index 0000000..0835aae --- /dev/null +++ b/gcc/config/ft32/ft32.h @@ -0,0 +1,518 @@ +/* Target Definitions for ft32. + Copyright (C) 2015 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/>. */ + +#ifndef GCC_FT32_H +#define GCC_FT32_H + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt0%O%s %{msim:crti.o%s} %{!msim:crti-hw.o%s} crtbegin.o%s" + +/* Provide an ENDFILE_SPEC appropriate for svr4. Here we tack on our own + magical crtend.o file (see crtstuff.c) which provides part of the + support for getting C++ file-scope static object constructed before + entering `main', followed by the normal svr3/svr4 "finalizer" file, + which is either `gcrtn.o' or `crtn.o'. */ + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +/* Provide a LIB_SPEC appropriate for svr4. Here we tack on the default + standard C library (unless we are building a shared library) and + the simulator BSP code. */ + +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{!symbolic:-lc}} \ + %{msim:-Tsim.ld}" + +#undef LINK_SPEC +#define LINK_SPEC "%{h*} %{v:-V} \ + %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic}" + +/* Layout of Source Language Data Types */ + +#define INT_TYPE_SIZE 32 +#define SHORT_TYPE_SIZE 16 +#define LONG_TYPE_SIZE 32 +#define LONG_LONG_TYPE_SIZE 64 + +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE 64 + +#define DEFAULT_SIGNED_CHAR 1 + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +#define REGISTER_NAMES { \ + "$fp", "$sp", "$r0", "$r1", \ + "$r2", "$r3", "$r4", "$r5", \ + "$r6", "$r7", "$r8", "$r9", \ + "$r10", "$r11", "$r12", "$r13", \ + "$r14", "$r15", "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27", "$r28", "$cc", \ + "?fp", "?ap", "$pc", "?cc" } + +#define FT32_FP 0 +#define FT32_SP 1 +#define FT32_R0 2 +#define FT32_R1 3 +#define FT32_R2 4 +#define FT32_R3 5 +#define FT32_R4 6 +#define FT32_R5 7 +#define FT32_R6 8 +#define FT32_R7 9 +#define FT32_R8 10 +#define FT32_R9 11 +#define FT32_R10 12 +#define FT32_R11 13 +#define FT32_R12 14 +#define FT32_R13 15 +#define FT32_R14 16 +#define FT32_R15 17 +#define FT32_R16 18 +#define FT32_R17 19 +#define FT32_R18 20 +#define FT32_R19 21 +#define FT32_R20 22 +#define FT32_R21 23 +#define FT32_R22 24 +#define FT32_R23 25 +#define FT32_R24 26 +#define FT32_R25 27 +#define FT32_R26 28 +#define FT32_R27 29 +#define FT32_R28 30 +#define FT32_R29 31 +#define FT32_QAP (32 + 1) +#define FT32_PC (32 + 2) +#define FT32_CC (32 + 3) +#define FIRST_PSEUDO_REGISTER (32 + 4) + +enum reg_class +{ + NO_REGS, + GENERAL_REGS, + SPECIAL_REGS, + CC_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define REG_CLASS_CONTENTS \ +{ { 0x00000000, 0x00000000 }, /* Empty */ \ + { 0xFFFFFFFF, 0x00000003 }, /* $fp, $sp, $r0 to $r13, ?fp */ \ + { 0x00000000, 0x00000004 }, /* $pc */ \ + { 0x00000000, 0x00000008 }, /* ?cc */ \ + { 0xFFFFFFFF, 0x0000000F } /* All registers */ \ +} + +#define N_REG_CLASSES LIM_REG_CLASSES + +#define REG_CLASS_NAMES {\ + "NO_REGS", \ + "GENERAL_REGS", \ + "SPECIAL_REGS", \ + "CC_REGS", \ + "ALL_REGS" } + +#define FIXED_REGISTERS /* fp sp r0 r1 */ { 1, 1, 0, 0, \ + /* r2 r3 r4 r5 */ 0, 0, 0, 0, \ + /* r6 r7 r8 r9 */ 0, 0, 0, 0, \ + /* r10 r11 r12 r13 */ 0, 0, 0, 0, \ + /* r14 r15 r16 r17 */ 0, 0, 0, 0, \ + /* r18 r19 r20 r21 */ 0, 0, 0, 0, \ + /* r22 r23 r24 r25 */ 0, 0, 0, 0, \ + /* r26 r27 r28 r29 */ 0, 0, 1, 1, \ + /* r30 r31 */ 1, 1, 1, 1 } + +#define CALL_USED_REGISTERS \ + /* fp sp r0 r1 */ { 1, 1, 1, 1, \ + /* r2 r3 r4 r5 */ 1, 1, 1, 1, \ + /* r6 r7 r8 r9 */ 1, 1, 1, 1, \ + /* r10 r11 r12 r13 */ 1, 1, 1, 0, \ + /* r14 r15 r16 r17 */ 0, 0, 0, 0, \ + /* r18 r19 r20 r21 */ 0, 0, 0, 0, \ + /* r22 r23 r24 r25 */ 0, 0, 0, 0, \ + /* r26 r27 r28 r29 */ 0, 0, 1, 1, \ + /* r30 r31 */ 1, 1, 1, 1 } + +/* We can't copy to or from our CC register. */ +#define AVOID_CCMODE_COPIES 1 + +/* A C expression that is nonzero if it is permissible to store a + value of mode MODE in hard register number REGNO (or in several + registers starting with that one). All gstore registers are + equivalent, so we can set this to 1. */ +#define HARD_REGNO_MODE_OK(R,M) 1 + +/* A C expression whose value is a register class containing hard + register REGNO. */ +#define REGNO_REG_CLASS(R) ((R < FT32_PC) ? GENERAL_REGS : \ + (R == FT32_CC ? CC_REGS : SPECIAL_REGS)) + +/* A C expression for the number of consecutive hard registers, + starting at register number REGNO, required to hold a value of mode + MODE. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \ + / UNITS_PER_WORD) + +/* A C expression that is nonzero if a value of mode MODE1 is + accessible in mode MODE2 without copying. */ +#define MODES_TIEABLE_P(MODE1, MODE2) 1 + +/* The Overall Framework of an Assembler File */ + +#undef ASM_SPEC +#define ASM_COMMENT_START "#" +#define ASM_APP_ON "" +#define ASM_APP_OFF "" + +#define FILE_ASM_OP "\t.file\n" + +/* Switch to the text or data segment. */ +#define TEXT_SECTION_ASM_OP "\t.text" +#define DATA_SECTION_ASM_OP "\t.data" + +/* Assembler Commands for Alignment */ + +#define ASM_OUTPUT_ALIGN(STREAM,POWER) \ + fprintf (STREAM, "\t.p2align\t%d\n", POWER); + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand X. */ +#define PRINT_OPERAND(STREAM, X, CODE) ft32_print_operand (STREAM, X, CODE) + +#define PRINT_OPERAND_ADDRESS(STREAM ,X) ft32_print_operand_address (STREAM, X) + +/* Output and Generation of Labels */ + +#define GLOBAL_ASM_OP "\t.global\t" + +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +/* This is how to output an element of a case-vector that is absolute. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + fprintf (FILE, "\tjmp\t.L%d\n", VALUE); \ + +/* Passing Arguments in Registers */ + +/* A C type for declaring a variable that is used as the first + argument of `FUNCTION_ARG' and other related values. */ +#define CUMULATIVE_ARGS unsigned int + +/* If defined, the maximum amount of space required for outgoing arguments + will be computed and placed into the variable + `current_function_outgoing_args_size'. No space will be pushed + onto the stack for each call; instead, the function prologue should + increase the stack frame size by this amount. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* A C statement (sans semicolon) for initializing the variable CUM + for the state at the beginning of the argument list. + For ft32, the first arg is passed in register 2 (aka $r0). */ +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,FNDECL,N_NAMED_ARGS) \ + (CUM = FT32_R0) + +/* How Scalar Function Values Are Returned */ + +/* STACK AND CALLING */ + +/* Define this macro if pushing a word onto the stack moves the stack + pointer to a smaller address. */ +#define STACK_GROWS_DOWNWARD + +#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) (DEPTH) = 0 + +/* Offset from the frame pointer to the first local variable slot to + be allocated. */ +#define STARTING_FRAME_OFFSET 0 + +/* Define this if the above stack space is to be considered part of the + space allocated by the caller. */ +#define OUTGOING_REG_PARM_STACK_SPACE(FNTYPE) 1 +/* #define STACK_PARMS_IN_REG_PARM_AREA */ + +/* Define this if it is the responsibility of the caller to allocate + the area reserved for arguments passed in registers. */ +#define REG_PARM_STACK_SPACE(FNDECL) (6 * UNITS_PER_WORD) + +/* Offset from the argument pointer register to the first argument's + address. On some machines it may depend on the data type of the + function. */ +#define FIRST_PARM_OFFSET(F) 0 + +/* Define this macro to nonzero value if the addresses of local variable slots + are at negative offsets from the frame pointer. */ +#define FRAME_GROWS_DOWNWARD 1 + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. */ + +#define EXIT_IGNORE_STACK 0 + +/* Define this macro as a C expression that is nonzero for registers that are + used by the epilogue or the return pattern. The stack and frame + pointer registers are already assumed to be used as needed. */ +#define EPILOGUE_USES(R) (R == FT32_R5) + +/* A C expression whose value is RTL representing the location of the + incoming return address at the beginning of any function, before + the prologue. */ +#define INCOMING_RETURN_ADDR_RTX \ + gen_frame_mem (Pmode, \ + plus_constant (Pmode, stack_pointer_rtx, 333 * UNITS_PER_WORD)) + +#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) \ + ((COUNT) == 0 \ + ? gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, arg_pointer_rtx, GEN_INT (-4))) \ + : NULL_RTX) + +/* Describe how we implement __builtin_eh_return. */ +#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N+2) : INVALID_REGNUM) + +/* Store the return handler into the call frame. */ +#define EH_RETURN_HANDLER_RTX \ + gen_frame_mem (Pmode, \ + plus_constant (Pmode, frame_pointer_rtx, UNITS_PER_WORD)) + +/* Storage Layout */ + +#define BITS_BIG_ENDIAN 0 +#define BYTES_BIG_ENDIAN 0 +#define WORDS_BIG_ENDIAN 0 + +/* Alignment required for a function entry point, in bits. */ +#define FUNCTION_BOUNDARY 32 + +#define BRANCH_COST(speed_p, predictable_p) 2 + +/* Define this macro as a C expression which is nonzero if accessing + less than a word of memory (i.e. a `char' or a `short') is no + faster than accessing a word of memory. */ +#define SLOW_BYTE_ACCESS 1 + +#define STORE_FLAG_VALUE 1 + +#define MOVE_RATIO(speed) ((speed) ? 6 : 2) + +/* Number of storage units in a word; normally the size of a + general-purpose register, a power of two from 1 or 8. */ +#define UNITS_PER_WORD 4 + +/* Define this macro to the minimum alignment enforced by hardware + for the stack pointer on this machine. The definition is a C + expression for the desired alignment (measured in bits). */ +#define STACK_BOUNDARY 32 + +/* Normal alignment required for function parameters on the stack, in + bits. All stack parameters receive at least this much alignment + regardless of data type. */ +#define PARM_BOUNDARY 32 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 32 + +/* The best alignment to use in cases where we have a choice. */ +#define FASTEST_ALIGNMENT 32 + +/* Align definitions of arrays, unions and structures so that + initializations and copies can be made more efficient. This is not + ABI-changing, so it only affects places where we can see the + definition. Increasing the alignment tends to introduce padding, + so don't do this when optimizing for size/conserving stack space. */ +#define FT32_EXPAND_ALIGNMENT(COND, EXP, ALIGN) \ + (((COND) && ((ALIGN) < BITS_PER_WORD) \ + && (TREE_CODE (EXP) == ARRAY_TYPE \ + || TREE_CODE (EXP) == UNION_TYPE \ + || TREE_CODE (EXP) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN)) + +/* Make arrays of chars word-aligned for the same reasons. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (TREE_CODE (TYPE) == ARRAY_TYPE \ + && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ + && (ALIGN) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Similarly, make sure that objects on the stack are sensibly aligned. */ +#define LOCAL_ALIGNMENT(EXP, ALIGN) \ + FT32_EXPAND_ALIGNMENT(/*!flag_conserve_stack*/ 1, EXP, ALIGN) + +/* Every structures size must be a multiple of 8 bits. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* Look at the fundamental type that is used for a bit-field and use + that to impose alignment on the enclosing structure. + struct s {int a:8}; should have same alignment as "int", not "char". */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* Largest integer machine mode for structures. If undefined, the default + is GET_MODE_SIZE(DImode). */ +#define MAX_FIXED_MODE_SIZE 32 + +/* Make strings word-aligned so strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < FASTEST_ALIGNMENT) \ + ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* Generating Code for Profiling */ +#define FUNCTION_PROFILER(FILE,LABELNO) (abort (), 0) + +/* Trampolines for Nested Functions. */ +#define TRAMPOLINE_SIZE (2 + 6 + 6 + 2 + 2 + 6) + +/* Alignment required for trampolines, in bits. */ +#define TRAMPOLINE_ALIGNMENT 32 + +/* An alias for the machine mode for pointers. */ +#define Pmode SImode + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ +do { \ + if (((MODE) == HImode) \ + || ((MODE) == QImode)) \ + (MODE) = SImode; \ +} while (0) + +/* An alias for the machine mode used for memory references to + functions being called, in `call' RTL expressions. */ +#define FUNCTION_MODE QImode + +#define STATIC_CHAIN_REGNUM FT32_R28 + +/* The register number of the stack pointer register, which must also + be a fixed register according to `FIXED_REGISTERS'. */ +#define STACK_POINTER_REGNUM FT32_SP + +/* The register number of the frame pointer register, which is used to + access automatic variables in the stack frame. */ +#define FRAME_POINTER_REGNUM FT32_FP + +/* The register number of the arg pointer register, which is used to + access the function's argument list. */ +#define ARG_POINTER_REGNUM FT32_QAP + +#define ELIMINABLE_REGS \ +{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} + + +/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It + specifies the initial difference between the specified pair of + registers. This macro must be defined if `ELIMINABLE_REGS' is + defined. */ +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + do { \ + (OFFSET) = ft32_initial_elimination_offset ((FROM), (TO)); \ + } while (0) + +/* A C expression that is nonzero if REGNO is the number of a hard + register in which function arguments are sometimes passed. */ +#define FUNCTION_ARG_REGNO_P(r) (r >= FT32_R0 && r <= FT32_R5) + +/* A macro whose definition is the name of the class to which a valid + base register must belong. A base register is one used in an + address which is the register value plus a displacement. */ +#define BASE_REG_CLASS GENERAL_REGS + +#define INDEX_REG_CLASS NO_REGS + +#define HARD_REGNO_OK_FOR_BASE_P(NUM) \ + ((unsigned) (NUM) < FIRST_PSEUDO_REGISTER \ + && (REGNO_REG_CLASS(NUM) == GENERAL_REGS \ + || (NUM) == HARD_FRAME_POINTER_REGNUM)) + +/* A C expression which is nonzero if register number NUM is suitable + for use as a base register in operand addresses. */ +#ifdef REG_OK_STRICT +#define REGNO_OK_FOR_BASE_P(NUM) \ + (HARD_REGNO_OK_FOR_BASE_P(NUM) \ + || HARD_REGNO_OK_FOR_BASE_P(reg_renumber[(NUM)])) +#else +#define REGNO_OK_FOR_BASE_P(NUM) \ + ((NUM) >= FIRST_PSEUDO_REGISTER || HARD_REGNO_OK_FOR_BASE_P(NUM)) +#endif + +/* A C expression which is nonzero if register number NUM is suitable + for use as an index register in operand addresses. */ +#define REGNO_OK_FOR_INDEX_P(NUM) FT32_FP + +/* The maximum number of bytes that a single instruction can move + quickly between memory and registers or between two memory + locations. */ +#define MOVE_MAX 4 +#define TRULY_NOOP_TRUNCATION(op,ip) 1 + +/* Define this to be nonzero if shift instructions ignore all but the low-order + few bits. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* All load operations zero extend. */ +#define LOAD_EXTEND_OP(MEM) ZERO_EXTEND + +/* A number, the maximum number of registers that can appear in a + valid memory address. */ +#define MAX_REGS_PER_ADDRESS 1 + +/* An alias for a machine mode name. This is the machine mode that + elements of a jump-table should have. */ +#define CASE_VECTOR_MODE SImode + +/* Run-time Target Specification */ + +#define TARGET_CPU_CPP_BUILTINS() \ + { \ + builtin_define ("__FT32__"); \ + } + +#define HAS_LONG_UNCOND_BRANCH true + +#define NO_FUNCTION_CSE 1 + +#define ADDR_SPACE_PM 1 + +#define REGISTER_TARGET_PRAGMAS() do { \ + c_register_addr_space ("__flash__", ADDR_SPACE_PM); \ +} while (0); + +extern int ft32_is_mem_pm(rtx o); + +#endif /* GCC_FT32_H */ diff --git a/gcc/config/ft32/ft32.md b/gcc/config/ft32/ft32.md new file mode 100644 index 0000000..68dc683 --- /dev/null +++ b/gcc/config/ft32/ft32.md @@ -0,0 +1,932 @@ +;; Machine description for FT32 +;; Copyright (C) 2015 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/>. + +;; ------------------------------------------------------------------------- +;; FT32 specific constraints, predicates and attributes +;; ------------------------------------------------------------------------- + +(include "constraints.md") +(include "predicates.md") + +(define_constants [ + (FP_REG 0) + (SP_REG 1) + (CC_REG 35) +]) + +(define_c_enum "unspec" + [UNSPEC_STRLEN + UNSPEC_MOVMEM + UNSPEC_SETMEM + UNSPEC_STPCPY + UNSPEC_INDEX_JMP + UNSPEC_LPM + UNSPEC_FMUL + UNSPEC_FMULS + UNSPEC_FMULSU + UNSPEC_COPYSIGN + UNSPEC_IDENTITY + UNSPEC_INSERT_BITS + UNSPEC_JMP_EPILOG + UNSPEC_JMP_EPILOG24 + UNSPEC_JMP_PROLOG + UNSPEC_XCHG + ]) + +;; ------------------------------------------------------------------------- +;; nop instruction +;; ------------------------------------------------------------------------- + +(define_insn "nop" + [(const_int 0)] + "" + "nop") + +;; ------------------------------------------------------------------------- +;; Arithmetic instructions +;; ------------------------------------------------------------------------- + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "KA,r"))) + ] + "" + "add.l %0,%1,%2") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (minus:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "KA,r")))] + "" + "sub.l %0,%1,%2") + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mult:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "KA,r")))] + "" + "mul.l %0,%1,%2") + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r,r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r")) + (zero_extend:DI (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))) + (clobber (reg:CC CC_REG))] + "" + "mul.l $cc,%1,%2\;muluh.l %h0,%1,%2\;move.l %0,$cc") + +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (div:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" + "div.l %0,%1,%2") + +(define_insn "modsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mod:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" + "mod.l %0,%1,%2") + +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (udiv:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" + "udiv.l %0,%1,%2") + +(define_insn "umodsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (umod:SI + (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "register_operand" "r,KA")))] + "" + "umod.l %0,%1,%2") + +(define_insn "extvsi" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "ft32_bwidth_operand" "b") + (match_operand:SI 3 "const_int_operand" "i")))] + "" + "bexts.l %0,%1,((15 & %2) << 5) | (%3)") + +(define_insn "extzvsi" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "ft32_bwidth_operand" "b") + (match_operand:SI 3 "const_int_operand" "i")))] + "" + "bextu.l %0,%1,((15 & %2) << 5) | (%3)") + +(define_insn "insvsi" + [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r,r") + (match_operand:SI 1 "ft32_bwidth_operand" "b,b") + (match_operand:SI 2 "const_int_operand" "i,i")) + (match_operand:SI 3 "general_operand" "r,O")) + (clobber (match_scratch:SI 4 "=&r,r"))] + "" + { + if (which_alternative == 0) + { + return \"ldl.l %4,%3,((%1&15)<<5)|(%2)\;bins.l %0,%0,%4\"; + } + else + { + if ((INTVAL(operands[3]) == 0) || (INTVAL(operands[1]) == 1)) + return \"bins.l %0,%0,(%3<<9)|((%1&15)<<5)|(%2)\"; + else + return \"ldk.l %4,(%3<<10)|((%1&15)<<5)|(%2)\;bins.l %0,%0,%4\"; + } + }) + +;; ------------------------------------------------------------------------- +;; Unary arithmetic instructions +;; ------------------------------------------------------------------------- + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "xor.l %0,%1,-1") + +;; ------------------------------------------------------------------------- +;; Logical operators +;; ------------------------------------------------------------------------- + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (and:SI (match_operand:SI 1 "register_operand" "r,r,r") + (match_operand:SI 2 "general_operand" "r,x,KA")))] + "" + "@ + and.l %0,%1,%2 + bins.l %0,%1,%g2 + and.l %0,%1,%2") + +(define_insn "andqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r") + (and:QI (match_operand:QI 1 "register_operand" "r,r,r") + (match_operand:QI 2 "general_operand" "r,x,KA")))] + "" + "@ + and.b %0,%1,%2 + bins.b %0,%1,%g2 + and.b %0,%1,%2") + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (xor:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" +{ + return "xor.l %0,%1,%2"; +}) + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (ior:SI (match_operand:SI 1 "register_operand" "r,r,r") + (match_operand:SI 2 "general_operand" "r,w,KA")))] + "" + "@ + or.l %0,%1,%2 + bins.l %0,%1,%f2 + or.l %0,%1,%2") + +;; ------------------------------------------------------------------------- +;; Shifters +;; ------------------------------------------------------------------------- + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ashift:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" +{ + return "ashl.l %0,%1,%2"; +}) + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" +{ + return "ashr.l %0,%1,%2"; +}) + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))] + "" +{ + return "lshr.l %0,%1,%2"; +}) + +;; ------------------------------------------------------------------------- +;; Move instructions +;; ------------------------------------------------------------------------- + +;; SImode + +;; Push a register onto the stack +(define_insn "movsi_push" + [(set (mem:SI (pre_dec:SI (reg:SI SP_REG))) + (match_operand:SI 0 "register_operand" "r"))] + "" + "push.l %0") + +;; Pop a register from the stack +(define_insn "movsi_pop" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (post_inc:SI (reg:SI SP_REG))))] + "" + "pop.l %0") + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" +{ + /* If this is a store, force the value into a register. */ + if (!(reload_in_progress || reload_completed)) + { + if (MEM_P (operands[0])) + { + operands[1] = force_reg (SImode, operands[1]); + if (MEM_P (XEXP (operands[0], 0))) + operands[0] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[0], 0))); + } + else + { + if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0))) + operands[1] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[1], 0))); + } + /* + if (MEM_P (operands[0])) { + rtx o = XEXP (operands[0], 0); + if (!REG_P(o) && + !CONST_INT_P(o) && + GET_CODE(o) != SYMBOL_REF && + GET_CODE(o) != LABEL_REF) { + operands[0] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[0], 0))); + } + } + */ + } +}) + +(define_insn "*rtestsi" + [(set (reg:SI CC_REG) + (match_operand:SI 0 "register_operand" "r"))] + "" + "cmp.l %0,0" +) + +(define_insn "*rtestqi" + [(set (reg:QI CC_REG) + (match_operand:QI 0 "register_operand" "r"))] + "" + "cmp.b %0,0" +) + +(define_insn "*movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,BW,r,r,r,r,A,r,r") + (match_operand:SI 1 "ft32_general_movsrc_operand" "r,r,BW,A,S,i,r,e,f"))] + "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)" + "@ + move.l %0,%1 + sti.l %0,%1 + ldi.l %0,%1 + lda.l %0,%1 + ldk.l %0,%1 + *return ft32_load_immediate(operands[0], INTVAL(operands[1])); + sta.l %0,%1 + lpm.l %0,%1 + lpmi.l %0,%1" +) + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" +{ + /* If this is a store, force the value into a register. */ + if (!(reload_in_progress || reload_completed)) + { + if (MEM_P (operands[0])) + { + operands[1] = force_reg (QImode, operands[1]); + if (MEM_P (XEXP (operands[0], 0))) + operands[0] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[0], 0))); + } + else + { + if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0))) + operands[1] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[1], 0))); + } + if (MEM_P (operands[0]) && !REG_P(XEXP (operands[0], 0))) + { + operands[0] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[0], 0))); + } + } +}) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "BW,r,f")))] + "" + "@ + ldi.b %0,%1 + and.l %0,%1,255 + lpmi.b %0,%1" +) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r")))] + "" + "bexts.l %0,%1,(8<<5)|0" +) + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "BW,r,f")))] + "" + "@ + ldi.s %0,%1 + bextu.l %0,%1,(0<<5)|0 + lpmi.s %0,%1" +) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))] + "" + "bexts.l %0,%1,(0<<5)|0" +) + +(define_insn "*movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r,BW,r,r,A,r,r,r") + (match_operand:QI 1 "ft32_general_movsrc_operand" "r,r,BW,A,r,I,e,f"))] + "register_operand (operands[0], QImode) + || register_operand (operands[1], QImode)" + "@ + move.b %0,%1 + sti.b %0,%1 + ldi.b %0,%1 + lda.b %0,%1 + sta.b %0,%1 + ldk.b %0,%1 + lpm.b %0,%1 + lpmi.b %0,%1" +) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" +{ + /* If this is a store, force the value into a register. */ + if (!(reload_in_progress || reload_completed)) + { + if (MEM_P (operands[0])) + { + operands[1] = force_reg (HImode, operands[1]); + if (MEM_P (XEXP (operands[0], 0))) + operands[0] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[0], 0))); + } + else + { + if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0))) + operands[1] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[1], 0))); + } + if (MEM_P (operands[0])) + { + rtx o = XEXP (operands[0], 0); + if (!REG_P(o) && + !CONST_INT_P(o) && + GET_CODE(o) != SYMBOL_REF && + GET_CODE(o) != LABEL_REF) { + operands[0] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[0], 0))); + } + } + } +}) + +(define_insn "*movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,BW,r,r,A,r,r,r") + (match_operand:HI 1 "ft32_general_movsrc_operand" "r,r,BW,A,r,I,e,f"))] + "(register_operand (operands[0], HImode) + || register_operand (operands[1], HImode))" + "@ + move.s %0,%1 + sti.s %0,%1 + ldi.s %0,%1 + lda.s %0,%1 + sta.s %0,%1 + ldk.s %0,%1 + lpm.s %0,%1 + lpmi.s %0,%1" +) + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" +{ + /* If this is a store, force the value into a register. */ + if (MEM_P (operands[0])) + operands[1] = force_reg (SFmode, operands[1]); + if (CONST_DOUBLE_P(operands[1])) + operands[1] = force_const_mem(SFmode, operands[1]); +}) + +(define_insn "*movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,BW,r,r,A,r,r") + (match_operand:SF 1 "ft32_general_movsrc_operand" "r,r,BW,A,r,I,f"))] + "(register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" + "@ + move.l %0,%1 + sti.l %0,%1 + ldi.l %0,%1 + lda.l %0,%1 + sta.l %0,%1 + ldk.l %0,%1 + lpmi.l %0,%1" +) + +;; ------------------------------------------------------------------------- +;; Compare instructions +;; ------------------------------------------------------------------------- + +(define_expand "cbranchsi4" + [(set (reg:CC CC_REG) + (compare:CC + (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "ft32_rimm_operand" ""))) + (set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(reg:CC CC_REG) (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + "") + +(define_insn "cmpsi" + [(set (reg:CC CC_REG) + (compare:CC + (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "ft32_rimm_operand" "r,KA")))] + "" + "cmp.l %0,%1") + +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "const_int_operand" "i")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:CC CC_REG))] + "" + "btst.l %0,(1<<5)|%1\;jmpc nz,%l2") + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "const_int_operand" "i")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:CC CC_REG))] + "" + "btst.l %0,(1<<5)|%1\;jmpc z,%l2") + +(define_expand "cbranchqi4" + [(set (reg:CC CC_REG) + (compare:CC + (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "ft32_rimm_operand" ""))) + (set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(reg:CC CC_REG) (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + "") + +(define_insn "*cmpqi" + [(set (reg:CC CC_REG) + (compare:CC + (match_operand:QI 0 "register_operand" "r,r") + (match_operand:QI 1 "ft32_rimm_operand" "r,KA")))] + "" + "cmp.b %0,%1") + +;; ------------------------------------------------------------------------- +;; Branch instructions +;; ------------------------------------------------------------------------- + +(define_code_iterator cond [ne eq lt ltu gt gtu ge le geu leu]) +(define_code_attr CC [(ne "nz") (eq "z") (lt "lt") (ltu "b") + (gt "gt") (gtu "a") (ge "gte") (le "lte") + (geu "ae") (leu "be") ]) +(define_code_attr rCC [(ne "z") (eq "nz") (lt "gte") (ltu "ae") + (gt "lte") (gtu "be") (ge "lt") (le "gt") + (geu "b") (leu "a") ]) + +(define_insn "*b<cond:code>" + [(set (pc) + (if_then_else (cond (reg:CC CC_REG) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" +{ + return "jmpc <CC>,%l0"; +} +) + +(define_expand "cstoresi4" + [(set (reg:CC CC_REG) + (compare:CC (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "ft32_rimm_operand" "r,KA"))) + (set (match_operand:SI 0 "register_operand") + (match_operator:SI 1 "ordered_comparison_operator" + [(reg:CC CC_REG) (const_int 0)]))] + "" +{ + rtx test; + + switch (GET_CODE (operands[1])) { + case NE: + case GEU: + case LT: + case LE: + case LEU: + test = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[1])), + SImode, operands[2], operands[3]); + emit_insn(gen_cstoresi4(operands[0], test, operands[2], operands[3])); + emit_insn(gen_xorsi3(operands[0], operands[0], gen_int_mode(1, SImode))); + DONE; + default: + ; + } +}) + +(define_insn "*seq" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (reg CC_REG) (const_int 0)))] + "" + "bextu.l %0,$cc,32|0" +) + +(define_insn "*sltu" + [(set (match_operand:SI 0 "register_operand" "=r") + (ltu:SI (reg CC_REG) (const_int 0)))] + "" + "bextu.l %0,$cc,32|1" +) + +(define_insn "*sge" + [(set (match_operand:SI 0 "register_operand" "=r") + (ge:SI (reg CC_REG) (const_int 0)))] + "" + "bextu.l %0,$cc,32|4" +) + +(define_insn "*sgt" + [(set (match_operand:SI 0 "register_operand" "=r") + (gt:SI (reg CC_REG) (const_int 0)))] + "" + "bextu.l %0,$cc,32|5" +) + +(define_insn "*sgtu" + [(set (match_operand:SI 0 "register_operand" "=r") + (gtu:SI (reg CC_REG) (const_int 0)))] + "" + "bextu.l %0,$cc,32|6" +) + +;; ------------------------------------------------------------------------- +;; Call and Jump instructions +;; ------------------------------------------------------------------------- + +(define_expand "call" + [(call (match_operand:QI 0 "memory_operand" "") + (match_operand 1 "general_operand" ""))] + "" +{ + gcc_assert (MEM_P (operands[0])); +}) + +(define_insn "*call" + [(call (mem:QI (match_operand:SI + 0 "nonmemory_operand" "i,r")) + (match_operand 1 "" ""))] + "" + "@ + call %0 + calli %0" +) + +(define_expand "call_value" + [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "memory_operand" "") + (match_operand 2 "" "")))] + "" +{ + gcc_assert (MEM_P (operands[1])); +}) + +(define_insn "*call_value" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:QI (match_operand:SI + 1 "immediate_operand" "i")) + (match_operand 2 "" "")))] + "" + "call %1" +) + +(define_insn "*call_value_indirect" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:QI (match_operand:SI + 1 "register_operand" "r")) + (match_operand 2 "" "")))] + "" + "calli %1" +) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "r"))] + "" + "jmpi %0") + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "jmp %l0" +) + +(define_insn "call_prolog" + [(unspec:SI [(match_operand 0 "" "")] + UNSPEC_JMP_PROLOG)] + "" + "call __prolog_%0" +) + +(define_insn "jump_epilog" + [(unspec:SI [(match_operand 0 "" "")] + UNSPEC_JMP_EPILOG)] + "" + "jmp __epilog_%0" +) + +(define_insn "jump_epilog24" + [(unspec:SI [(match_operand 0 "" "")] + UNSPEC_JMP_EPILOG24)] + "" + "jmp __epilog24_%0" +) + + +;; Subroutines of "casesi". +;; operand 0 is index +;; operand 1 is the minimum bound +;; operand 2 is the maximum bound - minimum bound + 1 +;; operand 3 is CODE_LABEL for the table; +;; operand 4 is the CODE_LABEL to go to if index out of range. + +(define_expand "casesi" + [(match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "const_int_operand" "") + (match_operand:SI 2 "const_int_operand" "") + (match_operand 3 "" "") + (match_operand 4 "" "")] + "" + " +{ + if (GET_CODE (operands[0]) != REG) + operands[0] = force_reg (SImode, operands[0]); + + if (operands[1] != const0_rtx) + { + rtx index = gen_reg_rtx (SImode); + rtx offset = gen_reg_rtx (SImode); + + emit_insn (gen_movsi (offset, operands[1])); + emit_insn (gen_subsi3 (index, operands[0], offset)); + operands[0] = index; + } + + { + rtx test = gen_rtx_GTU (VOIDmode, operands[0], operands[2]); + emit_jump_insn (gen_cbranchsi4 (test, operands[0], operands[2], operands[4])); + } + + emit_jump_insn (gen_casesi0 (operands[0], operands[3])); + DONE; +}") + +(define_insn "casesi0" + [(set (pc) (mem:SI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r") + (const_int 4)) + (label_ref (match_operand 1 "" ""))))) + (clobber (match_scratch:SI 2 "=&r")) + ] + "" + "ldk.l\t$cc,%l1\;ashl.l\t%2,%0,2\;add.l\t%2,%2,$cc\;jmpi\t%2" + ) + +;; ------------------------------------------------------------------------- +;; Atomic exchange instruction +;; ------------------------------------------------------------------------- + +(define_insn "atomic_exchangesi" + [(set (match_operand:SI 0 "register_operand" "=&r,r") ;; output + (match_operand:SI 1 "memory_operand" "+BW,A")) ;; memory + (set (match_dup 1) + (unspec:SI + [(match_operand:SI 2 "register_operand" "0,0") ;; input + (match_operand:SI 3 "const_int_operand")] ;; model + UNSPEC_XCHG))] + "" + "@ + exi.l %0,%1 + exa.l %0,%1") + +(define_insn "atomic_exchangehi" + [(set (match_operand:HI 0 "register_operand" "=&r,r") ;; output + (match_operand:HI 1 "memory_operand" "+BW,A")) ;; memory + (set (match_dup 1) + (unspec:HI + [(match_operand:HI 2 "register_operand" "0,0") ;; input + (match_operand:HI 3 "const_int_operand")] ;; model + UNSPEC_XCHG))] + "" + "@ + exi.s %0,%1 + exa.s %0,%1") + +(define_insn "atomic_exchangeqi" + [(set (match_operand:QI 0 "register_operand" "=&r,r") ;; output + (match_operand:QI 1 "memory_operand" "+BW,A")) ;; memory + (set (match_dup 1) + (unspec:QI + [(match_operand:QI 2 "register_operand" "0,0") ;; input + (match_operand:QI 3 "const_int_operand")] ;; model + UNSPEC_XCHG))] + "" + "@ + exi.b %0,%1 + exa.b %0,%1") + +;; ------------------------------------------------------------------------- +;; String instructions +;; ------------------------------------------------------------------------- + +(define_insn "cmpstrsi" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (compare:SI (match_operand:BLK 1 "memory_operand" "W,BW") + (match_operand:BLK 2 "memory_operand" "W,BW"))) + (clobber (match_operand:SI 3)) + ] + "" + "strcmp.%d3 %0,%b1,%b2" +) + +(define_insn "movstr" +[(set (match_operand:BLK 1 "memory_operand" "=W") + (match_operand:BLK 2 "memory_operand" "W")) + (use (match_operand:SI 0)) + (clobber (match_dup 0)) + ] +"0" +"stpcpy %b1,%b2 # %0 %b1 %b2" +) + +(define_insn "movmemsi" + [(set (match_operand:BLK 0 "memory_operand" "=W,W,BW") + (match_operand:BLK 1 "memory_operand" "W,W,BW")) + (use (match_operand:SI 2 "ft32_rimm_operand" "r,KA,rKA")) + (use (match_operand:SI 3)) + ] + "" + "memcpy.%d3 %b0,%b1,%2 # %3!" +) + +(define_insn "setmemsi" + [(set (match_operand:BLK 0 "memory_operand" "=BW,BW") (unspec:BLK [ + (use (match_operand:QI 2 "register_operand" "r,r")) + (use (match_operand:SI 1 "ft32_rimm_operand" "r,KA")) + ] UNSPEC_SETMEM)) + (use (match_operand:SI 3)) + ] + "" + "memset.%d3 %b0,%2,%1" +) + +(define_insn "strlensi" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:BLK 1 "memory_operand" "W") + (match_operand:QI 2 "const_int_operand" "") + (match_operand:SI 3 "ft32_rimm_operand" "")] + UNSPEC_STRLEN))] + "" + "strlen.%d3 %0,%b1 # %2 %3" +) + +;; ------------------------------------------------------------------------- +;; Prologue & Epilogue +;; ------------------------------------------------------------------------- + +(define_expand "prologue" + [(clobber (const_int 0))] + "" +{ + extern void ft32_expand_prologue(); + ft32_expand_prologue (); + DONE; +}) + +(define_expand "epilogue" + [(return)] + "" +{ + extern void ft32_expand_epilogue(); + ft32_expand_epilogue (); + DONE; +}) + +(define_insn "link" + [ +;; (set (mem:SI (pre_dec:SI (reg:SI SP_REG))) +;; (reg:SI FP_REG)) + (set (match_operand:SI 0) + (reg:SI SP_REG)) + (set (reg:SI SP_REG) + (plus:SI (reg:SI SP_REG) + (match_operand:SI 1 "general_operand" "L")))] + "" + "link %0,%m1" +) + +(define_insn "unlink" + [(set (reg:SI FP_REG) + (mem:SI (reg:SI FP_REG))) + (set (reg:SI SP_REG) + (plus:SI (reg:SI FP_REG) + (const_int 4)))] + "" + "unlink $r29" +) + +(define_insn "returner" + [(return)] + "reload_completed" + "return") + +(define_insn "returner24" + [ + (set (reg:SI SP_REG) + (plus:SI + (reg:SI SP_REG) + (const_int 24))) + (return)] + "" + "jmp __epilog24") diff --git a/gcc/config/ft32/ft32.opt b/gcc/config/ft32/ft32.opt new file mode 100644 index 0000000..00816ec --- /dev/null +++ b/gcc/config/ft32/ft32.opt @@ -0,0 +1,27 @@ +; Options for the FT32 port of the compiler. + +; Copyright (C) 2015 Free Software Foundation, Inc. +; +; 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/>. + +msim +Target Report Mask(SIM) +target the software simulator + +mlra +Target Report Var(ft32_lra_flag) Init(0) Save +Use LRA instead of reload diff --git a/gcc/config/ft32/predicates.md b/gcc/config/ft32/predicates.md new file mode 100644 index 0000000..ac800ae --- /dev/null +++ b/gcc/config/ft32/predicates.md @@ -0,0 +1,85 @@ +;; Predicate definitions for FT32 +;; Copyright (C) 2015 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/>. + +;; ------------------------------------------------------------------------- +;; Predicates +;; ------------------------------------------------------------------------- + +;; Nonzero if OP can be source of a simple move operation. + +(define_predicate "ft32_general_movsrc_operand" + (match_code "mem,const_int,reg,subreg,symbol_ref,label_ref,const") +{ + /* Any (MEM LABEL_REF) is OK. That is a pc-relative load. */ + if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == LABEL_REF) + return 1; + + if (MEM_P (op) + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST) + return 1; + + return general_operand (op, mode); +}) + +(define_predicate "ft32_general_movdst_operand" + (match_code "mem,const_int,reg,subreg,symbol_ref,label_ref,const") +{ + if (MEM_P (op) + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG + && CONST_INT_P (XEXP (XEXP (op, 0), 1))) + return 1; + if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == SYMBOL_REF) + return 1; + return REG_P(op) || + (MEM_P(op) && REG_P(XEXP (op, 0))); +}) + +(define_predicate "reg_indirect" + (match_code "mem") +{ + return (MEM_P(op) && REG_P(XEXP (op, 0))); +}) + +;; Nonzero if OP can be an operand to an add/inc/dec instruction. + +(define_predicate "ft32_add_operand" + (ior (match_code "reg") + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -32768, 32767)")))) + +;; Nonzero if OP can be an operand to an sub/dec instruction. + +(define_predicate "ft32_sub_operand" + (ior (match_code "reg") + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -32767, 32768)")))) + + +(define_predicate "ft32_rimm_operand" + (ior (match_code "reg") + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), -512, 511)")))) + +(define_predicate "ft32_bwidth_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 1, 16)"))) diff --git a/gcc/config/ft32/t-ft32 b/gcc/config/ft32/t-ft32 new file mode 100644 index 0000000..9376313 --- /dev/null +++ b/gcc/config/ft32/t-ft32 @@ -0,0 +1,19 @@ +# Target Makefile Fragment for ft32 +# Copyright (C) 2015 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/>. |