Commit cfe52743 by David Alan Gilbert Committed by Richard Sandiford

arm.c (arm_output_ldrex): Support ldrexd.

gcc/
2011-10-14  David Alan Gilbert  <david.gilbert@linaro.org>

	* config/arm/arm.c (arm_output_ldrex): Support ldrexd.
	(arm_output_strex): Support strexd.
	(arm_output_it): New helper to output it in Thumb2 mode only.
	(arm_output_sync_loop): Support DI mode.  Change comment to
	not support const_int.
	(arm_expand_sync): Support DI mode.
	* config/arm/arm.h (TARGET_HAVE_LDREXBHD): Split into LDREXBH
	and LDREXD.
	* config/arm/iterators.md (NARROW): move from sync.md.
	(QHSD): New iterator for all current ARM integer modes.
	(SIDI): New iterator for SI and DI modes only.
	* config/arm/sync.md (sync_predtab): New mode_attr.
	(sync_compare_and_swapsi): Fold into sync_compare_and_swap<mode>.
	(sync_lock_test_and_setsi): Fold into sync_lock_test_and_setsi<mode>.
	(sync_<sync_optab>si): Fold into sync_<sync_optab><mode>.
	(sync_nandsi): Fold into sync_nand<mode>.
	(sync_new_<sync_optab>si): Fold into sync_new_<sync_optab><mode>.
	(sync_new_nandsi): Fold into sync_new_nand<mode>.
	(sync_old_<sync_optab>si): Fold into sync_old_<sync_optab><mode>.
	(sync_old_nandsi): Fold into sync_old_nand<mode>.
	(sync_compare_and_swap<mode>): Support SI & DI.
	(sync_lock_test_and_set<mode>): Likewise.
	(sync_<sync_optab><mode>): Likewise.
	(sync_nand<mode>): Likewise.
	(sync_new_<sync_optab><mode>): Likewise.
	(sync_new_nand<mode>): Likewise.
	(sync_old_<sync_optab><mode>): Likewise.
	(sync_old_nand<mode>): Likewise.
	(arm_sync_compare_and_swapsi): Turn into iterator on SI & DI.
	(arm_sync_lock_test_and_setsi): Likewise.
	(arm_sync_new_<sync_optab>si): Likewise.
	(arm_sync_new_nandsi): Likewise.
	(arm_sync_old_<sync_optab>si): Likewise.
	(arm_sync_old_nandsi): Likewise.
	(arm_sync_compare_and_swap<mode> NARROW): use sync_predtab, fix indent.
	(arm_sync_lock_test_and_setsi<mode> NARROW): Likewise.
	(arm_sync_new_<sync_optab><mode> NARROW): Likewise.
	(arm_sync_new_nand<mode> NARROW): Likewise.
	(arm_sync_old_<sync_optab><mode> NARROW): Likewise.
	(arm_sync_old_nand<mode> NARROW): Likewise.

