xtensa-config.h (XCHAL_HAVE_THREADPTR): New.

include/
	* xtensa-config.h (XCHAL_HAVE_THREADPTR): New.
	(XCHAL_HAVE_RELEASE_SYNC, XCHAL_HAVE_S32C1I): New.
gcc/
	* config/xtensa/xtensa.c (xtensa_expand_mask_and_shift): New.
	(struct alignment_context, init_alignment_context): New.
	(xtensa_expand_compare_and_swap, xtensa_expand_atomic): New.
	* config/xtensa/xtensa.h (XCHAL_HAVE_RELEASE_SYNC): Add default.
	(XCHAL_HAVE_S32C1I): Likewise.
	(TARGET_RELEASE_SYNC, TARGET_S32C1I): New.
	* config/xtensa/xtensa.md (UNSPECV_MEMW): New constant.
	(UNSPECV_S32RI, UNSPECV_S32C1I): Likewise.
	(ATOMIC, HQI): New macros.
	(memory_barrier, *memory_barrier): New.
	(sync_lock_releasesi): New.
	(sync_compare_and_swapsi, sync_compare_and_swap<mode>): New.
	(sync_lock_test_and_set<mode>): New.
	(sync_<atomic><mode>): New.
	(sync_old_<atomic><mode>, sync_new_<atomic><mode>): New.
	* config/xtensa/xtensa-protos.h (xtensa_expand_compare_and_swap): New.
	(xtensa_expand_atomic): New.
gcc/testsuite/	
	* lib/target-supports.exp (check_effective_target_sync_int_long):
	Enable for xtensa.
	(check_effective_target_sync_char_short): Likewise.

From-SVN: r126728
This commit is contained in:
Bob Wilson 2007-07-18 18:51:21 +00:00 committed by Bob Wilson
parent 8a91c45bbf
commit 2a48b790b2
9 changed files with 435 additions and 3 deletions

View File

@ -1,3 +1,23 @@
2007-07-18 Bob Wilson <bob.wilson@acm.org>
* config/xtensa/xtensa.c (xtensa_expand_mask_and_shift): New.
(struct alignment_context, init_alignment_context): New.
(xtensa_expand_compare_and_swap, xtensa_expand_atomic): New.
* config/xtensa/xtensa.h (XCHAL_HAVE_RELEASE_SYNC): Add default.
(XCHAL_HAVE_S32C1I): Likewise.
(TARGET_RELEASE_SYNC, TARGET_S32C1I): New.
* config/xtensa/xtensa.md (UNSPECV_MEMW): New constant.
(UNSPECV_S32RI, UNSPECV_S32C1I): Likewise.
(ATOMIC, HQI): New macros.
(memory_barrier, *memory_barrier): New.
(sync_lock_releasesi): New.
(sync_compare_and_swapsi, sync_compare_and_swap<mode>): New.
(sync_lock_test_and_set<mode>): New.
(sync_<atomic><mode>): New.
(sync_old_<atomic><mode>, sync_new_<atomic><mode>): New.
* config/xtensa/xtensa-protos.h (xtensa_expand_compare_and_swap): New.
(xtensa_expand_atomic): New.
2007-07-18 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
PR target/30652

View File

@ -47,6 +47,8 @@ extern void xtensa_split_operand_pair (rtx *, enum machine_mode);
extern int xtensa_emit_move_sequence (rtx *, enum machine_mode);
extern rtx xtensa_copy_incoming_a7 (rtx);
extern void xtensa_expand_nonlocal_goto (rtx *);
extern void xtensa_expand_compare_and_swap (rtx, rtx, rtx, rtx);
extern void xtensa_expand_atomic (enum rtx_code, rtx, rtx, rtx, bool);
extern void xtensa_emit_loop_end (rtx, rtx *);
extern char *xtensa_emit_branch (bool, bool, rtx *);
extern char *xtensa_emit_bit_branch (bool, bool, rtx *);

View File

