Commit 9cd7b720 by Matthew Wahab Committed by Matthew Wahab

[AArch64] Use atomic instructions for swap and fetch-update operations.

gcc/
2015-09-22  Matthew Wahab  <matthew.wahab@arm.com>

	* config/aarch64/aarch64-protos.h (aarch64_gen_atomic_ldop):
	Declare.
	* config/aarch64/aarch64.c (aarch64_emit_atomic_swap): New.
	(aarch64_gen_atomic_ldop): New.
	(aarch64_split_atomic_op): Fix whitespace and add a comment.
	* config/aarch64/atomics.md (UNSPECV_ATOMIC_SWP): New.
	(aarch64_compare_and_swap<mode>_lse): Fix some whitespace.
	(atomic_exchange<mode>): Replace with an expander.
	(aarch64_atomic_exchange<mode>): New.
	(aarch64_atomic_exchange<mode>_lse): New.
	(aarch64_atomic_<atomic_optab><mode>): Fix some whitespace.
	(aarch64_atomic_swp<mode>): New.

gcc/testsuite/
2015-09-22  Matthew Wahab  <matthew.wahab@arm.com>

	* gcc.target/aarch64/atomic-inst-ops.inc: (TEST_MODEL): New.
	(TEST_ONE): New.
        * gcc.target/aarch64/atomic-inst-swap.c: New.

