diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 23a29c6..294f97d 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -112,6 +112,12 @@ extern bool gen_stm_seq (rtx *, int); extern bool gen_const_stm_seq (rtx *, int); extern rtx arm_gen_load_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *); extern rtx arm_gen_store_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *); + +extern bool bad_operands_ldrd_strd (rtx rt, rtx rt2, rtx rtn, + HOST_WIDE_INT off, bool wback, bool load); +extern bool gen_operands_ldrd_strd (rtx *operands, int nops, bool load, bool commute); +extern bool gen_operands_const_strd (rtx *operands, int nops); + extern int arm_gen_movmemqi (rtx *); extern enum machine_mode arm_select_cc_mode (RTX_CODE, rtx, rtx); extern enum machine_mode arm_select_dominance_cc_mode (rtx, rtx, diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index f1ada6f..d52c7ae 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -264,7 +264,10 @@ static reg_class_t arm_preferred_rename_class (reg_class_t rclass); static unsigned int arm_autovectorize_vector_sizes (void); static int arm_default_branch_cost (bool, bool); static int arm_cortex_a5_branch_cost (bool, bool); - +static bool bad_mem_for_ldrd_strd(rtx addr, rtx *base, rtx *offset); +static bool bad_offset_ldrd_strd (HOST_WIDE_INT offset); +static bool find_free_regs_for_arm_const_strd(int len, rtx *operands, rtx base, + HOST_WIDE_INT offset); /* Table of machine attributes. */ static const struct attribute_spec arm_attribute_table[] = @@ -24972,4 +24975,378 @@ arm_count_output_move_double_insns (rtx *operands) return count; } +/* Returns false iff OFFSET is valid for use in an LDRD/STRD instruction, + assuming that the address in the base register is word aligned. */ +bool +bad_offset_ldrd_strd (HOST_WIDE_INT offset) +{ + HOST_WIDE_INT max_offset; + if (TARGET_THUMB2) + max_offset = 1020; + else if (TARGET_ARM) + max_offset = 255; + else + gcc_unreachable(); + + return ((offset > max_offset) + || (offset < -max_offset) + /* Offset is not a multiple of 4. */ + || ((offset & 3) != 0)); +} + +/* Checks validity of operands for use in an LDRD/STRD instruction. + Returns true iff the operands are invalid, under the following assumptions. + Assumes that rt, rt2, and rtn are REG, and off is + This condition should be checked by the operand predicates + in all patterns that call this function. + Assumes that the address in the base register is word aligned. */ +bool +bad_operands_ldrd_strd (rtx rt, rtx rt2, rtx rtn, HOST_WIDE_INT offset, bool wback, bool load) +{ + unsigned int t, t2, n; + + t = REGNO (rt); + t2 = REGNO (rt2); + n = REGNO (rtn); + + if (bad_offset_ldrd_strd(offset)) + return true; + + if (TARGET_THUMB2) + return ((wback && (n == t || n == t2)) + || (t == SP_REGNUM) + || (t == PC_REGNUM) + || (t2 == SP_REGNUM) + || (t2 == PC_REGNUM) + || (load && (t == t2)) + /* Triggers Cortex-M3 LDRD errata. */ + || (!wback && load && fix_cm3_ldrd && (n == t))); + else if (TARGET_ARM) + return ((wback && (n == t || n == t2)) + || (t2 == PC_REGNUM) + || ((t % 2) != 0) /* First destination register is not even. */ + || (t2 != t + 1) + /* PC be used as base register for offset addressing only, + but it is depricated. */ + || (n == PC_REGNUM)); + else + gcc_unreachable(); +} + +/* Helper for gen_operands_ldrd_strd. + Returns true iff the memory operand ADDR is + an immediate offset from the base register and is not volatile. + Sets BASE and OFFSET accordingly. */ +bool +bad_mem_for_ldrd_strd(rtx addr, rtx *base, rtx *offset) +{ + /* Convert a subreg of mem into mem itself. */ + if (GET_CODE (addr) == SUBREG) + addr = alter_subreg (&addr); + gcc_assert (GET_CODE (addr) == MEM); + + /* Don't modify volatile memory accesses. */ + if (MEM_VOLATILE_P (addr)) + return true; + + *offset = const0_rtx; + + addr = XEXP (addr, 0); + if (GET_CODE (addr) == REG) + { + *base = addr; + return false; + } + else if (GET_CODE (addr) == PLUS) + { + *base = XEXP (addr, 0); + *offset = XEXP (addr, 1); + if ((GET_CODE (*base) != REG) || (GET_CODE (*offset) != CONST_INT)) + return true; + return false; + } + return true; +} + +/* Called from a peephole2 to replace two word-size loads/stores + with a single LDRD/STRD instruction. + OPERANDS are the operands found by the peephole matcher; + LEN indicates how many insns are matched by the pattern, + and used for checking dead registers. + COMMUTE indicates that register operands may be reordered. + OPERANDS[0,1] are register operands + OPERANDS[2,3] are memory operands. + Returns true iff we can generate a new instruction sequence. + That is, both accesses use the same base register and + the gap between constant offsets is 4. + This function may reorder its operands to match ldrd/strd RTL templates. */ +bool +gen_operands_ldrd_strd (rtx *operands, int len, bool load, bool commute) +{ + int nops = 2; + HOST_WIDE_INT offsets[2], offset; + rtx base; + rtx cur_base, cur_offset, tmp; + int i, gap; + + gcc_assert (len >= 2); + + /* Check that the memory references are immediate offsets + from the same base register. + Extract the base register, the destination registers, + and the corresponding memory offsets. */ + for (i = 0; i < nops; i++) + { + if (bad_mem_for_ldrd_strd(operands[nops+i], &cur_base, &cur_offset)) return false; + if (i == 0) + base = cur_base; + else if ( REGNO(base) != REGNO (cur_base)) + return false; + + offsets[i] = INTVAL (cur_offset); + if (GET_CODE (operands[i]) == SUBREG) + operands[i] = SUBREG_REG (operands[i]); + } + + /* Make sure there is no dependency between the individual loads. */ + if (load && (REGNO(operands[0]) == REGNO(base))) return false; /* RAW */ + if (load && (REGNO(operands[0]) == REGNO(operands[1]))) return false; /* WAW */ + + /* Make sure the instructions are ordered with lower memory access first. */ + if (offsets[0] > offsets[1]) + { + gap = (offsets[0] - offsets[1]); + offset = offsets[1]; + + /* Swap the instructions such that lower memory is accessed first. */ + tmp = operands[0]; + operands[0] = operands[1]; + operands[1] = tmp; + + tmp = operands[2]; + operands[2] = operands[3]; + operands[3] = tmp; + } + else + { + gap = (offsets[1] - offsets[0]); + offset = offsets[0]; + } + + /* Make sure accesses are to consecutive memory locations. */ + if (gap != 4) return false; + + /* Make sure we generate legal instructions. */ + if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, load)) + return true; + + if (commute) + { + /* Try reordering registers. */ + tmp = operands[0]; + operands[0] = operands[1]; + operands[1] = tmp; + if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, load)) + return true; + } + return false; +} + +/* Called from a peephole2 replace a pair of stores that are + preceded by constant loads into an STRD instruction. + OPERANDS are the operands found by the peephole matcher; + NOPS indicates how many separate stores we are trying to combine; + there are 2 * NOPS instructions in the peephole. + Returns true iff we could generate a new instruction. */ +/* This uses peephole2's ability to allocate free registers. */ +/* Assumes that the constants are different. Other optimizations, + applied before peephole, should have taken care of + the case in which constants are the same. */ +bool +gen_operands_const_strd (rtx *operands, int len) +{ + int nops = 2; + HOST_WIDE_INT offsets[2], offset; + rtx base; + rtx cur_base, cur_offset, tmp; + int i, gap; + HARD_REG_SET regset; + + CLEAR_HARD_REG_SET (regset); + + /* Check that the memory references are immediate offsets + from the same base register. + Extract the base register, the destination registers, + and the corresponding memory offsets. */ + for (i = 0; i < nops; i++) + { + if (bad_mem_for_ldrd_strd(operands[nops+i], &cur_base, &cur_offset)) return false; + if (i == 0) + { + base = cur_base; + } + else if ( REGNO(base) != REGNO (cur_base)) + return false; + + offsets[i] = INTVAL (cur_offset); + if (GET_CODE (operands[i]) == SUBREG) + operands[i] = SUBREG_REG (operands[i]); + } + + /* If the same input register is used in both stores, + try to find a free register. */ + if (REGNO(operands[0]) == REGNO(operands[1])) + { + /* We might choice a bad register here and fix it later. */ + rtx reg = peep2_find_free_register (0, 4, + TARGET_THUMB1 ? "l" : "r", + SImode, ®set); + if (reg == NULL_RTX) + return false; + + /* Use the new register in the first load to ensure that + if the original input register is not dead after peephole, + then it will have the correct constant value. */ + operands[0] = reg; + } + + /* Make sure the instructions are ordered with lower memory access first. */ + if (offsets[0] > offsets[1]) + { + gap = (offsets[0] - offsets[1]); + offset = offsets[1]; + + /* Swap the instructions such that lower memory is accessed first. */ + tmp = operands[0]; + operands[0] = operands[1]; + operands[1] = tmp; + + tmp = operands[2]; + operands[2] = operands[3]; + operands[3] = tmp; + + tmp = operands[4]; + operands[4] = operands[5]; + operands[5] = tmp; + } + else + { + gap = (offsets[1] - offsets[0]); + offset = offsets[0]; + } + + /* Make sure accesses are to consecutive memory locations. */ + if (gap != 4) return false; + + /* If the offset is illegal, we don't try to fix it. + (We can fix it if the base register is dead, but it's not worth the trouble.) */ + if (bad_offset_ldrd_strd (offset)) + return false; + + /* Is it a leagl STRD? */ + if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false)) + return true; + + /* This STRD is illegal because of the input registers it uses. + In Thumb state, where registers are almost unconstrained, + there is little hope to fix it. In ARM state, if an input register is dead, + we try to replace it by an appropriate consecutive register. */ + if (TARGET_THUMB2) + return false; + else if (TARGET_ARM) + return find_free_regs_for_arm_const_strd (len, operands, base, offset); + else + gcc_unreachable(); +} + +/* + Called by peephole2 patterns to turn two LDRs into an LDRD. + Returns true iff created a legal STRD for ARM by changing the input registers. + If input registers are dead after current insn + LEN, they can be reordered + or replaced by other registers that are free from current insn for LEN insns. */ +bool +find_free_regs_for_arm_const_strd(int len, rtx *operands, rtx base, HOST_WIDE_INT offset) +{ + /* Who is dead? */ + bool dead[2]; + int i; + unsigned int t; + rtx tmp; + HARD_REG_SET regset; + + for (i = 0; i < 2; i++) + dead[i] = peep2_reg_dead_p (len, operands[i]); + + if ((!dead[0]) && (!dead[1])) + return false; + + /* If both are dead, we can reorder them or replace them with free registers. */ + if (dead[0] && dead[1]) + { + /* Try to reorder the input registers. */ + if (!bad_operands_ldrd_strd (operands[1], operands[0], base, offset, false, false)) + { + tmp = operands[0]; + operands[0] = operands[1]; + operands[1] = tmp; + return true; + } + + /* Try to find a free DI register. */ + CLEAR_HARD_REG_SET (regset); + add_to_hard_reg_set (®set, SImode, REGNO(operands[0])); + add_to_hard_reg_set (®set, SImode, REGNO(operands[1])); + while (true) + { + tmp = peep2_find_free_register (0, len, + TARGET_THUMB1 ? "l" : "r", + DImode, ®set); + if (tmp == NULL_RTX) + return false; + + /* DREG must be an even-numbered register in DImode. + Split it into SI registers. */ + t = REGNO(tmp); + operands[0] = gen_rtx_REG(SImode, t); + operands[1] = gen_rtx_REG(SImode, t+1); + gcc_assert ((REGNO(operands[0]) % 2) == 0); + gcc_assert (operands[0] != NULL_RTX); + gcc_assert (operands[1] != NULL_RTX); + + return (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false)); + } + gcc_unreachable(); + } + + /* One of the input registers is dead, the other is live. + Try to replace the dead register with a free register + adjacent to the live register. */ + if (dead[0]) + { + t = REGNO(operands[1]); + if ((t % 2) == 0) + return false; + t = t - 1; + operands[0] = gen_rtx_REG(SImode, t); + } + else if (dead[1]) + { + t = REGNO(operands[0]); + if ((t % 2) != 0) + return false; + t = t + 1; + operands[1] = gen_rtx_REG(SImode, t); + } + else gcc_unreachable(); + + /* Check if register number t is free. Is there a better way to check it? */ + SET_HARD_REG_SET(regset); + remove_from_hard_reg_set (®set, SImode, t); + tmp = peep2_find_free_register (0, 4, TARGET_THUMB1 ? "l" : "r", + SImode, ®set); + if (tmp == NULL_RTX) return false; + gcc_assert (REGNO(tmp) == t); + + return (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false)); +} #include "gt-arm.h" diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index a78ba88..d812bbf 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -11292,8 +11292,12 @@ " ) +;; Load/store double-words patterns. +(include "ldrdstrd0.md") + ;; Load the load/store multiple patterns (include "ldmstm.md") + ;; Load the FPA co-processor patterns (include "fpa.md") ;; Load the Maverick co-processor patterns diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md index d8ce982..3c24c6d 100644 --- a/gcc/config/arm/constraints.md +++ b/gcc/config/arm/constraints.md @@ -22,6 +22,7 @@ ;; - in ARM/Thumb-2 state: f, t, v, w, x, y, z ;; - in Thumb state: h, b ;; - in both states: l, c, k +;; - in ARM state with LDRD support: q ;; In ARM state, 'l' is an alias for 'r' ;; The following normal constraints have been used: @@ -90,6 +91,9 @@ (define_register_constraint "k" "STACK_REG" "@internal The stack register.") +(define_register_constraint "q" "TARGET_ARM && TARGET_LDRD ? CORE_REGS : GENERAL_REGS" + "@internal In ARM state with LDRD support, core registers, otherwise general registers.") + (define_register_constraint "b" "TARGET_THUMB ? BASE_REGS : NO_REGS" "@internal Thumb only. The union of the low registers and the stack register.") diff --git a/gcc/config/arm/ldrdstrd0.md b/gcc/config/arm/ldrdstrd0.md new file mode 100644 index 0000000..6f1bf74 --- /dev/null +++ b/gcc/config/arm/ldrdstrd0.md @@ -0,0 +1,164 @@ +(define_insn "*ldrd_0" + [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q") + (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") + (match_operand:SI 2 "const_int_operand" "")))) + (set (match_operand:SI 3 "arm_hard_core_register_operand" "=q") + (mem:SI (plus:SI (match_dup 1) + (match_operand:SI 4 "const_int_operand" ""))))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && ((INTVAL (operands[2]) + 4) == INTVAL (operands[4])) + && (!bad_operands_ldrd_strd (operands[0], operands[3], + operands[1], INTVAL (operands[2]), + false, true))" + "ldrd%?\t%0, %3, [%1, %2]" + [(set_attr "type" "load2") + (set_attr "predicable" "yes")]) + +(define_insn "*ldrd_base_0" + [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q") + (mem:SI (match_operand:SI 1 "s_register_operand" "rk"))) + (set (match_operand:SI 2 "arm_hard_core_register_operand" "=q") + (mem:SI (plus:SI (match_dup 1) + (const_int 4))))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && (!bad_operands_ldrd_strd (operands[0], operands[2], + operands[1], 0, false, true))" + "ldrd%?\t%0, %2, [%1]" + [(set_attr "type" "load2") + (set_attr "predicable" "yes")]) + +(define_insn "*ldrd_base_neg_0" + [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q") + (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") + (const_int -4)))) + (set (match_operand:SI 2 "arm_hard_core_register_operand" "=q") + (mem:SI (match_dup 1)))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && (!bad_operands_ldrd_strd (operands[0], operands[2], + operands[1], -4, false, true))" + "ldrd%?\t%0, %2, [%1, #-4]" + [(set_attr "type" "load2") + (set_attr "predicable" "yes")]) + +(define_insn "*strd_0" + [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk") + (match_operand:SI 1 "const_int_operand" ""))) + (match_operand:SI 2 "arm_hard_core_register_operand" "q")) + (set (mem:SI (plus:SI (match_dup 0) + (match_operand:SI 3 "const_int_operand" ""))) + (match_operand:SI 4 "arm_hard_core_register_operand" "q"))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && ((INTVAL (operands[1]) + 4) == INTVAL (operands[3])) + && (!bad_operands_ldrd_strd (operands[2], operands[4], + operands[0], INTVAL (operands[1]), + false, false))" + "strd%?\t%2, %4, [%0, %1]" + [(set_attr "type" "store2") + (set_attr "predicable" "yes")]) + +(define_insn "*strd_base_0" + [(set (mem:SI (match_operand:SI 0 "s_register_operand" "rk")) + (match_operand:SI 1 "arm_hard_core_register_operand" "q")) + (set (mem:SI (plus:SI (match_dup 0) + (const_int 4))) + (match_operand:SI 2 "arm_hard_core_register_operand" "q"))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && (!bad_operands_ldrd_strd (operands[1], operands[2], + operands[0], 0, false, false))" + "strd%?\t%1, %2, [%0]" + [(set_attr "type" "store2") + (set_attr "predicable" "yes")]) + +(define_insn "*strd_base_neg_0" + [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk") + (const_int -4))) + (match_operand:SI 1 "arm_hard_core_register_operand" "q")) + (set (mem:SI (match_dup 0)) + (match_operand:SI 2 "arm_hard_core_register_operand" "q"))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0) + && (!bad_operands_ldrd_strd (operands[1], operands[2], + operands[0], -4, false, false))" + "strd%?\t%1, %2, [%0, #-4]" + [(set_attr "type" "store2") + (set_attr "predicable" "yes")]) + +;; The following peephole optimizations identify consecutive memory accesses, +;; and try to rearrange the operands to enable generation of ldrd/strd. + +(define_peephole2 ; ldrd + [(set (match_operand:SI 0 "arm_hard_core_register_operand" "") + (match_operand:SI 2 "memory_operand" "")) + (set (match_operand:SI 1 "arm_hard_core_register_operand" "") + (match_operand:SI 3 "memory_operand" ""))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) + || 0)" + [(parallel [(set (match_dup 0) (match_dup 2)) + (set (match_dup 1) (match_dup 3))])] +{ + if (!gen_operands_ldrd_strd (operands, 2, true, false)) + FAIL; +}) + +(define_peephole2 ; strd + [(set (match_operand:SI 2 "memory_operand" "") + (match_operand:SI 0 "arm_hard_core_register_operand" "")) + (set (match_operand:SI 3 "memory_operand" "") + (match_operand:SI 1 "arm_hard_core_register_operand" ""))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) + || 0)" + [(parallel [(set (match_dup 2) (match_dup 0)) + (set (match_dup 3) (match_dup 1))])] +{ + if (!gen_operands_ldrd_strd (operands, 2, false, false)) + FAIL; +}) + +;; The following peepholes reorder registers to enable LDRD/STRD. +(define_peephole2 ; strd of constants for thumb + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 4 "const_int_operand" "")) + (set (match_operand:SI 2 "memory_operand" "") + (match_dup 0)) + (set (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 5 "const_int_operand" "")) + (set (match_operand:SI 3 "memory_operand" "") + (match_dup 1))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)" + [(set (match_dup 0) (match_dup 4)) + (set (match_dup 1) (match_dup 5)) + (parallel [(set (match_dup 2) (match_dup 0)) + (set (match_dup 3) (match_dup 1))])] +{ + if (!gen_operands_const_strd (operands, 4)) + FAIL; +}) + +(define_peephole2 ; strd of constants for thumb + [(set (match_operand:SI 0 "s_register_operand" "") + (match_operand:SI 4 "const_int_operand" "")) + (set (match_operand:SI 1 "s_register_operand" "") + (match_operand:SI 5 "const_int_operand" "")) + (set (match_operand:SI 2 "memory_operand" "") + (match_dup 0)) + (set (match_operand:SI 3 "memory_operand" "") + (match_dup 1))] + "TARGET_LDRD && TARGET_THUMB2 + && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)" + [(set (match_dup 0) (match_dup 4)) + (set (match_dup 1) (match_dup 5)) + (parallel [(set (match_dup 2) (match_dup 0)) + (set (match_dup 3) (match_dup 1))])] +{ + if (!gen_operands_const_strd (operands, 4)) + FAIL; +}) + diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md index 92eb004..be13326 100644 --- a/gcc/config/arm/predicates.md +++ b/gcc/config/arm/predicates.md @@ -38,6 +38,13 @@ return REGNO (op) < FIRST_PSEUDO_REGISTER; }) +;; Hard core register (non vpf, non pseudo) +(define_predicate "arm_hard_core_register_operand" + (match_code "reg") +{ + return (REGNO (op) <= LAST_ARM_REGNUM); +}) + ;; A low register. (define_predicate "low_register_operand" (and (match_code "reg") diff --git a/gcc/testsuite/gcc.target/arm/pr40457-1.c b/gcc/testsuite/gcc.target/arm/pr40457-1.c index 815fd38..990bc49 100644 --- a/gcc/testsuite/gcc.target/arm/pr40457-1.c +++ b/gcc/testsuite/gcc.target/arm/pr40457-1.c @@ -1,5 +1,6 @@ -/* { dg-options "-Os" } */ +/* { dg-options "-march=armv7-a -Os" } */ /* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ int bar(int* p) { @@ -7,4 +8,4 @@ int bar(int* p) return x; } -/* { dg-final { scan-assembler "ldm" } } */ +/* { dg-final { scan-assembler "ldrd|ldm" } } */ diff --git a/gcc/testsuite/gcc.target/arm/pr40457-2.c b/gcc/testsuite/gcc.target/arm/pr40457-2.c index 187f7bf..acf9f71 100644 --- a/gcc/testsuite/gcc.target/arm/pr40457-2.c +++ b/gcc/testsuite/gcc.target/arm/pr40457-2.c @@ -1,5 +1,6 @@ -/* { dg-options "-O2" } */ +/* { dg-options "-march=armv7-a -O2" } */ /* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ void foo(int* p) { @@ -7,4 +8,4 @@ void foo(int* p) p[1] = 0; } -/* { dg-final { scan-assembler "stm" } } */ +/* { dg-final { scan-assembler "strd|stm" } } */ diff --git a/gcc/testsuite/gcc.target/arm/pr40457-3.c b/gcc/testsuite/gcc.target/arm/pr40457-3.c index 9bd5a17..e7098cb 100644 --- a/gcc/testsuite/gcc.target/arm/pr40457-3.c +++ b/gcc/testsuite/gcc.target/arm/pr40457-3.c @@ -1,5 +1,6 @@ -/* { dg-options "-Os" } */ +/* { dg-options "-march=armv7-a -Os" } */ /* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ void foo(int* p) { @@ -7,4 +8,4 @@ void foo(int* p) p[1] = 0; } -/* { dg-final { scan-assembler "stm" } } */ +/* { dg-final { scan-assembler "strd|stm" } } */ diff --git a/gcc/testsuite/gcc.target/arm/pr45335-2.c b/gcc/testsuite/gcc.target/arm/pr45335-2.c new file mode 100644 index 0000000..b519580 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr45335-2.c @@ -0,0 +1,10 @@ +/* { dg-options "-march=armv7-a -O2" } */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ +/* { dg-final { scan-assembler "stm|strd" } } */ +void foo(int a, int b, int* p) +{ + p[2] = a; + p[3] = b; +} + diff --git a/gcc/testsuite/gcc.target/arm/pr45335-3.c b/gcc/testsuite/gcc.target/arm/pr45335-3.c new file mode 100644 index 0000000..cff58d2 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr45335-3.c @@ -0,0 +1,13 @@ +/* { dg-options "-march=armv7-a -O2" } */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ +/* { dg-final { scan-assembler "ldrd|ldm" } } */ +int foo(int a, int b, int* p, int *q) +{ + a = p[2] + p[3]; + *q = a; + *p = a; + return a; +} + + diff --git a/gcc/testsuite/gcc.target/arm/pr45335.c b/gcc/testsuite/gcc.target/arm/pr45335.c new file mode 100644 index 0000000..d9aa6c7 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr45335.c @@ -0,0 +1,23 @@ +/* { dg-options "-O2 -march=armv7-a" } */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ +/* { dg-final { scan-assembler "ldm|ldrd" } } */ +/* { dg-final { scan-assembler "stm|strd" } } */ + +struct S +{ + void* p1; + void* p2; + void* p3; + void* p4; +}; + +extern printf(char*, ...); + +void foo1(struct S* fp, struct S* otherSaveArea) +{ + struct S* saveA = fp - 1; + printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveA, otherSaveArea); + printf("prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n", + saveA->p1, saveA->p2, saveA->p3, saveA->p4, *(unsigned int*)fp); +}