@ -1198,6 +1198,263 @@ xtensa_init_machine_status (void)
}
/* Shift VAL of mode MODE left by COUNT bits. */
static inline rtx
xtensa_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
{
val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
NULL_RTX, 1, OPTAB_DIRECT);
return expand_simple_binop (SImode, ASHIFT, val, count,
NULL_RTX, 1, OPTAB_DIRECT);
}
/* Structure to hold the initial parameters for a compare_and_swap operation
in HImode and QImode. */
struct alignment_context
{
rtx memsi; /* SI aligned memory location. */
rtx shift; /* Bit offset with regard to lsb. */
rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */
rtx modemaski; /* ~modemask */
};
/* Initialize structure AC for word access to HI and QI mode memory. */
static void
init_alignment_context (struct alignment_context *ac, rtx mem)
{
enum machine_mode mode = GET_MODE (mem);
rtx byteoffset = NULL_RTX;
bool aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
if (aligned)
ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */
else
{
/* Alignment is unknown. */
rtx addr, align;
/* Force the address into a register. */
addr = force_reg (Pmode, XEXP (mem, 0));
/* Align it to SImode. */
align = expand_simple_binop (Pmode, AND, addr,
GEN_INT (-GET_MODE_SIZE (SImode)),
NULL_RTX, 1, OPTAB_DIRECT);
/* Generate MEM. */
ac->memsi = gen_rtx_MEM (SImode, align);
MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
byteoffset = expand_simple_binop (Pmode, AND, addr,
GEN_INT (GET_MODE_SIZE (SImode) - 1),
NULL_RTX, 1, OPTAB_DIRECT);
}
/* Calculate shiftcount. */
if (TARGET_BIG_ENDIAN)
{
ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
if (!aligned)
ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
NULL_RTX, 1, OPTAB_DIRECT);
}
else
{
if (aligned)
ac->shift = NULL_RTX;
else
ac->shift = byteoffset;
}
if (ac->shift != NULL_RTX)
{
/* Shift is the byte count, but we need the bitcount. */
ac->shift = expand_simple_binop (SImode, MULT, ac->shift,
GEN_INT (BITS_PER_UNIT),
NULL_RTX, 1, OPTAB_DIRECT);
ac->modemask = expand_simple_binop (SImode, ASHIFT,
GEN_INT (GET_MODE_MASK (mode)),
ac->shift,
NULL_RTX, 1, OPTAB_DIRECT);
}
else
ac->modemask = GEN_INT (GET_MODE_MASK (mode));
ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
}
/* Expand an atomic compare and swap operation for HImode and QImode.
MEM is the memory location, CMP the old value to compare MEM with
and NEW the value to set if CMP == MEM. */
void
xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new)
{
enum machine_mode mode = GET_MODE (mem);
struct alignment_context ac;
rtx tmp, cmpv, newv, val;
rtx oldval = gen_reg_rtx (SImode);
rtx res = gen_reg_rtx (SImode);
rtx csloop = gen_label_rtx ();
rtx csend = gen_label_rtx ();
init_alignment_context (&ac, mem);
if (ac.shift != NULL_RTX)
{
cmp = xtensa_expand_mask_and_shift (cmp, mode, ac.shift);
new = xtensa_expand_mask_and_shift (new, mode, ac.shift);
}
/* Load the surrounding word into VAL with the MEM value masked out. */
val = force_reg (SImode, expand_simple_binop (SImode, AND, ac.memsi,
ac.modemaski, NULL_RTX, 1,
OPTAB_DIRECT));
emit_label (csloop);
/* Patch CMP and NEW into VAL at correct position. */
cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
NULL_RTX, 1, OPTAB_DIRECT));
newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val,
NULL_RTX, 1, OPTAB_DIRECT));
/* Jump to end if we're done. */
emit_insn (gen_sync_compare_and_swapsi (res, ac.memsi, cmpv, newv));
emit_cmp_and_jump_insns (res, cmpv, EQ, const0_rtx, SImode, true, csend);
/* Check for changes outside mode. */
emit_move_insn (oldval, val);
tmp = expand_simple_binop (SImode, AND, res, ac.modemaski,
val, 1, OPTAB_DIRECT);
if (tmp != val)
emit_move_insn (val, tmp);
/* Loop internal if so. */
emit_cmp_and_jump_insns (oldval, val, NE, const0_rtx, SImode, true, csloop);
emit_label (csend);
/* Return the correct part of the bitfield. */
convert_move (target,
(ac.shift == NULL_RTX ? res
: expand_simple_binop (SImode, LSHIFTRT, res, ac.shift,
NULL_RTX, 1, OPTAB_DIRECT)),
1);
}
/* Expand an atomic operation CODE of mode MODE (either HImode or QImode --
the default expansion works fine for SImode). MEM is the memory location
and VAL the value to play with. If AFTER is true then store the value
MEM holds after the operation, if AFTER is false then store the value MEM
holds before the operation. If TARGET is zero then discard that value, else
store it to TARGET. */
void
xtensa_expand_atomic (enum rtx_code code, rtx target, rtx mem, rtx val,
bool after)
{
enum machine_mode mode = GET_MODE (mem);
struct alignment_context ac;
rtx csloop = gen_label_rtx ();
rtx cmp, tmp;
rtx old = gen_reg_rtx (SImode);
rtx new = gen_reg_rtx (SImode);
rtx orig = NULL_RTX;
init_alignment_context (&ac, mem);
/* Prepare values before the compare-and-swap loop. */
if (ac.shift != NULL_RTX)
val = xtensa_expand_mask_and_shift (val, mode, ac.shift);
switch (code)
{
case PLUS:
case MINUS:
orig = gen_reg_rtx (SImode);
convert_move (orig, val, 1);
break;
case SET:
case IOR:
case XOR:
break;
case MULT: /* NAND */
case AND:
/* val = "11..1<val>11..1" */
val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
NULL_RTX, 1, OPTAB_DIRECT);
break;
default:
gcc_unreachable ();
}
/* Load full word. Subsequent loads are performed by S32C1I. */
cmp = force_reg (SImode, ac.memsi);
emit_label (csloop);
emit_move_insn (old, cmp);
switch (code)
{
case PLUS:
case MINUS:
val = expand_simple_binop (SImode, code, old, orig,
NULL_RTX, 1, OPTAB_DIRECT);
val = expand_simple_binop (SImode, AND, val, ac.modemask,
NULL_RTX, 1, OPTAB_DIRECT);
/* FALLTHRU */
case SET:
tmp = expand_simple_binop (SImode, AND, old, ac.modemaski,
NULL_RTX, 1, OPTAB_DIRECT);
tmp = expand_simple_binop (SImode, IOR, tmp, val,
new, 1, OPTAB_DIRECT);
break;
case AND:
case IOR:
case XOR:
tmp = expand_simple_binop (SImode, code, old, val,
new, 1, OPTAB_DIRECT);
break;
case MULT: /* NAND */
tmp = expand_simple_binop (SImode, XOR, old, ac.modemask,
NULL_RTX, 1, OPTAB_DIRECT);
tmp = expand_simple_binop (SImode, AND, tmp, val,
new, 1, OPTAB_DIRECT);
break;
default:
gcc_unreachable ();
}
if (tmp != new)
emit_move_insn (new, tmp);
emit_insn (gen_sync_compare_and_swapsi (cmp, ac.memsi, old, new));
emit_cmp_and_jump_insns (cmp, old, NE, const0_rtx, SImode, true, csloop);
if (target)
{
tmp = (after ? new : cmp);
convert_move (target,
(ac.shift == NULL_RTX ? tmp
: expand_simple_binop (SImode, LSHIFTRT, tmp, ac.shift,
NULL_RTX, 1, OPTAB_DIRECT)),
1);
}
}
void
xtensa_setup_frame_addresses (void)
{

View File

@ -47,6 +47,12 @@ extern unsigned xtensa_current_frame_size;
#ifndef XCHAL_HAVE_MUL32_HIGH
#define XCHAL_HAVE_MUL32_HIGH 0
#endif
#ifndef XCHAL_HAVE_RELEASE_SYNC
#define XCHAL_HAVE_RELEASE_SYNC 0
#endif
#ifndef XCHAL_HAVE_S32C1I
#define XCHAL_HAVE_S32C1I 0
#endif
#define TARGET_BIG_ENDIAN XCHAL_HAVE_BE
#define TARGET_DENSITY XCHAL_HAVE_DENSITY
#define TARGET_MAC16 XCHAL_HAVE_MAC16
@ -65,6 +71,8 @@ extern unsigned xtensa_current_frame_size;
#define TARGET_HARD_FLOAT_RSQRT XCHAL_HAVE_FP_RSQRT
#define TARGET_ABS XCHAL_HAVE_ABS
#define TARGET_ADDX XCHAL_HAVE_ADDX
#define TARGET_RELEASE_SYNC XCHAL_HAVE_RELEASE_SYNC
#define TARGET_S32C1I XCHAL_HAVE_S32C1I
#define TARGET_DEFAULT ( \
(XCHAL_HAVE_L32R ? 0 : MASK_CONST16))

