diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 009d45fc718..0ee7588955a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2012-04-30 Ian Bolton + Sameera Deshpande + Greta Yorsh + + * config/arm/arm-protos.h (ldm_stm_operation_p): New declaration. + * config/arm/arm.c (ldm_stm_operation_p): New function. + * config/arm/predicates.md (load_multiple_operation): Update predicate. + (store_multiple_operation): Likewise. + 2012-04-30 Uros Bizjak * config/i386/i386.md (and3): Expand masking operations with diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 900d09a09a3..753e109273c 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -62,6 +62,7 @@ extern bool arm_legitimize_reload_address (rtx *, enum machine_mode, int, int, extern rtx thumb_legitimize_reload_address (rtx *, enum machine_mode, int, int, int); extern int thumb1_legitimate_address_p (enum machine_mode, rtx, int); +extern bool ldm_stm_operation_p (rtx, bool); extern int arm_const_double_rtx (rtx); extern int neg_const_double_rtx_ok_for_fpa (rtx); extern int vfp3_const_double_rtx (rtx); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 2cecf4546cd..c856af8e09f 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -10166,6 +10166,150 @@ adjacent_mem_locations (rtx a, rtx b) return 0; } +/* Return true if OP is a valid load or store multiple operation. LOAD is true + for load operations, false for store operations. + The pattern we are trying to match for load is: + [(SET (R_d0) (MEM (PLUS (addr) (offset)))) + (SET (R_d1) (MEM (PLUS (addr) (offset + )))) + : + : + (SET (R_dn) (MEM (PLUS (addr) (offset + n * )))) + ] + where + 1. If offset is 0, first insn should be (SET (R_d0) (MEM (src_addr))). + 2. REGNO (R_d0) < REGNO (R_d1) < ... < REGNO (R_dn). + 3. If consecutive is TRUE, then for kth register being loaded, + REGNO (R_dk) = REGNO (R_d0) + k. + The pattern for store is similar. */ +bool +ldm_stm_operation_p (rtx op, bool load) +{ + HOST_WIDE_INT count = XVECLEN (op, 0); + rtx reg, mem, addr; + unsigned regno; + HOST_WIDE_INT i = 1, base = 0, offset = 0; + rtx elt; + bool addr_reg_in_reglist = false; + bool update = false; + int reg_increment; + int offset_adj; + + reg_increment = 4; + offset_adj = 0; + + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, offset_adj)) != SET + || (load && !REG_P (SET_DEST (XVECEXP (op, 0, offset_adj))))) + return false; + + /* Check if this is a write-back. */ + elt = XVECEXP (op, 0, offset_adj); + if (GET_CODE (SET_SRC (elt)) == PLUS) + { + i++; + base = 1; + update = true; + + /* The offset adjustment must be the number of registers being + popped times the size of a single register. */ + if (!REG_P (SET_DEST (elt)) + || !REG_P (XEXP (SET_SRC (elt), 0)) + || (REGNO (SET_DEST (elt)) != REGNO (XEXP (SET_SRC (elt), 0))) + || !CONST_INT_P (XEXP (SET_SRC (elt), 1)) + || INTVAL (XEXP (SET_SRC (elt), 1)) != + ((count - 1 - offset_adj) * reg_increment)) + return false; + } + + i = i + offset_adj; + base = base + offset_adj; + /* Perform a quick check so we don't blow up below. */ + if (count <= i) + return false; + + elt = XVECEXP (op, 0, i - 1); + if (GET_CODE (elt) != SET) + return false; + + if (load) + { + reg = SET_DEST (elt); + mem = SET_SRC (elt); + } + else + { + reg = SET_SRC (elt); + mem = SET_DEST (elt); + } + + if (!REG_P (reg) || !MEM_P (mem)) + return false; + + regno = REGNO (reg); + addr = XEXP (mem, 0); + if (GET_CODE (addr) == PLUS) + { + if (!CONST_INT_P (XEXP (addr, 1))) + return false; + + offset = INTVAL (XEXP (addr, 1)); + addr = XEXP (addr, 0); + } + + if (!REG_P (addr)) + return false; + + for (; i < count; i++) + { + elt = XVECEXP (op, 0, i); + if (GET_CODE (elt) != SET) + return false; + + if (load) + { + reg = SET_DEST (elt); + mem = SET_SRC (elt); + } + else + { + reg = SET_SRC (elt); + mem = SET_DEST (elt); + } + + if (!REG_P (reg) + || GET_MODE (reg) != SImode + || REGNO (reg) <= regno + || !MEM_P (mem) + || GET_MODE (mem) != SImode + || ((GET_CODE (XEXP (mem, 0)) != PLUS + || !rtx_equal_p (XEXP (XEXP (mem, 0), 0), addr) + || !CONST_INT_P (XEXP (XEXP (mem, 0), 1)) + || (INTVAL (XEXP (XEXP (mem, 0), 1)) != + offset + (i - base) * reg_increment)) + && (!REG_P (XEXP (mem, 0)) + || offset + (i - base) * reg_increment != 0))) + return false; + + regno = REGNO (reg); + if (regno == REGNO (addr)) + addr_reg_in_reglist = true; + } + + if (load) + { + if (update && addr_reg_in_reglist) + return false; + + /* For Thumb-1, address register is always modified - either by write-back + or by explicit load. If the pattern does not describe an update, + then the address register must be in the list of loaded registers. */ + if (TARGET_THUMB1) + return update || addr_reg_in_reglist; + } + + return true; +} + /* Return true iff it would be profitable to turn a sequence of NOPS loads or stores (depending on IS_STORE) into a load-multiple or store-multiple instruction. ADD_OFFSET is nonzero if the base address register needs diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md index 9171d7377fe..20a64ec3701 100644 --- a/gcc/config/arm/predicates.md +++ b/gcc/config/arm/predicates.md @@ -380,154 +380,13 @@ (define_special_predicate "load_multiple_operation" (match_code "parallel") { - HOST_WIDE_INT count = XVECLEN (op, 0); - unsigned dest_regno; - rtx src_addr; - HOST_WIDE_INT i = 1, base = 0; - HOST_WIDE_INT offset = 0; - rtx elt; - bool addr_reg_loaded = false; - bool update = false; - - if (count <= 1 - || GET_CODE (XVECEXP (op, 0, 0)) != SET - || !REG_P (SET_DEST (XVECEXP (op, 0, 0)))) - return false; - - /* Check to see if this might be a write-back. */ - if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) - { - i++; - base = 1; - update = true; - - /* Now check it more carefully. */ - if (GET_CODE (SET_DEST (elt)) != REG - || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG - || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT - || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) - return false; - } - - /* Perform a quick check so we don't blow up below. */ - if (count <= i - || GET_CODE (XVECEXP (op, 0, i - 1)) != SET - || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG - || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM) - return false; - - dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1))); - src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0); - if (GET_CODE (src_addr) == PLUS) - { - if (GET_CODE (XEXP (src_addr, 1)) != CONST_INT) - return false; - offset = INTVAL (XEXP (src_addr, 1)); - src_addr = XEXP (src_addr, 0); - } - if (!REG_P (src_addr)) - return false; - - for (; i < count; i++) - { - elt = XVECEXP (op, 0, i); - - if (GET_CODE (elt) != SET - || GET_CODE (SET_DEST (elt)) != REG - || GET_MODE (SET_DEST (elt)) != SImode - || REGNO (SET_DEST (elt)) <= dest_regno - || GET_CODE (SET_SRC (elt)) != MEM - || GET_MODE (SET_SRC (elt)) != SImode - || ((GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS - || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) - || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT - || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != offset + (i - base) * 4) - && (!REG_P (XEXP (SET_SRC (elt), 0)) - || offset + (i - base) * 4 != 0))) - return false; - dest_regno = REGNO (SET_DEST (elt)); - if (dest_regno == REGNO (src_addr)) - addr_reg_loaded = true; - } - /* For Thumb, we only have updating instructions. If the pattern does - not describe an update, it must be because the address register is - in the list of loaded registers - on the hardware, this has the effect - of overriding the update. */ - if (update && addr_reg_loaded) - return false; - if (TARGET_THUMB1) - return update || addr_reg_loaded; - return true; + return ldm_stm_operation_p (op, /*load=*/true); }) (define_special_predicate "store_multiple_operation" (match_code "parallel") { - HOST_WIDE_INT count = XVECLEN (op, 0); - unsigned src_regno; - rtx dest_addr; - HOST_WIDE_INT i = 1, base = 0, offset = 0; - rtx elt; - - if (count <= 1 - || GET_CODE (XVECEXP (op, 0, 0)) != SET) - return false; - - /* Check to see if this might be a write-back. */ - if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS) - { - i++; - base = 1; - - /* Now check it more carefully. */ - if (GET_CODE (SET_DEST (elt)) != REG - || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG - || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT - || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4) - return false; - } - - /* Perform a quick check so we don't blow up below. */ - if (count <= i - || GET_CODE (XVECEXP (op, 0, i - 1)) != SET - || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM - || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG) - return false; - - src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1))); - dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0); - - if (GET_CODE (dest_addr) == PLUS) - { - if (GET_CODE (XEXP (dest_addr, 1)) != CONST_INT) - return false; - offset = INTVAL (XEXP (dest_addr, 1)); - dest_addr = XEXP (dest_addr, 0); - } - if (!REG_P (dest_addr)) - return false; - - for (; i < count; i++) - { - elt = XVECEXP (op, 0, i); - - if (GET_CODE (elt) != SET - || GET_CODE (SET_SRC (elt)) != REG - || GET_MODE (SET_SRC (elt)) != SImode - || REGNO (SET_SRC (elt)) <= src_regno - || GET_CODE (SET_DEST (elt)) != MEM - || GET_MODE (SET_DEST (elt)) != SImode - || ((GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS - || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) - || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT - || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != offset + (i - base) * 4) - && (!REG_P (XEXP (SET_DEST (elt), 0)) - || offset + (i - base) * 4 != 0))) - return false; - src_regno = REGNO (SET_SRC (elt)); - } - - return true; + return ldm_stm_operation_p (op, /*load=*/false); }) (define_special_predicate "multi_register_push"