mirror of git://gcc.gnu.org/git/gcc.git
re PR target/54760 ([SH] Add __builtin_thread_pointer, __builtin_set_thread_pointer)
PR target/54760 * config/sh/sh.md (*mov<mode>_gbr_load, *mov<mode>_gbr_store): New insns and accompanying unnamed splits. * config/sh/predicates.md (general_movsrc_operand, general_movdst_operand): Reject GBR addresses. * config/sh/sh-protos.h (sh_find_equiv_gbr_addr): New declaration. * config/sh/sh.c (sh_address_cost, sh_legitimate_address_p, sh_secondary_reload): Handle GBR addresses. (base_reg_disp): New class. (sh_find_base_reg_disp, sh_find_equiv_gbr_addr): New functions. PR target/54760 * gcc.target/sh/pr54760-2.c: New. * gcc.target/sh/pr54760-3.c: New. From-SVN: r192193
This commit is contained in:
parent
7f7b06c14f
commit
fce1e5fb8f
|
|
@ -1,3 +1,16 @@
|
|||
2012-10-08 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/54760
|
||||
* config/sh/sh.md (*mov<mode>_gbr_load, *mov<mode>_gbr_store): New
|
||||
insns and accompanying unnamed splits.
|
||||
* config/sh/predicates.md (general_movsrc_operand,
|
||||
general_movdst_operand): Reject GBR addresses.
|
||||
* config/sh/sh-protos.h (sh_find_equiv_gbr_addr): New declaration.
|
||||
* config/sh/sh.c (sh_address_cost, sh_legitimate_address_p,
|
||||
sh_secondary_reload): Handle GBR addresses.
|
||||
(base_reg_disp): New class.
|
||||
(sh_find_base_reg_disp, sh_find_equiv_gbr_addr): New functions.
|
||||
|
||||
2012-10-08 Hans-Peter Nilsson <hp@bitrange.com>
|
||||
|
||||
* config/mmix/mmix.c (mmix_output_octa): Don't assume
|
||||
|
|
|
|||
|
|
@ -409,6 +409,14 @@
|
|||
if (MEM_P (op))
|
||||
{
|
||||
rtx inside = XEXP (op, 0);
|
||||
|
||||
/* Disallow mems with GBR address here. They have to go through
|
||||
separate special patterns. */
|
||||
if ((REG_P (inside) && REGNO (inside) == GBR_REG)
|
||||
|| (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0))
|
||||
&& REGNO (XEXP (inside, 0)) == GBR_REG))
|
||||
return 0;
|
||||
|
||||
if (GET_CODE (inside) == CONST)
|
||||
inside = XEXP (inside, 0);
|
||||
|
||||
|
|
@ -466,6 +474,17 @@
|
|||
if (t_reg_operand (op, mode))
|
||||
return 0;
|
||||
|
||||
if (MEM_P (op))
|
||||
{
|
||||
rtx inside = XEXP (op, 0);
|
||||
/* Disallow mems with GBR address here. They have to go through
|
||||
separate special patterns. */
|
||||
if ((REG_P (inside) && REGNO (inside) == GBR_REG)
|
||||
|| (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0))
|
||||
&& REGNO (XEXP (inside, 0)) == GBR_REG))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only pre dec allowed. */
|
||||
if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == POST_INC)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ extern bool sh_vector_mode_supported_p (enum machine_mode);
|
|||
extern bool sh_cfun_trap_exit_p (void);
|
||||
extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
|
||||
enum machine_mode mode = VOIDmode);
|
||||
|
||||
extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
|
||||
#endif /* RTX_CODE */
|
||||
|
||||
extern void sh_cpu_cpp_builtins (cpp_reader* pfile);
|
||||
|
|
|
|||
|
|
@ -3610,6 +3610,10 @@ static int
|
|||
sh_address_cost (rtx x, enum machine_mode mode,
|
||||
addr_space_t as ATTRIBUTE_UNUSED, bool speed ATTRIBUTE_UNUSED)
|
||||
{
|
||||
/* 'GBR + 0'. Account one more because of R0 restriction. */
|
||||
if (REG_P (x) && REGNO (x) == GBR_REG)
|
||||
return 2;
|
||||
|
||||
/* Simple reg, post-inc, pre-dec addressing. */
|
||||
if (REG_P (x) || GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
|
||||
return 1;
|
||||
|
|
@ -3618,6 +3622,11 @@ sh_address_cost (rtx x, enum machine_mode mode,
|
|||
if (GET_CODE (x) == PLUS
|
||||
&& REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1)))
|
||||
{
|
||||
/* 'GBR + disp'. Account one more because of R0 restriction. */
|
||||
if (REGNO (XEXP (x, 0)) == GBR_REG
|
||||
&& gbr_displacement (XEXP (x, 1), mode))
|
||||
return 2;
|
||||
|
||||
const HOST_WIDE_INT offset = INTVAL (XEXP (x, 1));
|
||||
|
||||
if (offset == 0)
|
||||
|
|
@ -10185,11 +10194,16 @@ sh_legitimate_index_p (enum machine_mode mode, rtx op, bool consider_sh2a,
|
|||
REG+disp
|
||||
REG+r0
|
||||
REG++
|
||||
--REG */
|
||||
--REG
|
||||
GBR
|
||||
GBR+disp */
|
||||
|
||||
static bool
|
||||
sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
|
||||
{
|
||||
if (REG_P (x) && REGNO (x) == GBR_REG)
|
||||
return true;
|
||||
|
||||
if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
|
||||
return true;
|
||||
else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
|
||||
|
|
@ -10202,6 +10216,9 @@ sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
|
|||
rtx xop0 = XEXP (x, 0);
|
||||
rtx xop1 = XEXP (x, 1);
|
||||
|
||||
if (REG_P (xop0) && REGNO (xop0) == GBR_REG)
|
||||
return gbr_displacement (xop1, mode);
|
||||
|
||||
if (GET_MODE_SIZE (mode) <= 8
|
||||
&& MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
|
||||
&& sh_legitimate_index_p (mode, xop1, TARGET_SH2A, false))
|
||||
|
|
@ -13014,6 +13031,17 @@ sh_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
|
|||
{
|
||||
enum reg_class rclass = (enum reg_class) rclass_i;
|
||||
|
||||
if (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS
|
||||
&& REG_P (XEXP (XEXP (x, 0), 0))
|
||||
&& REGNO (XEXP (XEXP (x, 0), 0)) == GBR_REG)
|
||||
return rclass == R0_REGS ? NO_REGS : R0_REGS;
|
||||
|
||||
if (MEM_P (x) && REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) == GBR_REG)
|
||||
return rclass == R0_REGS ? NO_REGS : R0_REGS;
|
||||
|
||||
if (REG_P (x) && REGNO (x) == GBR_REG)
|
||||
return NO_REGS;
|
||||
|
||||
if (in_p)
|
||||
{
|
||||
if (REGCLASS_HAS_FP_REG (rclass)
|
||||
|
|
@ -13248,4 +13276,150 @@ sh_can_use_simple_return_p (void)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
Address mode optimization support code
|
||||
*/
|
||||
|
||||
typedef HOST_WIDE_INT disp_t;
|
||||
static const disp_t MIN_DISP = HOST_WIDE_INT_MIN;
|
||||
static const disp_t MAX_DISP = HOST_WIDE_INT_MAX;
|
||||
static const disp_t INVALID_DISP = MAX_DISP;
|
||||
|
||||
/* A memory reference which is described by a base register and a
|
||||
displacement. */
|
||||
class base_reg_disp
|
||||
{
|
||||
public:
|
||||
base_reg_disp (rtx br, disp_t d);
|
||||
|
||||
bool is_reg (void) const;
|
||||
bool is_disp (void) const;
|
||||
rtx reg (void) const;
|
||||
disp_t disp (void) const;
|
||||
|
||||
private:
|
||||
rtx reg_;
|
||||
disp_t disp_;
|
||||
};
|
||||
|
||||
inline
|
||||
base_reg_disp::base_reg_disp (rtx br, disp_t d)
|
||||
: reg_ (br), disp_ (d)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool
|
||||
base_reg_disp::is_reg (void) const
|
||||
{
|
||||
return reg_ != NULL_RTX && disp_ != INVALID_DISP;
|
||||
}
|
||||
|
||||
inline bool
|
||||
base_reg_disp::is_disp (void) const
|
||||
{
|
||||
return reg_ == NULL_RTX && disp_ != INVALID_DISP;
|
||||
}
|
||||
|
||||
inline rtx
|
||||
base_reg_disp::reg (void) const
|
||||
{
|
||||
return reg_;
|
||||
}
|
||||
|
||||
inline disp_t
|
||||
base_reg_disp::disp (void) const
|
||||
{
|
||||
return disp_;
|
||||
}
|
||||
|
||||
/* Find the base register and calculate the displacement for a given
|
||||
address rtx 'x'.
|
||||
This is done by walking the insn list backwards and following SET insns
|
||||
that set the value of the specified reg 'x'. */
|
||||
static base_reg_disp
|
||||
sh_find_base_reg_disp (rtx insn, rtx x, disp_t disp = 0, rtx base_reg = NULL)
|
||||
{
|
||||
if (REG_P (x))
|
||||
{
|
||||
if (REGNO (x) == GBR_REG)
|
||||
return base_reg_disp (x, disp);
|
||||
|
||||
/* We've reached a hard-reg. This is probably the point where
|
||||
function args are copied to pseudos. Do not go any further and
|
||||
stick to the pseudo. If the original mem addr was in a hard reg
|
||||
from the beginning, it will become the base reg. */
|
||||
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
|
||||
return base_reg_disp (base_reg != NULL ? base_reg : x, disp);
|
||||
|
||||
/* Try to find the previous insn that sets the reg. */
|
||||
for (rtx i = prev_nonnote_insn (insn); i != NULL;
|
||||
i = prev_nonnote_insn (i))
|
||||
{
|
||||
if (!NONJUMP_INSN_P (i))
|
||||
continue;
|
||||
|
||||
rtx p = PATTERN (i);
|
||||
if (p != NULL && GET_CODE (p) == SET && REG_P (XEXP (p, 0))
|
||||
&& REGNO (XEXP (p, 0)) == REGNO (x))
|
||||
{
|
||||
/* If the recursion can't find out any more details about the
|
||||
source of the set, then this reg becomes our new base reg. */
|
||||
return sh_find_base_reg_disp (i, XEXP (p, 1), disp, XEXP (p, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/* When here, no previous insn was found that sets the reg.
|
||||
The input reg is already the base reg. */
|
||||
return base_reg_disp (x, disp);
|
||||
}
|
||||
|
||||
else if (GET_CODE (x) == PLUS)
|
||||
{
|
||||
base_reg_disp left_val = sh_find_base_reg_disp (insn, XEXP (x, 0));
|
||||
base_reg_disp right_val = sh_find_base_reg_disp (insn, XEXP (x, 1));
|
||||
|
||||
/* Either left or right val must be a reg.
|
||||
We don't handle the case of 'reg + reg' here. */
|
||||
if (left_val.is_reg () && right_val.is_disp ())
|
||||
return base_reg_disp (left_val.reg (), left_val.disp ()
|
||||
+ right_val.disp () + disp);
|
||||
else if (right_val.is_reg () && left_val.is_disp ())
|
||||
return base_reg_disp (right_val.reg (), right_val.disp ()
|
||||
+ left_val.disp () + disp);
|
||||
else
|
||||
return base_reg_disp (base_reg, disp);
|
||||
}
|
||||
|
||||
else if (CONST_INT_P (x))
|
||||
return base_reg_disp (NULL, disp + INTVAL (x));
|
||||
|
||||
/* Didn't find anything useful. */
|
||||
return base_reg_disp (base_reg, disp);
|
||||
}
|
||||
|
||||
/* Given an insn and a memory operand, try to find an equivalent GBR
|
||||
based memory address and return the corresponding new memory address.
|
||||
Return NULL_RTX if not found. */
|
||||
rtx
|
||||
sh_find_equiv_gbr_addr (rtx insn, rtx mem)
|
||||
{
|
||||
if (!MEM_P (mem))
|
||||
return NULL_RTX;
|
||||
|
||||
/* Leave post/pre inc/dec or any other side effect addresses alone. */
|
||||
if (side_effects_p (XEXP (mem, 0)))
|
||||
return NULL_RTX;
|
||||
|
||||
base_reg_disp gbr_disp = sh_find_base_reg_disp (insn, XEXP (mem, 0));
|
||||
|
||||
if (gbr_disp.is_reg () && REGNO (gbr_disp.reg ()) == GBR_REG)
|
||||
{
|
||||
rtx disp = GEN_INT (gbr_disp.disp ());
|
||||
if (gbr_displacement (disp, GET_MODE (mem)))
|
||||
return gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, GBR_REG), disp);
|
||||
}
|
||||
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
#include "gt-sh.h"
|
||||
|
|
|
|||
|
|
@ -10060,6 +10060,135 @@ label:
|
|||
"ldc %0,gbr"
|
||||
[(set_attr "type" "move")])
|
||||
|
||||
;;------------------------------------------------------------------------------
|
||||
;; Thread pointer relative memory loads and stores.
|
||||
;;
|
||||
;; On SH there are GBR displacement address modes which can be utilized to
|
||||
;; access memory behind the thread pointer.
|
||||
;; Since we do not allow using GBR for general purpose memory accesses, these
|
||||
;; GBR addressing modes are formed by the combine pass.
|
||||
;; This could be done with fewer patterns than below by using a mem predicate
|
||||
;; for the GBR mem, but then reload would try to reload addresses with a
|
||||
;; zero displacement for some strange reason.
|
||||
|
||||
(define_insn "*mov<mode>_gbr_load"
|
||||
[(set (match_operand:QIHISI 0 "register_operand" "=z")
|
||||
(mem:QIHISI (plus:SI (reg:SI GBR_REG)
|
||||
(match_operand:QIHISI 1 "gbr_displacement"))))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bwl> @(%O1,gbr),%0"
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*mov<mode>_gbr_load"
|
||||
[(set (match_operand:QIHISI 0 "register_operand" "=z")
|
||||
(mem:QIHISI (reg:SI GBR_REG)))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bwl> @(0,gbr),%0"
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*mov<mode>_gbr_load"
|
||||
[(set (match_operand:SI 0 "register_operand" "=z")
|
||||
(sign_extend:SI
|
||||
(mem:QIHI (plus:SI (reg:SI GBR_REG)
|
||||
(match_operand:QIHI 1 "gbr_displacement")))))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bw> @(%O1,gbr),%0"
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*mov<mode>_gbr_load"
|
||||
[(set (match_operand:SI 0 "register_operand" "=z")
|
||||
(sign_extend:SI (mem:QIHI (reg:SI GBR_REG))))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bw> @(0,gbr),%0"
|
||||
[(set_attr "type" "load")])
|
||||
|
||||
(define_insn "*mov<mode>_gbr_store"
|
||||
[(set (mem:QIHISI (plus:SI (reg:SI GBR_REG)
|
||||
(match_operand:QIHISI 0 "gbr_displacement")))
|
||||
(match_operand:QIHISI 1 "register_operand" "z"))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bwl> %1,@(%O0,gbr)"
|
||||
[(set_attr "type" "store")])
|
||||
|
||||
(define_insn "*mov<mode>_gbr_store"
|
||||
[(set (mem:QIHISI (reg:SI GBR_REG))
|
||||
(match_operand:QIHISI 0 "register_operand" "z"))]
|
||||
"TARGET_SH1"
|
||||
"mov.<bwl> %0,@(0,gbr)"
|
||||
[(set_attr "type" "store")])
|
||||
|
||||
;; Sometimes memory accesses do not get combined with the store_gbr insn,
|
||||
;; in particular when the displacements are in the range of the regular move
|
||||
;; insns. Thus, in the first split pass after the combine pass we search
|
||||
;; for missed opportunities and try to fix them up ourselves.
|
||||
;; If an equivalent GBR address can be determined the load / store is split
|
||||
;; into one of the GBR load / store patterns.
|
||||
;; All of that must happen before reload (GBR address modes use R0 as the
|
||||
;; other operand) and there's no point of doing it if the GBR is not
|
||||
;; referenced in a function at all.
|
||||
(define_split
|
||||
[(set (match_operand:QIHISI 0 "register_operand")
|
||||
(match_operand:QIHISI 1 "memory_operand"))]
|
||||
"TARGET_SH1 && !reload_in_progress && !reload_completed
|
||||
&& df_regs_ever_live_p (GBR_REG)"
|
||||
[(set (match_dup 0) (match_dup 1))]
|
||||
{
|
||||
rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
|
||||
if (gbr_mem != NULL_RTX)
|
||||
operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem);
|
||||
else
|
||||
FAIL;
|
||||
})
|
||||
|
||||
(define_split
|
||||
[(set (match_operand:SI 0 "register_operand")
|
||||
(sign_extend:SI (match_operand:QIHI 1 "memory_operand")))]
|
||||
"TARGET_SH1 && !reload_in_progress && !reload_completed
|
||||
&& df_regs_ever_live_p (GBR_REG)"
|
||||
[(set (match_dup 0) (sign_extend:SI (match_dup 1)))]
|
||||
{
|
||||
rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
|
||||
if (gbr_mem != NULL_RTX)
|
||||
operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem);
|
||||
else
|
||||
FAIL;
|
||||
})
|
||||
|
||||
;; On SH2A we've got movu.b and movu.w for doing zero-extending mem loads.
|
||||
;; Split those so that a GBR load can be used.
|
||||
(define_split
|
||||
[(set (match_operand:SI 0 "register_operand")
|
||||
(zero_extend:SI (match_operand:QIHI 1 "memory_operand")))]
|
||||
"TARGET_SH2A && !reload_in_progress && !reload_completed
|
||||
&& df_regs_ever_live_p (GBR_REG)"
|
||||
[(set (match_dup 2) (match_dup 1))
|
||||
(set (match_dup 0) (zero_extend:SI (match_dup 2)))]
|
||||
{
|
||||
rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]);
|
||||
if (gbr_mem != NULL_RTX)
|
||||
{
|
||||
operands[2] = gen_reg_rtx (GET_MODE (operands[1]));
|
||||
operands[1] = change_address (operands[1], GET_MODE (operands[1]),
|
||||
gbr_mem);
|
||||
}
|
||||
else
|
||||
FAIL;
|
||||
})
|
||||
|
||||
(define_split
|
||||
[(set (match_operand:QIHISI 0 "memory_operand")
|
||||
(match_operand:QIHISI 1 "register_operand"))]
|
||||
"TARGET_SH1 && !reload_in_progress && !reload_completed
|
||||
&& df_regs_ever_live_p (GBR_REG)"
|
||||
[(set (match_dup 0) (match_dup 1))]
|
||||
{
|
||||
rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[0]);
|
||||
if (gbr_mem != NULL_RTX)
|
||||
operands[0] = change_address (operands[0], GET_MODE (operands[0]), gbr_mem);
|
||||
else
|
||||
FAIL;
|
||||
})
|
||||
|
||||
;;------------------------------------------------------------------------------
|
||||
;; case instruction for switch statements.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
2012-10-08 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/54760
|
||||
* gcc.target/sh/pr54760-2.c: New.
|
||||
* gcc.target/sh/pr54760-3.c: New.
|
||||
|
||||
2012-10-07 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
PR c++/51422
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
/* Check that thread pointer relative memory accesses are converted to
|
||||
gbr displacement address modes. If we see a gbr register store
|
||||
instruction something is not working properly. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O1" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
|
||||
/* { dg-final { scan-assembler-times "stc\tgbr" 0 } } */
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Simple GBR load.
|
||||
*/
|
||||
#define func(name, type, disp)\
|
||||
int \
|
||||
name ## _tp_load (void) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
return tp[disp]; \
|
||||
}
|
||||
|
||||
func (test00, int, 0)
|
||||
func (test01, int, 5)
|
||||
func (test02, int, 255)
|
||||
|
||||
func (test03, short, 0)
|
||||
func (test04, short, 5)
|
||||
func (test05, short, 255)
|
||||
|
||||
func (test06, char, 0)
|
||||
func (test07, char, 5)
|
||||
func (test08, char, 255)
|
||||
|
||||
func (test09, unsigned int, 0)
|
||||
func (test10, unsigned int, 5)
|
||||
func (test11, unsigned int, 255)
|
||||
|
||||
func (test12, unsigned short, 0)
|
||||
func (test13, unsigned short, 5)
|
||||
func (test14, unsigned short, 255)
|
||||
|
||||
func (test15, unsigned char, 0)
|
||||
func (test16, unsigned char, 5)
|
||||
func (test17, unsigned char, 255)
|
||||
|
||||
#undef func
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Simple GBR store.
|
||||
*/
|
||||
#define func(name, type, disp)\
|
||||
void \
|
||||
name ## _tp_store (int a) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
tp[disp] = (type)a; \
|
||||
}
|
||||
|
||||
func (test00, int, 0)
|
||||
func (test01, int, 5)
|
||||
func (test02, int, 255)
|
||||
|
||||
func (test03, short, 0)
|
||||
func (test04, short, 5)
|
||||
func (test05, short, 255)
|
||||
|
||||
func (test06, char, 0)
|
||||
func (test07, char, 5)
|
||||
func (test08, char, 255)
|
||||
|
||||
func (test09, unsigned int, 0)
|
||||
func (test10, unsigned int, 5)
|
||||
func (test11, unsigned int, 255)
|
||||
|
||||
func (test12, unsigned short, 0)
|
||||
func (test13, unsigned short, 5)
|
||||
func (test14, unsigned short, 255)
|
||||
|
||||
func (test15, unsigned char, 0)
|
||||
func (test16, unsigned char, 5)
|
||||
func (test17, unsigned char, 255)
|
||||
|
||||
#undef func
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Arithmetic on the result of a GBR load.
|
||||
*/
|
||||
#define func(name, type, disp, op, opname)\
|
||||
int \
|
||||
name ## _tp_load_arith_ ##opname (int a) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
return tp[disp] op a; \
|
||||
}
|
||||
|
||||
#define funcs(op, opname) \
|
||||
func (test00, int, 0, op, opname) \
|
||||
func (test01, int, 5, op, opname) \
|
||||
func (test02, int, 255, op, opname) \
|
||||
func (test03, short, 0, op, opname) \
|
||||
func (test04, short, 5, op, opname) \
|
||||
func (test05, short, 255, op, opname) \
|
||||
func (test06, char, 0, op, opname) \
|
||||
func (test07, char, 5, op, opname) \
|
||||
func (test08, char, 255, op, opname) \
|
||||
func (test09, unsigned int, 0, op, opname) \
|
||||
func (test10, unsigned int, 5, op, opname) \
|
||||
func (test11, unsigned int, 255, op, opname) \
|
||||
func (test12, unsigned short, 0, op, opname) \
|
||||
func (test13, unsigned short, 5, op, opname) \
|
||||
func (test14, unsigned short, 255, op, opname) \
|
||||
func (test15, unsigned char, 0, op, opname) \
|
||||
func (test16, unsigned char, 5, op, opname) \
|
||||
func (test17, unsigned char, 255, op, opname) \
|
||||
|
||||
funcs (+, plus)
|
||||
funcs (-, minus)
|
||||
funcs (*, mul)
|
||||
funcs (&, and)
|
||||
funcs (|, or)
|
||||
funcs (^, xor)
|
||||
|
||||
#undef funcs
|
||||
#undef func
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Arithmetic of the result of two GBR loads.
|
||||
*/
|
||||
#define func(name, type, disp0, disp1, op, opname)\
|
||||
int \
|
||||
name ## _tp_load_load_arith_ ##opname (void) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
return tp[disp0] op tp[disp1]; \
|
||||
}
|
||||
|
||||
#define funcs(op, opname) \
|
||||
func (test00, int, 0, 5, op, opname) \
|
||||
func (test02, int, 1, 255, op, opname) \
|
||||
func (test03, short, 0, 5, op, opname) \
|
||||
func (test05, short, 1, 255, op, opname) \
|
||||
func (test06, char, 0, 5, op, opname) \
|
||||
func (test08, char, 1, 255, op, opname) \
|
||||
func (test09, unsigned int, 0, 5, op, opname) \
|
||||
func (test11, unsigned int, 1, 255, op, opname) \
|
||||
func (test12, unsigned short, 0, 5, op, opname) \
|
||||
func (test14, unsigned short, 1, 255, op, opname) \
|
||||
func (test15, unsigned char, 0, 5, op, opname) \
|
||||
func (test17, unsigned char, 1, 255, op, opname) \
|
||||
|
||||
funcs (+, plus)
|
||||
funcs (-, minus)
|
||||
funcs (*, mul)
|
||||
funcs (&, and)
|
||||
funcs (|, or)
|
||||
funcs (^, xor)
|
||||
|
||||
#undef funcs
|
||||
#undef func
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
GBR load GBR store copy.
|
||||
*/
|
||||
|
||||
#define func(name, type, disp0, disp1)\
|
||||
void \
|
||||
name ## _tp_copy (void) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
tp[disp0] = tp[disp1]; \
|
||||
}
|
||||
|
||||
func (test00, int, 0, 5)
|
||||
func (test02, int, 1, 255)
|
||||
func (test03, short, 0, 5)
|
||||
func (test05, short, 1, 255)
|
||||
func (test06, char, 0, 5)
|
||||
func (test08, char, 1, 255)
|
||||
func (test09, unsigned int, 0, 5)
|
||||
func (test11, unsigned int, 1, 255)
|
||||
func (test12, unsigned short, 0, 5)
|
||||
func (test14, unsigned short, 1, 255)
|
||||
func (test15, unsigned char, 0, 5)
|
||||
func (test17, unsigned char, 1, 255)
|
||||
|
||||
#undef func
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
GBR load, arithmetic, GBR store
|
||||
*/
|
||||
|
||||
#define func(name, type, disp, op, opname)\
|
||||
void \
|
||||
name ## _tp_load_arith_store_ ##opname (int a) \
|
||||
{ \
|
||||
type* tp = (type*)__builtin_thread_pointer (); \
|
||||
tp[disp] op a; \
|
||||
}
|
||||
|
||||
#define funcs(op, opname) \
|
||||
func (test00, int, 0, op, opname) \
|
||||
func (test01, int, 5, op, opname) \
|
||||
func (test02, int, 255, op, opname) \
|
||||
func (test03, short, 0, op, opname) \
|
||||
func (test04, short, 5, op, opname) \
|
||||
func (test05, short, 255, op, opname) \
|
||||
func (test06, char, 0, op, opname) \
|
||||
func (test07, char, 5, op, opname) \
|
||||
func (test08, char, 255, op, opname) \
|
||||
func (test09, unsigned int, 0, op, opname) \
|
||||
func (test10, unsigned int, 5, op, opname) \
|
||||
func (test11, unsigned int, 255, op, opname) \
|
||||
func (test12, unsigned short, 0, op, opname) \
|
||||
func (test13, unsigned short, 5, op, opname) \
|
||||
func (test14, unsigned short, 255, op, opname) \
|
||||
func (test15, unsigned char, 0, op, opname) \
|
||||
func (test16, unsigned char, 5, op, opname) \
|
||||
func (test17, unsigned char, 255, op, opname) \
|
||||
|
||||
funcs (+=, plus)
|
||||
funcs (-=, minus)
|
||||
funcs (*=, mul)
|
||||
funcs (&=, and)
|
||||
funcs (|=, or)
|
||||
funcs (^=, xor)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* Check that these thread relative memory accesses play along with
|
||||
surrounding code.
|
||||
These should be moved to C torture tests once there are target
|
||||
independent thread_pointer built-in functions available. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O1" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
|
||||
|
||||
int
|
||||
test00 (void* p, int x)
|
||||
{
|
||||
int* tcb = (int*)__builtin_thread_pointer ();
|
||||
int r = tcb[4];
|
||||
|
||||
__builtin_set_thread_pointer (p);
|
||||
|
||||
tcb = (int*)__builtin_thread_pointer ();
|
||||
return tcb[255] + r;
|
||||
}
|
||||
|
||||
int
|
||||
test01 (void)
|
||||
{
|
||||
unsigned short* tcb = (unsigned short*)__builtin_thread_pointer ();
|
||||
return tcb[500];
|
||||
}
|
||||
|
||||
void
|
||||
test02 (int* x, int a, int b)
|
||||
{
|
||||
int* tcb = (int*)__builtin_thread_pointer ();
|
||||
tcb[50] = a;
|
||||
|
||||
__builtin_set_thread_pointer (x);
|
||||
|
||||
tcb = (int*)__builtin_thread_pointer ();
|
||||
tcb[40] = b;
|
||||
}
|
||||
|
||||
int
|
||||
test03 (const int* x, int c)
|
||||
{
|
||||
volatile int* tcb = (volatile int*)__builtin_thread_pointer ();
|
||||
|
||||
int s = 0;
|
||||
int i;
|
||||
for (i = 0; i < c; ++i)
|
||||
s ^= x[i] + tcb[40];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
test04 (const int* x, int c, int** xx, int d)
|
||||
{
|
||||
int s = 0;
|
||||
int i;
|
||||
for (i = 0; i < c; ++i)
|
||||
{
|
||||
volatile int* tcb = (volatile int*)__builtin_thread_pointer ();
|
||||
tcb[20] = s;
|
||||
|
||||
__builtin_set_thread_pointer (xx[i]);
|
||||
|
||||
tcb = (volatile int*)__builtin_thread_pointer ();
|
||||
s ^= x[i] + tcb[40] + d;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
Loading…
Reference in New Issue