Commit 04a9ae28 by Nick Clifton Committed by Nick Clifton

gcc * config.gcc (extra_gcc_objs): Define for MSP430.

        * common/config/msp430/msp430-common.c (msp430_handle_option):
	Pass both -mmcu and -mcpu on to the back end if they are both
	defined.
	* config/msp430/msp430.c (hwmult_name): New function.
        (msp430_option_override): If an unrecognised MCU name is
	detected only warn if the user has not provided suitable
        -mhwmult and -mcpu options.  Use msp430_warn_mcu to control
	warning messages.  Generate warnings about conflicts between
	-mmcu and -mcpu and -mhwmult options. 
	If neither -mcpu nor -mmcu have been specified but -mhwmult=
	f5series has the select the 430X isa.
	(msp430_no_hwmult): If -mmcu has not been specified and
	msp430_hwmult_type is AUTO then return true.
	* config/msp430/msp430.h (EXTRA_SPEC_FUNCTIONS): Define.
	(LIB_SPEC): Add hardware multiply library selection.
	* config/msp430/t-msp430: Delete hardware multiply multilibs.
	Add rule to build driver-msp430.o
	* config/msp430/driver-msp430.c: New file.
	* config/msp430/msp430.opt (warn-mcu): New option.
	* doc/invoke.texi: Update description of -mhwmult=auto.
        Document -mwarn-mcu option.

tests	* gcc.target/msp430/msp_abi_div_funcs.c: New test.
	* gcc.target/msp430/mul_main.h: New test support file.
	* gcc.target/msp430/mul_none.c: New test.
	* gcc.target/msp430/mul_16bit.c: New test.
	* gcc.target/msp430/mul_32bit.c: New test.
	* gcc.target/msp430/mul_f5.c: New test.

libgcc	* config/msp430/mpy.c (__mulhi3): Use a faster algorithm.
	Allow for the second argument being negative.
	* config.host (extra_parts): Define for MSP430.  Create separate
	libraries for each of the hardware multiply formats.
	* config/msp430/lib2hw_mul.S: Build only the multiply routines
	that are needed.
	* config/msp430/lib2mul.c: Likewise.
	* config/msp430/t-msp430 (LIB2ADD): Remove lib2hw_mul.S.
	Add rules to build hardware multiply libraries.
	* config/msp430/lib2divSI.c: (__mspabi_divlu): Alias for
	__mspabi_divul function.
	(__mspabi_divllu): New stub function.

