aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/rs6000/rs6000.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/rs6000/rs6000.c')
-rw-r--r--gcc/config/rs6000/rs6000.c172
1 files changed, 171 insertions, 1 deletions
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 46b46d74986..567749c5cb1 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -7001,6 +7001,164 @@ rs6000_expand_vector_extract (rtx target, rtx vec, rtx elt)
emit_move_insn (target, adjust_address_nv (mem, inner_mode, 0));
}
+/* Adjust a memory address (MEM) of a vector type to point to a scalar field
+ within the vector (ELEMENT) with a mode (SCALAR_MODE). Use a base register
+ temporary (BASE_TMP) to fixup the address. Return the new memory address
+ that is valid for reads or writes to a given register (SCALAR_REG). */
+
+rtx
+rs6000_adjust_vec_address (rtx scalar_reg,
+ rtx mem,
+ rtx element,
+ rtx base_tmp,
+ machine_mode scalar_mode)
+{
+ unsigned scalar_size = GET_MODE_SIZE (scalar_mode);
+ rtx addr = XEXP (mem, 0);
+ rtx element_offset;
+ rtx new_addr;
+ bool valid_addr_p;
+
+ /* Vector addresses should not have PRE_INC, PRE_DEC, or PRE_MODIFY. */
+ gcc_assert (GET_RTX_CLASS (GET_CODE (addr)) != RTX_AUTOINC);
+
+ /* Calculate what we need to add to the address to get the element
+ address. */
+ if (CONST_INT_P (element))
+ element_offset = GEN_INT (INTVAL (element) * scalar_size);
+ else
+ {
+ int byte_shift = exact_log2 (scalar_size);
+ gcc_assert (byte_shift >= 0);
+
+ if (byte_shift == 0)
+ element_offset = element;
+
+ else
+ {
+ if (TARGET_POWERPC64)
+ emit_insn (gen_ashldi3 (base_tmp, element, GEN_INT (byte_shift)));
+ else
+ emit_insn (gen_ashlsi3 (base_tmp, element, GEN_INT (byte_shift)));
+
+ element_offset = base_tmp;
+ }
+ }
+
+ /* Create the new address pointing to the element within the vector. If we
+ are adding 0, we don't have to change the address. */
+ if (element_offset == const0_rtx)
+ new_addr = addr;
+
+ /* A simple indirect address can be converted into a reg + offset
+ address. */
+ else if (REG_P (addr) || SUBREG_P (addr))
+ new_addr = gen_rtx_PLUS (Pmode, addr, element_offset);
+
+ /* Optimize D-FORM addresses with constant offset with a constant element, to
+ include the element offset in the address directly. */
+ else if (GET_CODE (addr) == PLUS)
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+ rtx insn;
+
+ gcc_assert (REG_P (op0) || SUBREG_P (op0));
+ if (CONST_INT_P (op1) && CONST_INT_P (element_offset))
+ {
+ HOST_WIDE_INT offset = INTVAL (op1) + INTVAL (element_offset);
+ rtx offset_rtx = GEN_INT (offset);
+
+ if (IN_RANGE (offset, -32768, 32767)
+ && (scalar_size < 8 || (offset & 0x3) == 0))
+ new_addr = gen_rtx_PLUS (Pmode, op0, offset_rtx);
+ else
+ {
+ emit_move_insn (base_tmp, offset_rtx);
+ new_addr = gen_rtx_PLUS (Pmode, op0, base_tmp);
+ }
+ }
+ else
+ {
+ if (REG_P (op1) || SUBREG_P (op1))
+ {
+ insn = gen_add3_insn (base_tmp, op1, element_offset);
+ gcc_assert (insn != NULL_RTX);
+ emit_insn (insn);
+ }
+
+ else if (REG_P (element_offset) || SUBREG_P (element_offset))
+ {
+ insn = gen_add3_insn (base_tmp, element_offset, op1);
+ gcc_assert (insn != NULL_RTX);
+ emit_insn (insn);
+ }
+
+ else
+ {
+ emit_move_insn (base_tmp, op1);
+ emit_insn (gen_add2_insn (base_tmp, element_offset));
+ }
+
+ new_addr = gen_rtx_PLUS (Pmode, op0, base_tmp);
+ }
+ }
+
+ else
+ {
+ emit_move_insn (base_tmp, addr);
+ new_addr = gen_rtx_PLUS (Pmode, base_tmp, element_offset);
+ }
+
+ /* If we have a PLUS, we need to see whether the particular register class
+ allows for D-FORM or X-FORM addressing. */
+ if (GET_CODE (new_addr) == PLUS)
+ {
+ rtx op1 = XEXP (new_addr, 1);
+ addr_mask_type addr_mask;
+ int scalar_regno;
+
+ if (REG_P (scalar_reg))
+ scalar_regno = REGNO (scalar_reg);
+ else if (SUBREG_P (scalar_reg))
+ scalar_regno = subreg_regno (scalar_reg);
+ else
+ gcc_unreachable ();
+
+ gcc_assert (scalar_regno < FIRST_PSEUDO_REGISTER);
+ if (INT_REGNO_P (scalar_regno))
+ addr_mask = reg_addr[scalar_mode].addr_mask[RELOAD_REG_GPR];
+
+ else if (FP_REGNO_P (scalar_regno))
+ addr_mask = reg_addr[scalar_mode].addr_mask[RELOAD_REG_FPR];
+
+ else if (ALTIVEC_REGNO_P (scalar_regno))
+ addr_mask = reg_addr[scalar_mode].addr_mask[RELOAD_REG_VMX];
+
+ else
+ gcc_unreachable ();
+
+ if (REG_P (op1) || SUBREG_P (op1))
+ valid_addr_p = (addr_mask & RELOAD_REG_INDEXED) != 0;
+ else
+ valid_addr_p = (addr_mask & RELOAD_REG_OFFSET) != 0;
+ }
+
+ else if (REG_P (new_addr) || SUBREG_P (new_addr))
+ valid_addr_p = true;
+
+ else
+ valid_addr_p = false;
+
+ if (!valid_addr_p)
+ {
+ emit_move_insn (base_tmp, new_addr);
+ new_addr = base_tmp;
+ }
+
+ return change_address (mem, scalar_mode, new_addr);
+}
+
/* Split a variable vec_extract operation into the component instructions. */
void
@@ -7014,7 +7172,18 @@ rs6000_split_vec_extract_var (rtx dest, rtx src, rtx element, rtx tmp_gpr,
gcc_assert (byte_shift >= 0);
- if (REG_P (src) || SUBREG_P (src))
+ /* If we are given a memory address, optimize to load just the element. We
+ don't have to adjust the vector element number on little endian
+ systems. */
+ if (MEM_P (src))
+ {
+ gcc_assert (REG_P (tmp_gpr));
+ emit_move_insn (dest, rs6000_adjust_vec_address (dest, src, element,
+ tmp_gpr, scalar_mode));
+ return;
+ }
+
+ else if (REG_P (src) || SUBREG_P (src))
{
int bit_shift = byte_shift + 3;
rtx element2;
@@ -38759,6 +38928,7 @@ rtx_is_swappable_p (rtx op, unsigned int *special)
case UNSPEC_VSX_CVSPDP:
case UNSPEC_VSX_CVSPDPN:
case UNSPEC_VSX_EXTRACT:
+ case UNSPEC_VSX_VSLO:
return 0;
case UNSPEC_VSPLT_DIRECT:
*special = SH_SPLAT;