From-SVN: r227998
parent 9915b1c9
2015-09-22 Matthew Wahab <matthew.wahab@arm.com>
* config/aarch64/aarch64-protos.h (aarch64_gen_atomic_ldop):
Declare.
* config/aarch64/aarch64.c (aarch64_emit_atomic_swap): New.
(aarch64_gen_atomic_ldop): New.
(aarch64_split_atomic_op): Fix whitespace and add a comment.
* config/aarch64/atomics.md (UNSPECV_ATOMIC_SWP): New.
(aarch64_compare_and_swap<mode>_lse): Fix some whitespace.
(atomic_exchange<mode>): Replace with an expander.
(aarch64_atomic_exchange<mode>): New.
(aarch64_atomic_exchange<mode>_lse): New.
(aarch64_atomic_<atomic_optab><mode>): Fix some whitespace.
(aarch64_atomic_swp<mode>): New.
2015-09-22 Manuel López-Ibáñez <manu@gcc.gnu.org>
* tree-inline.c (expand_call_inline): Use inform for extra note.
......@@ -378,6 +378,7 @@ rtx aarch64_load_tp (rtx);
void aarch64_expand_compare_and_swap (rtx op[]);
void aarch64_split_compare_and_swap (rtx op[]);
void aarch64_gen_atomic_cas (rtx, rtx, rtx, rtx, rtx);
void aarch64_gen_atomic_ldop (enum rtx_code, rtx, rtx, rtx, rtx);
void aarch64_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx, rtx);
bool aarch64_gen_adjusted_ldpstp (rtx *, bool, enum machine_mode, RTX_CODE);
......
......@@ -10992,11 +10992,54 @@ aarch64_split_compare_and_swap (rtx operands[])
aarch64_emit_post_barrier (model);
}
/* Emit an atomic swap. */
static void
aarch64_emit_atomic_swap (machine_mode mode, rtx dst, rtx value,
rtx mem, rtx model)
{
rtx (*gen) (rtx, rtx, rtx, rtx);
switch (mode)
{
case QImode: gen = gen_aarch64_atomic_swpqi; break;
case HImode: gen = gen_aarch64_atomic_swphi; break;
case SImode: gen = gen_aarch64_atomic_swpsi; break;
case DImode: gen = gen_aarch64_atomic_swpdi; break;
default:
gcc_unreachable ();
}
emit_insn (gen (dst, mem, value, model));
}
/* Emit an atomic operation where the architecture supports it. */
void
aarch64_gen_atomic_ldop (enum rtx_code code, rtx out_data,
rtx mem, rtx value, rtx model_rtx)
{
machine_mode mode = GET_MODE (mem);
out_data = gen_lowpart (mode, out_data);
switch (code)
{
case SET:
aarch64_emit_atomic_swap (mode, out_data, value, mem, model_rtx);
return;
default:
/* The operation can't be done with atomic instructions. */
gcc_unreachable ();
}
}
/* Split an atomic operation. */
void
aarch64_split_atomic_op (enum rtx_code code, rtx old_out, rtx new_out, rtx mem,
rtx value, rtx model_rtx, rtx cond)
rtx value, rtx model_rtx, rtx cond)
{
machine_mode mode = GET_MODE (mem);
machine_mode wmode = (mode == DImode ? DImode : SImode);
......@@ -11005,6 +11048,7 @@ aarch64_split_atomic_op (enum rtx_code code, rtx old_out, rtx new_out, rtx mem,
rtx_code_label *label;
rtx x;
/* Split the atomic operation into a sequence. */
label = gen_label_rtx ();
emit_label (label);
......
......@@ -27,6 +27,7 @@
UNSPECV_ATOMIC_CMPSW ; Represent an atomic compare swap.
UNSPECV_ATOMIC_EXCHG ; Represent an atomic exchange.
UNSPECV_ATOMIC_CAS ; Represent an atomic CAS.
UNSPECV_ATOMIC_SWP ; Represent an atomic SWP.
UNSPECV_ATOMIC_OP ; Represent an atomic operation.
])
......@@ -134,7 +135,7 @@
(match_operand:SI 5 "const_int_operand") ;; mod_s
(match_operand:SI 6 "const_int_operand")] ;; mod_f
UNSPECV_ATOMIC_CMPSW))]
"TARGET_LSE "
"TARGET_LSE"
"#"
"&& reload_completed"
[(const_int 0)]
......@@ -146,7 +147,28 @@
}
)
(define_insn_and_split "atomic_exchange<mode>"
(define_expand "atomic_exchange<mode>"
[(match_operand:ALLI 0 "register_operand" "")
(match_operand:ALLI 1 "aarch64_sync_memory_operand" "")
(match_operand:ALLI 2 "register_operand" "")
(match_operand:SI 3 "const_int_operand" "")]
""
{
rtx (*gen) (rtx, rtx, rtx, rtx);
/* Use an atomic SWP when available. */
if (TARGET_LSE)
gen = gen_aarch64_atomic_exchange<mode>_lse;
else
gen = gen_aarch64_atomic_exchange<mode>;
emit_insn (gen (operands[0], operands[1], operands[2], operands[3]));
DONE;
}
)
(define_insn_and_split "aarch64_atomic_exchange<mode>"
[(set (match_operand:ALLI 0 "register_operand" "=&r") ;; output
(match_operand:ALLI 1 "aarch64_sync_memory_operand" "+Q")) ;; memory
(set (match_dup 1)
......@@ -162,7 +184,26 @@
[(const_int 0)]
{
aarch64_split_atomic_op (SET, operands[0], NULL, operands[1],
operands[2], operands[3], operands[4]);
operands[2], operands[3], operands[4]);
DONE;
}
)
(define_insn_and_split "aarch64_atomic_exchange<mode>_lse"
[(set (match_operand:ALLI 0 "register_operand" "=&r")
(match_operand:ALLI 1 "aarch64_sync_memory_operand" "+Q"))
(set (match_dup 1)
(unspec_volatile:ALLI
[(match_operand:ALLI 2 "register_operand" "r")
(match_operand:SI 3 "const_int_operand" "")]
UNSPECV_ATOMIC_EXCHG))]
"TARGET_LSE"
"#"
"&& reload_completed"
[(const_int 0)]
{
aarch64_gen_atomic_ldop (SET, operands[0], operands[1],
operands[2], operands[3]);
DONE;
}
)
......@@ -183,7 +224,7 @@
[(const_int 0)]
{
aarch64_split_atomic_op (<CODE>, NULL, operands[3], operands[0],
operands[1], operands[2], operands[4]);
operands[1], operands[2], operands[4]);
DONE;
}
)
......@@ -425,6 +466,28 @@
;; ARMv8.1 LSE instructions.
;; Atomic swap with memory.
(define_insn "aarch64_atomic_swp<mode>"
[(set (match_operand:ALLI 0 "register_operand" "+&r")
(match_operand:ALLI 1 "aarch64_sync_memory_operand" "+Q"))
(set (match_dup 1)
(unspec_volatile:ALLI
[(match_operand:ALLI 2 "register_operand" "r")
(match_operand:SI 3 "const_int_operand" "")]
UNSPECV_ATOMIC_SWP))]
"TARGET_LSE && reload_completed"
{
enum memmodel model = memmodel_from_int (INTVAL (operands[3]));
if (is_mm_relaxed (model))
return "swp<atomic_sfx>\t%<w>2, %<w>0, %1";
else if (is_mm_acquire (model) || is_mm_consume (model))
return "swpa<atomic_sfx>\t%<w>2, %<w>0, %1";
else if (is_mm_release (model))
return "swpl<atomic_sfx>\t%<w>2, %<w>0, %1";
else
return "swpal<atomic_sfx>\t%<w>2, %<w>0, %1";
})
;; Atomic compare-and-swap: HI and smaller modes.
(define_insn "aarch64_atomic_cas<mode>"
......
2015-09-22 Matthew Wahab <matthew.wahab@arm.com>
* gcc.target/aarch64/atomic-inst-ops.inc: (TEST_MODEL): New.
(TEST_ONE): New.
* gcc.target/aarch64/atomic-inst-swap.c: New.
2015-09-22 Manuel López-Ibáñez <manu@gcc.gnu.org>
* gcc.target/i386/inline_error.c (int bar): Use dg-message for note.
......
......@@ -32,6 +32,15 @@ typedef __uint128_t uint128;
TEST_M##N (NAME, FN, int128, MODEL1, MODEL2) \
TEST_M##N (NAME, FN, uint128, MODEL1, MODEL2)
/* Models to test. */
#define TEST_MODEL(NAME, FN, N) \
TEST_TY (NAME##_relaxed, FN, N, __ATOMIC_RELAXED, DUMMY) \
TEST_TY (NAME##_consume, FN, N, __ATOMIC_CONSUME, DUMMY) \
TEST_TY (NAME##_acquire, FN, N, __ATOMIC_ACQUIRE, DUMMY) \
TEST_TY (NAME##_release, FN, N, __ATOMIC_RELEASE, DUMMY) \
TEST_TY (NAME##_acq_rel, FN, N, __ATOMIC_ACQ_REL, DUMMY) \
TEST_TY (NAME##_seq_cst, FN, N, __ATOMIC_SEQ_CST, DUMMY) \
/* Cross-product of models to test. */
#define TEST_MODEL_M1(NAME, FN, N, M) \
TEST_TY (NAME##_relaxed, FN, N, M, __ATOMIC_RELAXED) \
......@@ -51,3 +60,7 @@ typedef __uint128_t uint128;
/* Expand functions for a cross-product of memory models and types. */
#define TEST_TWO(NAME, FN) TEST_MODEL_M2 (NAME, FN)
/* Expand functions for a set of memory models and types. */
#define TEST_ONE(NAME, FN) TEST_MODEL (NAME, FN, 1)
/* { dg-do compile } */
/* { dg-options "-O2 -march=armv8-a+lse" } */
/* Test ARMv8.1-A SWP instruction. */
#include "atomic-inst-ops.inc"
#define TEST TEST_ONE
#define SWAP_ATOMIC(FN, TY, MODEL) \
TY FNNAME (FN, TY) (TY* val, TY foo) \
{ \
return __atomic_exchange_n (val, foo, MODEL); \
}
#define SWAP_ATOMIC_NORETURN(FN, TY, MODEL) \
void FNNAME (FN, TY) (TY* val, TY* foo, TY* bar) \
{ \
__atomic_exchange (val, foo, bar, MODEL); \
}
TEST (swap_atomic, SWAP_ATOMIC)
TEST (swap_atomic_noreturn, SWAP_ATOMIC_NORETURN)
/* { dg-final { scan-assembler-times "swpb\t" 4} } */
/* { dg-final { scan-assembler-times "swpab\t" 8} } */
/* { dg-final { scan-assembler-times "swplb\t" 4} } */
/* { dg-final { scan-assembler-times "swpalb\t" 8} } */
/* { dg-final { scan-assembler-times "swph\t" 4} } */
/* { dg-final { scan-assembler-times "swpah\t" 8} } */
/* { dg-final { scan-assembler-times "swplh\t" 4} } */
/* { dg-final { scan-assembler-times "swpalh\t" 8} } */
/* { dg-final { scan-assembler-times "swp\t" 8} } */
/* { dg-final { scan-assembler-times "swpa\t" 16} } */
/* { dg-final { scan-assembler-times "swpl\t" 8} } */
/* { dg-final { scan-assembler-times "swpal\t" 16} } */
/* { dg-final { scan-assembler-not "ldaxr\t" } } */
/* { dg-final { scan-assembler-not "stlxr\t" } } */
/* { dg-final { scan-assembler-not "dmb" } } */
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