From-SVN: r231286
parent 4b5d538b
2015-12-04 Segher Boessenkool <segher@kernel.crashing.org>
2015-11-25 Nick Clifton <nickc@redhat.com>
* config.gcc (extra_gcc_objs): Define for MSP430.
* common/config/msp430/msp430-common.c (msp430_handle_option):
Pass both -mmcu and -mcpu on to the back end if they are both
defined.
* config/msp430/msp430.c (hwmult_name): New function.
(msp430_option_override): If an unrecognised MCU name is
detected only warn if the user has not provided suitable
-mhwmult and -mcpu options. Use msp430_warn_mcu to control
warning messages. Generate warnings about conflicts between
-mmcu and -mcpu and -mhwmult options.
If neither -mcpu nor -mmcu have been specified but -mhwmult=
f5series has the select the 430X isa.
(msp430_no_hwmult): If -mmcu has not been specified and
msp430_hwmult_type is AUTO then return true.
* config/msp430/msp430.h (EXTRA_SPEC_FUNCTIONS): Define.
(LIB_SPEC): Add hardware multiply library selection.
* config/msp430/t-msp430: Delete hardware multiply multilibs.
Add rule to build driver-msp430.o
* config/msp430/driver-msp430.c: New file.
* config/msp430/msp430.opt (warn-mcu): New option.
* doc/invoke.texi: Update description of -mhwmult=auto.
Document -mwarn-mcu option.
2015-12-04 Segher Boessenkool <segher&kernel.crashing.org>
* (cstore<mode>4_signed): New expander.
(cstore<mode>4): Call it.
......@@ -27,9 +27,9 @@
#include "opts.h"
#include "flags.h"
/* Handle -mcpu= and -mmcu= here. We want to ensure that only one
of these two options - the last specified on the command line -
is passed on to the msp430 backend. */
/* Check for generic -mcpu= and -mmcu= names here. If found then we
convert to a baseline cpu name. Otherwise we allow the option to
be passed on to the backend where it can be checked more fully. */
static bool
msp430_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED,
......@@ -46,13 +46,11 @@ msp430_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED,
|| strcasecmp (decoded->arg, "430xv2") == 0)
{
target_cpu = "msp430x";
target_mcu = NULL;
}
else if (strcasecmp (decoded->arg, "msp430") == 0
|| strcasecmp (decoded->arg, "430") == 0)
{
target_cpu = "msp430";
target_mcu = NULL;
}
else
{
......@@ -77,8 +75,6 @@ msp430_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED,
target_cpu = "msp430x";
target_mcu = NULL;
}
else
target_cpu = NULL;
break;
}
......
......@@ -2236,6 +2236,7 @@ msp430*-*-*)
c_target_objs="msp430-c.o"
cxx_target_objs="msp430-c.o"
tmake_file="${tmake_file} msp430/t-msp430"
extra_gcc_objs="driver-msp430.o"
;;
nds32le-*-*)
target_cpu_default="0"
......
......@@ -90,7 +90,7 @@ msp430_init_machine_status (void)
#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/devices-msp430.c
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. */
......@@ -717,6 +717,20 @@ msp430_mcu_name (void)
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)
{
......@@ -724,37 +738,90 @@ msp430_option_override (void)
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 != AUTO
&& msp430_hwmult_type != NONE)
warning (0, "MCU '%s' does not have hardware multiply support, but -mhwmult is set to %s",
target_mcu,
msp430_hwmult_type == SMALL ? "16-bit" : msp430_hwmult_type == LARGE ? "32-bit" : "f5series");
else if (msp430_hwmult_type == 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 == 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 == 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 == AUTO)
{
if (msp430_warn_mcu)
{
if (target_cpu == NULL)
warning (0,
"Unrecognised MCU name '%s', assuming that it is just a MSP430 with no hardware multiply.\nUse the -mcpu and -mhwmult options to set these explicitly.",
target_mcu);
else
warning (0,
"Unrecognised MCU name '%s', assuming that it has no hardware multiply.\nUse the -mhwmult option to set this explicitly.",
target_mcu);
}
msp430_hwmult_type = NONE;
}
else if (target_cpu == NULL)
{
if (msp430_warn_mcu)
warning (0,
"Unrecognised 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,
"Unrecognised 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 == F5SERIES)
msp430x = true;
if (TARGET_LARGE && !msp430x)
error ("-mlarge requires a 430X-compatible -mmcu=");
......@@ -955,7 +1022,8 @@ msp430_addr_space_pointer_mode (addr_space_t addrspace)
static machine_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. */
......@@ -1930,7 +1998,7 @@ const struct attribute_spec msp430_attribute_table[] =
{ 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 }
{ NULL, 0, 0, false, false, false, NULL, false }
};
#undef TARGET_ASM_FUNCTION_PROLOGUE
......@@ -2795,6 +2863,7 @@ msp430_init_dwarf_reg_sizes_extra (tree address)
rtx addr = expand_normal (address);
rtx mem = gen_rtx_MEM (BLKmode, addr);
/* This needs to match msp430_unwind_word_mode (above). */
if (!msp430x)
return;
......@@ -3089,8 +3158,8 @@ static const struct
{ "__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" },
......@@ -3186,9 +3255,12 @@ msp430_no_hwmult (void)
if (msp430_hwmult_type == NONE)
return true;
if (target_mcu == NULL || msp430_hwmult_type != AUTO)
if (msp430_hwmult_type != AUTO)
return false;
if (target_mcu == NULL)
return true;
if (target_mcu == cached_match)
return cached_result;
......
......@@ -65,13 +65,37 @@ extern bool msp430x;
is enabled (the GDB testsuite relies upon unused entities not being deleted). */
#define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}}"
extern const char * msp430_select_hwmult_lib (int, const char **);
# define EXTRA_SPEC_FUNCTIONS \
{ "msp430_hwmult_lib", msp430_select_hwmult_lib },
/* Specify the libraries to include on the linker command line.
Selecting the hardware multiply library to use is quite complex.
If the user has specified -mhwmult=FOO then the mapping is quite
easy (and could be handled here in the SPEC string), unless FOO
is set to AUTO. In this case the -mmcu= option must be consulted
instead. If the -mhwmult= option is not specified then the -mmcu=
option must then be examined. If neither -mhwmult= nor -mmcu= are
specified then a default hardware multiply library is used.
Examining the -mmcu=FOO option is difficult, and it is so this
reason that a spec function is used. There are so many possible
values of FOO that a table is used to look up the name and map
it to a hardware multiply library. This table (in device-msp430.c)
must be kept in sync with the same table in msp430.c. */
#undef LIB_SPEC
#define LIB_SPEC " \
--start-group \
%{mhwmult=auto:%{mmcu=*:%:msp430_hwmult_lib(mcu %{mmcu=*:%*});:%:msp430_hwmult_lib(default)}; \
mhwmult=*:%:msp430_hwmult_lib(hwmult %{mhwmult=*:%*}); \
mmcu=*:%:msp430_hwmult_lib(mcu %{mmcu=*:%*}); \
:%:msp430_hwmult_lib(default)} \
-lc \
-lgcc \
-lcrt \
%{msim:-lsim} \
%{!msim:-lnosys} \
--end-group \
%{!T*:%{!msim:%{mmcu=*:--script=%*.ld}}} \
%{!T*:%{!msim:%{!mmcu=*:%Tmsp430.ld}}} \
......
......@@ -10,6 +10,10 @@ mmcu=
Target Report ToLower Joined RejectNegative Var(target_mcu)
Specify the MCU to build for.
mwarn-mcu
Target Report Var(msp430_warn_mcu) Init(1)
Warn if an MCU name is unrecognised or conflicts with other options (default: on).
mcpu=
Target Report Joined RejectNegative Var(target_cpu)
Specify the ISA to build for: msp430, msp430x, msp430xv2.
......
......@@ -845,6 +845,7 @@ Objective-C and Objective-C++ Dialects}.
@emph{MSP430 Options}
@gccoptlist{-msim -masm-hex -mmcu= -mcpu= -mlarge -msmall -mrelax @gol
-mwarn-mcu @gol
-mcode-region= -mdata-region= @gol
-msilicon-errata= -msilicon-errata-warn= @gol
-mhwmult= -minrt}
......@@ -18510,6 +18511,16 @@ cause the linker to search for a script called @file{xxx.ld}.
This option is also passed on to the assembler.
@item -mwarn-mcu
@itemx -mno-warn-mcu
@opindex mwarn-mcu
@opindex mno-warn-mcu
This option enables or disables warnings about conflicts between the
MCU name specified by the @option{-mmcu} option and the ISA set by the
@option{-mcpu} option and/or the hardware multiply support set by the
@option{-mhwmult} option. It also toggles warnings about unrecognised
MCU names. This option is on by default.
@item -mcpu=
@opindex mcpu=
Specifies the ISA to use. Accepted values are @samp{msp430},
......@@ -18544,10 +18555,9 @@ for the original 16-bit-only multiply supported by early MCUs.
@samp{f5series} for the 16/32-bit multiply supported by F5-series MCUs.
A value of @samp{auto} can also be given. This tells GCC to deduce
the hardware multiply support based upon the MCU name provided by the
@option{-mmcu} option. If no @option{-mmcu} option is specified then
@samp{32bit} hardware multiply support is assumed. If the MCU name is
not recognised then no hardware multiply support is assumed.
@code{auto} is the default setting.
@option{-mmcu} option. If no @option{-mmcu} option is specified or if
the MCU name is not recognised then no hardware multiply support is
assumed. @code{auto} is the default setting.
Hardware multiplies are normally performed by calling a library
routine. This saves space in the generated code. When compiling at
......
2015-11-25 Nick Clifton <nickc@redhat.com>
* gcc.target/msp430/msp_abi_div_funcs.c: New test.
* gcc.target/msp430/mul_main.h: New test support file.
* gcc.target/msp430/mul_none.c: New test.
* gcc.target/msp430/mul_16bit.c: New test.
* gcc.target/msp430/mul_32bit.c: New test.
* gcc.target/msp430/mul_f5.c: New test.
2015-12-04 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/68680
......
......@@ -5,4 +5,4 @@
#define _GNU_SOURCE /* { dg-warning "redefined" } */
/* { dg-message "" "#define _GNU_SOURCE" {target *-*-* } 0 }
/* { dg-message "" "#define _GNU_SOURCE" {target *-*-* } 0 } */
/* { dg-do run } */
/* { dg-options "-std=c99" } */
extern int printf (const char *, ...);
extern void abort (void) __attribute__((noreturn));
typedef unsigned long uint32;
typedef unsigned long long uint64;
extern uint32 __mspabi_divul (uint32, uint32);
extern uint32 __mspabi_divlu (uint32, uint32);
extern uint64 __mspabi_divull (uint64, uint64);
extern uint64 __mspabi_divllu (uint64, uint64);
uint32 func1 (uint32, uint32) __attribute__ ((noinline));
uint32 func2 (uint32, uint32) __attribute__ ((noinline));
uint32 func3 (uint32, uint32) __attribute__ ((noinline));
uint64 func4 (uint64, uint64) __attribute__ ((noinline));
uint64 func5 (uint64, uint64) __attribute__ ((noinline));
uint64 func6 (uint64, uint64) __attribute__ ((noinline));
#define DEBUG 0
int
main (void)
{
int fail = 0;
if (func1 (7UL, 3UL) != 2UL)
{
#if DEBUG
printf ("FAIL: func1: 7 / 3 returns %lu\n", func1 (7UL, 3UL));
#endif
++ fail;
}
if (func2 (7UL, 3UL) != 2UL)
{
#if DEBUG
printf ("FAIL: func2: 7 / 3 returns %lu\n", func2 (7UL, 3UL));
#endif
++ fail;
}
if (func3 (7UL, 3UL) != 2UL)
{
#if DEBUG
printf ("FAIL: func4: 7 / 3 returns %lu\n", func3 (7UL, 3UL));
#endif
++ fail;
}
if (func4 (7ULL, 3ULL) != 2ULL)
{
#if DEBUG
printf ("FAIL: func4: 7 / 3 returns %llu\n", func4 (7ULL, 3ULL));
#endif
++ fail;
}
if (func5 (7ULL, 3ULL) != 2ULL)
{
#if DEBUG
printf ("FAIL: func5: 7 / 3 returns %llu\n", func5 (7ULL, 3ULL));
#endif
++ fail;
}
if (func6 (7ULL, 3ULL) != 2ULL)
{
#if DEBUG
printf ("FAIL: func6: 7 / 3 returns %llu\n", func6 (7ULL, 3ULL));
#endif
++ fail;
}
if (fail)
abort ();
return 0;
}
/* At high levels of optimization gcc will probably fold func1 and func4 into
main, but this does not really matter. Those two functions are just there
for a sanity check at low levels of optimization. */
uint32 func1 (uint32 a, uint32 b) { return a / b; }
uint32 func2 (uint32 a, uint32 b) { return __mspabi_divul (a, b); }
uint32 func3 (uint32 a, uint32 b) { return __mspabi_divlu (a, b); }
uint64 func4 (uint64 a, uint64 b) { return a / b; }
uint64 func5 (uint64 a, uint64 b) { return __mspabi_divull (a, b); }
uint64
func6 (uint64 a, uint64 b)
{
uint64 ret;
/* This test function is special. The correctly spelt ABI function
__mspabi_divull takes its first argument in registers R8::R11 and its
second argument in registers R12::R15, but GCC knows that __mspabi_divllu
is not the correct spelling and so it will use the normal function
calling convention - first argument in R12::R15, second argument on the
stack.
The stub function for __mspabi_divllu in libgcc just does a BRAnch to
the real __mspabi_divull function - it does *not* rearrange the arguments
or pull anything off the stack. This is correct, because in real code
that calls __mspabi_divllu, compiled by *old* versions of gcc, the
arguments will already be in the special ABI mandated locations.
As a result, in order to test __mspabi_divllu here, we have to put the
arguments into the correct registers ourselves and call __mspabi_divllu
manually. This does lead to some very inefficient code generation, but
that is not our concern here. */
#ifdef __MSP430X_LARGE__
__asm ("mov %A1, r8\n\
mov %B1, r9\n\
mov %C1, r10\n\
mov %D1, r11\n\
mov %A2, r12\n\
mov %B2, r13\n\
mov %C2, r14\n\
mov %D2, r15\n\
calla #__mspabi_divllu\n\
mov r12, %A0\n\
mov r13, %B0\n\
mov r14, %C0\n\
mov r15, %D0\n"
: "=r" (ret) : "r" (a), "m" (b));
#else
__asm ("mov %A1, r8\n\
mov %B1, r9\n\
mov %C1, r10\n\
mov %D1, r11\n\
mov %A2, r12\n\
mov %B2, r13\n\
mov %C2, r14\n\
mov %D2, r15\n\
call #__mspabi_divllu\n\
mov r12, %A0\n\
mov r13, %B0\n\
mov r14, %C0\n\
mov r15, %D0\n"
: "=r" (ret) : "r" (a), "m" (b));
#endif
return ret;
}
/* { dg-do run } */
/* { dg-options "-mhwmult=16bit" } */
#include "mul_main.h"
/* { dg-do run } */
/* { dg-options "-mhwmult=32bit" } */
#include "mul_main.h"
/* { dg-do run } */
/* { dg-options "-mhwmult=f5series" } */
#include "mul_main.h"
extern void abort (void) __attribute__((noreturn));
extern int printf (const char *, ...);
int func1 (int, int) __attribute__((noinline));
long func2 (int, int) __attribute__((noinline));
long func3 (long, long) __attribute__((noinline));
long long func4 (long, long) __attribute__((noinline));
long long func5 (long long, long long) __attribute__((noinline));
unsigned long func6 (unsigned int, unsigned int) __attribute__((noinline));
unsigned long long func7 (unsigned long, unsigned long) __attribute__((noinline));
#define DEBUG 0
int
main (void)
{
if (func1 (6, 7) != 42)
{
#if DEBUG
printf ("test1 fail: 6 * 7 = %d!\n", func1 (6, 7));
#endif
abort ();
}
if (func2 (1000, 1000) != 1000000L)
{
#if DEBUG
printf ("test2 fail: 1000 * 1000 = %ld!\n", func2 (1000, 1000));
#endif
abort ();
}
if (func3 (70000L, -32L) != -2240000L)
{
#if DEBUG
printf ("test3 fail: 70000 * -32 = %ld!\n", func3 (70000L, -32L));
#endif
abort ();
}
if (func4 (-40L, -80L) != 3200LL)
{
#if DEBUG
printf ("test4 fail: -40 * -80 = %lld!\n", func4 (-40L, -80L));
#endif
abort ();
}
if (func5(9LL, 9LL) != 81LL)
{
#if DEBUG
printf ("test5 fail: 9 * 9 = %lld!\n", func5 (9LL, 9LL));
#endif
abort ();
}
if (func6 (-2U, 8U) != 524272LU)
{
#if DEBUG
printf ("test6 fail: -2 * 8 = %lu!\n", func6 (-2U, 8U));
#endif
abort ();
}
if (func7 (99UL, 101UL) != 9999LLU)
{
#if DEBUG
printf ("test7 fail: 99 * 101 = %llu!\n", func7 (99UL, 101UL));
#endif
abort ();
}
return 0;
}
int func1 (int a, int b) { return a * b; }
long func2 (int a, int b) { return (long) a * (long) b; }
long func3 (long a, long b) { return a * b; }
long long func4 (long a, long b) { return (long long) a * (long long) b; }
long long func5 (long long a, long long b) { return a * b; }
unsigned long func6 (unsigned int a, unsigned int b) { return (unsigned long) a * (unsigned long) b; }
unsigned long long func7 (unsigned long a, unsigned long b) { return (unsigned long long) a * (unsigned long long) b; }
/* { dg-do run } */
/* { dg-options "-mhwmult=none" } */
#include "mul_main.h"
2015-12-04 Nick Clifton <nickc@redhat.com>
* config/msp430/mpy.c (__mulhi3): Use a faster algorithm.
Allow for the second argument being negative.
* config.host (extra_parts): Define for MSP430. Create separate
libraries for each of the hardware multiply formats.
* config/msp430/lib2hw_mul.S: Build only the multiply routines
that are needed.
* config/msp430/lib2mul.c: Likewise.
* config/msp430/t-msp430 (LIB2ADD): Remove lib2hw_mul.S.
Add rules to build hardware multiply libraries.
* config/msp430/lib2divSI.c: (__mspabi_divlu): Alias for
__mspabi_divul function.
(__mspabi_divllu): New stub function.
2015-12-01 John David Anglin <danglin@gcc.gnu.org>
* config/pa/fptr.c (__canonicalize_funcptr_for_compare): Initialize
......
......@@ -942,6 +942,7 @@ moxie-*-rtems*)
;;
msp430*-*-elf)
tmake_file="$tm_file t-crtstuff t-fdpbit msp430/t-msp430"
extra_parts="$extra_parts libmul_none.a libmul_16.a libmul_32.a libmul_f5.a"
;;
nds32*-elf*)
# Basic makefile fragment and extra_parts for crt stuff.
......
......@@ -40,3 +40,32 @@ typedef int word_type __attribute__ ((mode (__word__)));
#define NAME_MODE si
#include "msp430-divmod.h"
/* ---------------------------------------------------------------------*/
/* There is a typo in the MSP430 ABI document. It calls the unsigned
long integer division function __mspabi_divlu when it should be
__mspabi_divul. Likewise the unsigned long long integer division
function is called __mspabi_divllu when it should be __mspabi_divull.
Earlier versions of this toolchain used generate the ABI compliant
names, so in order to support object files built with those tools
we provide stub functions that call the correct routines. */
asm (".global __mspabi_divlu\n\
.set __mspabi_divlu, __mspabi_divul");
/* We cannot use the same trick for __mspabi_divllu as that is defined
in a different file. Instead we create a stub here. The cost of
executing the branch instruction will be trivial compared to the
cost of executing a long long division. */
#ifdef __MSP430X_LARGE__
asm (".global __mspabi_divllu\n\
__mspabi_divllu:\n\
BRA #__mspabi_divull");
#else
asm (".global __mspabi_divllu\n\
__mspabi_divllu:\n\
BR #__mspabi_divull");
#endif
......@@ -20,17 +20,42 @@
; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
; <http://www.gnu.org/licenses/>.
.macro start_func name
.pushsection .text.\name,"ax",@progbits
;; Macro to start a multiply function. Each function has three
;; names, and hence three entry points - although they all go
;; through the same code. The first name is the version generated
;; by GCC. The second is the MSP430 EABI mandated name for the
;; *software* version of the function. The third is the EABI
;; mandated name for the *hardware* version of the function.
;;
;; Since we are using the hardware and software names to point
;; to the same code this effectively means that we are mapping
;; the software function onto the hardware function. Thus if
;; the library containing this code is linked into an application
;; (before the libgcc.a library) *all* multiply functions will
;; be mapped onto the hardware versions.
;;
;; We construct each function in its own section so that linker
;; garbage collection can be used to delete any unused functions
;; from this file.
.macro start_func gcc_name eabi_soft_name eabi_hard_name
.pushsection .text.\gcc_name,"ax",@progbits
.p2align 1
.global \name
.type \name , @function
\name:
.global \eabi_hard_name
.type \eabi_hard_name , @function
\eabi_hard_name:
.global \eabi_soft_name
.type \eabi_soft_name , @function
\eabi_soft_name:
.global \gcc_name
.type \gcc_name , @function
\gcc_name:
PUSH.W sr ; Save current interrupt state
DINT ; Disable interrupts
NOP ; Account for latency
.endm
;; End a function started with the start_func macro.
.macro end_func name
#ifdef __MSP430X_LARGE__
POP.W sr
......@@ -42,6 +67,29 @@
.popsection
.endm
;; Like the start_func macro except that it is used to
;; create a false entry point that just jumps to the
;; software function (implemented elsewhere).
.macro fake_func gcc_name eabi_soft_name eabi_hard_name
.pushsection .text.\gcc_name,"ax",@progbits
.p2align 1
.global \eabi_hard_name
.type \eabi_hard_name , @function
\eabi_hard_name:
.global \gcc_name
.type \gcc_name , @function
\gcc_name:
#ifdef __MSP430X_LARGE__
BRA \eabi_soft_name
#else
BR \eabi_soft_name
#endif
.size \gcc_name , . - \gcc_name
.popsection
.endm
.macro mult16 OP1, OP2, RESULT
;* * 16-bit hardware multiply: int16 = int16 * int16
;*
......@@ -160,7 +208,66 @@
.endm
;; First generation MSP430 hardware multiplies ....
;; EABI mandated names:
;;
;; int16 __mspabi_mpyi (int16 x, int16 y)
;; Multiply int by int.
;; int16 __mspabi_mpyi_hw (int16 x, int16 y)
;; Multiply int by int. Uses hardware MPY16 or MPY32.
;; int16 __mspabi_mpyi_f5hw (int16 x, int16 y)
;; Multiply int by int. Uses hardware MPY32 (F5xx devices and up).
;;
;; int32 __mspabi_mpyl (int32 x, int32 y);
;; Multiply long by long.
;; int32 __mspabi_mpyl_hw (int32 x, int32 y)
;; Multiply long by long. Uses hardware MPY16.
;; int32 __mspabi_mpyl_hw32 (int32 x, int32 y)
;; Multiply long by long. Uses hardware MPY32 (F4xx devices).
;; int32 __mspabi_mpyl_f5hw (int32 x, int32 y)
;; Multiply long by long. Uses hardware MPY32 (F5xx devices and up).
;;
;; int64 __mspabi_mpyll (int64 x, int64 y)
;; Multiply long long by long long.
;; int64 __mspabi_mpyll_hw (int64 x, int64 y)
;; Multiply long long by long long. Uses hardware MPY16.
;; int64 __mspabi_mpyll_hw32 (int64 x, int64 y)
;; Multiply long long by long long. Uses hardware MPY32 (F4xx devices).
;; int64 __mspabi_mpyll_f5hw (int64 x, int64 y)
;; Multiply long long by long long. Uses hardware MPY32 (F5xx devices and up).
;;
;; int32 __mspabi_mpysl (int16 x, int16 y)
;; Multiply int by int; result is long.
;; int32 __mspabi_mpysl_hw(int16 x, int16 y)
;; Multiply int by int; result is long. Uses hardware MPY16 or MPY32
;; int32 __mspabi_mpysl_f5hw(int16 x, int16 y)
;; Multiply int by int; result is long. Uses hardware MPY32 (F5xx devices and up).
;;
;; int64 __mspabi_mpysll(int32 x, int32 y)
;; Multiply long by long; result is long long.
;; int64 __mspabi_mpysll_hw(int32 x, int32 y)
;; Multiply long by long; result is long long. Uses hardware MPY16.
;; int64 __mspabi_mpysll_hw32(int32 x, int32 y)
;; Multiply long by long; result is long long. Uses hardware MPY32 (F4xx devices).
;; int64 __mspabi_mpysll_f5hw(int32 x, int32 y)
;; Multiply long by long; result is long long. Uses hardware MPY32 (F5xx devices and up).
;;
;; uint32 __mspabi_mpyul(uint16 x, uint16 y)
;; Multiply unsigned int by unsigned int; result is unsigned long.
;; uint32 __mspabi_mpyul_hw(uint16 x, uint16 y)
;; Multiply unsigned int by unsigned int; result is unsigned long. Uses hardware MPY16 or MPY32
;; uint32 __mspabi_mpyul_f5hw(uint16 x, uint16 y)
;; Multiply unsigned int by unsigned int; result is unsigned long. Uses hardware MPY32 (F5xx devices and up).
;;
;; uint64 __mspabi_mpyull(uint32 x, uint32 y)
;; Multiply unsigned long by unsigned long; result is unsigned long long.
;; uint64 __mspabi_mpyull_hw(uint32 x, uint32 y)
;; Multiply unsigned long by unsigned long; result is unsigned long long. Uses hardware MPY16
;; uint64 __mspabi_mpyull_hw32(uint32 x, uint32 y)
;; Multiply unsigned long by unsigned long; result is unsigned long long. Uses hardware MPY32 (F4xx devices).
;; uint64 _ _mspabi_mpyull_f5hw(uint32 x, uint32 y)
;; Multiply unsigned long by unsigned long; result is unsigned long long. Uses hardware MPY32 (F5xx devices and up)
.set MPY_OP1, 0x0130
.set MPY_OP1_S, 0x0132
......@@ -169,58 +276,94 @@
.set MAC_OP2, 0x0138
.set RESULT_LO, 0x013A
.set RESULT_HI, 0x013C
start_func __mulhi2
#if defined MUL_16
;; First generation MSP430 hardware multiplies ...
start_func __mulhi2 __mspabi_mpyi __mspabi_mpyi_hw
mult16 MPY_OP1, MPY_OP2, RESULT_LO
end_func __mulhi2
end_func __mulhi2
start_func __mulsihi2
start_func __mulsihi2 __mspabi_mpysl __mspabi_mpysl_hw
mult1632 MPY_OP1_S, MPY_OP2, RESULT_LO, RESULT_HI
end_func __mulsihi2
end_func __mulsihi2
start_func __umulsihi2
start_func __umulsihi2 __mspabi_mpyul _mspabi_mpyul_hw
mult1632 MPY_OP1, MPY_OP2, RESULT_LO, RESULT_HI
end_func __umulsihi2
end_func __umulsihi2
start_func __mulsi2
start_func __mulsi2 __mspabi_mpyl __mspabi_mpyl_hw
mult32 MPY_OP1, MPY_OP2, MAC_OP1, MAC_OP2, RESULT_LO, RESULT_HI
end_func __mulsi2
end_func __mulsi2
start_func __mulsi2_hw32
;; FIXME: We do not have hardware implementations of these
;; routines, so just jump to the software versions instead.
fake_func __muldisi2 __mspabi_mpysll __mspabi_mpysll_hw
fake_func __umuldisi2 __mspabi_mpyull __mspabi_mpyull_hw
fake_func __muldi3 __mspabi_mpyll __mspabi_mpyll_hw
#elif defined MUL_32
;; Second generation MSP430 hardware multiplies ...
start_func __mulhi2 __mspabi_mpyi __mspabi_mpyi_hw
mult16 MPY_OP1, MPY_OP2, RESULT_LO
end_func __mulhi2
start_func __mulsihi2 __mspabi_mpysl __mspabi_mpysl_hw
mult1632 MPY_OP1_S, MPY_OP2, RESULT_LO, RESULT_HI
end_func __mulsihi2
start_func __umulsihi2 __mspabi_mpyul _mspabi_mpyul_hw
mult1632 MPY_OP1, MPY_OP2, RESULT_LO, RESULT_HI
end_func __umulsihi2
start_func __mulsi2_hw32 __mspabi_mpyl __mspabi_mpyl_hw32
mult32_hw 0x0140, 0x0142, 0x0150, 0x0152, 0x0154, 0x0156
end_func __mulsi2_hw32
end_func __mulsi2_hw32
start_func __muldisi2_hw32
start_func __muldisi2 __mspabi_mpysll __mspabi_mpysll_hw32
mult3264_hw 0x0144, 0x146, 0x0150, 0x0152, 0x0154, 0x0156, 0x0158, 0x015A
end_func __muldisi2_hw32
end_func __muldisi2
start_func __umuldisi2_hw32
start_func __umuldisi2 __mspabi_mpyull __mspabi_mpyull_hw32
mult3264_hw 0x0140, 0x142, 0x0150, 0x0152, 0x0154, 0x0156, 0x0158, 0x015A
end_func __umuldisi2_hw32
/* The F5xxx series of MCUs support the same 16-bit hardware
multiply, but it is accessed from different memory registers. */
end_func __umuldisi2
;; FIXME: Add a hardware version of this function.
fake_func __muldi3 __mspabi_mpyll __mspabi_mpyll_hw32
start_func __mulhi2_f5
#elif defined MUL_F5
/* The F5xxx series of MCUs support the same 16-bit and 32-bit multiply
as the second generation hardware, but they are accessed from different
memory registers. */
start_func __mulhi2_f5 __mspabi_mpyi __mspabi_mpyi_f5hw
mult16 0x04C0, 0x04C8, 0x04CA
end_func __mulhi2_f5
end_func __mulhi2_f5
start_func __mulsihi2_f5
start_func __mulsihi2 __mspabi_mpysl __mspabi_mpysl_f5hw
mult1632 0x04C2, 0x04C8, 0x04CA, 0x04CC
end_func __mulsihi2_f5
end_func __mulsihi2
start_func __umulsihi2_f5
start_func __umulsihi2 __mspabi_mpyul _mspabi_mpyul_f5hw
mult1632 0x04C0, 0x04C8, 0x04CA, 0x04CC
end_func __umulsihi2_f5
end_func __umulsihi2
start_func __mulsi2_f5
start_func __mulsi2_f5 __mspabi_mpyl __mspabi_mpyl_f5hw
mult32_hw 0x04D0, 0x04D2, 0x04E0, 0x04E2, 0x04E4, 0x04E6
end_func __mulsi2_f5
end_func __mulsi2_f5
start_func __muldisi2_f5
start_func __muldisi2 __mspabi_mpysll __mspabi_mpysll_f5hw
mult3264_hw 0x04D4, 0x04D6, 0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA
end_func __muldisi2_f5
end_func __muldisi2
start_func __umuldisi2_f5
start_func __umuldisi2 __mspabi_mpyull __mspabi_mpyull_f5hw
mult3264_hw 0x04D0, 0x04D2, 0x04E0, 0x04E2, 0x04E4, 0x04E6, 0x04E8, 0x04EA
end_func __umuldisi2_f5
end_func __umuldisi2
;; FIXME: Add a hardware version of this function.
fake_func __muldi3 __mspabi_mpyll __mspabi_mpyll_f5hw
#else
#error MUL type not defined
#endif
......@@ -30,29 +30,44 @@ typedef unsigned int uint08_type __attribute__ ((mode (QI)));
#define C3B(a,b,c) a##b##c
#define C3(a,b,c) C3B(a,b,c)
#if defined MUL_NONE
#define UINT_TYPE uint16_type
#define BITS_MINUS_1 15
#define NAME_MODE hi
#include "msp430-mul.h"
/* The software multiply library needs __mspabi_mpyll. */
#undef UINT_TYPE
#undef BITS_MINUS_1
#undef NAME_MODE
#define UINT_TYPE uint08_type
#define BITS_MINUS_1 7
#define NAME_MODE qi
#define UINT_TYPE uint32_type
#define BITS_MINUS_1 31
#define NAME_MODE si
#include "msp430-mul.h"
#elif defined MUL_16
signed long long
__mspabi_mpysll (signed long a, signed long b)
{
return (signed long long) a * (signed long long) b;
}
unsigned long long
__mspabi_mpyull (unsigned long a, unsigned long b)
{
return (unsigned long long) a * (unsigned long long) b;
}
#else
#undef UINT_TYPE
#undef BITS_MINUS_1
#undef NAME_MODE
#define UINT_TYPE uint32_type
#define BITS_MINUS_1 31
#define NAME_MODE si
#define UINT_TYPE uint08_type
#define BITS_MINUS_1 7
#define NAME_MODE qi
#include "msp430-mul.h"
#endif /* MUL_NONE */
......@@ -4,12 +4,23 @@ extern int __mulhi3 (int, int);
int
__mulhi3 (int x, int y)
{
volatile int rv = 0;
char bit;
int neg = 0;
int rv = 0;
while (y > 0)
if (y < 0)
{
rv += x;
y --;
y = - y;
neg = 1;
}
return rv;
for (bit = 0; y && bit < sizeof (y) * 8; bit ++)
{
if (y & 1)
rv += x;
x <<= 1;
y >>= 1;
}
return neg ? - rv : rv;
}
......@@ -35,7 +35,6 @@ LIB2ADD = \
$(srcdir)/config/msp430/srai.S \
$(srcdir)/config/msp430/srli.S \
$(srcdir)/config/msp430/cmpsi2.S \
$(srcdir)/config/msp430/lib2hw_mul.S \
$(srcdir)/config/msp430/floatunhisf.c \
$(srcdir)/config/msp430/floatunhidf.c \
$(srcdir)/config/msp430/floathidf.c \
......@@ -44,6 +43,33 @@ LIB2ADD = \
HOST_LIBGCC2_CFLAGS += -Os -ffunction-sections -fdata-sections -mhwmult=none
lib2_mul_none.o: $(srcdir)/config/msp430/lib2mul.c
$(gcc_compile) $^ -c -DMUL_NONE
lib2_mul_16bit.o: $(srcdir)/config/msp430/lib2mul.c
$(gcc_compile) $^ -c -DMUL_16
lib2hw_mul_16.o: $(srcdir)/config/msp430/lib2hw_mul.S
$(gcc_compile) $^ -c -DMUL_16
lib2hw_mul_32.o: $(srcdir)/config/msp430/lib2hw_mul.S
$(gcc_compile) $^ -c -DMUL_32
lib2hw_mul_f5.o: $(srcdir)/config/msp430/lib2hw_mul.S
$(gcc_compile) $^ -c -DMUL_F5
libmul_none.a: lib2_mul_none.o
$(AR_CREATE_FOR_TARGET) $@ $^
libmul_16.a: lib2hw_mul_16.o lib2_mul_16bit.o
$(AR_CREATE_FOR_TARGET) $@ $^
libmul_32.a: lib2hw_mul_32.o
$(AR_CREATE_FOR_TARGET) $@ $^
libmul_f5.a: lib2hw_mul_f5.o
$(AR_CREATE_FOR_TARGET) $@ $^
# Local Variables:
# mode: Makefile
# End:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment