From 2a48b790b26895fd7fb56c9ce1d64f083dd278bb Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Wed, 18 Jul 2007 18:51:21 +0000 Subject: [PATCH] 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): New. (sync_lock_test_and_set): New. (sync_): New. (sync_old_, sync_new_): 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 --- gcc/ChangeLog | 20 ++ gcc/config/xtensa/xtensa-protos.h | 2 + gcc/config/xtensa/xtensa.c | 257 ++++++++++++++++++++++++++ gcc/config/xtensa/xtensa.h | 8 + gcc/config/xtensa/xtensa.md | 123 ++++++++++++ gcc/testsuite/ChangeLog | 6 + gcc/testsuite/lib/target-supports.exp | 6 +- include/ChangeLog | 5 + include/xtensa-config.h | 11 +- 9 files changed, 435 insertions(+), 3 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 24b7c2e91e1..484d9b522ec 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2007-07-18 Bob Wilson + + * 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): New. + (sync_lock_test_and_set): New. + (sync_): New. + (sync_old_, sync_new_): New. + * config/xtensa/xtensa-protos.h (xtensa_expand_compare_and_swap): New. + (xtensa_expand_atomic): New. + 2007-07-18 Kaveh R. Ghazi PR target/30652 diff --git a/gcc/config/xtensa/xtensa-protos.h b/gcc/config/xtensa/xtensa-protos.h index f6cca7ac975..27b0a48ba89 100644 --- a/gcc/config/xtensa/xtensa-protos.h +++ b/gcc/config/xtensa/xtensa-protos.h @@ -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 *); diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c index 186872712da..4d21b65677f 100644 --- a/gcc/config/xtensa/xtensa.c +++ b/gcc/config/xtensa/xtensa.c @@ -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..111..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) { diff --git a/gcc/config/xtensa/xtensa.h b/gcc/config/xtensa/xtensa.h index 11c4184a6d7..e85dc26aafd 100644 --- a/gcc/config/xtensa/xtensa.h +++ b/gcc/config/xtensa/xtensa.h @@ -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)) diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md index 12dae4d48f9..f2d218da895 100644 --- a/gcc/config/xtensa/xtensa.md +++ b/gcc/config/xtensa/xtensa.md @@ -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" + [(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" + [(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_" + [(set (match_operand:HQI 0 "memory_operand") + (ATOMIC:HQI (match_dup 0) + (match_operand:HQI 1 "register_operand")))] + "TARGET_S32C1I" +{ + xtensa_expand_atomic (, NULL_RTX, operands[0], operands[1], false); + DONE; +}) + +(define_expand "sync_old_" + [(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 (, operands[0], operands[1], operands[2], false); + DONE; +}) + +(define_expand "sync_new_" + [(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 (, operands[0], operands[1], operands[2], true); + DONE; +}) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ab18376ccf3..72564be48eb 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2007-07-18 Bob Wilson + + * 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 * gcc.dg/pr28796-2.c: Add more cases. diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 4a5ae033631..c52a050829f 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -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 } } diff --git a/include/ChangeLog b/include/ChangeLog index 6d54a48880a..fe03cb2fd77 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2007-07-18 Bob Wilson + + * xtensa-config.h (XCHAL_HAVE_THREADPTR): New. + (XCHAL_HAVE_RELEASE_SYNC, XCHAL_HAVE_S32C1I): New. + 2007-07-17 Nick Clifton * COPYING3: New file. Contains version 3 of the GNU General diff --git a/include/xtensa-config.h b/include/xtensa-config.h index 36f9719fe33..2aca5feda05 100644 --- a/include/xtensa-config.h +++ b/include/xtensa-config.h @@ -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