View File

@ -30,8 +30,12 @@
(UNSPEC_NOP 2)
(UNSPEC_PLT 3)
(UNSPEC_RET_ADDR 4)
(UNSPECV_SET_FP 1)
(UNSPECV_ENTRY 2)
(UNSPECV_MEMW 3)
(UNSPECV_S32RI 4)
(UNSPECV_S32C1I 5)
])
;; This code macro allows signed and unsigned widening multiplications
@ -63,6 +67,15 @@
;; This code macro is for floating-point comparisons.
(define_code_macro any_scc_sf [eq lt le])
;; These macros allow to combine most atomic operations.
(define_code_macro ATOMIC [and ior xor plus minus mult])
(define_code_attr atomic [(and "and") (ior "ior") (xor "xor")
(plus "add") (minus "sub") (mult "nand")])
;; These mode macros allow the HI and QI patterns to be defined from
;; the same template.
(define_mode_macro HQI [HI QI])
;; Attributes.
@ -1687,3 +1700,113 @@ srli\t%3, %3, 30\;slli\t%0, %1, 2\;ssai\t2\;src\t%0, %3, %0"
[(set_attr "type" "jump")
(set_attr "mode" "none")
(set_attr "length" "3")])
;; Atomic operations
(define_expand "memory_barrier"
[(set (mem:BLK (match_dup 0))
(unspec_volatile:BLK [(mem:BLK (match_dup 0))] UNSPECV_MEMW))]
""
{
operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (SImode));
MEM_VOLATILE_P (operands[0]) = 1;
})
(define_insn "*memory_barrier"
[(set (match_operand:BLK 0 "" "")
(unspec_volatile:BLK [(match_operand:BLK 1 "" "")] UNSPECV_MEMW))]
""
"memw"
[(set_attr "type" "unknown")
(set_attr "mode" "none")
(set_attr "length" "3")])
;; sync_lock_release is only implemented for SImode.
;; For other modes, just use the default of a store with a memory_barrier.
(define_insn "sync_lock_releasesi"
[(set (match_operand:SI 0 "mem_operand" "=U")
(unspec_volatile:SI
[(match_operand:SI 1 "register_operand" "r")]
UNSPECV_S32RI))]
"TARGET_RELEASE_SYNC"
"s32ri\t%1, %0"
[(set_attr "type" "store")
(set_attr "mode" "SI")
(set_attr "length" "3")])
(define_insn "sync_compare_and_swapsi"
[(parallel
[(set (match_operand:SI 0 "register_operand" "=a")
(match_operand:SI 1 "mem_operand" "+U"))
(set (match_dup 1)
(unspec_volatile:SI
[(match_dup 1)
(match_operand:SI 2 "register_operand" "r")
(match_operand:SI 3 "register_operand" "0")]
UNSPECV_S32C1I))])]
"TARGET_S32C1I"
"wsr\t%2, SCOMPARE1\;s32c1i\t%3, %1"
[(set_attr "type" "multi")
(set_attr "mode" "SI")
(set_attr "length" "6")])
(define_expand "sync_compare_and_swap<mode>"
[(parallel
[(set (match_operand:HQI 0 "register_operand" "")
(match_operand:HQI 1 "mem_operand" ""))
(set (match_dup 1)
(unspec_volatile:HQI
[(match_dup 1)
(match_operand:HQI 2 "register_operand" "")
(match_operand:HQI 3 "register_operand" "")]
UNSPECV_S32C1I))])]
"TARGET_S32C1I"
{
xtensa_expand_compare_and_swap (operands[0], operands[1],
operands[2], operands[3]);
DONE;
})
(define_expand "sync_lock_test_and_set<mode>"
[(match_operand:HQI 0 "register_operand")
(match_operand:HQI 1 "memory_operand")
(match_operand:HQI 2 "register_operand")]
"TARGET_S32C1I"
{
xtensa_expand_atomic (SET, operands[0], operands[1], operands[2], false);
DONE;
})
(define_expand "sync_<atomic><mode>"
[(set (match_operand:HQI 0 "memory_operand")
(ATOMIC:HQI (match_dup 0)
(match_operand:HQI 1 "register_operand")))]
"TARGET_S32C1I"
{
xtensa_expand_atomic (<CODE>, NULL_RTX, operands[0], operands[1], false);
DONE;
})
(define_expand "sync_old_<atomic><mode>"
[(set (match_operand:HQI 0 "register_operand")
(match_operand:HQI 1 "memory_operand"))
(set (match_dup 1)
(ATOMIC:HQI (match_dup 1)
(match_operand:HQI 2 "register_operand")))]
"TARGET_S32C1I"
{
xtensa_expand_atomic (<CODE>, operands[0], operands[1], operands[2], false);
DONE;
})
(define_expand "sync_new_<atomic><mode>"
[(set (match_operand:HQI 0 "register_operand")
(ATOMIC:HQI (match_operand:HQI 1 "memory_operand")
(match_operand:HQI 2 "register_operand")))
(set (match_dup 1) (ATOMIC:HQI (match_dup 1) (match_dup 2)))]
"TARGET_S32C1I"
{
xtensa_expand_atomic (<CODE>, operands[0], operands[1], operands[2], true);
DONE;
})

View File

@ -1,3 +1,9 @@
2007-07-18 Bob Wilson <bob.wilson@acm.org>
* lib/target-supports.exp (check_effective_target_sync_int_long):
Enable for xtensa.
(check_effective_target_sync_char_short): Likewise.
2007-07-18 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcc.dg/pr28796-2.c: Add more cases.

View File

@ -2188,7 +2188,8 @@ proc check_effective_target_sync_int_long { } {
|| [istarget s390*-*-*]
|| [istarget powerpc*-*-*]
|| [istarget sparc64-*-*]
|| [istarget sparcv9-*-*] } {
|| [istarget sparcv9-*-*]
|| [istarget xtensa-*-*] } {
set et_sync_int_long_saved 1
}
}
@ -2215,7 +2216,8 @@ proc check_effective_target_sync_char_short { } {
|| [istarget s390*-*-*]
|| [istarget powerpc*-*-*]
|| [istarget sparc64-*-*]
|| [istarget sparcv9-*-*] } {
|| [istarget sparcv9-*-*]
|| [istarget xtensa-*-*] } {
set et_sync_char_short_saved 1
}
}

View File

@ -1,3 +1,8 @@
2007-07-18 Bob Wilson <bob.wilson@acm.org>
* xtensa-config.h (XCHAL_HAVE_THREADPTR): New.
(XCHAL_HAVE_RELEASE_SYNC, XCHAL_HAVE_S32C1I): New.
2007-07-17 Nick Clifton <nickc@redhat.com>
* COPYING3: New file. Contains version 3 of the GNU General

View File

@ -1,5 +1,5 @@
/* Xtensa configuration settings.
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
@ -73,6 +73,15 @@
#undef XCHAL_HAVE_LOOPS
#define XCHAL_HAVE_LOOPS 1
#undef XCHAL_HAVE_THREADPTR
#define XCHAL_HAVE_THREADPTR 1
#undef XCHAL_HAVE_RELEASE_SYNC
#define XCHAL_HAVE_RELEASE_SYNC 0
#undef XCHAL_HAVE_S32C1I
#define XCHAL_HAVE_S32C1I 0
#undef XCHAL_HAVE_BOOLEANS
#define XCHAL_HAVE_BOOLEANS 0