diff options
author | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
---|---|---|
committer | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
commit | 1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch) | |
tree | c607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/gcc/config/msp430 | |
parent | 283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff) | |
download | toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.gz toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.bz2 toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.zip |
Initial checkin of GCC 4.9.0 from trunk (r208799).
Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba
Diffstat (limited to 'gcc-4.9/gcc/config/msp430')
-rw-r--r-- | gcc-4.9/gcc/config/msp430/README.txt | 7 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/constraints.md | 85 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430-c.c | 36 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430-modes.def | 3 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430-protos.h | 49 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430.c | 2338 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430.h | 411 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430.md | 1370 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/msp430.opt | 38 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/predicates.md | 80 | ||||
-rw-r--r-- | gcc-4.9/gcc/config/msp430/t-msp430 | 257 |
11 files changed, 4674 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/config/msp430/README.txt b/gcc-4.9/gcc/config/msp430/README.txt new file mode 100644 index 000000000..e7343cfca --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/README.txt @@ -0,0 +1,7 @@ +Random Notes +------------ + +The MSP430 port does not use leading underscores. However, the +assembler has no way of differentiating between, for example, register +R12 and symbol R12. So, if you do "int r12;" in your C program, you +may get an assembler error, and will certainly have runtime problems. diff --git a/gcc-4.9/gcc/config/msp430/constraints.md b/gcc-4.9/gcc/config/msp430/constraints.md new file mode 100644 index 000000000..055125f5b --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/constraints.md @@ -0,0 +1,85 @@ +;; Machine Description for TI MSP43* processors +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. +;; Contributed by Red Hat. + +;; 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_register_constraint "R12" "R12_REGS" + "Register R12.") + +(define_register_constraint "R13" "R13_REGS" + "Register R13.") + +(define_constraint "K" + "Integer constant 1." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 1, 1)"))) + +(define_constraint "L" + "Integer constant -1^20..1^19." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -1 << 20, 1 << 19)"))) + +(define_constraint "M" + "Integer constant 1-4." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 1, 4)"))) + +;; We do not allow arbitrary constants, eg symbols or labels, +;; because their address may be above the 16-bit address limit +;; supported by the offset used in the MOVA instruction. +(define_constraint "Ya" + "Memory reference, any type, but restricted range of constants" + (and (match_code "mem") + (ior (match_code "reg" "0") + (and (match_code "plus" "0") + (match_code "reg" "00") + (match_test ("CONST_INT_P (XEXP (XEXP (op, 0), 1))"))) + (match_test "CONSTANT_P (XEXP (op, 0))") + ))) + +(define_constraint "Yl" + "Memory reference, labels only." + (and (match_code "mem") + (match_code "label_ref" "0"))) + + +;; These are memory references that are safe to use with the X suffix, +;; because we know/assume they need not index across the 64k boundary. +(define_constraint "Ys" + "Memory reference, stack only." + (and (match_code "mem") + (ior + (and (match_code "plus" "0") + (and (match_code "reg" "00") + (match_test ("CONST_INT_P (XEXP (XEXP (op, 0), 1))")) + (match_test ("IN_RANGE (INTVAL (XEXP (XEXP (op, 0), 1)), -1 << 15, (1 << 15)-1)")))) + (match_code "reg" "0") + ))) + +(define_constraint "Yc" + "Memory reference, for CALL - we can't use SP." + (and (match_code "mem") + (match_code "mem" "0") + (not (ior + (and (match_code "plus" "00") + (and (match_code "reg" "000") + (match_test ("REGNO (XEXP (XEXP (op, 0), 0)) != SP_REGNO")))) + (and (match_code "reg" "0") + (match_test ("REGNO (XEXP (XEXP (op, 0), 0)) != SP_REGNO"))) + )))) + diff --git a/gcc-4.9/gcc/config/msp430/msp430-c.c b/gcc-4.9/gcc/config/msp430/msp430-c.c new file mode 100644 index 000000000..b637192a0 --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430-c.c @@ -0,0 +1,36 @@ +/* MSP430 C-specific support + Copyright (C) 2013-2014 Free Software Foundation, Inc. + Contributed by Red Hat, 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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "c-family/c-common.h" +#include "msp430-protos.h" + +/* Implements REGISTER_TARGET_PRAGMAS. */ +void +msp430_register_pragmas (void) +{ + c_register_addr_space ("__near", ADDR_SPACE_NEAR); + if (msp430x) + c_register_addr_space ("__far", ADDR_SPACE_FAR); +} diff --git a/gcc-4.9/gcc/config/msp430/msp430-modes.def b/gcc-4.9/gcc/config/msp430/msp430-modes.def new file mode 100644 index 000000000..4e94a6df5 --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430-modes.def @@ -0,0 +1,3 @@ +/* 20-bit address */ +PARTIAL_INT_MODE (SI, 20, PSI); + diff --git a/gcc-4.9/gcc/config/msp430/msp430-protos.h b/gcc-4.9/gcc/config/msp430/msp430-protos.h new file mode 100644 index 000000000..7f999abbd --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430-protos.h @@ -0,0 +1,49 @@ +/* Exported function prototypes from the TI MSP430 backend. + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Contributed by Red Hat. + + 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_MSP430_PROTOS_H +#define GCC_MSP430_PROTOS_H + +rtx msp430_eh_return_stackadj_rtx (void); +void msp430_expand_eh_return (rtx); +void msp430_expand_epilogue (int); +void msp430_expand_helper (rtx *operands, const char *, bool); +void msp430_expand_prologue (void); +const char * msp430x_extendhisi (rtx *); +void msp430_fixup_compare_operands (enum machine_mode, rtx *); +int msp430_hard_regno_mode_ok (int, enum machine_mode); +int msp430_hard_regno_nregs (int, enum machine_mode); +bool msp430_hwmult_enabled (void); +rtx msp430_incoming_return_addr_rtx (void); +void msp430_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, int); +int msp430_initial_elimination_offset (int, int); +bool msp430_is_f5_mcu (void); +bool msp430_is_interrupt_func (void); +const char * msp430x_logical_shift_right (rtx); +const char * msp430_mcu_name (void); +bool msp430_modes_tieable_p (enum machine_mode, enum machine_mode); +void msp430_output_labelref (FILE *, const char *); +void msp430_register_pragmas (void); +rtx msp430_return_addr_rtx (int); +void msp430_split_movsi (rtx *); +void msp430_start_function (FILE *, const char *, tree); +rtx msp430_subreg (enum machine_mode, rtx, enum machine_mode, int); + +#endif /* GCC_MSP430_PROTOS_H */ diff --git a/gcc-4.9/gcc/config/msp430/msp430.c b/gcc-4.9/gcc/config/msp430/msp430.c new file mode 100644 index 000000000..80a17a6e3 --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430.c @@ -0,0 +1,2338 @@ +/* Subroutines used for code generation on TI MSP430 processors. + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Contributed by Red Hat. + + 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 "tree.h" +#include "stor-layout.h" +#include "calls.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "optabs.h" +#include "libfuncs.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "toplev.h" +#include "reload.h" +#include "df.h" +#include "ggc.h" +#include "tm_p.h" +#include "debug.h" +#include "target.h" +#include "target-def.h" +#include "langhooks.h" +#include "msp430-protos.h" +#include "dumpfile.h" +#include "opts.h" + + +static void msp430_compute_frame_info (void); + + + +/* Run-time Target Specification. */ + +bool msp430x = true; + +struct GTY(()) machine_function +{ + /* If set, the rest of the fields have been computed. */ + int computed; + /* Which registers need to be saved in the pro/epilogue. */ + int need_to_save [FIRST_PSEUDO_REGISTER]; + + /* These fields describe the frame layout... */ + /* arg pointer */ + /* 2/4 bytes for saved PC */ + int framesize_regs; + /* frame pointer */ + int framesize_locals; + int framesize_outgoing; + /* stack pointer */ + int framesize; + + /* How much we adjust the stack when returning from an exception + handler. */ + rtx eh_stack_adjust; +}; + +/* This is our init_machine_status, as set in + msp_option_override. */ +static struct machine_function * +msp430_init_machine_status (void) +{ + struct machine_function *m; + + m = ggc_alloc_cleared_machine_function (); + + return m; +} + +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION msp430_handle_option + +bool +msp430_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED, + struct gcc_options *opts_set ATTRIBUTE_UNUSED, + const struct cl_decoded_option *decoded ATTRIBUTE_UNUSED, + location_t loc ATTRIBUTE_UNUSED) +{ + return true; +} + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE msp430_option_override + +static const char * msp430_mcu_names [] = +{ +"msp430afe221", "msp430afe222", "msp430afe223", "msp430afe231", +"msp430afe232", "msp430afe233", "msp430afe251", "msp430afe252", +"msp430afe253", "msp430c091", "msp430c092", "msp430c111", +"msp430c1111", "msp430c112", "msp430c1121", "msp430c1331", +"msp430c1351", "msp430c311s", "msp430c312", "msp430c313", +"msp430c314", "msp430c315", "msp430c323", "msp430c325", +"msp430c336", "msp430c337", "msp430c412", "msp430c413", +"msp430e112", "msp430e313", "msp430e315", "msp430e325", +"msp430e337", "msp430f110", "msp430f1101", "msp430f1101a", +"msp430f1111", "msp430f1111a", "msp430f112", "msp430f1121", +"msp430f1121a", "msp430f1122", "msp430f1132", "msp430f122", +"msp430f1222", "msp430f123", "msp430f1232", "msp430f133", +"msp430f135", "msp430f147", "msp430f1471", "msp430f148", +"msp430f1481", "msp430f149", "msp430f1491", "msp430f155", +"msp430f156", "msp430f157", "msp430f1610", "msp430f1611", +"msp430f1612", "msp430f167", "msp430f168", "msp430f169", +"msp430f2001", "msp430f2002", "msp430f2003", "msp430f2011", +"msp430f2012", "msp430f2013", "msp430f2101", "msp430f2111", +"msp430f2112", "msp430f2121", "msp430f2122", "msp430f2131", +"msp430f2132", "msp430f2232", "msp430f2234", "msp430f2252", +"msp430f2254", "msp430f2272", "msp430f2274", "msp430f233", +"msp430f2330", "msp430f235", "msp430f2350", "msp430f2370", +"msp430f2410", "msp430f247", "msp430f2471", "msp430f248", +"msp430f2481", "msp430f249", "msp430f2491", "msp430f412", +"msp430f413", "msp430f4132", "msp430f415", "msp430f4152", +"msp430f417", "msp430f423", "msp430f423a", "msp430f425", +"msp430f4250", "msp430f425a", "msp430f4260", "msp430f427", +"msp430f4270", "msp430f427a", "msp430f435", "msp430f4351", +"msp430f436", "msp430f4361", "msp430f437", "msp430f4371", +"msp430f438", "msp430f439", "msp430f447", "msp430f448", +"msp430f4481", "msp430f449", "msp430f4491", "msp430f477", +"msp430f478", "msp430f4783", "msp430f4784", "msp430f479", +"msp430f4793", "msp430f4794", "msp430fe423", "msp430fe4232", +"msp430fe423a", "msp430fe4242", "msp430fe425", "msp430fe4252", +"msp430fe425a", "msp430fe427", "msp430fe4272", "msp430fe427a", +"msp430fg4250", "msp430fg4260", "msp430fg4270", "msp430fg437", +"msp430fg438", "msp430fg439", "msp430fg477", "msp430fg478", +"msp430fg479", "msp430fw423", "msp430fw425", "msp430fw427", +"msp430fw428", "msp430fw429", "msp430g2001", "msp430g2101", +"msp430g2102", "msp430g2111", "msp430g2112", "msp430g2113", +"msp430g2121", "msp430g2131", "msp430g2132", "msp430g2152", +"msp430g2153", "msp430g2201", "msp430g2202", "msp430g2203", +"msp430g2210", "msp430g2211", "msp430g2212", "msp430g2213", +"msp430g2221", "msp430g2230", "msp430g2231", "msp430g2232", +"msp430g2233", "msp430g2252", "msp430g2253", "msp430g2302", +"msp430g2303", "msp430g2312", "msp430g2313", "msp430g2332", +"msp430g2333", "msp430g2352", "msp430g2353", "msp430g2402", +"msp430g2403", "msp430g2412", "msp430g2413", "msp430g2432", +"msp430g2433", "msp430g2444", "msp430g2452", "msp430g2453", +"msp430g2513", "msp430g2533", "msp430g2544", "msp430g2553", +"msp430g2744", "msp430g2755", "msp430g2855", "msp430g2955", +"msp430i2020", "msp430i2021", "msp430i2030", "msp430i2031", +"msp430i2040", "msp430i2041", "msp430l092", "msp430p112", +"msp430p313", "msp430p315", "msp430p315s", "msp430p325", +"msp430p337", "msp430tch5e" +}; + +/* Generate a C preprocessor symbol based upon the MCU selected by the user. + If a specific MCU has not been selected then return a generic symbol instead. */ + +const char * +msp430_mcu_name (void) +{ + if (target_mcu) + { + unsigned int i; + static char mcu_name [64]; + + snprintf (mcu_name, sizeof (mcu_name) - 1, "__%s__", target_mcu); + for (i = strlen (mcu_name); i--;) + mcu_name[i] = TOUPPER (mcu_name[i]); + return mcu_name; + } + + return msp430x ? "__MSP430XGENERIC__" : "__MSP430GENERIC__"; +} + +static void +msp430_option_override (void) +{ + init_machine_status = msp430_init_machine_status; + + if (target_cpu) + { + if (strcasecmp (target_cpu, "msp430x") == 0 + || strcasecmp (target_cpu, "msp430xv2") == 0 + || strcasecmp (target_cpu, "430x") == 0 + || strcasecmp (target_cpu, "430xv2") == 0) + msp430x = true; + else if (strcasecmp (target_cpu, "msp430") == 0 + || strcasecmp (target_cpu, "430") == 0) + msp430x = false; + else + error ("unrecognised argument of -mcpu: %s", target_cpu); + } + + if (target_mcu) + { + int i; + + /* If we are given an MCU name, we assume that it supports 430X. + Then we check to see if it is one of the known MCUs that only + supports 430. */ + msp430x = true; + + /* For backwards compatibility we recognise two generic MCU + 430X names. However we want to be able to generate special C + preprocessor defines for them, which is why we set target_mcu + to NULL. */ + if (strcasecmp (target_mcu, "msp430") == 0) + { + msp430x = false; + target_mcu = NULL; + } + else if (strcasecmp (target_mcu, "msp430x") == 0 + || strcasecmp (target_mcu, "msp430xv2") == 0) + target_mcu = NULL; + else + for (i = ARRAY_SIZE (msp430_mcu_names); i--;) + if (strcasecmp (msp430_mcu_names[i], target_mcu) == 0) + { + msp430x = false; + break; + } + /* It is not an error if we do not match the MCU name. There are + hundreds of them. */ + } + + if (TARGET_LARGE && !msp430x) + error ("-mlarge requires a 430X-compatible -mmcu="); + + if (flag_exceptions || flag_non_call_exceptions + || flag_unwind_tables || flag_asynchronous_unwind_tables) + flag_omit_frame_pointer = false; + else + flag_omit_frame_pointer = true; + + /* This is a hack to work around a problem with the newlib build + mechanism. Newlib always appends CFLAGS to the end of the GCC + command line and always sets -O2 in CFLAGS. Thus it is not + possible to build newlib with -Os enabled. Until now... */ + if (TARGET_OPT_SPACE && optimize < 3) + optimize_size = 1; +} + + + +/* Storage Layout */ + +#undef TARGET_MS_BITFIELD_LAYOUT_P +#define TARGET_MS_BITFIELD_LAYOUT_P msp430_ms_bitfield_layout_p + +bool +msp430_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED) +{ + return false; +} + + + +/* Register Usage */ + +/* Implements HARD_REGNO_NREGS. MSP430X registers can hold a single + PSImode value, but not an SImode value. */ +int +msp430_hard_regno_nregs (int regno ATTRIBUTE_UNUSED, + enum machine_mode mode) +{ + if (mode == PSImode && msp430x) + return 1; + return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); +} + +/* Implements HARD_REGNO_MODE_OK. */ +int +msp430_hard_regno_mode_ok (int regno ATTRIBUTE_UNUSED, + enum machine_mode mode) +{ + return regno <= (ARG_POINTER_REGNUM - msp430_hard_regno_nregs (regno, mode)); +} + +/* Implements MODES_TIEABLE_P. */ +bool +msp430_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2) +{ + if ((mode1 == PSImode || mode2 == SImode) + || (mode1 == SImode || mode2 == PSImode)) + return false; + + return ((GET_MODE_CLASS (mode1) == MODE_FLOAT + || GET_MODE_CLASS (mode1) == MODE_COMPLEX_FLOAT) + == (GET_MODE_CLASS (mode2) == MODE_FLOAT + || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT)); +} + +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED msp430_frame_pointer_required + +static bool +msp430_frame_pointer_required (void) +{ + return false; +} + +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE msp430_can_eliminate + +static bool +msp430_can_eliminate (const int from_reg ATTRIBUTE_UNUSED, + const int to_reg ATTRIBUTE_UNUSED) +{ + return true; +} + +/* Implements INITIAL_ELIMINATION_OFFSET. */ +int +msp430_initial_elimination_offset (int from, int to) +{ + int rv = 0; /* As if arg to arg. */ + + msp430_compute_frame_info (); + + switch (to) + { + case STACK_POINTER_REGNUM: + rv += cfun->machine->framesize_outgoing; + rv += cfun->machine->framesize_locals; + /* Fall through. */ + case FRAME_POINTER_REGNUM: + rv += cfun->machine->framesize_regs; + /* Allow for the saved return address. */ + rv += (TARGET_LARGE ? 4 : 2); + /* NB/ No need to allow for crtl->args.pretend_args_size. + GCC does that for us. */ + break; + default: + gcc_unreachable (); + } + + switch (from) + { + case FRAME_POINTER_REGNUM: + /* Allow for the fall through above. */ + rv -= (TARGET_LARGE ? 4 : 2); + rv -= cfun->machine->framesize_regs; + case ARG_POINTER_REGNUM: + break; + default: + gcc_unreachable (); + } + + return rv; +} + +/* Named Address Space support */ + + +/* Return the appropriate mode for a named address pointer. */ +#undef TARGET_ADDR_SPACE_POINTER_MODE +#define TARGET_ADDR_SPACE_POINTER_MODE msp430_addr_space_pointer_mode +#undef TARGET_ADDR_SPACE_ADDRESS_MODE +#define TARGET_ADDR_SPACE_ADDRESS_MODE msp430_addr_space_pointer_mode + +static enum machine_mode +msp430_addr_space_pointer_mode (addr_space_t addrspace) +{ + switch (addrspace) + { + default: + case ADDR_SPACE_GENERIC: + return Pmode; + case ADDR_SPACE_NEAR: + return HImode; + case ADDR_SPACE_FAR: + return PSImode; + } +} + +/* Function pointers are stored in unwind_word sized + variables, so make sure that unwind_word is big enough. */ +#undef TARGET_UNWIND_WORD_MODE +#define TARGET_UNWIND_WORD_MODE msp430_unwind_word_mode + +static enum machine_mode +msp430_unwind_word_mode (void) +{ + return TARGET_LARGE ? SImode : HImode; +} + +/* Determine if one named address space is a subset of another. */ +#undef TARGET_ADDR_SPACE_SUBSET_P +#define TARGET_ADDR_SPACE_SUBSET_P msp430_addr_space_subset_p +static bool +msp430_addr_space_subset_p (addr_space_t subset, addr_space_t superset) +{ + if (subset == superset) + return true; + else + return (subset != ADDR_SPACE_FAR && superset == ADDR_SPACE_FAR); +} + +#undef TARGET_ADDR_SPACE_CONVERT +#define TARGET_ADDR_SPACE_CONVERT msp430_addr_space_convert +/* Convert from one address space to another. */ +static rtx +msp430_addr_space_convert (rtx op, tree from_type, tree to_type) +{ + addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type)); + addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type)); + rtx result; + + if (to_as != ADDR_SPACE_FAR && from_as == ADDR_SPACE_FAR) + { + /* This is unpredictable, as we're truncating off usable address + bits. */ + + if (CONSTANT_P (op)) + return gen_rtx_CONST (HImode, op); + + result = gen_reg_rtx (HImode); + emit_insn (gen_truncpsihi2 (result, op)); + return result; + } + else if (to_as == ADDR_SPACE_FAR && from_as != ADDR_SPACE_FAR) + { + /* This always works. */ + + if (CONSTANT_P (op)) + return gen_rtx_CONST (PSImode, op); + + result = gen_reg_rtx (PSImode); + emit_insn (gen_zero_extendhipsi2 (result, op)); + return result; + } + else + gcc_unreachable (); +} + +/* Stack Layout and Calling Conventions. */ + +/* For each function, we list the gcc version and the TI version on + each line, where we're converting the function names. */ +static char const * const special_convention_function_names [] = +{ + "__muldi3", "__mspabi_mpyll", + "__udivdi3", "__mspabi_divull", + "__umoddi3", "__mspabi_remull", + "__divdi3", "__mspabi_divlli", + "__moddi3", "__mspabi_remlli", + "__mspabi_srall", + "__mspabi_srlll", + "__mspabi_sllll", + "__adddf3", "__mspabi_addd", + "__subdf3", "__mspabi_subd", + "__muldf3", "__mspabi_mpyd", + "__divdf3", "__mspabi_divd", + "__mspabi_cmpd", + NULL +}; + +/* TRUE if the function passed is a "speical" function. Special + functions pass two DImode parameters in registers. */ +static bool +msp430_special_register_convention_p (const char *name) +{ + int i; + + for (i = 0; special_convention_function_names [i]; i++) + if (! strcmp (name, special_convention_function_names [i])) + return true; + + return false; +} + +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P msp430_function_value_regno_p + +bool +msp430_function_value_regno_p (unsigned int regno) +{ + return regno == 12; +} + + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE msp430_function_value + +rtx +msp430_function_value (const_tree ret_type, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (TYPE_MODE (ret_type), 12); +} + +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE msp430_libcall_value + +rtx +msp430_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, 12); +} + +/* Implements INIT_CUMULATIVE_ARGS. */ +void +msp430_init_cumulative_args (CUMULATIVE_ARGS *ca, + tree fntype ATTRIBUTE_UNUSED, + rtx libname ATTRIBUTE_UNUSED, + tree fndecl ATTRIBUTE_UNUSED, + int n_named_args ATTRIBUTE_UNUSED) +{ + const char *fname; + memset (ca, 0, sizeof(*ca)); + + ca->can_split = 1; + + if (fndecl) + fname = IDENTIFIER_POINTER (DECL_NAME (fndecl)); + else if (libname) + fname = XSTR (libname, 0); + else + fname = NULL; + + if (fname && msp430_special_register_convention_p (fname)) + ca->special_p = 1; +} + +/* Helper function for argument passing; this function is the common + code that determines where an argument will be passed. */ +static void +msp430_evaluate_arg (cumulative_args_t cap, + enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, + bool named) +{ + CUMULATIVE_ARGS *ca = get_cumulative_args (cap); + int nregs = GET_MODE_SIZE (mode); + int i; + + ca->reg_count = 0; + ca->mem_count = 0; + + if (!named) + return; + + if (mode == PSImode) + nregs = 1; + else + nregs = (nregs + 1) / 2; + + if (ca->special_p) + { + /* Function is passed two DImode operands, in R8:R11 and + R12:15. */ + ca->start_reg = 8; + ca->reg_count = 4; + return; + } + + switch (nregs) + { + case 1: + for (i = 0; i < 4; i++) + if (! ca->reg_used [i]) + { + ca->reg_count = 1; + ca->start_reg = CA_FIRST_REG + i; + return; + } + break; + case 2: + for (i = 0; i < 3; i++) + if (! ca->reg_used [i] && ! ca->reg_used [i + 1]) + { + ca->reg_count = 2; + ca->start_reg = CA_FIRST_REG + i; + return; + } + if (! ca->reg_used [3] && ca->can_split) + { + ca->reg_count = 1; + ca->mem_count = 2; + ca->start_reg = CA_FIRST_REG + 3; + return; + } + break; + case 3: + case 4: + ca->can_split = 0; + if (! ca->reg_used [0] + && ! ca->reg_used [1] + && ! ca->reg_used [2] + && ! ca->reg_used [3]) + { + ca->reg_count = 4; + ca->start_reg = CA_FIRST_REG; + return; + } + break; + } +} + +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES msp430_promote_prototypes + +bool +msp430_promote_prototypes (const_tree fntype ATTRIBUTE_UNUSED) +{ + return false; +} + +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG msp430_function_arg + +rtx +msp430_function_arg (cumulative_args_t cap, + enum machine_mode mode, + const_tree type, + bool named) +{ + CUMULATIVE_ARGS *ca = get_cumulative_args (cap); + + msp430_evaluate_arg (cap, mode, type, named); + + if (ca->reg_count) + return gen_rtx_REG (mode, ca->start_reg); + + return 0; +} + +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES msp430_arg_partial_bytes + +int +msp430_arg_partial_bytes (cumulative_args_t cap, + enum machine_mode mode, + tree type, + bool named) +{ + CUMULATIVE_ARGS *ca = get_cumulative_args (cap); + + msp430_evaluate_arg (cap, mode, type, named); + + if (ca->reg_count && ca->mem_count) + return ca->reg_count * UNITS_PER_WORD; + + return 0; +} + +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE msp430_pass_by_reference + +static bool +msp430_pass_by_reference (cumulative_args_t cap ATTRIBUTE_UNUSED, + enum machine_mode mode, + const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + return (mode == BLKmode + || (type && TREE_CODE (type) == RECORD_TYPE) + || (type && TREE_CODE (type) == UNION_TYPE)); +} + +#undef TARGET_CALLEE_COPIES +#define TARGET_CALLEE_COPIES msp430_callee_copies + +static bool +msp430_callee_copies (cumulative_args_t cap ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + const_tree type ATTRIBUTE_UNUSED, + bool named ATTRIBUTE_UNUSED) +{ + return true; +} + +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE msp430_function_arg_advance + +void +msp430_function_arg_advance (cumulative_args_t cap, + enum machine_mode mode, + const_tree type, + bool named) +{ + CUMULATIVE_ARGS *ca = get_cumulative_args (cap); + int i; + + msp430_evaluate_arg (cap, mode, type, named); + + if (ca->start_reg >= CA_FIRST_REG) + for (i = 0; i < ca->reg_count; i ++) + ca->reg_used [i + ca->start_reg - CA_FIRST_REG] = 1; + + ca->special_p = 0; +} + +#undef TARGET_FUNCTION_ARG_BOUNDARY +#define TARGET_FUNCTION_ARG_BOUNDARY msp430_function_arg_boundary + +static unsigned int +msp430_function_arg_boundary (enum machine_mode mode, const_tree type) +{ + if (mode == BLKmode + && int_size_in_bytes (type) > 1) + return 16; + if (GET_MODE_BITSIZE (mode) > 8) + return 16; + return 8; +} + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY msp430_return_in_memory + +static bool +msp430_return_in_memory (const_tree ret_type, const_tree fntype ATTRIBUTE_UNUSED) +{ + enum machine_mode mode = TYPE_MODE (ret_type); + + if (mode == BLKmode + || (fntype && TREE_CODE (TREE_TYPE (fntype)) == RECORD_TYPE) + || (fntype && TREE_CODE (TREE_TYPE (fntype)) == UNION_TYPE)) + return true; + + if (GET_MODE_SIZE (mode) > 8) + return true; + + return false; +} + +#undef TARGET_GET_RAW_ARG_MODE +#define TARGET_GET_RAW_ARG_MODE msp430_get_raw_arg_mode + +static enum machine_mode +msp430_get_raw_arg_mode (int regno) +{ + return (regno == ARG_POINTER_REGNUM) ? VOIDmode : Pmode; +} + +#undef TARGET_GET_RAW_RESULT_MODE +#define TARGET_GET_RAW_RESULT_MODE msp430_get_raw_result_mode + +static enum machine_mode +msp430_get_raw_result_mode (int regno ATTRIBUTE_UNUSED) +{ + return Pmode; +} + +/* Addressing Modes */ + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P msp430_legitimate_address_p + +static bool +reg_ok_for_addr (rtx r, bool strict) +{ + int rn = REGNO (r); + + if (strict && rn >= FIRST_PSEUDO_REGISTER) + rn = reg_renumber [rn]; + if (strict && 0 <= rn && rn < FIRST_PSEUDO_REGISTER) + return true; + if (!strict) + return true; + return false; +} + +bool +msp430_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, + rtx x ATTRIBUTE_UNUSED, + bool strict ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (x)) + { + case MEM: + return false; + + case PLUS: + if (REG_P (XEXP (x, 0))) + { + if (GET_MODE (x) != GET_MODE (XEXP (x, 0))) + return false; + if (!reg_ok_for_addr (XEXP (x, 0), strict)) + return false; + switch (GET_CODE (XEXP (x, 1))) + { + case CONST: + case SYMBOL_REF: + case CONST_INT: + return true; + default: + return false; + } + } + return false; + + case REG: + if (!reg_ok_for_addr (x, strict)) + return false; + /* else... */ + case CONST: + case SYMBOL_REF: + case CONST_INT: + return true; + + default: + return false; + } +} + +#undef TARGET_LEGITIMATE_CONSTANT_P +#define TARGET_LEGITIMATE_CONSTANT_P msp430_legitimate_constant + +static bool +msp430_legitimate_constant (enum machine_mode mode, rtx x) +{ + return ! CONST_INT_P (x) + || mode != PSImode + /* GCC does not know the width of the PSImode, so make + sure that it does not try to use a constant value that + is out of range. */ + || (INTVAL (x) < (1 << 20) && INTVAL (x) >= (-1 << 20)); +} + + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS msp430_rtx_costs + +static bool msp430_rtx_costs (rtx x ATTRIBUTE_UNUSED, + int code, + int outer_code ATTRIBUTE_UNUSED, + int opno ATTRIBUTE_UNUSED, + int * total, + bool speed ATTRIBUTE_UNUSED) +{ + switch (code) + { + case SIGN_EXTEND: + if (GET_MODE (x) == SImode && outer_code == SET) + { + *total = COSTS_N_INSNS (4); + return true; + } + break; + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (!msp430x) + { + *total = COSTS_N_INSNS (100); + return true; + } + break; + } + return false; +} + +/* Function Entry and Exit */ + +/* The MSP430 call frame looks like this: + + <higher addresses> + +--------------------+ + | | + | Stack Arguments | + | | + +--------------------+ <-- "arg pointer" + | | + | PC from call | (2 bytes for 430, 4 for TARGET_LARGE) + | | + +--------------------+ + | SR if this func has| + | been called via an | + | interrupt. | + +--------------------+ <-- SP before prologue, also AP + | | + | Saved Regs | (2 bytes per reg for 430, 4 per for TARGET_LARGE) + | | + +--------------------+ <-- "frame pointer" + | | + | Locals | + | | + +--------------------+ + | | + | Outgoing Args | + | | + +--------------------+ <-- SP during function + <lower addresses> + +*/ + +/* We use this to wrap all emitted insns in the prologue, so they get + the "frame-related" (/f) flag set. */ +static rtx +F (rtx x) +{ + RTX_FRAME_RELATED_P (x) = 1; + return x; +} + +/* This is the one spot that decides if a register is to be saved and + restored in the prologue/epilogue. */ +static bool +msp430_preserve_reg_p (int regno) +{ + /* PC, SP, SR, and the constant generator. */ + if (regno <= 3) + return false; + + /* FIXME: add interrupt, EH, etc. */ + if (crtl->calls_eh_return) + return true; + + /* Shouldn't be more than the above, but just in case... */ + if (fixed_regs [regno]) + return false; + + /* Interrupt handlers save all registers they use, even + ones which are call saved. If they call other functions + then *every* register is saved. */ + if (msp430_is_interrupt_func ()) + return ! crtl->is_leaf || df_regs_ever_live_p (regno); + + if (!call_used_regs [regno] + && df_regs_ever_live_p (regno)) + return true; + + return false; +} + +/* Compute all the frame-related fields in our machine_function + structure. */ +static void +msp430_compute_frame_info (void) +{ + int i; + + cfun->machine->computed = 1; + cfun->machine->framesize_regs = 0; + cfun->machine->framesize_locals = get_frame_size (); + cfun->machine->framesize_outgoing = crtl->outgoing_args_size; + + for (i = 0; i < ARG_POINTER_REGNUM; i ++) + if (msp430_preserve_reg_p (i)) + { + cfun->machine->need_to_save [i] = 1; + cfun->machine->framesize_regs += (TARGET_LARGE ? 4 : 2); + } + else + cfun->machine->need_to_save [i] = 0; + + if ((cfun->machine->framesize_locals + cfun->machine->framesize_outgoing) & 1) + cfun->machine->framesize_locals ++; + + cfun->machine->framesize = (cfun->machine->framesize_regs + + cfun->machine->framesize_locals + + cfun->machine->framesize_outgoing); +} + +static inline bool +is_attr_func (const char * attr) +{ + return lookup_attribute (attr, DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE; +} + +/* Returns true if the current function has the "interrupt" attribute. */ + +bool +msp430_is_interrupt_func (void) +{ + if (current_function_decl == NULL) + return false; + return is_attr_func ("interrupt"); +} + +static bool +is_wakeup_func (void) +{ + return msp430_is_interrupt_func () && is_attr_func ("wakeup"); +} + +static inline bool +is_naked_func (void) +{ + return is_attr_func ("naked"); +} + +static inline bool +is_reentrant_func (void) +{ + return is_attr_func ("reentrant"); +} + +static inline bool +is_critical_func (void) +{ + return is_attr_func ("critical"); +} + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE msp430_start_function + +static void +msp430_start_function (FILE *outfile, HOST_WIDE_INT hwi_local ATTRIBUTE_UNUSED) +{ + int r, n; + + fprintf (outfile, "; start of function\n"); + + if (DECL_ATTRIBUTES (current_function_decl) != NULL_TREE) + { + fprintf (outfile, "; attributes: "); + if (is_naked_func ()) + fprintf (outfile, "naked "); + if (msp430_is_interrupt_func ()) + fprintf (outfile, "interrupt "); + if (is_reentrant_func ()) + fprintf (outfile, "reentrant "); + if (is_critical_func ()) + fprintf (outfile, "critical "); + if (is_wakeup_func ()) + fprintf (outfile, "wakeup "); + fprintf (outfile, "\n"); + } + + fprintf (outfile, "; framesize_regs: %d\n", cfun->machine->framesize_regs); + fprintf (outfile, "; framesize_locals: %d\n", cfun->machine->framesize_locals); + fprintf (outfile, "; framesize_outgoing: %d\n", cfun->machine->framesize_outgoing); + fprintf (outfile, "; framesize: %d\n", cfun->machine->framesize); + fprintf (outfile, "; elim ap -> fp %d\n", msp430_initial_elimination_offset (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM)); + fprintf (outfile, "; elim fp -> sp %d\n", msp430_initial_elimination_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM)); + + n = 0; + fprintf (outfile, "; saved regs:"); + for (r = 0; r < ARG_POINTER_REGNUM; r++) + if (cfun->machine->need_to_save [r]) + { + fprintf (outfile, " %s", reg_names [r]); + n = 1; + } + if (n == 0) + fprintf (outfile, "(none)"); + fprintf (outfile, "\n"); +} + +/* Common code to change the stack pointer. */ +static void +increment_stack (HOST_WIDE_INT amount) +{ + rtx inc; + rtx sp = stack_pointer_rtx; + + if (amount == 0) + return; + + if (amount < 0) + { + inc = GEN_INT (- amount); + if (TARGET_LARGE) + F (emit_insn (gen_subpsi3 (sp, sp, inc))); + else + F (emit_insn (gen_subhi3 (sp, sp, inc))); + } + else + { + inc = GEN_INT (amount); + if (TARGET_LARGE) + emit_insn (gen_addpsi3 (sp, sp, inc)); + else + emit_insn (gen_addhi3 (sp, sp, inc)); + } +} + +/* Verify MSP430 specific attributes. */ + +static tree +msp430_attr (tree * node, + tree name, + tree args, + int flags ATTRIBUTE_UNUSED, + bool * no_add_attrs) +{ + gcc_assert (DECL_P (* node)); + + if (args != NULL) + { + tree value = TREE_VALUE (args); + + switch (TREE_CODE (value)) + { + case STRING_CST: + if ( strcmp (TREE_STRING_POINTER (value), "reset") + && strcmp (TREE_STRING_POINTER (value), "nmi") + && strcmp (TREE_STRING_POINTER (value), "watchdog")) + /* Allow the attribute to be added - the linker script + being used may still recognise this name. */ + warning (OPT_Wattributes, + "unrecognised interrupt vector argument of %qE attribute", + name); + break; + + case INTEGER_CST: + if (TREE_INT_CST_LOW (value) > 63) + /* Allow the attribute to be added - the linker script + being used may still recognise this value. */ + warning (OPT_Wattributes, + "numeric argument of %qE attribute must be in range 0..63", + name); + break; + + default: + warning (OPT_Wattributes, + "argument of %qE attribute is not a string constant or number", + name); + *no_add_attrs = true; + break; + } + } + + if (TREE_CODE (* node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, + "%qE attribute only applies to functions", + name); + * no_add_attrs = true; + } + + /* FIXME: We ought to check that the interrupt handler + attribute has been applied to a void function. */ + /* FIXME: We should check that reentrant and critical + functions are not naked and that critical functions + are not reentrant. */ + + return NULL_TREE; +} + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE msp430_attribute_table + +/* Table of MSP430-specific attributes. */ +const struct attribute_spec msp430_attribute_table[] = +{ + /* Name min_len decl_req, fn_type_req, affects_type_identity + max_len, type_req, handler. */ + { "interrupt", 0, 1, true, false, false, msp430_attr, false }, + { "naked", 0, 0, true, false, false, msp430_attr, false }, + { "reentrant", 0, 0, true, false, false, msp430_attr, false }, + { "critical", 0, 0, true, false, false, msp430_attr, false }, + { "wakeup", 0, 0, true, false, false, msp430_attr, false }, + { NULL, 0, 0, false, false, false, NULL, false } +}; + +void +msp430_start_function (FILE *file, const char *name, tree decl) +{ + tree int_attr; + + int_attr = lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl)); + if (int_attr != NULL_TREE) + { + tree intr_vector = TREE_VALUE (int_attr); + + if (intr_vector != NULL_TREE) + { + char buf[101]; + + intr_vector = TREE_VALUE (intr_vector); + + /* The interrupt attribute has a vector value. Turn this into a + section name, switch to that section and put the address of + the current function into that vector slot. Note msp430_attr() + has already verified the vector name for us. */ + if (TREE_CODE (intr_vector) == STRING_CST) + sprintf (buf, "__interrupt_vector_%.80s", + TREE_STRING_POINTER (intr_vector)); + else /* TREE_CODE (intr_vector) == INTEGER_CST */ + sprintf (buf, "__interrupt_vector_%u", + (unsigned int) TREE_INT_CST_LOW (intr_vector)); + + switch_to_section (get_section (buf, SECTION_CODE, decl)); + fputs ("\t.word\t", file); + assemble_name (file, name); + fputc ('\n', file); + fputc ('\t', file); + } + } + + switch_to_section (function_section (decl)); + ASM_OUTPUT_FUNCTION_LABEL (file, name, decl); +} + +static section * +msp430_function_section (tree decl, enum node_frequency freq, bool startup, bool exit) +{ + /* In large mode we must make sure that interrupt handlers are put into + low memory as the vector table only accepts 16-bit addresses. */ + if (TARGET_LARGE + && lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl))) + return get_section (".lowtext", SECTION_CODE | SECTION_WRITE , decl); + + /* Otherwise, use the default function section. */ + return default_function_section (decl, freq, startup, exit); +} + +#undef TARGET_ASM_FUNCTION_SECTION +#define TARGET_ASM_FUNCTION_SECTION msp430_function_section + +enum msp430_builtin +{ + MSP430_BUILTIN_BIC_SR, + MSP430_BUILTIN_BIS_SR, + MSP430_BUILTIN_max +}; + +static GTY(()) tree msp430_builtins [(int) MSP430_BUILTIN_max]; + +static void +msp430_init_builtins (void) +{ + tree void_ftype_int = build_function_type_list (void_type_node, integer_type_node, NULL); + + msp430_builtins[MSP430_BUILTIN_BIC_SR] = + add_builtin_function ( "__bic_SR_register_on_exit", void_ftype_int, + MSP430_BUILTIN_BIC_SR, BUILT_IN_MD, NULL, NULL_TREE); + + msp430_builtins[MSP430_BUILTIN_BIS_SR] = + add_builtin_function ( "__bis_SR_register_on_exit", void_ftype_int, + MSP430_BUILTIN_BIS_SR, BUILT_IN_MD, NULL, NULL_TREE); +} + +static tree +msp430_builtin_decl (unsigned code, bool initialize ATTRIBUTE_UNUSED) +{ + switch (code) + { + case MSP430_BUILTIN_BIC_SR: + case MSP430_BUILTIN_BIS_SR: + return msp430_builtins[code]; + default: + return error_mark_node; + } +} + +static rtx +msp430_expand_builtin (tree exp, + rtx target ATTRIBUTE_UNUSED, + rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + rtx arg1 = expand_normal (CALL_EXPR_ARG (exp, 0)); + + if (! msp430_is_interrupt_func ()) + { + error ("MSP430 builtin functions only work inside interrupt handlers"); + return NULL_RTX; + } + + if (! REG_P (arg1) && ! CONSTANT_P (arg1)) + arg1 = force_reg (mode, arg1); + + switch (fcode) + { + case MSP430_BUILTIN_BIC_SR: emit_insn (gen_bic_SR (arg1)); break; + case MSP430_BUILTIN_BIS_SR: emit_insn (gen_bis_SR (arg1)); break; + default: + internal_error ("bad builtin code"); + break; + } + return NULL_RTX; +} + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS msp430_init_builtins + +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN msp430_expand_builtin + +#undef TARGET_BUILTIN_DECL +#define TARGET_BUILTIN_DECL msp430_builtin_decl + +void +msp430_expand_prologue (void) +{ + int i, j; + int fs; + /* Always use stack_pointer_rtx instead of calling + rtx_gen_REG ourselves. Code elsewhere in GCC assumes + that there is a single rtx representing the stack pointer, + namely stack_pointer_rtx, and uses == to recognize it. */ + rtx sp = stack_pointer_rtx; + rtx p; + + if (is_naked_func ()) + return; + + emit_insn (gen_prologue_start_marker ()); + + if (is_critical_func ()) + { + emit_insn (gen_push_intr_state ()); + emit_insn (gen_disable_interrupts ()); + } + else if (is_reentrant_func ()) + emit_insn (gen_disable_interrupts ()); + + if (!cfun->machine->computed) + msp430_compute_frame_info (); + + if (flag_stack_usage_info) + current_function_static_stack_size = cfun->machine->framesize; + + if (crtl->args.pretend_args_size) + { + rtx note; + + gcc_assert (crtl->args.pretend_args_size == 2); + + p = emit_insn (gen_grow_and_swap ()); + + /* Document the stack decrement... */ + note = F (gen_rtx_SET (Pmode, stack_pointer_rtx, + gen_rtx_MINUS (Pmode, stack_pointer_rtx, GEN_INT (2)))); + add_reg_note (p, REG_FRAME_RELATED_EXPR, note); + + /* ...and the establishment of a new location for the return address. */ + note = F (gen_rtx_SET (Pmode, gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (-2))), + pc_rtx)); + add_reg_note (p, REG_CFA_OFFSET, note); + F (p); + } + + for (i = 15; i >= 4; i--) + if (cfun->machine->need_to_save [i]) + { + int seq, count; + rtx note; + + for (seq = i - 1; seq >= 4 && cfun->machine->need_to_save[seq]; seq --) + ; + count = i - seq; + + if (msp430x) + { + /* Note: with TARGET_LARGE we still use PUSHM as PUSHX.A is two bytes bigger. */ + p = F (emit_insn (gen_pushm (gen_rtx_REG (Pmode, i), + GEN_INT (count)))); + + note = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (count + 1)); + + XVECEXP (note, 0, 0) + = F (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (count * (TARGET_LARGE ? -4 : -2))))); + + /* *sp-- = R[i-j] */ + /* sp+N R10 + ... + sp R4 */ + for (j = 0; j < count; j ++) + { + rtx addr; + int ofs = (count - j - 1) * (TARGET_LARGE ? 4 : 2); + + if (ofs) + addr = gen_rtx_PLUS (Pmode, sp, GEN_INT (ofs)); + else + addr = stack_pointer_rtx; + + XVECEXP (note, 0, j + 1) = + F (gen_rtx_SET (VOIDmode, + gen_rtx_MEM (Pmode, addr), + gen_rtx_REG (Pmode, i - j)) ); + } + + add_reg_note (p, REG_FRAME_RELATED_EXPR, note); + i -= count - 1; + } + else + F (emit_insn (gen_push (gen_rtx_REG (Pmode, i)))); + } + + if (frame_pointer_needed) + F (emit_move_insn (gen_rtx_REG (Pmode, FRAME_POINTER_REGNUM), sp)); + + fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing; + + increment_stack (- fs); + + emit_insn (gen_prologue_end_marker ()); +} + +void +msp430_expand_epilogue (int is_eh) +{ + int i; + int fs; + int helper_n = 0; + + if (is_naked_func ()) + return; + + if (cfun->machine->need_to_save [10]) + { + /* Check for a helper function. */ + helper_n = 7; /* For when the loop below never sees a match. */ + for (i = 9; i >= 4; i--) + if (!cfun->machine->need_to_save [i]) + { + helper_n = 10 - i; + for (; i >= 4; i--) + if (cfun->machine->need_to_save [i]) + { + helper_n = 0; + break; + } + break; + } + } + + emit_insn (gen_epilogue_start_marker ()); + + if (cfun->decl && strcmp (IDENTIFIER_POINTER (DECL_NAME (cfun->decl)), "main") == 0) + emit_insn (gen_msp430_refsym_need_exit ()); + + if (is_wakeup_func ()) + /* Clear the SCG1, SCG0, OSCOFF and CPUOFF bits in the saved copy of the + status register current residing on the stack. When this function + executes its RETI instruction the SR will be updated with this saved + value, thus ensuring that the processor is woken up from any low power + state in which it may be residing. */ + emit_insn (gen_bic_SR (GEN_INT (0xf0))); + + fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing; + + increment_stack (fs); + + if (is_eh) + { + /* We need to add the right "SP" register save just after the + regular ones, so that when we pop it off we're in the EH + return frame, not this one. This overwrites our own return + address, but we're not going to be returning anyway. */ + rtx r12 = gen_rtx_REG (Pmode, 12); + rtx (*addPmode)(rtx, rtx, rtx) = TARGET_LARGE ? gen_addpsi3 : gen_addhi3; + + /* R12 will hold the new SP. */ + i = cfun->machine->framesize_regs; + emit_move_insn (r12, stack_pointer_rtx); + emit_insn (addPmode (r12, r12, EH_RETURN_STACKADJ_RTX)); + emit_insn (addPmode (r12, r12, GEN_INT (i))); + emit_move_insn (gen_rtx_MEM (Pmode, plus_constant (Pmode, stack_pointer_rtx, i)), r12); + } + + for (i = 4; i <= 15; i++) + if (cfun->machine->need_to_save [i]) + { + int seq, count; + + for (seq = i + 1; seq <= 15 && cfun->machine->need_to_save[seq]; seq ++) + ; + count = seq - i; + + if (msp430x) + { + /* Note: With TARGET_LARGE we still use + POPM as POPX.A is two bytes bigger. */ + emit_insn (gen_popm (stack_pointer_rtx, GEN_INT (seq - 1), + GEN_INT (count))); + i += count - 1; + } + else if (i == 11 - helper_n + && ! msp430_is_interrupt_func () + && ! is_reentrant_func () + && ! is_critical_func () + && crtl->args.pretend_args_size == 0 + /* Calling the helper takes as many bytes as the POP;RET sequence. */ + && helper_n > 1 + && !is_eh) + { + emit_insn (gen_epilogue_helper (GEN_INT (helper_n))); + return; + } + else + emit_insn (gen_pop (gen_rtx_REG (Pmode, i))); + } + + if (is_eh) + { + /* Also pop SP, which puts us into the EH return frame. Except + that you can't "pop" sp, you have to just load it off the + stack. */ + emit_move_insn (stack_pointer_rtx, gen_rtx_MEM (Pmode, stack_pointer_rtx)); + } + + if (crtl->args.pretend_args_size) + emit_insn (gen_swap_and_shrink ()); + + if (is_critical_func ()) + emit_insn (gen_pop_intr_state ()); + else if (is_reentrant_func ()) + emit_insn (gen_enable_interrupts ()); + + emit_jump_insn (gen_msp_return ()); +} + +/* Implements EH_RETURN_STACKADJ_RTX. Saved and used later in + m32c_emit_eh_epilogue. */ +rtx +msp430_eh_return_stackadj_rtx (void) +{ + if (!cfun->machine->eh_stack_adjust) + { + rtx sa; + + sa = gen_rtx_REG (Pmode, 15); + cfun->machine->eh_stack_adjust = sa; + } + return cfun->machine->eh_stack_adjust; +} + +/* This function is called before reload, to "fix" the stack in + preparation for an EH return. */ +void +msp430_expand_eh_return (rtx eh_handler) +{ + /* These are all Pmode */ + rtx ap, sa, ra, tmp; + + ap = arg_pointer_rtx; + sa = msp430_eh_return_stackadj_rtx (); + ra = eh_handler; + + tmp = ap; + tmp = gen_rtx_PLUS (Pmode, ap, sa); + tmp = plus_constant (Pmode, tmp, TARGET_LARGE ? -4 : -2); + tmp = gen_rtx_MEM (Pmode, tmp); + emit_move_insn (tmp, ra); +} + +/* This is a list of MD patterns that implement fixed-count shifts. */ +static struct +{ + const char *name; + int count; + int need_430x; + rtx (*genfunc)(rtx,rtx); +} + const_shift_helpers[] = +{ +#define CSH(N,C,X,G) { "__mspabi_"N, C, X, gen_##G } + + CSH ("slli", 1, 1, slli_1), + CSH ("slll", 1, 1, slll_1), + CSH ("slll", 2, 1, slll_2), + + CSH ("srai", 1, 0, srai_1), + CSH ("sral", 1, 0, sral_1), + CSH ("sral", 2, 0, sral_2), + + CSH ("srll", 1, 0, srll_1), + CSH ("srll", 2, 1, srll_2x), + { 0, 0, 0, 0 } +#undef CSH +}; + +/* The MSP430 ABI defines a number of helper functions that should be + used for, for example, 32-bit shifts. This function is called to + emit such a function, using the table above to optimize some + cases. */ +void +msp430_expand_helper (rtx *operands, const char *helper_name, bool const_variants) +{ + rtx c, f; + char *helper_const = NULL; + int arg2 = 13; + int arg1sz = 1; + enum machine_mode arg0mode = GET_MODE (operands[0]); + enum machine_mode arg1mode = GET_MODE (operands[1]); + enum machine_mode arg2mode = GET_MODE (operands[2]); + int have_430x = msp430x ? 1 : 0; + + if (CONST_INT_P (operands[2])) + { + int i; + + for (i=0; const_shift_helpers[i].name; i++) + { + if (const_shift_helpers[i].need_430x <= have_430x + && strcmp (helper_name, const_shift_helpers[i].name) == 0 + && INTVAL (operands[2]) == const_shift_helpers[i].count) + { + emit_insn (const_shift_helpers[i].genfunc (operands[0], operands[1])); + return; + } + } + } + + if (arg1mode == VOIDmode) + arg1mode = arg0mode; + if (arg2mode == VOIDmode) + arg2mode = arg0mode; + + if (arg1mode == SImode) + { + arg2 = 14; + arg1sz = 2; + } + + if (const_variants + && CONST_INT_P (operands[2]) + && INTVAL (operands[2]) >= 1 + && INTVAL (operands[2]) <= 15) + { + /* Note that the INTVAL is limited in value and length by the conditional above. */ + int len = strlen (helper_name) + 4; + helper_const = (char *) xmalloc (len); + snprintf (helper_const, len, "%s_%d", helper_name, (int) INTVAL (operands[2])); + } + + emit_move_insn (gen_rtx_REG (arg1mode, 12), + operands[1]); + if (!helper_const) + emit_move_insn (gen_rtx_REG (arg2mode, arg2), + operands[2]); + + c = gen_call_value_internal (gen_rtx_REG (arg0mode, 12), + gen_rtx_SYMBOL_REF (VOIDmode, helper_const ? helper_const : helper_name), + GEN_INT (0)); + c = emit_call_insn (c); + RTL_CONST_CALL_P (c) = 1; + + f = 0; + use_regs (&f, 12, arg1sz); + if (!helper_const) + use_regs (&f, arg2, 1); + add_function_usage_to (c, f); + + emit_move_insn (operands[0], + gen_rtx_REG (arg0mode, 12)); +} + +/* Called by cbranch<mode>4 to coerce operands into usable forms. */ +void +msp430_fixup_compare_operands (enum machine_mode my_mode, rtx * operands) +{ + /* constants we're looking for, not constants which are allowed. */ + int const_op_idx = 1; + + if (msp430_reversible_cmp_operator (operands[0], VOIDmode)) + const_op_idx = 2; + + if (GET_CODE (operands[const_op_idx]) != REG + && GET_CODE (operands[const_op_idx]) != MEM) + operands[const_op_idx] = copy_to_mode_reg (my_mode, operands[const_op_idx]); +} + +/* Simplify_gen_subreg() doesn't handle memory references the way we + need it to below, so we use this function for when we must get a + valid subreg in a "natural" state. */ +rtx +msp430_subreg (enum machine_mode mode, rtx r, enum machine_mode omode, int byte) +{ + rtx rv; + + if (GET_CODE (r) == SUBREG + && SUBREG_BYTE (r) == 0) + { + rtx ireg = SUBREG_REG (r); + enum machine_mode imode = GET_MODE (ireg); + + /* special case for (HI (SI (PSI ...), 0)) */ + if (imode == PSImode + && mode == HImode + && byte == 0) + rv = gen_rtx_SUBREG (mode, ireg, byte); + else + rv = simplify_gen_subreg (mode, ireg, imode, byte); + } + else if (GET_CODE (r) == MEM) + rv = adjust_address (r, mode, byte); + else + rv = simplify_gen_subreg (mode, r, omode, byte); + + if (!rv) + gcc_unreachable (); + + return rv; +} + +/* Called by movsi_x to generate the HImode operands. */ +void +msp430_split_movsi (rtx *operands) +{ + rtx op00, op02, op10, op12; + + op00 = msp430_subreg (HImode, operands[0], SImode, 0); + op02 = msp430_subreg (HImode, operands[0], SImode, 2); + + if (GET_CODE (operands[1]) == CONST + || GET_CODE (operands[1]) == SYMBOL_REF) + { + op10 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (0)); + op10 = gen_rtx_CONST (HImode, op10); + op12 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (16)); + op12 = gen_rtx_CONST (HImode, op12); + } + else + { + op10 = msp430_subreg (HImode, operands[1], SImode, 0); + op12 = msp430_subreg (HImode, operands[1], SImode, 2); + } + + if (rtx_equal_p (operands[0], operands[1])) + { + operands[2] = op02; + operands[4] = op12; + operands[3] = op00; + operands[5] = op10; + } + else if (rtx_equal_p (op00, op12) + /* Catch the case where we are loading (rN, rN+1) from mem (rN). */ + || (REG_P (op00) && reg_mentioned_p (op00, op10)) + /* Or storing (rN) into mem (rN). */ + || (REG_P (op10) && reg_mentioned_p (op10, op00)) + ) + { + operands[2] = op02; + operands[4] = op12; + operands[3] = op00; + operands[5] = op10; + } + else + { + operands[2] = op00; + operands[4] = op10; + operands[3] = op02; + operands[5] = op12; + } +} + + +/* The MSPABI specifies the names of various helper functions, many of + which are compatible with GCC's helpers. This table maps the GCC + name to the MSPABI name. */ +static const struct +{ + char const * const gcc_name; + char const * const ti_name; +} + helper_function_name_mappings [] = +{ + /* Floating point to/from integer conversions. */ + { "__truncdfsf2", "__mspabi_cvtdf" }, + { "__extendsfdf2", "__mspabi_cvtfd" }, + { "__fixdfhi", "__mspabi_fixdi" }, + { "__fixdfsi", "__mspabi_fixdli" }, + { "__fixdfdi", "__mspabi_fixdlli" }, + { "__fixunsdfhi", "__mspabi_fixdu" }, + { "__fixunsdfsi", "__mspabi_fixdul" }, + { "__fixunsdfdi", "__mspabi_fixdull" }, + { "__fixsfhi", "__mspabi_fixfi" }, + { "__fixsfsi", "__mspabi_fixfli" }, + { "__fixsfdi", "__mspabi_fixflli" }, + { "__fixunsfhi", "__mspabi_fixfu" }, + { "__fixunsfsi", "__mspabi_fixful" }, + { "__fixunsfdi", "__mspabi_fixfull" }, + { "__floathisf", "__mspabi_fltif" }, + { "__floatsisf", "__mspabi_fltlif" }, + { "__floatdisf", "__mspabi_fltllif" }, + { "__floathidf", "__mspabi_fltid" }, + { "__floatsidf", "__mspabi_fltlid" }, + { "__floatdidf", "__mspabi_fltllid" }, + { "__floatunhisf", "__mspabi_fltuf" }, + { "__floatunsisf", "__mspabi_fltulf" }, + { "__floatundisf", "__mspabi_fltullf" }, + { "__floatunhidf", "__mspabi_fltud" }, + { "__floatunsidf", "__mspabi_fltuld" }, + { "__floatundidf", "__mspabi_fltulld" }, + + /* Floating point comparisons. */ + /* GCC uses individual functions for each comparison, TI uses one + compare <=> function. */ + + /* Floating point arithmatic */ + { "__adddf3", "__mspabi_addd" }, + { "__addsf3", "__mspabi_addf" }, + { "__divdf3", "__mspabi_divd" }, + { "__divsf3", "__mspabi_divf" }, + { "__muldf3", "__mspabi_mpyd" }, + { "__mulsf3", "__mspabi_mpyf" }, + { "__subdf3", "__mspabi_subd" }, + { "__subsf3", "__mspabi_subf" }, + /* GCC does not use helper functions for negation */ + + /* Integer multiply, divide, remainder. */ + { "__mulhi3", "__mspabi_mpyi" }, + { "__mulsi3", "__mspabi_mpyl" }, + { "__muldi3", "__mspabi_mpyll" }, +#if 0 + /* Clarify signed vs unsigned first. */ + { "__mulhisi3", "__mspabi_mpysl" }, /* gcc doesn't use widening multiply (yet?) */ + { "__mulsidi3", "__mspabi_mpysll" }, /* gcc doesn't use widening multiply (yet?) */ +#endif + + { "__divhi3", "__mspabi_divi" }, + { "__divsi3", "__mspabi_divli" }, + { "__divdi3", "__mspabi_divlli" }, + { "__udivhi3", "__mspabi_divu" }, + { "__udivsi3", "__mspabi_divlu" }, + { "__udivdi3", "__mspabi_divllu" }, + { "__modhi3", "__mspabi_remi" }, + { "__modsi3", "__mspabi_remli" }, + { "__moddi3", "__mspabi_remlli" }, + { "__umodhi3", "__mspabi_remu" }, + { "__umodsi3", "__mspabi_remul" }, + { "__umoddi3", "__mspabi_remull" }, + + /* Bitwise operations. */ + /* Rotation - no rotation support yet. */ + /* Logical left shift - gcc already does these itself. */ + /* Arithmetic left shift - gcc already does these itself. */ + /* Arithmetic right shift - gcc already does these itself. */ + + { NULL, NULL } +}; + +/* Returns true if the current MCU is an F5xxx series. */ +bool +msp430_is_f5_mcu (void) +{ + if (target_mcu == NULL) + return false; + return strncasecmp (target_mcu, "msp430f5", 8) == 0; +} + +/* Returns true id the current MCU has a second generation 32-bit hardware multiplier. */ +static bool +has_32bit_hw_mult (void) +{ + static const char * known_32bit_mult_mcus [] = + { + "msp430f4783", "msp430f4793", "msp430f4784", + "msp430f4794", "msp430f47126", "msp430f47127", + "msp430f47163", "msp430f47173", "msp430f47183", + "msp430f47193", "msp430f47166", "msp430f47176", + "msp430f47186", "msp430f47196", "msp430f47167", + "msp430f47177", "msp430f47187", "msp430f47197" + }; + int i; + if (target_mcu == NULL) + return false; + + for (i = ARRAY_SIZE (known_32bit_mult_mcus); i--;) + if (strcasecmp (target_mcu, known_32bit_mult_mcus[i]) == 0) + return true; + + return false; +} + +/* Returns true if hardware multiply is supported by the chosen MCU. */ +bool +msp430_hwmult_enabled (void) +{ + if (target_mcu == NULL) + return false; + + if (!ENABLE_HWMULT) + return false; + + if (msp430_is_interrupt_func ()) + return false; + + if (msp430_is_f5_mcu () || has_32bit_hw_mult ()) + return true; + + return false; +} + +/* This function does the same as the default, but it will replace GCC + function names with the MSPABI-specified ones. */ +void +msp430_output_labelref (FILE *file, const char *name) +{ + int i; + + for (i = 0; helper_function_name_mappings [i].gcc_name; i++) + if (strcmp (helper_function_name_mappings [i].gcc_name, name) == 0) + { + name = helper_function_name_mappings [i].ti_name; + break; + } + + /* If we have been given a specific MCU name then we may be + able to make use of its hardware multiply capabilities. */ + if (msp430_hwmult_enabled ()) + { + if (strcmp ("__mspabi_mpyi", name) == 0) + { + if (msp430_is_f5_mcu ()) + name = "__mulhi2_f5"; + else + name = "__mulhi2"; + } + else if (strcmp ("__mspabi_mpyl", name) == 0) + { + if (msp430_is_f5_mcu ()) + name = "__mulsi2_f5"; + else if (has_32bit_hw_mult ()) + name = "__mulsi2_hw32"; + else + name = "__mulsi2"; + } + } + + fputs (name, file); +} + +/* Common code for msp430_print_operand... */ + +static void +msp430_print_operand_raw (FILE * file, rtx op) +{ + HOST_WIDE_INT i; + + switch (GET_CODE (op)) + { + case REG: + fprintf (file, "%s", reg_names [REGNO (op)]); + break; + + case CONST_INT: + i = INTVAL (op); + if (TARGET_ASM_HEX) + fprintf (file, "%#" HOST_WIDE_INT_PRINT "x", i); + else + fprintf (file, "%" HOST_WIDE_INT_PRINT "d", i); + break; + + case CONST: + case PLUS: + case MINUS: + case SYMBOL_REF: + case LABEL_REF: + output_addr_const (file, op); + break; + + default: + print_rtl (file, op); + break; + } +} + +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS msp430_print_operand_addr + +/* Output to stdio stream FILE the assembler syntax for an + instruction operand that is a memory reference whose address + is ADDR. */ + +static void +msp430_print_operand_addr (FILE * file, rtx addr) +{ + switch (GET_CODE (addr)) + { + case PLUS: + msp430_print_operand_raw (file, XEXP (addr, 1)); + gcc_assert (REG_P (XEXP (addr, 0))); + fprintf (file, "(%s)", reg_names [REGNO (XEXP (addr, 0))]); + return; + + case REG: + fprintf (file, "@"); + break; + + case CONST: + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + fprintf (file, "&"); + break; + + default: + break; + } + + msp430_print_operand_raw (file, addr); +} + +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND msp430_print_operand + +/* A low 16-bits of int/lower of register pair + B high 16-bits of int/higher of register pair + C bits 32-47 of a 64-bit value/reg 3 of a DImode value + D bits 48-63 of a 64-bit value/reg 4 of a DImode value + H like %B (for backwards compatibility) + I inverse of value + J an integer without a # prefix + L like %A (for backwards compatibility) + O offset of the top of the stack + Q like X but generates an A postfix + R inverse of condition code, unsigned. + X X instruction postfix in large mode + Y value - 4 + Z value - 1 + b .B or .W or .A, depending upon the mode + p bit position + r inverse of condition code + x like X but only for pointers. */ + +static void +msp430_print_operand (FILE * file, rtx op, int letter) +{ + rtx addr; + + /* We can't use c, n, a, or l. */ + switch (letter) + { + case 'Z': + gcc_assert (CONST_INT_P (op)); + /* Print the constant value, less one. */ + fprintf (file, "#%ld", INTVAL (op) - 1); + return; + case 'Y': + gcc_assert (CONST_INT_P (op)); + /* Print the constant value, less four. */ + fprintf (file, "#%ld", INTVAL (op) - 4); + return; + case 'I': + if (GET_CODE (op) == CONST_INT) + { + /* Inverse of constants */ + int i = INTVAL (op); + fprintf (file, "%d", ~i); + return; + } + op = XEXP (op, 0); + break; + case 'r': /* Conditional jump where the condition is reversed. */ + switch (GET_CODE (op)) + { + case EQ: fprintf (file, "NE"); break; + case NE: fprintf (file, "EQ"); break; + case GEU: fprintf (file, "LO"); break; + case LTU: fprintf (file, "HS"); break; + case GE: fprintf (file, "L"); break; + case LT: fprintf (file, "GE"); break; + /* Assume these have reversed operands. */ + case GTU: fprintf (file, "HS"); break; + case LEU: fprintf (file, "LO"); break; + case GT: fprintf (file, "GE"); break; + case LE: fprintf (file, "L"); break; + default: + msp430_print_operand_raw (file, op); + break; + } + return; + case 'R': /* Conditional jump where the operands are reversed. */ + switch (GET_CODE (op)) + { + case GTU: fprintf (file, "LO"); break; + case LEU: fprintf (file, "HS"); break; + case GT: fprintf (file, "L"); break; + case LE: fprintf (file, "GE"); break; + default: + msp430_print_operand_raw (file, op); + break; + } + return; + case 'p': /* Bit position. 0 == 0x01, 3 = 0x08 etc. */ + gcc_assert (CONST_INT_P (op)); + fprintf (file, "#%d", 1 << INTVAL (op)); + return; + case 'b': + switch (GET_MODE (op)) + { + case QImode: fprintf (file, ".B"); return; + case HImode: fprintf (file, ".W"); return; + case PSImode: fprintf (file, ".A"); return; + case SImode: fprintf (file, ".A"); return; + default: + return; + } + case 'A': + case 'L': /* Low half. */ + switch (GET_CODE (op)) + { + case MEM: + op = adjust_address (op, Pmode, 0); + break; + case REG: + break; + case CONST_INT: + op = GEN_INT (INTVAL (op) & 0xffff); + letter = 0; + break; + default: + /* If you get here, figure out a test case :-) */ + gcc_unreachable (); + } + break; + case 'B': + case 'H': /* high half */ + switch (GET_CODE (op)) + { + case MEM: + op = adjust_address (op, Pmode, 2); + break; + case REG: + op = gen_rtx_REG (Pmode, REGNO (op) + 1); + break; + case CONST_INT: + op = GEN_INT (INTVAL (op) >> 16); + letter = 0; + break; + default: + /* If you get here, figure out a test case :-) */ + gcc_unreachable (); + } + break; + case 'C': + switch (GET_CODE (op)) + { + case MEM: + op = adjust_address (op, Pmode, 3); + break; + case REG: + op = gen_rtx_REG (Pmode, REGNO (op) + 2); + break; + case CONST_INT: + op = GEN_INT ((long long) INTVAL (op) >> 32); + letter = 0; + break; + default: + /* If you get here, figure out a test case :-) */ + gcc_unreachable (); + } + break; + case 'D': + switch (GET_CODE (op)) + { + case MEM: + op = adjust_address (op, Pmode, 4); + break; + case REG: + op = gen_rtx_REG (Pmode, REGNO (op) + 3); + break; + case CONST_INT: + op = GEN_INT ((long long) INTVAL (op) >> 48); + letter = 0; + break; + default: + /* If you get here, figure out a test case :-) */ + gcc_unreachable (); + } + break; + + case 'X': + /* This is used to turn, for example, an ADD opcode into an ADDX + opcode when we're using 20-bit addresses. */ + if (TARGET_LARGE) + fprintf (file, "X"); + /* We don't care which operand we use, but we want 'X' in the MD + file, so we do it this way. */ + return; + + case 'x': + /* Similarly, but only for PSImodes. BIC, for example, needs this. */ + if (TARGET_LARGE && GET_MODE (op) == PSImode) + fprintf (file, "X"); + return; + + case 'Q': + /* Likewise, for BR -> BRA. */ + if (TARGET_LARGE) + fprintf (file, "A"); + return; + + case 'O': + /* Computes the offset to the top of the stack for the current frame. + This has to be done here rather than in, say, msp430_expand_builtin() + because builtins are expanded before the frame layout is determined. */ + fprintf (file, "%d", + msp430_initial_elimination_offset (ARG_POINTER_REGNUM, STACK_POINTER_REGNUM) + - 2); + return; + + case 'J': + gcc_assert (GET_CODE (op) == CONST_INT); + case 0: + break; + default: + output_operand_lossage ("invalid operand prefix"); + return; + } + + switch (GET_CODE (op)) + { + case REG: + msp430_print_operand_raw (file, op); + break; + + case MEM: + addr = XEXP (op, 0); + msp430_print_operand_addr (file, addr); + break; + + case CONST_INT: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (letter == 0) + fprintf (file, "#"); + msp430_print_operand_raw (file, op); + break; + + case EQ: fprintf (file, "EQ"); break; + case NE: fprintf (file, "NE"); break; + case GEU: fprintf (file, "HS"); break; + case LTU: fprintf (file, "LO"); break; + case GE: fprintf (file, "GE"); break; + case LT: fprintf (file, "L"); break; + + default: + print_rtl (file, op); + break; + } +} + + +/* Frame stuff. */ + +rtx +msp430_return_addr_rtx (int count) +{ + int ra_size; + if (count) + return NULL_RTX; + + ra_size = TARGET_LARGE ? 4 : 2; + if (crtl->args.pretend_args_size) + ra_size += 2; + + return gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, arg_pointer_rtx, GEN_INT (- ra_size))); +} + +rtx +msp430_incoming_return_addr_rtx (void) +{ + return gen_rtx_MEM (Pmode, stack_pointer_rtx); +} + +/* Instruction generation stuff. */ + +/* Generate a sequence of instructions to sign-extend an HI + value into an SI value. Handles the tricky case where + we are overwriting the destination. */ + +const char * +msp430x_extendhisi (rtx * operands) +{ + if (REGNO (operands[0]) == REGNO (operands[1])) + /* Low word of dest == source word. */ + return "BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0"; /* 8-bytes. */ + + if (! msp430x) + /* Note: This sequence is approximately the same length as invoking a helper + function to perform the sign-extension, as in: + + MOV.W %1, %L0 + MOV.W %1, r12 + CALL __mspabi_srai_15 + MOV.W r12, %H0 + + but this version does not involve any function calls or using argument + registers, so it reduces register pressure. */ + return "MOV.W\t%1, %L0 { BIT.W\t#0x8000, %L0 { SUBC.W\t%H0, %H0 { INV.W\t%H0, %H0"; /* 10-bytes. */ + + if (REGNO (operands[0]) + 1 == REGNO (operands[1])) + /* High word of dest == source word. */ + return "MOV.W\t%1, %L0 { RPT\t#15 { RRAX.W\t%H0"; /* 6-bytes. */ + + /* No overlap between dest and source. */ + return "MOV.W\t%1, %L0 { MOV.W\t%1, %H0 { RPT\t#15 { RRAX.W\t%H0"; /* 8-bytes. */ +} + +/* Likewise for logical right shifts. */ +const char * +msp430x_logical_shift_right (rtx amount) +{ + /* The MSP430X's logical right shift instruction - RRUM - does + not use an extension word, so we cannot encode a repeat count. + Try various alternatives to work around this. If the count + is in a register we are stuck, hence the assert. */ + gcc_assert (CONST_INT_P (amount)); + + if (INTVAL (amount) <= 0 + || INTVAL (amount) >= 16) + return "# nop logical shift."; + + if (INTVAL (amount) > 0 + && INTVAL (amount) < 5) + return "rrum.w\t%2, %0"; /* Two bytes. */ + + if (INTVAL (amount) > 4 + && INTVAL (amount) < 9) + return "rrum.w\t#4, %0 { rrum.w\t%Y2, %0 "; /* Four bytes. */ + + /* First we logically shift right by one. Now we know + that the top bit is zero and we can use the arithmetic + right shift instruction to perform the rest of the shift. */ + return "rrum.w\t#1, %0 { rpt\t%Z2 { rrax.w\t%0"; /* Six bytes. */ +} + +struct gcc_target targetm = TARGET_INITIALIZER; + +#include "gt-msp430.h" diff --git a/gcc-4.9/gcc/config/msp430/msp430.h b/gcc-4.9/gcc/config/msp430/msp430.h new file mode 100644 index 000000000..65d6ad66d --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430.h @@ -0,0 +1,411 @@ +/* GCC backend definitions for the TI MSP430 Processor + Copyright (C) 2012-2014 Free Software Foundation, Inc. + Contributed by Red Hat. + + 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/>. */ + + +/* Run-time Target Specification */ + +/* True if the MSP430x extensions are enabled. */ +#ifndef IN_LIBGCC2 +extern bool msp430x; +#endif + +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("NO_TRAMPOLINES"); \ + builtin_define ("__MSP430__"); \ + builtin_define (msp430_mcu_name ()); \ + if (msp430x) \ + { \ + builtin_define ("__MSP430X__"); \ + builtin_assert ("cpu=MSP430X"); \ + if (TARGET_LARGE) \ + builtin_define ("__MSP430X_LARGE__"); \ + } \ + else \ + builtin_assert ("cpu=MSP430"); \ + } \ + while (0) + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{minrt:crt0-minrt.o%s}%{!minrt:crt0.o%s}} %{!minrt:crtbegin.o%s}" + +/* -lgcc is included because crtend.o needs __mspabi_func_epilog_1. */ +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "%{!minrt:crtend.o%s} %{minrt:crtn-minrt.o%s}%{!minrt:crtn.o%s} -lgcc" + +#define ASM_SPEC "-mP " /* Enable polymorphic instructions. */ \ + "%{mcpu=*:-mcpu=%*}%{!mcpu=*:%{mmcu=*:-mmcu=%*}} " /* Pass the CPU type on to the assembler. */ \ + "%{mrelax=-mQ} " /* Pass the relax option on to the assembler. */ \ + "%{mlarge:-ml} " /* Tell the assembler if we are building for the LARGE pointer model. */ \ + "%{!msim:-md} %{msim:%{mlarge:-md}}" /* Copy data from ROM to RAM if necessary. */ \ + "%{ffunction-sections:-gdwarf-sections}" /* If function sections are being created then create DWARF line number sections as well. */ + +/* Enable linker section garbage collection by default, unless we + are creating a relocatable binary (gc does not work) or debugging + is enabled (the GDB testsuite relies upon unused entities not being deleted). */ +#define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}}" + +#undef LIB_SPEC +#define LIB_SPEC " \ +--start-group \ +-lc \ +-lgcc \ +-lcrt \ +%{msim:-lsim} \ +%{!msim:-lnosys} \ +--end-group \ +%{!T*:%{!msim:%{mmcu=*:--script=%*.ld}}} \ +%{!T*:%{!msim:%{!mmcu=*:%Tmsp430.ld}}} \ +%{!T*:%{msim:%{mlarge:%Tmsp430xl-sim.ld}%{!mlarge:%Tmsp430-sim.ld}}} \ +" + +/* Storage Layout */ + +#define BITS_BIG_ENDIAN 0 +#define BYTES_BIG_ENDIAN 0 +#define WORDS_BIG_ENDIAN 0 + + +#ifdef IN_LIBGCC2 +/* This is to get correct SI and DI modes in libgcc2.c (32 and 64 bits). */ +#define UNITS_PER_WORD 4 +/* We have a problem with libgcc2. It only defines two versions of + each function, one for "int" and one for "long long". Ie it assumes + that "sizeof (int) == sizeof (long)". For the MSP430 this is not true + and we need a third set of functions. We explicitly define + LIBGCC2_UNITS_PER_WORD here so that it is clear that we are expecting + to get the SI and DI versions from the libgcc2.c sources, and we + provide our own set of HI functions, which is why this + definition is surrounded by #ifndef..#endif. */ +#ifndef LIBGCC2_UNITS_PER_WORD +#define LIBGCC2_UNITS_PER_WORD 4 +#endif +#else +/* Actual width of a word, in units (bytes). */ +#define UNITS_PER_WORD 2 +#endif + +#define SHORT_TYPE_SIZE 16 +#define INT_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 /*DOUBLE_TYPE_SIZE*/ + +#define LIBGCC2_HAS_DF_MODE 1 +#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64 + +#define DEFAULT_SIGNED_CHAR 0 + +#define STRICT_ALIGNMENT 1 +#define FUNCTION_BOUNDARY 16 +#define BIGGEST_ALIGNMENT 16 +#define STACK_BOUNDARY 16 +#define PARM_BOUNDARY 8 +#define PCC_BITFIELD_TYPE_MATTERS 1 + +#define STACK_GROWS_DOWNWARD 1 +#define FRAME_GROWS_DOWNWARD 1 +#define FIRST_PARM_OFFSET(FNDECL) 0 + +#define MAX_REGS_PER_ADDRESS 1 + +#define Pmode (TARGET_LARGE ? PSImode : HImode) +/* Note: 32 is a lie. Large pointers are actually 20-bits wide. But gcc + thinks that any non-power-of-2 pointer size equates to BLKmode, which + causes all kinds of problems... */ +#define POINTER_SIZE (TARGET_LARGE ? 32 : 16) +#define POINTERS_EXTEND_UNSIGNED 1 + +#define ADDR_SPACE_NEAR 1 +#define ADDR_SPACE_FAR 2 + +#define REGISTER_TARGET_PRAGMAS() msp430_register_pragmas() + +#if 1 /* XXX */ +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. */ + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 2) \ + (MODE) = HImode; +#endif + +/* Layout of Source Language Data Types */ + +#undef SIZE_TYPE +#define SIZE_TYPE (TARGET_LARGE ? "long unsigned int" : "unsigned int") +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE (TARGET_LARGE ? "long int" : "int") +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD +#define FUNCTION_MODE HImode +#define CASE_VECTOR_MODE Pmode +#define HAS_LONG_COND_BRANCH 0 +#define HAS_LONG_UNCOND_BRANCH 0 + +#define LOAD_EXTEND_OP(M) ZERO_EXTEND +/*#define WORD_REGISTER_OPERATIONS 1*/ + +#define MOVE_MAX 8 +#define STARTING_FRAME_OFFSET 0 + +#define INCOMING_RETURN_ADDR_RTX \ + msp430_incoming_return_addr_rtx () + +#define RETURN_ADDR_RTX(COUNT, FA) \ + msp430_return_addr_rtx (COUNT) + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define SLOW_BYTE_ACCESS 0 + + +/* Register Usage */ + +/* gas doesn't recognize PC (R0), SP (R1), and SR (R2) as register + names. */ +#define REGISTER_NAMES \ +{ \ + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", \ + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", \ + "argptr" \ +} + +enum reg_class +{ + NO_REGS, + R12_REGS, + R13_REGS, + GEN_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "R12_REGS", \ + "R13_REGS", \ + "GEN_REGS", \ + "ALL_REGS" \ +} + +#define REG_CLASS_CONTENTS \ +{ \ + 0x00000000, \ + 0x00001000, \ + 0x00002000, \ + 0x0000fff2, \ + 0x0001ffff \ +} + +#define GENERAL_REGS GEN_REGS +#define BASE_REG_CLASS GEN_REGS +#define INDEX_REG_CLASS GEN_REGS +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define PC_REGNUM 0 +#define STACK_POINTER_REGNUM 1 +#define CC_REGNUM 2 +#define FRAME_POINTER_REGNUM 4 /* not usually used, call preserved */ +#define ARG_POINTER_REGNUM 16 +#define STATIC_CHAIN_REGNUM 5 /* FIXME */ + +#define FIRST_PSEUDO_REGISTER 17 + +#define REGNO_REG_CLASS(REGNO) ((REGNO) < 17 \ + ? GEN_REGS : NO_REGS) + +#define TRAMPOLINE_SIZE 4 /* FIXME */ +#define TRAMPOLINE_ALIGNMENT 16 /* FIXME */ + +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM }, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = msp430_initial_elimination_offset ((FROM), (TO)) + + +#define FUNCTION_ARG_REGNO_P(N) ((N) >= 8 && (N) < ARG_POINTER_REGNUM) +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* 1 == register can't be used by gcc, in general + 0 == register can be used by gcc, in general */ +#define FIXED_REGISTERS \ +{ \ + 1,0,1,1, 0,0,0,0, \ + 0,0,0,0, 0,0,0,0, \ + 1, \ +} + +/* 1 == value changes across function calls + 0 == value is the same after a call */ +/* R4 through R10 are callee-saved */ +#define CALL_USED_REGISTERS \ +{ \ + 1,0,1,1, 0,0,0,0, \ + 0,0,0,1, 1,1,1,1, \ + 1, \ +} + +#define REG_ALLOC_ORDER \ + { 12, 13, 14, 15, 10, 9, 8, 7, 6, 5, 4, 11, 0, 1, 2, 3, 16 } +/* { 11, 15, 14, 13, 12, 10, 9, 8, 7, 6, 5, 4, 0, 1, 2, 3, 16 }*/ + +#define REGNO_OK_FOR_BASE_P(regno) 1 +#define REGNO_OK_FOR_INDEX_P(regno) 1 + + + +typedef struct +{ + /* These two are the current argument status. */ + char reg_used[4]; +#define CA_FIRST_REG 12 + char can_split; + /* These two are temporaries used internally. */ + char start_reg; + char reg_count; + char mem_count; + char special_p; +} CUMULATIVE_ARGS; + +#define INIT_CUMULATIVE_ARGS(CA, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ + msp430_init_cumulative_args (&CA, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) + + +/* FIXME */ +#define NO_PROFILE_COUNTERS 1 +#define PROFILE_BEFORE_PROLOGUE 1 + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tcall\t__mcount\n"); + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + msp430_hard_regno_nregs (REGNO, MODE) + +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + msp430_hard_regno_mode_ok (REGNO, MODE) + +#define MODES_TIEABLE_P(MODE1, MODE2) \ + msp430_modes_tieable_p (MODE1, MODE2) + +/* Exception Handling */ + +/* R12,R13,R14 - EH data + R15 - stack adjustment */ + +#define EH_RETURN_DATA_REGNO(N) \ + (((N) < 3) ? ((N) + 12) : INVALID_REGNUM) + +#define EH_RETURN_HANDLER_RTX \ + gen_rtx_MEM(Pmode, gen_rtx_PLUS (Pmode, gen_rtx_REG(Pmode, SP_REGNO), gen_rtx_REG (Pmode, 15))) + +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 15) + +#define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) DW_EH_PE_udata4 + + +/* Stack Layout and Calling Conventions */ + + +/* Addressing Modes */ + + + +#define TEXT_SECTION_ASM_OP ".text" +#define DATA_SECTION_ASM_OP ".data" +#define BSS_SECTION_ASM_OP "\t.section .bss" + +#define ASM_COMMENT_START " ;" +#define ASM_APP_ON "" +#define ASM_APP_OFF "" +#define LOCAL_LABEL_PREFIX ".L" +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" + +#define GLOBAL_ASM_OP "\t.global\t" + +#define ASM_OUTPUT_LABELREF(FILE, SYM) msp430_output_labelref ((FILE), (SYM)) + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + fprintf (FILE, "\t.long .L%d\n", VALUE) + +/* This is how to output an element of a case-vector that is relative. + Note: The local label referenced by the "3b" below is emitted by + the tablejump insn. */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t.long .L%d - 1b\n", VALUE) + + +#define ASM_OUTPUT_ALIGN(STREAM, LOG) \ + do \ + { \ + if ((LOG) == 0) \ + break; \ + fprintf (STREAM, "\t.balign %d\n", 1 << (LOG)); \ + } \ + while (0) + +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +#undef DWARF2_ADDR_SIZE +#define DWARF2_ADDR_SIZE 4 + +#define INCOMING_FRAME_SP_OFFSET (POINTER_SIZE / BITS_PER_UNIT) + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#define DWARF2_ASM_LINE_DEBUG_INFO 1 + +/* Prevent reload (and others) from choosing HImode stack slots + when spilling hard registers when they may contain PSImode values. */ +#define HARD_REGNO_CALLER_SAVE_MODE(REGNO,NREGS,MODE) \ + ((TARGET_LARGE && ((NREGS) <= 2)) ? PSImode : choose_hard_reg_mode ((REGNO), (NREGS), false)) + +/* Also stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)). */ +#define CANNOT_CHANGE_MODE_CLASS(FROM,TO,CLASS) \ + ( ((TO) == PSImode && (FROM) == SImode) \ + || ((TO) == SImode && (FROM) == PSImode) \ + || ((TO) == DImode && (FROM) == PSImode) \ + || ((TO) == PSImode && (FROM) == DImode) \ + ) + +#define ACCUMULATE_OUTGOING_ARGS 1 + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + msp430_start_function ((FILE), (NAME), (DECL)) + +#define TARGET_HAS_NO_HW_DIVIDE (! TARGET_HWMULT) diff --git a/gcc-4.9/gcc/config/msp430/msp430.md b/gcc-4.9/gcc/config/msp430/msp430.md new file mode 100644 index 000000000..c0c97dae6 --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430.md @@ -0,0 +1,1370 @@ +;; Machine Description for TI MSP43* processors +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. +;; Contributed by Red Hat. + +;; 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_constants + [ + (PC_REGNO 0) + (SP_REGNO 1) + (CARRY 2) + ]) + +(define_c_enum "unspec" + [ + UNS_PROLOGUE_START_MARKER + UNS_PROLOGUE_END_MARKER + UNS_EPILOGUE_START_MARKER + UNS_EPILOGUE_HELPER + + UNS_PUSHM + UNS_POPM + + UNS_GROW_AND_SWAP + UNS_SWAP_AND_SHRINK + + UNS_DINT + UNS_EINT + UNS_PUSH_INTR + UNS_POP_INTR + UNS_BIC_SR + UNS_BIS_SR + + UNS_REFSYM_NEED_EXIT + ]) + +(include "predicates.md") +(include "constraints.md") + +(define_mode_iterator QHI [QI HI PSI]) + +;; There are two basic "family" tests we do here: +;; +;; msp430x - true if 430X instructions are available. +;; TARGET_LARGE - true if pointers are 20-bits +;; +;; Note that there are three supported cases, since the base 430 +;; doesn't have 20-bit pointers: +;; +;; 1. MSP430 cpu, small model +;; 2. MSP430X cpu, small model. +;; 3. MSP430X cpu, large model. + +;;------------------------------------------------------------ +;; Moves + +;; Push/Pop must be before the generic move patterns + +(define_insn "push" + [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNO))) + (match_operand:HI 0 "register_operand" "r"))] + "" + "PUSH\t%0" + ) + +(define_insn "pusha" + [(set (mem:PSI (pre_dec:PSI (reg:PSI SP_REGNO))) + (match_operand:PSI 0 "register_operand" "r"))] + "TARGET_LARGE" + "PUSHX.A\t%0" + ) + +(define_insn "pushm" + [(unspec_volatile [(match_operand 0 "register_operand" "r") + (match_operand 1 "immediate_operand" "n")] UNS_PUSHM)] + "" + "PUSHM%b0\t%1, %0" + ) + +(define_insn "pop" + [(set (match_operand:HI 0 "register_operand" "=r") + (mem:HI (post_inc:HI (reg:HI SP_REGNO))))] + "" + "POP\t%0" + ) + +(define_insn "popa" + [(set (match_operand:PSI 0 "register_operand" "=r") + (mem:PSI (post_inc:PSI (reg:PSI SP_REGNO))))] + "TARGET_LARGE" + "POPX.A\t%0" + ) + +;; This is nasty. Operand0 is bogus. It is only there so that we can get a +;; mode for the %b0 to work. We should use operand1 for this, but that does +;; not have a mode. +;; +;; Operand1 is actually a register, but we cannot accept (REG...) because the +;; cprop_hardreg pass can and will renumber registers even inside +;; unspec_volatiles. So we take an integer register number parameter and +;; fudge it to be a register name when we generate the assembler. +;; +;; The pushm pattern does not have this problem because of all of the +;; frame info cruft attached to it, so cprop_hardreg leaves it alone. +(define_insn "popm" + [(unspec_volatile [(match_operand 0 "register_operand" "r") + (match_operand 1 "immediate_operand" "i") + (match_operand 2 "immediate_operand" "i")] UNS_POPM)] + "" + "POPM%b0\t%2, r%J1" + ) + +;; The next two patterns are here to support a "feature" of how GCC implements +;; varargs. When a function uses varargs and the *second* to last named +;; argument is split between argument registers and the stack, gcc expects the +;; callee to allocate space on the stack that can contain the register-based +;; part of the argument. This space *has* to be just before the remaining +;; arguments (ie the ones that are fully on the stack). +;; +;; The problem is that the MSP430 CALL instruction pushes the return address +;; onto the stack in the exact place where the callee wants to allocate +;; this extra space. So we need a sequence of instructions that can allocate +;; the extra space and then move the return address down the stack, so that +;; the extra space is now adjacent to the remaining arguments. +;; +;; This could be constructed through regular insns, but they might be split up +;; by a misguided optimization, so an unspec volatile is used instead. + +(define_insn "grow_and_swap" + [(unspec_volatile [(const_int 0)] UNS_GROW_AND_SWAP)] + "" + "* + if (TARGET_LARGE) + return \"SUBA\t#2, r1 { MOVX.A\t2(r1), 0(r1)\"; + return \"SUB\t#2, r1 { MOV.W\t2(r1), 0(r1)\"; + " +) + +(define_insn "swap_and_shrink" + [(unspec_volatile [(const_int 0)] UNS_SWAP_AND_SHRINK)] + "" + "* return TARGET_LARGE + ? \"MOVX.A\t0(r1), 2(r1) { ADDA\t#2, SP\" + : \"MOV.W\t0(r1), 2(r1) { ADD\t#2, SP\"; + ") + +; I set LOAD_EXTEND_OP and WORD_REGISTER_OPERATIONS, but gcc puts in a +; zero_extend anyway. Catch it here. +(define_insn "movqihi" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (zero_extend:HI (match_operand:QI 1 "memory_operand" "Ys,m")))] + "" + "@ + MOV.B\t%1, %0 + MOV%X1.B\t%1, %0" +) + +(define_insn "movqi" + [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYs,rm") + (match_operand:QI 1 "msp_general_operand" "riYs,rmi"))] + "" + "@ + MOV.B\t%1, %0 + MOV%X0.B\t%1, %0" +) + +(define_insn "movhi" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,rm") + (match_operand:HI 1 "msp_general_operand" "riYs,rmi"))] + "" + "@ + MOV.W\t%1, %0 + MOV%X0.W\t%1, %0" +) + +(define_expand "movsi" + [(set (match_operand:SI 0 "nonimmediate_operand") + (match_operand:SI 1 "general_operand"))] + "" + "" + ) + +(define_insn_and_split "movsi_x" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (match_operand:SI 1 "general_operand" "rmi"))] + "" + "#" + "reload_completed" + [(set (match_operand:HI 2 "nonimmediate_operand") + (match_operand:HI 4 "general_operand")) + (set (match_operand:HI 3 "nonimmediate_operand") + (match_operand:HI 5 "general_operand"))] + "msp430_split_movsi (operands);" +) + +;; Some MOVX.A cases can be done with MOVA, this is only a few of them. +(define_insn "movpsi" + [(set (match_operand:PSI 0 "msp_nonimmediate_operand" "=r,Ya,rm") + (match_operand:PSI 1 "msp_general_operand" "riYa,r,rmi"))] + "" + "@ + MOV%Q0\t%1, %0 + MOV%Q0\t%1, %0 + MOV%X0.%Q0\t%1, %0") + +; This pattern is identical to the truncsipsi2 pattern except +; that it uses a SUBREG instead of a TRUNC. It is needed in +; order to prevent reload from converting (set:SI (SUBREG:PSI (SI))) +; into (SET:PSI (PSI)). +; +; Note: using POPM.A #1 is two bytes smaller than using POPX.A.... + +(define_insn "movsipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (subreg:PSI (match_operand:SI 1 "register_operand" "r") 0))] + "TARGET_LARGE" + "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A #1, %0 ; Move reg-pair %L1:%H1 into pointer %0" +) + +;;------------------------------------------------------------ +;; Math + +(define_insn "addpsi3" + [(set (match_operand:PSI 0 "msp_nonimmediate_operand" "=r,rm") + (plus:PSI (match_operand:PSI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:PSI 2 "msp_general_operand" "rLs,rmi")))] + "" + "@ + ADDA\t%2, %0 + ADDX.A\t%2, %0" +) + +(define_insn "addqi3" + [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYs,rm") + (plus:QI (match_operand:QI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:QI 2 "msp_general_operand" "riYs,rmi")))] + "" + "@ + ADD.B\t%2, %0 + ADD%X0.B\t%2, %0" +) + +(define_insn "addhi3" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,rm") + (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:HI 2 "msp_general_operand" "riYs,rmi")))] + "" + "@ + ADD.W\t%2, %0 + ADD%X0.W\t%2, %0" +) + +; This pattern is needed in order to avoid reload problems. +; It takes an SI pair of registers, adds a value to them, and +; then converts them into a single PSI register. + +(define_insn "addsipsi3" + [(set (subreg:SI (match_operand:PSI 0 "register_operand" "=&r") 0) + (plus:SI (match_operand:SI 1 "register_operand" "0") + (match_operand 2 "general_operand" "rmi")))] + "" + "ADD.W\t%L2, %L0 { ADDC.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0" +) + +(define_insn "addsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,rm") + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "r,mi")))] + "" + "@ + ADD\t%L2, %L0 { ADDC\t%H2, %H0 + ADD%X0\t%L2, %L0 { ADDC%X0\t%H2, %H0" +) + +; Version of addhi that exposes the carry operations, for SImode adds. +; +; NOTE - we are playing a dangerous game with GCC here. We have these two +; add patterns and the splitter that follows because our tests have shown +; that this results in a significant reduction in code size - because GCC is +; able to discard any unused part of the addition. We have to annotate the +; patterns with the set and use of the carry flag because otherwise GCC will +; discard parts of the addition when they are actually needed. But we have +; not annotated all the other patterns that set the CARRY flag as doing so +; results in an overall increase in code size[1]. Instead we just *hope* +; that GCC will not move a carry-setting instruction in between the first +; and second adds. +; +; So far our experiments have shown that GCC is likely to move MOV and CMP +; instructions in between the two adds, but not other instructions. MOV is +; safe, CMP is not. So we have annotated the CMP patterns and left the +; subtract, shift and other add patterns alone. At the moment this is +; working, but with future changes to the generic parts of GCC that might +; change. +; +; [1] It is not clear exactly why the code size increases. The cause appears +; to be that reload is more prevelent to spilling a variable onto the stack +; but why it does this is unknown. Possibly the additional CLOBBERs necessary +; to correctly annotate the other patterns makes reload think that there is +; increased register pressure. Or possibly reload does not handle ADD patterns +; that are not single_set() very well. + +(define_insn "addhi3_cy" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rm") + (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:HI 2 "msp_nonimmediate_operand" "r,rm"))) + (set (reg:BI CARRY) + (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1)) + (zero_extend:SI (match_dup 2))) + (const_int 16)))) + ] + "" + "@ + ADD\t%2, %1 ; cy + ADD%X0\t%2, %1 ; cy" + ) + +(define_insn "addhi3_cy_i" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,rm") + (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0") + (match_operand:HI 2 "immediate_operand" "i,i"))) + (set (reg:BI CARRY) + (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1)) + (match_operand 3 "immediate_operand" "i,i")) + (const_int 16)))) + ] + "" + "@ + ADD\t%2, %1 ; cy + ADD%X0\t%2, %1 ; cy" + ) + +; Version of addhi that adds the carry, for SImode adds. +(define_insn "addchi4_cy" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rm") + (plus:HI (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:HI 2 "msp_general_operand" "ri,rmi")) + (zero_extend:HI (reg:BI CARRY)))) + ] + "" + "@ + ADDC\t%2, %1 + ADDC%X0\t%2, %1" + ) + +; Split an SImode add into two HImode adds, keeping track of the carry +; so that gcc knows when it can and can't optimize away the two +; halves. +(define_split + [(set (match_operand:SI 0 "msp430_nonsubreg_operand") + (plus:SI (match_operand:SI 1 "nonimmediate_operand") + (match_operand:SI 2 "general_operand"))) + ] + "" + [(parallel [(set (match_operand:HI 3 "nonimmediate_operand" "=&rm") + (plus:HI (match_dup 4) + (match_dup 5))) + (set (reg:BI CARRY) + (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 4)) + (match_dup 9)) + (const_int 16)))) + ]) + (set (match_operand:HI 6 "nonimmediate_operand" "=&rm") + (plus:HI (plus:HI (match_dup 7) + (match_dup 8)) + (zero_extend:HI (reg:BI CARRY)))) + ] + " + operands[3] = msp430_subreg (HImode, operands[0], SImode, 0); + operands[4] = msp430_subreg (HImode, operands[1], SImode, 0); + operands[5] = msp430_subreg (HImode, operands[2], SImode, 0); + operands[6] = msp430_subreg (HImode, operands[0], SImode, 2); + operands[7] = msp430_subreg (HImode, operands[1], SImode, 2); + operands[8] = msp430_subreg (HImode, operands[2], SImode, 2); + if (GET_CODE (operands[5]) == CONST_INT) + { + operands[9] = GEN_INT (INTVAL (operands[5]) & 0xffff); + } + else + { + operands[9] = gen_rtx_ZERO_EXTEND (SImode, operands[5]); + } + " + ) + + +;; Alternatives 2 and 3 are to handle cases generated by reload. +(define_insn "subpsi3" + [(set (match_operand:PSI 0 "nonimmediate_operand" "=r, rm, &?r, ?&r") + (minus:PSI (match_operand:PSI 1 "general_operand" "0, 0, !r, !i") + (match_operand:PSI 2 "general_operand" "rLs, rmi, rmi, r")))] + "" + "@ + SUBA\t%2, %0 + SUBX.A\t%2, %0 + MOVX.A\t%1, %0 { SUBX.A\t%2, %0 + MOVX.A\t%1, %0 { SUBA\t%2, %0" +) + +;; Alternatives 2 and 3 are to handle cases generated by reload. +(define_insn "subqi3" + [(set (match_operand:QI 0 "nonimmediate_operand" "=rYs, rm, &?r, ?&r") + (minus:QI (match_operand:QI 1 "general_operand" "0, 0, !r, !i") + (match_operand:QI 2 "general_operand" " riYs, rmi, rmi, r")))] + "" + "@ + SUB.B\t%2, %0 + SUB%X0.B\t%2, %0 + MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0 + MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0" +) + +;; Alternatives 2 and 3 are to handle cases generated by reload. +(define_insn "subhi3" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rYs, rm, &?r, ?&r") + (minus:HI (match_operand:HI 1 "general_operand" "0, 0, !r, !i") + (match_operand:HI 2 "general_operand" " riYs, rmi, rmi, r")))] + "" + "@ + SUB.W\t%2, %0 + SUB%X0.W\t%2, %0 + MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0 + MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0" +) + +(define_insn "subsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&rm") + (minus:SI (match_operand:SI 1 "nonimmediate_operand" "0") + (match_operand:SI 2 "general_operand" "rmi")))] + "" + "SUB%X0\t%L2, %L0 { SUBC%X0\t%H2, %H0" +) + +(define_insn "*bic<mode>_cg" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,m") + (and:QHI (match_operand:QHI 1 "msp_general_operand" "0,0") + (match_operand 2 "msp430_inv_constgen_operator" "n,n")))] + "" + "@ + BIC%x0%b0\t#%I2, %0 + BIC%X0%b0\t#%I2, %0" +) + +(define_insn "bic<mode>3" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm") + (and:QHI (not:QHI (match_operand:QHI 1 "msp_general_operand" "rYs,rmn")) + (match_operand:QHI 2 "msp_nonimmediate_operand" "0,0")))] + "" + "@ + BIC%x0%b0\t%1, %0 + BIC%X0%b0\t%1, %0" +) + +(define_insn "and<mode>3" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm") + (and:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))] + "" + "@ + AND%x0%b0\t%2, %0 + AND%X0%b0\t%2, %0" +) + +(define_insn "ior<mode>3" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm") + (ior:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))] + "" + "@ + BIS%x0%b0\t%2, %0 + BIS%X0%b0\t%2, %0" +) + +(define_insn "xor<mode>3" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,rm") + (xor:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0") + (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))] + "" + "@ + XOR%x0%b0\t%2, %0 + XOR%X0%b0\t%2, %0" +) + +;; Macro : XOR #~0, %0 +(define_insn "one_cmpl<mode>2" + [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=rYs,m") + (not:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "0,0")))] + "" + "@ + INV%x0%b0\t%0 + INV%X0%b0\t%0" +) + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,m") + (sign_extend:HI (match_operand:QI 1 "msp_nonimmediate_operand" "0,0")))] + "" + "@ + SXT%X0\t%0 + SXT%X0\t%0" +) + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rYs,m") + (zero_extend:HI (match_operand:QI 1 "msp_nonimmediate_operand" "0,0")))] + "" + "@ + AND\t#0xff, %0 + AND%X0\t#0xff, %0" +) + +;; Eliminate extraneous zero-extends mysteriously created by gcc. +(define_peephole2 + [(set (match_operand:HI 0 "register_operand") + (zero_extend:HI (match_operand:QI 1 "general_operand"))) + (set (match_operand:HI 2 "register_operand") + (zero_extend:HI (match_operand:QI 3 "register_operand")))] + "REGNO (operands[0]) == REGNO (operands[2]) && REGNO (operands[2]) == REGNO (operands[3])" + [(set (match_dup 0) + (zero_extend:HI (match_dup 1)))] +) + +(define_insn "zero_extendhipsi2" + [(set (match_operand:PSI 0 "msp_nonimmediate_operand" "=r,m") + (zero_extend:PSI (match_operand:HI 1 "msp_nonimmediate_operand" "rm,r")))] + "" + "MOVX\t%1, %0" +) + +(define_insn "truncpsihi2" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rm") + (truncate:HI (match_operand:PSI 1 "register_operand" "r")))] + "" + "MOVX\t%1, %0" +) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))] + "" + { return msp430x_extendhisi (operands); } +) + +(define_insn "extendhipsi2" + [(set (match_operand:PSI 0 "nonimmediate_operand" "=r") + (subreg:PSI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")) 0))] + "TARGET_LARGE" + "RLAM #4, %0 { RRAM #4, %0" +) + +;; Look for cases where integer/pointer conversions are suboptimal due +;; to missing patterns, despite us not having opcodes for these +;; patterns. Doing these manually allows for alternate optimization +;; paths. +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")))] + "TARGET_LARGE" + "MOV.W\t#0,%H0" +) + +(define_insn "zero_extendhisipsi2" + [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r") + (subreg:PSI (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,r")) 0))] + "TARGET_LARGE" + "@ + AND.W\t#-1,%0 + MOV.W\t%1,%0" +) + +(define_insn "extend_and_shift1_hipsi2" + [(set (subreg:SI (match_operand:PSI 0 "nonimmediate_operand" "=r") 0) + (ashift:SI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")) + (const_int 1)))] + "TARGET_LARGE" + "RLAM #4, %0 { RRAM #3, %0" +) + +(define_insn "extend_and_shift2_hipsi2" + [(set (subreg:SI (match_operand:PSI 0 "nonimmediate_operand" "=r") 0) + (ashift:SI (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0")) + (const_int 2)))] + "TARGET_LARGE" + "RLAM #4, %0 { RRAM #2, %0" +) + +; Nasty - we are sign-extending a 20-bit PSI value in one register into +; two adjacent 16-bit registers to make an SI value. There is no MSP430X +; instruction that will do this, so we push the 20-bit value onto the stack +; and then pop it off as two 16-bit values. +; +; FIXME: The MSP430X documentation does not specify if zero-extension or +; sign-extension happens when the 20-bit value is pushed onto the stack. +; It is probably zero-extension, but if not this pattern will not work +; when the PSI value is negative.. +; +; Note: using PUSHM.A #1 is two bytes smaller than using PUSHX.A.... + +(define_insn "zero_extendpsisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:PSI 1 "register_operand" "r")))] + "" + "* + if (REGNO (operands[1]) == SP_REGNO) + /* If the source register is the stack pointer, the value + stored in the stack slot will be the value *after* the + stack pointer has been decremented. So allow for that + here. */ + return \"PUSHM.A\t#1, %1 { ADDX.W\t#4, @r1 { POPX.W\t%L0 { POPX.W\t%H0 ; get stack pointer into %L0:%H0\"; + else + return \"PUSHM.A\t#1, %1 { POPX.W\t%L0 { POPX.W\t%H0 ; move pointer in %1 into reg-pair %L0:%H0\"; + " +) + +;; We also need to be able to sign-extend pointer types (eg ptrdiff_t). +;; Since (we assume) pushing a 20-bit value onto the stack zero-extends +;; it, we use a different method here. + +(define_insn "extendpsisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:PSI 1 "register_operand" "r")))] + "TARGET_LARGE" + "* + /* The intention here is that we copy the bottom 16-bits of + %1 into %L0 (zeroing the top four bits). Then we copy the + entire 20-bits of %1 into %H0 and then arithmetically shift + it right by 16 bits, to get the top four bits of the pointer + sign-extended in %H0. */ + if (REGNO (operands[0]) == REGNO (operands[1])) + return \"MOVX.A\t%1, %H0 { MOV.W\t%1, %L0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\"; + else + return \"MOV.W\t%1, %L0 { MOVX.A\t%1, %H0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\"; + " +) + +; See the movsipsi2 pattern above for another way that GCC performs this +; conversion. +(define_insn "truncsipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (truncate:PSI (match_operand:SI 1 "register_operand" "r")))] + "" + "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A\t#1, %L0" +) + +;;------------------------------------------------------------ +;; Shift Functions + +;; Note: We do not use the RPT ... SHIFT instruction sequence +;; when the repeat count is in a register, because even though RPT +;; accepts counts in registers, it does not work if the count is +;; zero, and the actual count in the register has to be one less +;; than the required number of iterations. We could encode a +;; seqeunce like this: +;; +;; bit #0xf, Rn +;; bz 1f +;; dec Rn +;; rpt Rn +;; <shift> Rm +;; inc Rn +;; 1: +;; +;; But is longer than calling a helper function, and we are mostly +;; concerned with code size. FIXME: Maybe enable a sequence like +;; this at -O3 and above ? +;; +;; Note - we ignore shift counts of less than one or more than 15. +;; This is permitted by the ISO C99 standard as such shifts result +;; in "undefined" behaviour. [6.5.7 (3)] + +;; signed A << C + +(define_expand "ashlhi3" + [(set (match_operand:HI 0 "nonimmediate_operand") + (ashift:HI (match_operand:HI 1 "general_operand") + (match_operand:HI 2 "general_operand")))] + "" + { + if (msp430x + && REG_P (operands[0]) + && REG_P (operands[1]) + && CONST_INT_P (operands[2])) + emit_insn (gen_430x_shift_left (operands[0], operands[1], operands[2])); + else + msp430_expand_helper (operands, \"__mspabi_slli\", true); + DONE; + } +) + +(define_insn "slli_1" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") + (ashift:HI (match_operand:HI 1 "general_operand" "0") + (const_int 1)))] + "" + "RLA.W\t%0" ;; Note - this is a macro for ADD +) + +(define_insn "430x_shift_left" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashift:HI (match_operand:HI 1 "register_operand" "0") + (match_operand 2 "immediate_operand" "n")))] + "msp430x" + "* + if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 16) + return \"rpt\t%2 { rlax.w\t%0\"; + return \"# nop left shift\"; + " +) + +(define_insn "slll_1" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (ashift:SI (match_operand:SI 1 "general_operand" "0") + (const_int 1)))] + "" + "RLA.W\t%L0 { RLC.W\t%H0" +) + +(define_insn "slll_2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (ashift:SI (match_operand:SI 1 "general_operand" "0") + (const_int 2)))] + "" + "RLA.W\t%L0 { RLC.W\t%H0 { RLA.W\t%L0 { RLC.W\t%H0" +) + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "nonimmediate_operand") + (ashift:SI (match_operand:SI 1 "general_operand") + (match_operand:SI 2 "general_operand")))] + "" + "msp430_expand_helper (operands, \"__mspabi_slll\", true); + DONE;" +) + +;;---------- + +;; signed A >> C + +(define_expand "ashrhi3" + [(set (match_operand:HI 0 "nonimmediate_operand") + (ashiftrt:HI (match_operand:HI 1 "general_operand") + (match_operand:HI 2 "general_operand")))] + "" + { + if (msp430x + && REG_P (operands[0]) + && REG_P (operands[1]) + && CONST_INT_P (operands[2])) + emit_insn (gen_430x_arithmetic_shift_right (operands[0], operands[1], operands[2])); + else + msp430_expand_helper (operands, \"__mspabi_srai\", true); + DONE; + } +) + +(define_insn "srai_1" + [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=rm") + (ashiftrt:HI (match_operand:HI 1 "msp_general_operand" "0") + (const_int 1)))] + "" + "RRA.W\t%0" +) + +(define_insn "430x_arithmetic_shift_right" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand 2 "immediate_operand" "n")))] + "msp430x" + "* + if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 16) + return \"rpt\t%2 { rrax.w\t%0\"; + return \"# nop arith right shift\"; + " +) + +(define_insn "srap_1" + [(set (match_operand:PSI 0 "register_operand" "=r") + (ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0") + (const_int 1)))] + "msp430x" + "RRAM.A #1,%0" +) + +(define_insn "srap_2" + [(set (match_operand:PSI 0 "register_operand" "=r") + (ashiftrt:PSI (match_operand:PSI 1 "general_operand" "0") + (const_int 2)))] + "msp430x" + "RRAM.A #2,%0" +) + +(define_insn "sral_1" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") + (const_int 1)))] + "" + "RRA.W\t%H0 { RRC.W\t%L0" +) + +(define_insn "sral_2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") + (const_int 2)))] + "" + "RRA.W\t%H0 { RRC.W\t%L0 { RRA.W\t%H0 { RRC.W\t%L0" +) + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "nonimmediate_operand") + (ashiftrt:SI (match_operand:SI 1 "general_operand") + (match_operand:SI 2 "general_operand")))] + "" + "msp430_expand_helper (operands, \"__mspabi_sral\", true); + DONE;" +) + +;;---------- + +;; unsigned A >> C + +(define_expand "lshrhi3" + [(set (match_operand:HI 0 "nonimmediate_operand") + (lshiftrt:HI (match_operand:HI 1 "general_operand") + (match_operand:HI 2 "general_operand")))] + "" + { + if (msp430x + && REG_P (operands[0]) + && REG_P (operands[1]) + && CONST_INT_P (operands[2])) + emit_insn (gen_430x_logical_shift_right (operands[0], operands[1], operands[2])); + else + msp430_expand_helper (operands, \"__mspabi_srli\", true); + DONE; + } +) + +(define_insn "srli_1" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") + (lshiftrt:HI (match_operand:HI 1 "general_operand" "0") + (const_int 1)))] + "" + "CLRC { RRC.W\t%0" +) + +(define_insn "430x_logical_shift_right" + [(set (match_operand:HI 0 "register_operand" "=r") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand 2 "immediate_operand" "n")))] + "msp430x" + { + return msp430x_logical_shift_right (operands[2]); + } +) + +(define_insn "srlp_1" + [(set (match_operand:PSI 0 "register_operand" "=r") + (lshiftrt:PSI (match_operand:PSI 1 "general_operand" "0") + (const_int 1)))] + "" + "RRUM.A #1,%0" +) + +(define_insn "srll_1" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") + (const_int 1)))] + "" + "CLRC { RRC.W\t%H0 { RRC.W\t%L0" +) + +(define_insn "srll_2x" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") + (const_int 2)))] + "msp430x" + "RRUX.W\t%H0 { RRC.W\t%L0 { RRUX.W\t%H0 { RRC.W\t%L0" +) + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "nonimmediate_operand") + (lshiftrt:SI (match_operand:SI 1 "general_operand") + (match_operand:SI 2 "general_operand")))] + "" + "msp430_expand_helper (operands, \"__mspabi_srll\", true); + DONE;" +) + +;;------------------------------------------------------------ +;; Function Entry/Exit + +(define_expand "prologue" + [(const_int 0)] + "" + "msp430_expand_prologue (); DONE;" + ) + +(define_expand "epilogue" + [(const_int 0)] + "" + "msp430_expand_epilogue (0); DONE;" + ) + + +(define_insn "epilogue_helper" + [(unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER)] + "" + "BR%Q0\t#__mspabi_func_epilog_%J0" + ) + + +(define_insn "prologue_start_marker" + [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_START_MARKER)] + "" + "; start of prologue" + ) + +(define_insn "prologue_end_marker" + [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_END_MARKER)] + "" + "; end of prologue" + ) + +(define_insn "epilogue_start_marker" + [(unspec_volatile [(const_int 0)] UNS_EPILOGUE_START_MARKER)] + "" + "; start of epilogue" + ) + +;; This makes the linker add a call to exit() after the call to main() +;; in crt0 +(define_insn "msp430_refsym_need_exit" + [(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)] + "" + ".refsym\t__crt0_call_exit" + ) + +;;------------------------------------------------------------ +;; Jumps + +(define_expand "call" + [(call:HI (match_operand 0 "") + (match_operand 1 ""))] + "" + "" +) + +(define_insn "call_internal" + [(call (mem:HI (match_operand 0 "general_operand" "rYci")) + (match_operand 1 ""))] + "" + "CALL%Q0\t%0" +) + +(define_expand "call_value" + [(set (match_operand 0 "register_operand") + (call:HI (match_operand 1 "general_operand") + (match_operand 2 "")))] + "" + "" +) + +(define_insn "call_value_internal" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:HI (match_operand 1 "general_operand" "rYci")) + (match_operand 2 "")))] + "" + "CALL%Q0\t%1" +) + +(define_insn "msp_return" + [(return)] + "" + { return msp430_is_interrupt_func () ? "RETI" : (TARGET_LARGE ? "RETA" : "RET"); } +) + +;; This pattern is NOT, as expected, a return pattern. It's called +;; before reload and must only store its operands, and emit a +;; placeholder where the epilog needs to be. AFTER reload, the +;; placeholder should get expanded into a regular-type epilogue that +;; also does the EH return. +(define_expand "eh_return" + [(match_operand:HI 0 "")] + "" + "msp430_expand_eh_return (operands[0]); + emit_jump_insn (gen_msp430_eh_epilogue ()); + emit_barrier (); + DONE;" +) + +;; This is the actual EH epilogue. We emit it in the pattern above, +;; before reload, and convert it to a real epilogue after reload. +(define_insn_and_split "msp430_eh_epilogue" + [(eh_return)] + "" + "#" + "reload_completed" + [(const_int 0)] + "msp430_expand_epilogue (1); DONE;" + ) + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "BR%Q0\t#%l0" +) + +;; FIXME: GCC currently (8/feb/2013) cannot handle symbol_refs +;; in indirect jumps (cf gcc.c-torture/compile/991213-3.c). +(define_insn "indirect_jump" + [(set (pc) + (match_operand 0 "nonimmediate_operand" "rYl"))] + "" + "BR%Q0\t%0" +) + +;;------------------------------------------------------------ +;; Various Conditionals + +(define_expand "cbranch<mode>4" + [(parallel [(set (pc) (if_then_else + (match_operator 0 "" + [(match_operand:QHI 1 "nonimmediate_operand") + (match_operand:QHI 2 "general_operand")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY))] + )] + "" + "msp430_fixup_compare_operands (<MODE>mode, operands);" + ) + +(define_insn "cbranchpsi4_real" + [(set (pc) (if_then_else + (match_operator 0 "msp430_cmp_operator" + [(match_operand:PSI 1 "nonimmediate_operand" "r,rYs,rm") + (match_operand:PSI 2 "general_operand" "rLs,rYsi,rmi")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP%Q0\t%2, %1 { J%0\t%l3 + CMPX.A\t%2, %1 { J%0\t%l3 + CMPX.A\t%2, %1 { J%0\t%l3" + ) + +(define_insn "cbranchqi4_real" + [(set (pc) (if_then_else + (match_operator 0 "msp430_cmp_operator" + [(match_operand:QI 1 "nonimmediate_operand" "rYs,rm") + (match_operand:QI 2 "general_operand" "rYsi,rmi")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP.B\t%2, %1 { J%0\t%l3 + CMP%X0.B\t%2, %1 { J%0\t%l3" + ) + +(define_insn "cbranchhi4_real" + [(set (pc) (if_then_else + (match_operator 0 "msp430_cmp_operator" + [(match_operand:HI 1 "nonimmediate_operand" "rYs,rm") + (match_operand:HI 2 "general_operand" "rYsi,rmi")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP.W\t%2, %1 { J%0\t%l3 + CMP%X0.W\t%2, %1 { J%0\t%l3" + ) + +(define_insn "cbranchpsi4_reversed" + [(set (pc) (if_then_else + (match_operator 0 "msp430_reversible_cmp_operator" + [(match_operand:PSI 1 "general_operand" "rLs,rYsi,rmi") + (match_operand:PSI 2 "general_operand" "r,rYs,rm")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP%Q0\t%1, %2 { J%R0\t%l3 + CMPX.A\t%1, %2 { J%R0\t%l3 + CMPX.A\t%1, %2 { J%R0\t%l3" + ) + +(define_insn "cbranchqi4_reversed" + [(set (pc) (if_then_else + (match_operator 0 "msp430_reversible_cmp_operator" + [(match_operand:QI 1 "general_operand" "rYsi,rmi") + (match_operand:QI 2 "general_operand" "rYs,rm")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP.B\t%1, %2 { J%R0\t%l3 + CMP%X0.B\t%1, %2 { J%R0\t%l3" + ) + +(define_insn "cbranchhi4_reversed" + [(set (pc) (if_then_else + (match_operator 0 "msp430_reversible_cmp_operator" + [(match_operand:HI 1 "general_operand" "rYsi,rmi") + (match_operand:HI 2 "general_operand" "rYs,rm")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + CMP.W\t%1, %2 { J%R0\t%l3 + CMP%X0.W\t%1, %2 { J%R0\t%l3" + ) + +(define_insn "*bitbranch<mode>4" + [(set (pc) (if_then_else + (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYs,rm") + (match_operand:QHI 1 "msp_general_operand" "rYsi,rmi")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + BIT%x0%b0\t%1, %0 { JNE\t%l2 + BIT%X0%b0\t%1, %0 { JNE\t%l2" + ) + +(define_insn "*bitbranch<mode>4" + [(set (pc) (if_then_else + (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (match_operand:QHI 1 "msp_general_operand" "rmi")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%x0%X0%b0\t%1, %0 { JEQ\t%l2" + ) + +(define_insn "*bitbranch<mode>4" + [(set (pc) (if_then_else + (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (match_operand:QHI 1 "msp_general_operand" "rmi")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" "")))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%X0%b0\t%1, %0 { JNE\t%l2" + ) + +(define_insn "*bitbranch<mode>4" + [(set (pc) (if_then_else + (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (match_operand:QHI 1 "msp_general_operand" "rmi")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" "")))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%X0%b0\t%1, %0 { JEQ\t%l2" + ) + +;;------------------------------------------------------------ +;; zero-extract versions of the above + +(define_insn "*bitbranch<mode>4_z" + [(set (pc) (if_then_else + (ne (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYs,rm") + (const_int 1) + (match_operand 1 "msp430_bitpos" "i,i")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "@ + BIT%x0%b0\t%p1, %0 { JNE\t%l2 + BIT%X0%b0\t%p1, %0 { JNE\t%l2" + ) + +(define_insn "*bitbranch<mode>4_z" + [(set (pc) (if_then_else + (eq (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (const_int 1) + (match_operand 1 "msp430_bitpos" "i")) + (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%x0%X0%b0\t%p1, %0 { JEQ\t%l2" + ) + +(define_insn "*bitbranch<mode>4_z" + [(set (pc) (if_then_else + (eq (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (const_int 1) + (match_operand 1 "msp430_bitpos" "i")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" "")))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%X0%b0\t%p1, %0 { JNE\t%l2" + ) + +(define_insn "*bitbranch<mode>4_z" + [(set (pc) (if_then_else + (ne (zero_extract:HI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm") + (const_int 1) + (match_operand 1 "msp430_bitpos" "i")) + (const_int 0)) + (pc) + (label_ref (match_operand 2 "" "")))) + (clobber (reg:BI CARRY)) + ] + "" + "BIT%X0%b0\t%p1, %0 { JEQ\t%l2" + ) + +;;------------------------------------------------------------ +;; Misc + +(define_insn "nop" + [(const_int 0)] + "1" + "NOP" +) + +(define_insn "disable_interrupts" + [(unspec_volatile [(const_int 0)] UNS_DINT)] + "" + "DINT \; NOP" + ) + +(define_insn "enable_interrupts" + [(unspec_volatile [(const_int 0)] UNS_EINT)] + "" + "EINT" + ) + +(define_insn "push_intr_state" + [(unspec_volatile [(const_int 0)] UNS_PUSH_INTR)] + "" + "PUSH\tSR" + ) + +(define_insn "pop_intr_state" + [(unspec_volatile [(const_int 0)] UNS_POP_INTR)] + "" + "POP\tSR" + ) + +;; Clear bits in the copy of the status register that is currently +;; saved on the stack at the top of the interrupt handler. +(define_insn "bic_SR" + [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIC_SR)] + "" + "BIC.W\t%0, %O0(SP)" + ) + +;; Set bits in the copy of the status register that is currently +;; saved on the stack at the top of the interrupt handler. +(define_insn "bis_SR" + [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIS_SR)] + "" + "BIS.W\t%0, %O0(SP)" + ) + +;; For some reason GCC is generating (set (reg) (and (neg (reg)) (int))) +;; very late on in the compilation and not splitting it into separate +;; instructions, so we provide a pattern to support it here. +(define_insn "andneghi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (and:HI (neg:HI (match_operand:HI 1 "register_operand" "r")) + (match_operand 2 "immediate_operand" "n")))] + "" + "* + if (REGNO (operands[0]) != REGNO (operands[1])) + return \"MOV.W\t%1, %0 { SUB.W\t#0, %0 { AND.W\t%2, %0\"; + else + return \"SUB.W\t#0, %0 { AND.W\t%2, %0\"; + " + ) + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0")) + (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "optimize > 2 && msp430_hwmult_enabled ()" + "* + if (msp430_is_f5_mcu ()) + return \"MOV.W %1, &0x04C2 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0\"; + else + return \"MOV.W %1, &0x0132 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0\"; + " +) + +(define_insn "umulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0")) + (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "optimize > 2 && msp430_hwmult_enabled ()" + "* + if (msp430_is_f5_mcu ()) + return \"MOV.W %1, &0x04C0 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0\"; + else + return \"MOV.W %1, &0x0130 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0\"; + " +) + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "optimize > 2 && msp430_hwmult_enabled ()" + "* + if (msp430_is_f5_mcu ()) + return \"MOV.W %L1, &0x04D4 { MOV.W %H1, &0x04D6 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0\"; + else + return \"MOV.W %L1, &0x0144 { MOV.W %H1, &0x0146 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0\"; + " +) + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "optimize > 2 && msp430_hwmult_enabled ()" + "* + if (msp430_is_f5_mcu ()) + return \"MOV.W %L1, &0x04D0 { MOV.W %H1, &0x04D2 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0\"; + else + return \"MOV.W %L1, &0x0140 { MOV.W %H1, &0x0141 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0\"; + " +) diff --git a/gcc-4.9/gcc/config/msp430/msp430.opt b/gcc-4.9/gcc/config/msp430/msp430.opt new file mode 100644 index 000000000..5a447c0d2 --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/msp430.opt @@ -0,0 +1,38 @@ +msim +Target +Use simulator runtime + +masm-hex +Target Mask(ASM_HEX) +Force assembly output to always use hex constants + +mmcu= +Target ToLower Joined RejectNegative Var(target_mcu) +Specify the MCU to build for. + +mcpu= +Target Joined RejectNegative Var(target_cpu) +Specify the ISA to build for: msp430, mdsp430x, msp430xv2 + +mlarge +Target Mask(LARGE) RejectNegative +Select large model - 20-bit addresses/pointers + +msmall +Target InverseMask(LARGE) RejectNegative +Select small model - 16-bit addresses/pointers (default) + +mrelax +Target Report +Optimize opcode sizes at link time + +mOs +Target Undocumented Mask(OPT_SPACE) + +minrt +Target Report Mask(MINRT) RejectNegative +Use a minimum runtime (no static initializers or ctors) for memory-constrained devices. + +mhwmult +Target Report Var(ENABLE_HWMULT, 1) Init(1) +Enable hardware multiply (except in interrupt routines) diff --git a/gcc-4.9/gcc/config/msp430/predicates.md b/gcc-4.9/gcc/config/msp430/predicates.md new file mode 100644 index 000000000..9a8e2da0a --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/predicates.md @@ -0,0 +1,80 @@ +;; Machine Description for TI MSP43* processors +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. +;; Contributed by Red Hat. + +;; 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_predicate "msp_volatile_memory_operand" + (and (match_code "mem") + (match_test ("memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0), MEM_ADDR_SPACE (op))"))) +) + +; TRUE for any valid general operand. We do this because +; general_operand refuses to match volatile memory refs. + +(define_predicate "msp_general_operand" + (ior (match_operand 0 "general_operand") + (match_operand 0 "msp_volatile_memory_operand")) +) + +; Likewise for nonimmediate_operand. + +(define_predicate "msp_nonimmediate_operand" + (ior (match_operand 0 "nonimmediate_operand") + (match_operand 0 "msp_volatile_memory_operand")) +) + +(define_predicate "ubyte_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 255)"))) + +; TRUE for comparisons we support. +(define_predicate "msp430_cmp_operator" + (match_code "eq,ne,lt,ltu,ge,geu")) + +; TRUE for comparisons we need to reverse. +(define_predicate "msp430_reversible_cmp_operator" + (match_code "gt,gtu,le,leu")) + +; TRUE for constants the constant generator can produce +(define_predicate "msp430_constgen_operator" + (and (match_code "const_int") + (match_test (" INTVAL (op) == 0 + || INTVAL (op) == 1 + || INTVAL (op) == 2 + || INTVAL (op) == 4 + || INTVAL (op) == 8 + || INTVAL (op) == -1 ")))) + +; TRUE for constants the constant generator can produce +(define_predicate "msp430_inv_constgen_operator" + (and (match_code "const_int") + (match_test (" INTVAL (op) == ~0 + || INTVAL (op) == ~1 + || INTVAL (op) == ~2 + || INTVAL (op) == ~4 + || INTVAL (op) == ~8 + || INTVAL (op) == ~(-1) ")))) + +(define_predicate "msp430_nonsubreg_operand" + (match_code "reg,mem")) + +; TRUE for constants which are bit positions for zero_extract +(define_predicate "msp430_bitpos" + (and (match_code "const_int") + (match_test (" INTVAL (op) >= 0 + && INTVAL (op) <= 15 ")))) diff --git a/gcc-4.9/gcc/config/msp430/t-msp430 b/gcc-4.9/gcc/config/msp430/t-msp430 new file mode 100644 index 000000000..74a3c529f --- /dev/null +++ b/gcc-4.9/gcc/config/msp430/t-msp430 @@ -0,0 +1,257 @@ +# Makefile fragment for building GCC for the TI MSP430 target. +# Copyright (C) 2012-2014 Free Software Foundation, Inc. +# Contributed by Red Hat. +# +# 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/>. + +# Enable multilibs: + +MULTILIB_OPTIONS = mcpu=msp430 mlarge +MULTILIB_DIRNAMES = 430 large + +# Match -mcpu=430 +MULTILIB_MATCHES = mcpu?msp430=mcpu?430 + +# Match the known 430 ISA mcu names. +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe221 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe222 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe223 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe231 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe232 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe233 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe251 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe252 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe253 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c091 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c092 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c111 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1111 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1121 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1331 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1351 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c311s +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c312 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c313 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c314 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c315 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c323 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c325 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c336 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c337 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c412 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c413 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e313 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e315 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e325 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e337 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f110 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1101 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1101a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1111 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1111a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1121 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1121a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1122 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1132 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f122 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1222 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f123 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1232 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f133 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f135 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f147 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1471 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f148 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1481 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f149 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1491 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f155 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f156 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f157 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1610 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1611 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1612 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f167 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f168 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f169 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2001 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2002 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2003 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2011 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2012 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2013 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2101 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2111 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2121 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2122 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2131 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2132 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2232 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2234 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2252 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2254 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2272 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2274 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f233 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2330 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f235 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2350 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2370 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2410 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f247 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2471 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f248 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2481 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f249 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2491 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f412 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f413 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4132 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f415 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4152 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f417 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f423 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f423a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f425 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4250 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f425a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4260 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f427 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4270 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f427a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f435 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4351 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f436 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4361 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f437 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4371 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f438 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f439 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f447 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f448 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4481 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f449 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4491 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f477 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f478 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4783 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4784 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f479 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4793 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4794 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe423 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4232 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe423a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4242 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe425 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4252 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe425a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe427 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4272 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe427a +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4250 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4260 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4270 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg437 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg438 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg439 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg477 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg478 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg479 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw423 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw425 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw427 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw428 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw429 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2001 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2101 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2102 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2111 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2113 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2121 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2131 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2132 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2152 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2153 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2201 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2202 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2203 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2210 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2211 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2212 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2213 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2221 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2230 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2231 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2232 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2233 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2252 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2253 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2302 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2303 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2312 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2313 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2332 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2333 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2352 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2353 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2402 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2403 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2412 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2413 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2432 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2433 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2444 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2452 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2453 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2513 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2533 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2544 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2553 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2744 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2755 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2855 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2955 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2020 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2021 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2030 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2031 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2040 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2041 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430l092 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p112 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p313 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p315 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p315s +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p325 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p337 +MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430tch5e + +# Add additional MCU matches like this: +# MULTILIB_MATCHES += mcpu?msp430x=mmcu?xxxxxxxxxx + +MULTILIB_EXCEPTIONS = mcpu=msp430/mlarge + +MULTILIB_EXTRA_OPTS = + +msp430-c.o: $(srcdir)/config/msp430/msp430-c.c $(RTL_H) $(TREE_H) $(CONFIG_H) $(TM_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< |