/* Subroutines used for code generation on TI MSP430 processors.
- Copyright (C) 2012-2015 Free Software Foundation, Inc.
+ Copyright (C) 2012-2019 Free Software Foundation, Inc.
Contributed by Red Hat.
This file is part of GCC.
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#define IN_TARGET_CODE 1
+
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
#include "gimple-expr.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "regs.h"
#include "emit-rtl.h"
#include "expr.h"
#include "langhooks.h"
#include "builtins.h"
+#include "intl.h"
/* This file should be included last. */
#include "target-def.h"
#define TARGET_OPTION_OVERRIDE msp430_option_override
/* This is a copy of the same data structure found in gas/config/tc-msp430.c
- Also another (sort-of) copy can be found in gcc/config/msp430/t-msp430.
+ Also another (sort-of) copy can be found in gcc/config/msp430/t-msp430
Keep these three structures in sync.
- The data in this structure has been extracted from the devices.csv file
- released by TI, updated as of 8 October 2015. */
+ The data in this structure has been extracted from version 1.194 of the
+ devices.csv file released by TI in September 2016. */
struct msp430_mcu_data
{
{ "msp430fg6626",2,8 },
{ "msp430fr2032",2,0 },
{ "msp430fr2033",2,0 },
+ { "msp430fr2110",2,0 },
+ { "msp430fr2111",2,0 },
+ { "msp430fr2310",2,0 },
+ { "msp430fr2311",2,0 },
{ "msp430fr2433",2,8 },
+ { "msp430fr2532",2,8 },
+ { "msp430fr2533",2,8 },
+ { "msp430fr2632",2,8 },
+ { "msp430fr2633",2,8 },
{ "msp430fr2xx_4xxgeneric",2,8 },
{ "msp430fr4131",2,0 },
{ "msp430fr4132",2,0 },
{ "msp430fr5957",2,8 },
{ "msp430fr5958",2,8 },
{ "msp430fr5959",2,8 },
+ { "msp430fr5962",2,8 },
+ { "msp430fr5964",2,8 },
{ "msp430fr5967",2,8 },
{ "msp430fr5968",2,8 },
{ "msp430fr5969",2,8 },
{ "msp430fr5988",2,8 },
{ "msp430fr5989",2,8 },
{ "msp430fr59891",2,8 },
+ { "msp430fr5992",2,8 },
+ { "msp430fr5994",2,8 },
+ { "msp430fr59941",2,8 },
{ "msp430fr5xx_6xxgeneric",2,8 },
{ "msp430fr6820",2,8 },
{ "msp430fr6822",2,8 },
if (target_mcu)
{
unsigned int i;
- static char mcu_name [64];
+ unsigned int start_upper;
+ unsigned int end_upper;
+ static char mcu_name[64];
- snprintf (mcu_name, sizeof (mcu_name) - 1, "__%s__", target_mcu);
- for (i = strlen (mcu_name); i--;)
+ /* The 'i' in the device name symbol for msp430i* devices must be lower
+ case, to match the expected symbol in msp430.h. */
+ if (strncmp (target_mcu, "msp430i", 7) == 0)
+ {
+ snprintf (mcu_name, sizeof (mcu_name) - 1, "__MSP430i%s__",
+ target_mcu + 7);
+ start_upper = 9;
+ }
+ else
+ {
+ snprintf (mcu_name, sizeof (mcu_name) - 1, "__%s__", target_mcu);
+ start_upper = 2;
+ }
+ end_upper = strlen (mcu_name) - 2;
+ for (i = start_upper; i < end_upper; i++)
mcu_name[i] = TOUPPER (mcu_name[i]);
return mcu_name;
}
return msp430x ? "__MSP430XGENERIC__" : "__MSP430GENERIC__";
}
+static const char *
+hwmult_name (unsigned int val)
+{
+ switch (val)
+ {
+ case 0: return "none";
+ case 1: return "16-bit";
+ case 2: return "16-bit";
+ case 4: return "32-bit";
+ case 8: return "32-bit (5xx)";
+ default: gcc_unreachable ();
+ }
+}
+
static void
msp430_option_override (void)
{
+ /* The MSP430 architecture can safely dereference a NULL pointer. In fact,
+ there are memory mapped registers there. */
+ flag_delete_null_pointer_checks = 0;
+
init_machine_status = msp430_init_machine_status;
if (target_cpu)
{
+ /* gcc/common/config/msp430-common.c will have
+ already canonicalised the string in target_cpu. */
if (strcasecmp (target_cpu, "msp430x") == 0)
msp430x = true;
else /* target_cpu == "msp430" - already handled by the front end. */
msp430x = false;
}
- /* Note - the front end has already ensured at most
- one of target_cpu and target_mcu will be set. */
- else if (target_mcu)
+
+ 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;
-
- /* FIXME: This array is alpha sorted, so we could use a binary search. */
+ /* FIXME: If the array were alpha sorted, we could use a binary search. */
for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
if (strcasecmp (msp430_mcu_data[i].name, target_mcu) == 0)
{
- msp430x = msp430_mcu_data[i].revision >= 1;
+ bool xisa = msp430_mcu_data[i].revision >= 1;
+
+ if (msp430_warn_mcu)
+ {
+ if (target_cpu&& msp430x != xisa)
+ warning (0, "MCU '%s' supports %s ISA but %<-mcpu%> option "
+ "is set to %s",
+ target_mcu, xisa ? "430X" : "430", msp430x ? "430X" : "430");
+
+ if (msp430_mcu_data[i].hwmpy == 0
+ && msp430_hwmult_type != MSP430_HWMULT_AUTO
+ && msp430_hwmult_type != MSP430_HWMULT_NONE)
+ warning (0, "MCU '%s' does not have hardware multiply "
+ "support, but %<-mhwmult%> is set to %s",
+ target_mcu,
+ msp430_hwmult_type == MSP430_HWMULT_SMALL ? "16-bit"
+ : msp430_hwmult_type == MSP430_HWMULT_LARGE ? "32-bit" : "f5series");
+ else if (msp430_hwmult_type == MSP430_HWMULT_SMALL
+ && msp430_mcu_data[i].hwmpy != 1
+ && msp430_mcu_data[i].hwmpy != 2 )
+ warning (0, "MCU '%s' supports %s hardware multiply, "
+ "but %<-mhwmult%> is set to 16-bit",
+ target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+ else if (msp430_hwmult_type == MSP430_HWMULT_LARGE && msp430_mcu_data[i].hwmpy != 4)
+ warning (0, "MCU '%s' supports %s hardware multiply, "
+ "but %<-mhwmult%> is set to 32-bit",
+ target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+ else if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES && msp430_mcu_data[i].hwmpy != 8)
+ warning (0, "MCU '%s' supports %s hardware multiply, "
+ "but %<-mhwmult%> is set to f5series",
+ target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+ }
+
+ msp430x = xisa;
break;
}
+
if (i < 0)
{
- warning (0, "Unrecognised MCU name '%s', assuming that it is just a MSP430 with no hardware multiply",
- target_mcu);
- msp430x = false;
+ if (msp430_hwmult_type == MSP430_HWMULT_AUTO)
+ {
+ if (msp430_warn_mcu)
+ {
+ if (target_cpu == NULL)
+ warning (0,
+ "Unrecognized MCU name '%s', assuming that it is "
+ "just a MSP430 with no hardware multiply.\n"
+ "Use the %<-mcpu%> and %<-mhwmult%> options to "
+ "set these explicitly.",
+ target_mcu);
+ else
+ warning (0,
+ "Unrecognized MCU name '%s', assuming that it "
+ "has no hardware multiply.\nUse the %<-mhwmult%> "
+ "option to set this explicitly.",
+ target_mcu);
+ }
+
+ msp430_hwmult_type = MSP430_HWMULT_NONE;
+ }
+ else if (target_cpu == NULL)
+ {
+ if (msp430_warn_mcu)
+ warning (0,
+ "Unrecognized MCU name '%s', assuming that it just "
+ "supports the MSP430 ISA.\nUse the %<-mcpu%> option "
+ "to set the ISA explicitly.",
+ target_mcu);
+
+ msp430x = false;
+ }
+ else if (msp430_warn_mcu)
+ warning (0, "Unrecognized MCU name '%s'.", target_mcu);
}
}
+ /* The F5 series are all able to support the 430X ISA. */
+ if (target_cpu == NULL && target_mcu == NULL && msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
+ msp430x = true;
+
if (TARGET_LARGE && !msp430x)
- error ("-mlarge requires a 430X-compatible -mmcu=");
+ error ("%<-mlarge%> requires a 430X-compatible %<-mmcu=%>");
- if (msp430_code_region == UPPER && ! msp430x)
- error ("-mcode-region=upper requires 430X-compatible cpu");
- if (msp430_data_region == UPPER && ! msp430x)
- error ("-mdata-region=upper requires 430X-compatible cpu");
+ if (msp430_code_region == MSP430_REGION_UPPER && ! msp430x)
+ error ("%<-mcode-region=upper%> requires 430X-compatible cpu");
+ if (msp430_data_region == MSP430_REGION_UPPER && ! msp430x)
+ error ("%<-mdata-region=upper%> requires 430X-compatible cpu");
if (flag_exceptions || flag_non_call_exceptions
|| flag_unwind_tables || flag_asynchronous_unwind_tables)
#define TARGET_SCALAR_MODE_SUPPORTED_P msp430_scalar_mode_supported_p
static bool
-msp430_scalar_mode_supported_p (machine_mode m)
+msp430_scalar_mode_supported_p (scalar_mode m)
{
if (m == PSImode && msp430x)
return true;
/* 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,
- machine_mode mode)
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS msp430_hard_regno_nregs
+
+static unsigned int
+msp430_hard_regno_nregs (unsigned int, machine_mode mode)
{
if (mode == PSImode && msp430x)
return 1;
+ if (mode == CPSImode && msp430x)
+ return 2;
return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
/ UNITS_PER_WORD);
}
{
if (mode == PSImode)
return 2;
+ if (mode == CPSImode)
+ return 4;
return msp430_hard_regno_nregs (regno, mode);
}
-/* Implements HARD_REGNO_MODE_OK. */
-int
-msp430_hard_regno_mode_ok (int regno ATTRIBUTE_UNUSED,
- machine_mode mode)
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK msp430_hard_regno_mode_ok
+
+static bool
+msp430_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
- return regno <= (ARG_POINTER_REGNUM - msp430_hard_regno_nregs (regno, mode));
+ return regno <= (ARG_POINTER_REGNUM
+ - (unsigned int) msp430_hard_regno_nregs (regno, mode));
}
-/* Implements MODES_TIEABLE_P. */
-bool
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P msp430_modes_tieable_p
+
+static bool
msp430_modes_tieable_p (machine_mode mode1, machine_mode mode2)
{
if ((mode1 == PSImode || mode2 == SImode)
#undef TARGET_ADDR_SPACE_ADDRESS_MODE
#define TARGET_ADDR_SPACE_ADDRESS_MODE msp430_addr_space_pointer_mode
-static machine_mode
+static scalar_int_mode
msp430_addr_space_pointer_mode (addr_space_t addrspace)
{
switch (addrspace)
#undef TARGET_UNWIND_WORD_MODE
#define TARGET_UNWIND_WORD_MODE msp430_unwind_word_mode
-static machine_mode
+static scalar_int_mode
msp430_unwind_word_mode (void)
{
- return TARGET_LARGE ? PSImode : HImode;
+ /* This needs to match msp430_init_dwarf_reg_sizes_extra (below). */
+ return msp430x ? PSImode : HImode;
}
/* Determine if one named address space is a subset of another. */
#undef TARGET_GET_RAW_ARG_MODE
#define TARGET_GET_RAW_ARG_MODE msp430_get_raw_arg_mode
-static machine_mode
+static fixed_size_mode
msp430_get_raw_arg_mode (int regno)
{
- return (regno == ARG_POINTER_REGNUM) ? VOIDmode : Pmode;
+ return as_a <fixed_size_mode> (regno == ARG_POINTER_REGNUM
+ ? VOIDmode : Pmode);
}
#undef TARGET_GET_RAW_RESULT_MODE
#define TARGET_GET_RAW_RESULT_MODE msp430_get_raw_result_mode
-static machine_mode
+static fixed_size_mode
msp430_get_raw_result_mode (int regno ATTRIBUTE_UNUSED)
{
return Pmode;
if (boundary < TYPE_ALIGN (type))
{
type = build_variant_type_copy (type);
- TYPE_ALIGN (type) = boundary;
+ SET_TYPE_ALIGN (type, boundary);
}
/* Compute the rounded size of the type. */
return addr;
}
\f
+#undef TARGET_LRA_P
+#define TARGET_LRA_P hook_bool_void_false
+
/* Addressing Modes */
#undef TARGET_LEGITIMATE_ADDRESS_P
case REG:
if (!reg_ok_for_addr (x, strict))
return false;
- /* else... */
+ /* FALLTHRU */
case CONST:
case SYMBOL_REF:
case CONST_INT:
return has_attr (ATTR_CRIT, decl);
}
+static bool
+has_section_name (const char * name, tree decl = current_function_decl)
+{
+ if (decl == NULL_TREE)
+ return false;
+ return (DECL_SECTION_NAME (decl)
+ && (strcmp (name, DECL_SECTION_NAME (decl)) == 0));
+}
+
#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS msp430_allocate_stack_slots_for_args
return ! is_naked_func ();
}
+#undef TARGET_WARN_FUNC_RETURN
+#define TARGET_WARN_FUNC_RETURN msp430_warn_func_return
+
+static bool
+msp430_warn_func_return (tree decl)
+{
+ /* Naked functions are implemented entirely in assembly, including the
+ return sequence, so suppress warnings about this. */
+ return !is_naked_func (decl);
+}
+
/* Verify MSP430 specific attributes. */
#define TREE_NAME_EQ(NAME, STR) (strcmp (IDENTIFIER_POINTER (NAME), (STR)) == 0)
{
gcc_assert (DECL_P (* node));
+ /* Only the interrupt attribute takes an argument. */
if (args != NULL)
{
- gcc_assert (TREE_NAME_EQ (name, ATTR_INTR));
-
tree value = TREE_VALUE (args);
switch (TREE_CODE (value))
/* 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",
+ "unrecognized interrupt vector argument of %qE attribute",
name);
break;
case INTEGER_CST:
- if (wi::gtu_p (value, 63))
+ if (wi::gtu_p (wi::to_wide (value), 63))
/* Allow the attribute to be added - the linker script
being used may still recognise this value. */
warning (OPT_Wattributes,
if (TREE_CODE (TREE_TYPE (* node)) == FUNCTION_TYPE
&& ! VOID_TYPE_P (TREE_TYPE (TREE_TYPE (* node))))
message = "interrupt handlers must be void";
+ else
+ {
+ /* Ensure interrupt handlers never get optimised out. */
+ TREE_USED (* node) = 1;
+ DECL_PRESERVE_P (* node) = 1;
+ }
+ if (is_critical_func (* node))
+ {
+ warning (OPT_Wattributes,
+ "critical attribute has no effect on interrupt functions");
+ DECL_ATTRIBUTES (*node) = remove_attribute (ATTR_CRIT,
+ DECL_ATTRIBUTES (* node));
+ }
}
else if (TREE_NAME_EQ (name, ATTR_REENT))
{
message = "naked functions cannot be critical";
else if (is_reentrant_func (* node))
message = "reentrant functions cannot be critical";
+ else if (is_interrupt_func ( *node))
+ message = "critical attribute has no effect on interrupt functions";
}
else if (TREE_NAME_EQ (name, ATTR_NAKED))
{
gcc_assert (args == NULL);
if (TREE_CODE (* node) != VAR_DECL)
- message = "%qE attribute only applies to variables";
+ message = G_("%qE attribute only applies to variables");
+
+ /* Check that it's possible for the variable to have a section. */
+ if ((TREE_STATIC (* node) || DECL_EXTERNAL (* node) || in_lto_p)
+ && DECL_SECTION_NAME (* node))
+ message = G_("%qE attribute cannot be applied to variables with specific sections");
- if (DECL_SECTION_NAME (* node))
- message = "%qE attribute cannot be applied to variables with specific sections";
+ if (!message && TREE_NAME_EQ (name, ATTR_PERSIST) && !TREE_STATIC (* node)
+ && !TREE_PUBLIC (* node) && !DECL_EXTERNAL (* node))
+ message = G_("%qE attribute has no effect on automatic variables");
+
+ /* It's not clear if there is anything that can be set here to prevent the
+ front end placing the variable before the back end can handle it, in a
+ similar way to how DECL_COMMON is used below.
+ So just place the variable in the .persistent section now. */
+ if ((TREE_STATIC (* node) || DECL_EXTERNAL (* node) || in_lto_p)
+ && TREE_NAME_EQ (name, ATTR_PERSIST))
+ set_decl_section_name (* node, ".persistent");
/* If this var is thought to be common, then change this. Common variables
are assigned to sections before the backend has a chance to process them. */
/* Table of MSP430-specific attributes. */
const struct attribute_spec msp430_attribute_table[] =
{
- /* Name min_num_args type_req, affects_type_identity
- max_num_args, fn_type_req
- decl_req handler. */
- { ATTR_INTR, 0, 1, true, false, false, msp430_attr, false },
- { ATTR_NAKED, 0, 0, true, false, false, msp430_attr, false },
- { ATTR_REENT, 0, 0, true, false, false, msp430_attr, false },
- { ATTR_CRIT, 0, 0, true, false, false, msp430_attr, false },
- { ATTR_WAKEUP, 0, 0, true, false, false, msp430_attr, false },
-
- { ATTR_LOWER, 0, 0, true, false, false, msp430_section_attr, false },
- { ATTR_UPPER, 0, 0, true, false, false, msp430_section_attr, false },
- { ATTR_EITHER, 0, 0, true, false, false, msp430_section_attr, false },
-
- { ATTR_NOINIT, 0, 0, true, false, false, msp430_data_attr, false },
- { ATTR_PERSIST, 0, 0, true, false, false, msp430_data_attr, false },
-
- { NULL, 0, 0, false, false, false, NULL, false }
+ /* Name min_num_args type_req, handler
+ max_num_args, fn_type_req exclude
+ decl_req affects_type_identity. */
+ { ATTR_INTR, 0, 1, true, false, false, false, msp430_attr, NULL },
+ { ATTR_NAKED, 0, 0, true, false, false, false, msp430_attr, NULL },
+ { ATTR_REENT, 0, 0, true, false, false, false, msp430_attr, NULL },
+ { ATTR_CRIT, 0, 0, true, false, false, false, msp430_attr, NULL },
+ { ATTR_WAKEUP, 0, 0, true, false, false, false, msp430_attr, NULL },
+
+ { ATTR_LOWER, 0, 0, true, false, false, false, msp430_section_attr,
+ NULL },
+ { ATTR_UPPER, 0, 0, true, false, false, false, msp430_section_attr,
+ NULL },
+ { ATTR_EITHER, 0, 0, true, false, false, false, msp430_section_attr,
+ NULL },
+
+ { ATTR_NOINIT, 0, 0, true, false, false, false, msp430_data_attr,
+ NULL },
+ { ATTR_PERSIST, 0, 0, true, false, false, false, msp430_data_attr,
+ NULL },
+
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
};
#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)
+msp430_start_function (FILE *outfile)
{
int r, n;
{
char buf[101];
+ /* Interrupt vector sections should be unique, but use of weak
+ functions implies multiple definitions. */
+ if (DECL_WEAK (decl))
+ {
+ error ("argument to interrupt attribute is unsupported for weak functions");
+ }
+
intr_vector = TREE_VALUE (intr_vector);
/* The interrupt attribute has a vector value. Turn this into a
}
switch_to_section (function_section (decl));
+ ASM_OUTPUT_TYPE_DIRECTIVE(file, name, "function");
ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
}
if (has_attr ("section", decl))
return NULL;
+ /* If the function has been put in the .lowtext section (because it is an
+ interrupt handler, and the large memory model is used), then do not add
+ any prefixes. */
+ if (has_section_name (".lowtext", decl))
+ return NULL;
+
/* If the object has __attribute__((lower)) then use the ".lower." prefix. */
if (has_attr (ATTR_LOWER, decl))
return lower_prefix;
if (TREE_CODE (decl) == FUNCTION_DECL)
{
- if (msp430_code_region == LOWER)
+ if (msp430_code_region == MSP430_REGION_LOWER)
return lower_prefix;
- if (msp430_code_region == UPPER)
+ if (msp430_code_region == MSP430_REGION_UPPER)
return upper_prefix;
- if (msp430_code_region == EITHER)
+ if (msp430_code_region == MSP430_REGION_EITHER)
return either_prefix;
}
else
{
- if (msp430_data_region == LOWER)
+ if (msp430_data_region == MSP430_REGION_LOWER)
return lower_prefix;
- if (msp430_data_region == UPPER)
+ if (msp430_data_region == MSP430_REGION_UPPER)
return upper_prefix;
- if (msp430_data_region == EITHER)
+ if (msp430_data_region == MSP430_REGION_EITHER)
return either_prefix;
}
unsigned HOST_WIDE_INT size,
unsigned int align)
{
- if (msp430_data_region == ANY)
+ if (msp430_data_region == MSP430_REGION_ANY)
{
fprintf (stream, COMMON_ASM_OP);
assemble_name (stream, name);
else
switch (msp430_data_region)
{
- case UPPER: sec = get_named_section (NULL, ".upper.bss", 0); break;
- case LOWER: sec = get_named_section (NULL, ".lower.bss", 0); break;
- case EITHER: sec = get_named_section (NULL, ".either.bss", 0); break;
+ case MSP430_REGION_UPPER: sec = get_named_section (NULL, ".upper.bss", 0); break;
+ case MSP430_REGION_LOWER: sec = get_named_section (NULL, ".lower.bss", 0); break;
+ case MSP430_REGION_EITHER: sec = get_named_section (NULL, ".either.bss", 0); break;
default:
gcc_unreachable ();
}
short jumps when there is a chance that the instructions will end up in a low
section. */
return
- msp430_code_region == EITHER
- || msp430_code_region == LOWER
+ msp430_code_region == MSP430_REGION_EITHER
+ || msp430_code_region == MSP430_REGION_LOWER
|| has_attr (ATTR_EITHER, current_function_decl)
|| has_attr (ATTR_LOWER, current_function_decl);
}
{
if (c < 0)
{
- error ("__delay_cycles only takes non-negative cycle counts.");
+ error ("__delay_cycles only takes non-negative cycle counts");
return NULL_RTX;
}
}
c %= 4;
if ((unsigned long long) i > 0xffffffffULL)
{
- error ("__delay_cycles is limited to 32-bit loop counts.");
+ error ("__delay_cycles is limited to 32-bit loop counts");
return NULL_RTX;
}
}
rtx addr = expand_normal (address);
rtx mem = gen_rtx_MEM (BLKmode, addr);
+ /* This needs to match msp430_unwind_word_mode (above). */
if (!msp430x)
return;
{ "__divsi3", "__mspabi_divli" },
{ "__divdi3", "__mspabi_divlli" },
{ "__udivhi3", "__mspabi_divu" },
- { "__udivsi3", "__mspabi_divlu" },
- { "__udivdi3", "__mspabi_divllu" },
+ { "__udivsi3", "__mspabi_divul" },
+ { "__udivdi3", "__mspabi_divull" },
{ "__modhi3", "__mspabi_remi" },
{ "__modsi3", "__mspabi_remli" },
{ "__moddi3", "__mspabi_remlli" },
static const char * cached_match = NULL;
static bool cached_result;
- if (msp430_hwmult_type == F5SERIES)
+ if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
return true;
- if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+ if (target_mcu == NULL || msp430_hwmult_type != MSP430_HWMULT_AUTO)
return false;
if (target_mcu == cached_match)
static bool cached_result;
int i;
- if (msp430_hwmult_type == LARGE)
+ if (msp430_hwmult_type == MSP430_HWMULT_LARGE)
return true;
- if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+ if (target_mcu == NULL || msp430_hwmult_type != MSP430_HWMULT_AUTO)
return false;
if (target_mcu == cached_match)
static bool cached_result;
int i;
- if (msp430_hwmult_type == NONE)
+ if (msp430_hwmult_type == MSP430_HWMULT_NONE)
return true;
- if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+ if (msp430_hwmult_type != MSP430_HWMULT_AUTO)
return false;
+ if (target_mcu == NULL)
+ return true;
+
if (target_mcu == cached_match)
return cached_result;
/* 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_type != NONE)
+ if (msp430_hwmult_type != MSP430_HWMULT_NONE)
{
if (strcmp ("__mspabi_mpyi", name) == 0)
{
}
}
+ if (user_label_prefix[0] != 0)
+ fputs (user_label_prefix, file);
+
fputs (name, file);
}
}
}
+#undef TARGET_ASM_ALIGNED_PSI_OP
+#define TARGET_ASM_ALIGNED_PSI_OP "\t.long\t"
+#undef TARGET_ASM_UNALIGNED_PSI_OP
+#define TARGET_ASM_UNALIGNED_PSI_OP TARGET_ASM_ALIGNED_PSI_OP
+
#undef TARGET_PRINT_OPERAND_ADDRESS
#define TARGET_PRINT_OPERAND_ADDRESS msp430_print_operand_addr
is ADDR. */
static void
-msp430_print_operand_addr (FILE * file, rtx addr)
+msp430_print_operand_addr (FILE * file, machine_mode /*mode*/, rtx addr)
{
switch (GET_CODE (addr))
{
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;
+ case E_QImode: fprintf (file, ".B"); return;
+ case E_HImode: fprintf (file, ".W"); return;
+ case E_PSImode: fprintf (file, ".A"); return;
+ case E_SImode: fprintf (file, ".A"); return;
default:
return;
}
case MEM:
addr = XEXP (op, 0);
- msp430_print_operand_addr (file, addr);
+ msp430_print_operand_addr (file, GET_MODE (op), addr);
break;
case CONST:
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. */
}
+
+/* Stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)). */
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS msp430_can_change_mode_class
+
+static bool
+msp430_can_change_mode_class (machine_mode from, machine_mode to, reg_class_t)
+{
+ if ((to == PSImode && from == SImode)
+ || (to == SImode && from == PSImode)
+ || (to == DImode && from == PSImode)
+ || (to == PSImode && from == DImode))
+ return false;
+ return true;
+}
\f
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-msp430.h"