From-SVN: r179981
parent 3e2a46fe
2011-10-14 David Alan Gilbert <david.gilbert@linaro.org> 2011-10-14 David Alan Gilbert <david.gilbert@linaro.org>
* config/arm/arm.c (arm_output_ldrex): Support ldrexd.
(arm_output_strex): Support strexd.
(arm_output_it): New helper to output it in Thumb2 mode only.
(arm_output_sync_loop): Support DI mode. Change comment to
not support const_int.
(arm_expand_sync): Support DI mode.
* config/arm/arm.h (TARGET_HAVE_LDREXBHD): Split into LDREXBH
and LDREXD.
* config/arm/iterators.md (NARROW): move from sync.md.
(QHSD): New iterator for all current ARM integer modes.
(SIDI): New iterator for SI and DI modes only.
* config/arm/sync.md (sync_predtab): New mode_attr.
(sync_compare_and_swapsi): Fold into sync_compare_and_swap<mode>.
(sync_lock_test_and_setsi): Fold into sync_lock_test_and_setsi<mode>.
(sync_<sync_optab>si): Fold into sync_<sync_optab><mode>.
(sync_nandsi): Fold into sync_nand<mode>.
(sync_new_<sync_optab>si): Fold into sync_new_<sync_optab><mode>.
(sync_new_nandsi): Fold into sync_new_nand<mode>.
(sync_old_<sync_optab>si): Fold into sync_old_<sync_optab><mode>.
(sync_old_nandsi): Fold into sync_old_nand<mode>.
(sync_compare_and_swap<mode>): Support SI & DI.
(sync_lock_test_and_set<mode>): Likewise.
(sync_<sync_optab><mode>): Likewise.
(sync_nand<mode>): Likewise.
(sync_new_<sync_optab><mode>): Likewise.
(sync_new_nand<mode>): Likewise.
(sync_old_<sync_optab><mode>): Likewise.
(sync_old_nand<mode>): Likewise.
(arm_sync_compare_and_swapsi): Turn into iterator on SI & DI.
(arm_sync_lock_test_and_setsi): Likewise.
(arm_sync_new_<sync_optab>si): Likewise.
(arm_sync_new_nandsi): Likewise.
(arm_sync_old_<sync_optab>si): Likewise.
(arm_sync_old_nandsi): Likewise.
(arm_sync_compare_and_swap<mode> NARROW): use sync_predtab, fix indent.
(arm_sync_lock_test_and_setsi<mode> NARROW): Likewise.
(arm_sync_new_<sync_optab><mode> NARROW): Likewise.
(arm_sync_new_nand<mode> NARROW): Likewise.
(arm_sync_old_<sync_optab><mode> NARROW): Likewise.
(arm_sync_old_nand<mode> NARROW): Likewise.
2011-10-14 David Alan Gilbert <david.gilbert@linaro.org>
PR target/48126 PR target/48126
* config/arm/arm.c (arm_output_sync_loop): Move label before barrier. * config/arm/arm.c (arm_output_sync_loop): Move label before barrier.
...@@ -24042,12 +24042,26 @@ arm_output_ldrex (emit_f emit, ...@@ -24042,12 +24042,26 @@ arm_output_ldrex (emit_f emit,
rtx target, rtx target,
rtx memory) rtx memory)
{ {
const char *suffix = arm_ldrex_suffix (mode); rtx operands[3];
rtx operands[2];
operands[0] = target; operands[0] = target;
operands[1] = memory; if (mode != DImode)
arm_output_asm_insn (emit, 0, operands, "ldrex%s\t%%0, %%C1", suffix); {
const char *suffix = arm_ldrex_suffix (mode);
operands[1] = memory;
arm_output_asm_insn (emit, 0, operands, "ldrex%s\t%%0, %%C1", suffix);
}
else
{
/* The restrictions on target registers in ARM mode are that the two
registers are consecutive and the first one is even; Thumb is
actually more flexible, but DI should give us this anyway.
Note that the 1st register always gets the lowest word in memory. */
gcc_assert ((REGNO (target) & 1) == 0);
operands[1] = gen_rtx_REG (SImode, REGNO (target) + 1);
operands[2] = memory;
arm_output_asm_insn (emit, 0, operands, "ldrexd\t%%0, %%1, %%C2");
}
} }
/* Emit a strex{b,h,d, } instruction appropriate for the specified /* Emit a strex{b,h,d, } instruction appropriate for the specified
...@@ -24060,14 +24074,41 @@ arm_output_strex (emit_f emit, ...@@ -24060,14 +24074,41 @@ arm_output_strex (emit_f emit,
rtx value, rtx value,
rtx memory) rtx memory)
{ {
const char *suffix = arm_ldrex_suffix (mode); rtx operands[4];
rtx operands[3];
operands[0] = result; operands[0] = result;
operands[1] = value; operands[1] = value;
operands[2] = memory; if (mode != DImode)
arm_output_asm_insn (emit, 0, operands, "strex%s%s\t%%0, %%1, %%C2", suffix, {
cc); const char *suffix = arm_ldrex_suffix (mode);
operands[2] = memory;
arm_output_asm_insn (emit, 0, operands, "strex%s%s\t%%0, %%1, %%C2",
suffix, cc);
}
else
{
/* The restrictions on target registers in ARM mode are that the two
registers are consecutive and the first one is even; Thumb is
actually more flexible, but DI should give us this anyway.
Note that the 1st register always gets the lowest word in memory. */
gcc_assert ((REGNO (value) & 1) == 0 || TARGET_THUMB2);
operands[2] = gen_rtx_REG (SImode, REGNO (value) + 1);
operands[3] = memory;
arm_output_asm_insn (emit, 0, operands, "strexd%s\t%%0, %%1, %%2, %%C3",
cc);
}
}
/* Helper to emit an it instruction in Thumb2 mode only; although the assembler
will ignore it in ARM mode, emitting it will mess up instruction counts we
sometimes keep 'flags' are the extra t's and e's if it's more than one
instruction that is conditional. */
static void
arm_output_it (emit_f emit, const char *flags, const char *cond)
{
rtx operands[1]; /* Don't actually use the operand. */
if (TARGET_THUMB2)
arm_output_asm_insn (emit, 0, operands, "it%s\t%s", flags, cond);
} }
/* Helper to emit a two operand instruction. */ /* Helper to emit a two operand instruction. */
...@@ -24109,7 +24150,7 @@ arm_output_op3 (emit_f emit, const char *mnemonic, rtx d, rtx a, rtx b) ...@@ -24109,7 +24150,7 @@ arm_output_op3 (emit_f emit, const char *mnemonic, rtx d, rtx a, rtx b)
required_value: required_value:
RTX register or const_int representing the required old_value for RTX register representing the required old_value for
the modify to continue, if NULL no comparsion is performed. */ the modify to continue, if NULL no comparsion is performed. */
static void static void
arm_output_sync_loop (emit_f emit, arm_output_sync_loop (emit_f emit,
...@@ -24123,7 +24164,13 @@ arm_output_sync_loop (emit_f emit, ...@@ -24123,7 +24164,13 @@ arm_output_sync_loop (emit_f emit,
enum attr_sync_op sync_op, enum attr_sync_op sync_op,
int early_barrier_required) int early_barrier_required)
{ {
rtx operands[1]; rtx operands[2];
/* We'll use the lo for the normal rtx in the none-DI case
as well as the least-sig word in the DI case. */
rtx old_value_lo, required_value_lo, new_value_lo, t1_lo;
rtx old_value_hi, required_value_hi, new_value_hi, t1_hi;
bool is_di = mode == DImode;
gcc_assert (t1 != t2); gcc_assert (t1 != t2);
...@@ -24134,82 +24181,142 @@ arm_output_sync_loop (emit_f emit, ...@@ -24134,82 +24181,142 @@ arm_output_sync_loop (emit_f emit,
arm_output_ldrex (emit, mode, old_value, memory); arm_output_ldrex (emit, mode, old_value, memory);
if (is_di)
{
old_value_lo = gen_lowpart (SImode, old_value);
old_value_hi = gen_highpart (SImode, old_value);
if (required_value)
{
required_value_lo = gen_lowpart (SImode, required_value);
required_value_hi = gen_highpart (SImode, required_value);
}
else
{
/* Silence false potentially unused warning. */
required_value_lo = NULL_RTX;
required_value_hi = NULL_RTX;
}
new_value_lo = gen_lowpart (SImode, new_value);
new_value_hi = gen_highpart (SImode, new_value);
t1_lo = gen_lowpart (SImode, t1);
t1_hi = gen_highpart (SImode, t1);
}
else
{
old_value_lo = old_value;
new_value_lo = new_value;
required_value_lo = required_value;
t1_lo = t1;
/* Silence false potentially unused warning. */
t1_hi = NULL_RTX;
new_value_hi = NULL_RTX;
required_value_hi = NULL_RTX;
old_value_hi = NULL_RTX;
}
if (required_value) if (required_value)
{ {
rtx operands[2]; operands[0] = old_value_lo;
operands[1] = required_value_lo;
operands[0] = old_value;
operands[1] = required_value;
arm_output_asm_insn (emit, 0, operands, "cmp\t%%0, %%1"); arm_output_asm_insn (emit, 0, operands, "cmp\t%%0, %%1");
if (is_di)
{
arm_output_it (emit, "", "eq");
arm_output_op2 (emit, "cmpeq", old_value_hi, required_value_hi);
}
arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYB%%=", LOCAL_LABEL_PREFIX); arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYB%%=", LOCAL_LABEL_PREFIX);
} }
switch (sync_op) switch (sync_op)
{ {
case SYNC_OP_ADD: case SYNC_OP_ADD:
arm_output_op3 (emit, "add", t1, old_value, new_value); arm_output_op3 (emit, is_di ? "adds" : "add",
t1_lo, old_value_lo, new_value_lo);
if (is_di)
arm_output_op3 (emit, "adc", t1_hi, old_value_hi, new_value_hi);
break; break;
case SYNC_OP_SUB: case SYNC_OP_SUB:
arm_output_op3 (emit, "sub", t1, old_value, new_value); arm_output_op3 (emit, is_di ? "subs" : "sub",
t1_lo, old_value_lo, new_value_lo);
if (is_di)
arm_output_op3 (emit, "sbc", t1_hi, old_value_hi, new_value_hi);
break; break;
case SYNC_OP_IOR: case SYNC_OP_IOR:
arm_output_op3 (emit, "orr", t1, old_value, new_value); arm_output_op3 (emit, "orr", t1_lo, old_value_lo, new_value_lo);
if (is_di)
arm_output_op3 (emit, "orr", t1_hi, old_value_hi, new_value_hi);
break; break;
case SYNC_OP_XOR: case SYNC_OP_XOR:
arm_output_op3 (emit, "eor", t1, old_value, new_value); arm_output_op3 (emit, "eor", t1_lo, old_value_lo, new_value_lo);
if (is_di)
arm_output_op3 (emit, "eor", t1_hi, old_value_hi, new_value_hi);
break; break;
case SYNC_OP_AND: case SYNC_OP_AND:
arm_output_op3 (emit,"and", t1, old_value, new_value); arm_output_op3 (emit,"and", t1_lo, old_value_lo, new_value_lo);
if (is_di)
arm_output_op3 (emit, "and", t1_hi, old_value_hi, new_value_hi);
break; break;
case SYNC_OP_NAND: case SYNC_OP_NAND:
arm_output_op3 (emit, "and", t1, old_value, new_value); arm_output_op3 (emit, "and", t1_lo, old_value_lo, new_value_lo);
arm_output_op2 (emit, "mvn", t1, t1); if (is_di)
arm_output_op3 (emit, "and", t1_hi, old_value_hi, new_value_hi);
arm_output_op2 (emit, "mvn", t1_lo, t1_lo);
if (is_di)
arm_output_op2 (emit, "mvn", t1_hi, t1_hi);
break; break;
case SYNC_OP_NONE: case SYNC_OP_NONE:
t1 = new_value; t1 = new_value;
t1_lo = new_value_lo;
if (is_di)
t1_hi = new_value_hi;
break; break;
} }
/* Note that the result of strex is a 0/1 flag that's always 1 register. */
if (t2) if (t2)
{ {
arm_output_strex (emit, mode, "", t2, t1, memory); arm_output_strex (emit, mode, "", t2, t1, memory);
operands[0] = t2; operands[0] = t2;
arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0"); arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=", arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
LOCAL_LABEL_PREFIX); LOCAL_LABEL_PREFIX);
} }
else else
{ {
/* Use old_value for the return value because for some operations /* Use old_value for the return value because for some operations
the old_value can easily be restored. This saves one register. */ the old_value can easily be restored. This saves one register. */
arm_output_strex (emit, mode, "", old_value, t1, memory); arm_output_strex (emit, mode, "", old_value_lo, t1, memory);
operands[0] = old_value; operands[0] = old_value_lo;
arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0"); arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=", arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
LOCAL_LABEL_PREFIX); LOCAL_LABEL_PREFIX);
/* Note that we only used the _lo half of old_value as a temporary
so in DI we don't have to restore the _hi part. */
switch (sync_op) switch (sync_op)
{ {
case SYNC_OP_ADD: case SYNC_OP_ADD:
arm_output_op3 (emit, "sub", old_value, t1, new_value); arm_output_op3 (emit, "sub", old_value_lo, t1_lo, new_value_lo);
break; break;
case SYNC_OP_SUB: case SYNC_OP_SUB:
arm_output_op3 (emit, "add", old_value, t1, new_value); arm_output_op3 (emit, "add", old_value_lo, t1_lo, new_value_lo);
break; break;
case SYNC_OP_XOR: case SYNC_OP_XOR:
arm_output_op3 (emit, "eor", old_value, t1, new_value); arm_output_op3 (emit, "eor", old_value_lo, t1_lo, new_value_lo);
break; break;
case SYNC_OP_NONE: case SYNC_OP_NONE:
arm_output_op2 (emit, "mov", old_value, required_value); arm_output_op2 (emit, "mov", old_value_lo, required_value_lo);
break; break;
default: default:
...@@ -24315,7 +24422,7 @@ arm_expand_sync (enum machine_mode mode, ...@@ -24315,7 +24422,7 @@ arm_expand_sync (enum machine_mode mode,
target = gen_reg_rtx (mode); target = gen_reg_rtx (mode);
memory = arm_legitimize_sync_memory (memory); memory = arm_legitimize_sync_memory (memory);
if (mode != SImode) if (mode != SImode && mode != DImode)
{ {
rtx load_temp = gen_reg_rtx (SImode); rtx load_temp = gen_reg_rtx (SImode);
......
...@@ -297,8 +297,12 @@ extern void (*arm_lang_output_object_attributes_hook)(void); ...@@ -297,8 +297,12 @@ extern void (*arm_lang_output_object_attributes_hook)(void);
/* Nonzero if this chip supports ldrex and strex */ /* Nonzero if this chip supports ldrex and strex */
#define TARGET_HAVE_LDREX ((arm_arch6 && TARGET_ARM) || arm_arch7) #define TARGET_HAVE_LDREX ((arm_arch6 && TARGET_ARM) || arm_arch7)
/* Nonzero if this chip supports ldrex{bhd} and strex{bhd}. */ /* Nonzero if this chip supports ldrex{bh} and strex{bh}. */
#define TARGET_HAVE_LDREXBHD ((arm_arch6k && TARGET_ARM) || arm_arch7) #define TARGET_HAVE_LDREXBH ((arm_arch6k && TARGET_ARM) || arm_arch7)
/* Nonzero if this chip supports ldrexd and strexd. */
#define TARGET_HAVE_LDREXD (((arm_arch6k && TARGET_ARM) || arm_arch7) \
&& arm_arch_notm)
/* Nonzero if integer division instructions supported. */ /* Nonzero if integer division instructions supported. */
#define TARGET_IDIV ((TARGET_ARM && arm_arch_arm_hwdiv) \ #define TARGET_IDIV ((TARGET_ARM && arm_arch_arm_hwdiv) \
......
...@@ -33,6 +33,15 @@ ...@@ -33,6 +33,15 @@
;; A list of integer modes that are up to one word long ;; A list of integer modes that are up to one word long
(define_mode_iterator QHSI [QI HI SI]) (define_mode_iterator QHSI [QI HI SI])
;; A list of integer modes that are less than a word
(define_mode_iterator NARROW [QI HI])
;; A list of all the integer modes upto 64bit
(define_mode_iterator QHSD [QI HI SI DI])
;; A list of the 32bit and 64bit integer modes
(define_mode_iterator SIDI [SI DI])
;; Integer element sizes implemented by IWMMXT. ;; Integer element sizes implemented by IWMMXT.
(define_mode_iterator VMMX [V2SI V4HI V8QI]) (define_mode_iterator VMMX [V2SI V4HI V8QI])
......
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