aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mips
diff options
context:
space:
mode:
authorRichard Sandiford <rsandifo@gcc.gnu.org>2008-04-26 07:40:04 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2008-04-26 07:40:04 +0000
commit49bce30a21d7cec8632fba32fd83abe7db1acad5 (patch)
treecbe5674c673c745bd8140de43c196f316cde02a6 /gcc/config/mips
parenta93a597ae7f0feecd9dcf9d19c538e70d48e848d (diff)
downloadgcc-49bce30a21d7cec8632fba32fd83abe7db1acad5.zip
gcc-49bce30a21d7cec8632fba32fd83abe7db1acad5.tar.gz
gcc-49bce30a21d7cec8632fba32fd83abe7db1acad5.tar.bz2
mips.md (UNSPEC_COMPARE_AND_SWAP_12): New unspec_volitile.
gcc/ 2008-04-23 David Daney <ddaney@avtrex.com> * config/mips/mips.md (UNSPEC_COMPARE_AND_SWAP_12): New unspec_volitile. (UNSPEC_SYNC_OLD_OP, UNSPEC_SYNC_NEW_OP, UNSPEC_SYNC_EXCHANGE, UNSPEC_MEMORY_BARRIER, UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): Renumber. (sync_compare_and_swap<mode>): New expand for QI and HI modes. (compare_and_swap_12): New insn. * config/mips/mips-protos.h (mips_expand_compare_and_swap_12): Declare. * config/mips/mips.c (mips_force_binary): New function. (mips_emit_int_order_test, mips_expand_synci_loop): Use it. (mips_expand_compare_and_swap_12): New function. * config/mips/mips.h (MIPS_COMPARE_AND_SWAP_12): New macro. gcc/testsuite/ * gcc.target/mips/gcc-have-sync-compare-and-swap-1.c: Expect __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 and __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 to be defined. * gcc.target/mips/gcc-have-sync-compare-and-swap-2.c: Likewise. From-SVN: r134695
Diffstat (limited to 'gcc/config/mips')
-rw-r--r--gcc/config/mips/mips-protos.h1
-rw-r--r--gcc/config/mips/mips.c86
-rw-r--r--gcc/config/mips/mips.h24
-rw-r--r--gcc/config/mips/mips.md41
4 files changed, 141 insertions, 11 deletions
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index deb99a0..fbac8fc 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -292,5 +292,6 @@ extern bool mips_use_ins_ext_p (rtx, HOST_WIDE_INT, HOST_WIDE_INT);
extern const char *mips16e_output_save_restore (rtx, HOST_WIDE_INT);
extern bool mips16e_save_restore_pattern_p (rtx, HOST_WIDE_INT,
struct mips16e_save_restore_info *);
+extern void mips_expand_compare_and_swap_12 (rtx, rtx, rtx, rtx);
#endif /* ! GCC_MIPS_PROTOS_H */
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 94a1427..86072ac 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -1,6 +1,6 @@
/* Subroutines used for MIPS code generation.
Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
Contributed by A. Lichnewsky, lich@inria.inria.fr.
Changes by Michael Meissner, meissner@osf.org.
@@ -2121,6 +2121,19 @@ mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
}
+/* Compute (CODE OP0 OP1) and store the result in a new register
+ of mode MODE. Return that new register. */
+
+static rtx
+mips_force_binary (enum machine_mode mode, enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx reg;
+
+ reg = gen_reg_rtx (mode);
+ mips_emit_binary (code, reg, op0, op1);
+ return reg;
+}
+
/* Copy VALUE to a register and return that register. If new pseudos
are allowed, copy it into a new register, otherwise use DEST. */
@@ -3741,8 +3754,10 @@ mips_emit_int_order_test (enum rtx_code code, bool *invert_ptr,
}
else if (invert_ptr == 0)
{
- rtx inv_target = gen_reg_rtx (GET_MODE (target));
- mips_emit_binary (inv_code, inv_target, cmp0, cmp1);
+ rtx inv_target;
+
+ inv_target = mips_force_binary (GET_MODE (target),
+ inv_code, cmp0, cmp1);
mips_emit_binary (XOR, target, inv_target, const1_rtx);
}
else
@@ -5850,8 +5865,7 @@ mips_expand_synci_loop (rtx begin, rtx end)
emit_insn (gen_synci (begin));
- cmp = gen_reg_rtx (Pmode);
- mips_emit_binary (GTU, cmp, begin, end);
+ cmp = mips_force_binary (Pmode, GTU, begin, end);
mips_emit_binary (PLUS, begin, begin, inc);
@@ -5859,6 +5873,68 @@ mips_expand_synci_loop (rtx begin, rtx end)
emit_jump_insn (gen_condjump (cmp_result, label));
}
+/* Expand a QI or HI mode compare_and_swap. The operands are the same
+ as for the generator function. */
+
+void
+mips_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
+{
+ rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
+ rtx mask, inverted_mask, oldvalsi, old_shifted, newvalsi, new_shifted, res;
+
+ /* Compute the address of the containing SImode value. */
+ orig_addr = force_reg (Pmode, XEXP (mem, 0));
+ memsi_addr = mips_force_binary (Pmode, AND, orig_addr,
+ force_reg (Pmode, GEN_INT (-4)));
+
+ /* Create a memory reference for it. */
+ memsi = gen_rtx_MEM (SImode, memsi_addr);
+ set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
+ MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
+
+ /* Work out the byte offset of the QImode or HImode value,
+ counting from the least significant byte. */
+ shift = mips_force_binary (Pmode, AND, orig_addr, GEN_INT (3));
+ if (TARGET_BIG_ENDIAN)
+ mips_emit_binary (XOR, shift, shift,
+ GEN_INT (GET_MODE (mem) == QImode ? 3 : 2));
+
+ /* Multiply by eight to convert the shift value from bytes to bits. */
+ mips_emit_binary (ASHIFT, shift, shift, GEN_INT (3));
+
+ /* Make the final shift an SImode value, so that it can be used in
+ SImode operations. */
+ shiftsi = force_reg (SImode, gen_lowpart (SImode, shift));
+
+ /* Set MASK to an inclusive mask of the QImode or HImode value. */
+ unshifted_mask = GEN_INT (GET_MODE_MASK (GET_MODE (mem)));
+ unshifted_mask = force_reg (SImode, unshifted_mask);
+ mask = mips_force_binary (SImode, ASHIFT, unshifted_mask, shiftsi);
+
+ /* Compute the equivalent exclusive mask. */
+ inverted_mask = gen_reg_rtx (SImode);
+ emit_insn (gen_rtx_SET (VOIDmode, inverted_mask,
+ gen_rtx_NOT (SImode, mask)));
+
+ /* Shift the old value into place. */
+ oldvalsi = force_reg (SImode, gen_lowpart (SImode, oldval));
+ old_shifted = mips_force_binary (SImode, ASHIFT, oldvalsi, shiftsi);
+
+ /* Do the same for the new value. */
+ newvalsi = force_reg (SImode, gen_lowpart (SImode, newval));
+ new_shifted = mips_force_binary (SImode, ASHIFT, newvalsi, shiftsi);
+
+ /* Do the SImode atomic access. */
+ res = gen_reg_rtx (SImode);
+ emit_insn (gen_compare_and_swap_12 (res, memsi, mask, inverted_mask,
+ old_shifted, new_shifted));
+
+ /* Shift and convert the result. */
+ mips_emit_binary (AND, res, res, mask);
+ mips_emit_binary (LSHIFTRT, res, res, shiftsi);
+ mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+}
+
/* Return true if it is possible to use left/right accesses for a
bitfield of WIDTH bits starting BITPOS bits into *OP. When
returning true, update *OP, *LEFT and *RIGHT as follows:
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 3a0e58c..cda433a 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -2906,6 +2906,30 @@ while (0)
/* Return an asm string that atomically:
+ - Given that %2 contains a bit mask and %3 the inverted mask and
+ that %4 and %5 have already been ANDed with $2.
+
+ - Compares the bits in memory reference %1 selected by mask %2 to
+ register %4 and, if they are equal, changes the selected bits
+ in memory to %5.
+
+ - Sets register %0 to the old value of memory reference %1.
+ */
+#define MIPS_COMPARE_AND_SWAP_12 \
+ "%(%<%[%|sync\n" \
+ "1:\tll\t%0,%1\n" \
+ "\tand\t%@,%0,%2\n" \
+ "\tbne\t%@,%4,2f\n" \
+ "\tand\t%@,%0,%3\n" \
+ "\tor\t%@,%@,%5\n" \
+ "\tsc\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop\n" \
+ "\tsync%-%]%>%)\n" \
+ "2:\n"
+
+/* Return an asm string that atomically:
+
- Sets memory reference %0 to %0 INSN %1.
SUFFIX is the suffix that should be added to "ll" and "sc"
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index cbdcdc6..05adf22 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -54,12 +54,13 @@
(UNSPEC_SYNCI 35)
(UNSPEC_SYNC 36)
(UNSPEC_COMPARE_AND_SWAP 37)
- (UNSPEC_SYNC_OLD_OP 38)
- (UNSPEC_SYNC_NEW_OP 39)
- (UNSPEC_SYNC_EXCHANGE 40)
- (UNSPEC_MEMORY_BARRIER 41)
- (UNSPEC_SET_GOT_VERSION 42)
- (UNSPEC_UPDATE_GOT_VERSION 43)
+ (UNSPEC_COMPARE_AND_SWAP_12 38)
+ (UNSPEC_SYNC_OLD_OP 39)
+ (UNSPEC_SYNC_NEW_OP 40)
+ (UNSPEC_SYNC_EXCHANGE 41)
+ (UNSPEC_MEMORY_BARRIER 42)
+ (UNSPEC_SET_GOT_VERSION 43)
+ (UNSPEC_UPDATE_GOT_VERSION 44)
(UNSPEC_ADDRESS_FIRST 100)
@@ -4447,6 +4448,34 @@
}
[(set_attr "length" "32")])
+(define_expand "sync_compare_and_swap<mode>"
+ [(match_operand:SHORT 0 "register_operand")
+ (match_operand:SHORT 1 "memory_operand")
+ (match_operand:SHORT 2 "general_operand")
+ (match_operand:SHORT 3 "general_operand")]
+ "GENERATE_LL_SC"
+{
+ mips_expand_compare_and_swap_12 (operands[0], operands[1],
+ operands[2], operands[3]);
+ DONE;
+})
+
+;; Helper insn for mips_expand_compare_and_swap_12.
+(define_insn "compare_and_swap_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (match_operand:SI 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:SI [(match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (match_operand:SI 4 "register_operand" "d")
+ (match_operand:SI 5 "register_operand" "d")]
+ UNSPEC_COMPARE_AND_SWAP_12))]
+ "GENERATE_LL_SC"
+{
+ return MIPS_COMPARE_AND_SWAP_12;
+}
+ [(set_attr "length" "40")])
+
(define_insn "sync_add<mode>"
[(set (match_operand:GPR 0 "memory_operand" "+R,R")
(unspec_volatile:GPR