===================================================================
@@ -1626,32 +1626,11 @@
return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
})
-;; Return true if the operand is a pc-relative address.
+;; Return true if the operand is a pc-relative address to a local symbol.
(define_predicate "pcrel_address"
(match_code "label_ref,symbol_ref,const")
{
- if (!rs6000_pcrel_p (cfun))
- return false;
-
- if (GET_CODE (op) == CONST)
- op = XEXP (op, 0);
-
- /* Validate offset. */
- if (GET_CODE (op) == PLUS)
- {
- rtx op0 = XEXP (op, 0);
- rtx op1 = XEXP (op, 1);
-
- if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
- return false;
-
- op = op0;
- }
-
- if (LABEL_REF_P (op))
- return true;
-
- return (SYMBOL_REF_P (op) && SYMBOL_REF_LOCAL_P (op));
+ return pcrel_addr_p (op, true, false, PCREL_NULL);
})
;; Return true if the operand is an external symbol whose address can be loaded
@@ -1665,32 +1644,14 @@
(define_predicate "pcrel_external_address"
(match_code "symbol_ref,const")
{
- if (!rs6000_pcrel_p (cfun))
- return false;
-
- if (GET_CODE (op) == CONST)
- op = XEXP (op, 0);
-
- /* Validate offset. */
- if (GET_CODE (op) == PLUS)
- {
- rtx op0 = XEXP (op, 0);
- rtx op1 = XEXP (op, 1);
-
- if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
- return false;
-
- op = op0;
- }
-
- return (SYMBOL_REF_P (op) && !SYMBOL_REF_LOCAL_P (op));
+ return pcrel_addr_p (op, false, true, PCREL_NULL);
})
;; Return 1 if op is a prefixed memory operand.
(define_predicate "prefixed_mem_operand"
(match_code "mem")
{
- return rs6000_prefixed_address_mode_p (XEXP (op, 0), GET_MODE (op));
+ return prefixed_local_addr_p (XEXP (op, 0), mode, INSN_FORM_UNKNOWN);
})
;; Return 1 if op is a memory operand to an external variable when we
@@ -1699,7 +1660,7 @@
(define_predicate "pcrel_external_mem_operand"
(match_code "mem")
{
- return pcrel_external_address (XEXP (op, 0), Pmode);
+ return pcrel_addr_p (XEXP (op, 0), false, true, PCREL_NULL);
})
;; Match the first insn (addis) in fusing the combination of addis and loads to
===================================================================
@@ -47,7 +47,19 @@ extern bool legitimate_indirect_address_p (rtx, in
extern bool legitimate_indexed_address_p (rtx, int);
extern bool avoiding_indexed_address_p (machine_mode);
extern rtx rs6000_force_indexed_or_indirect_mem (rtx x);
+extern enum insn_form reg_to_insn_form (rtx, machine_mode);
+extern bool prefixed_local_addr_p (rtx, machine_mode, enum insn_form);
+/* Pc-relative address broken into component parts by pcrel_addr_p. */
+typedef struct {
+ rtx base_addr; /* SYMBOL_REF or LABEL_REF. */
+ HOST_WIDE_INT offset; /* Offset from the base address. */
+ bool external_p; /* Is the symbol external? */
+} pcrel_info_type;
+
+#define PCREL_NULL ((pcrel_info_type *)0)
+
+extern bool pcrel_addr_p (rtx, bool, bool, pcrel_info_type *);
extern rtx rs6000_got_register (rtx);
extern rtx find_addr_reg (rtx);
extern rtx gen_easy_altivec_constant (rtx);
@@ -154,7 +166,6 @@ extern align_flags rs6000_loop_align (rtx);
extern void rs6000_split_logical (rtx [], enum rtx_code, bool, bool, bool);
extern bool rs6000_pcrel_p (struct function *);
extern bool rs6000_fndecl_pcrel_p (const_tree);
-extern bool rs6000_prefixed_address_mode_p (rtx, machine_mode);
#endif /* RTX_CODE */
#ifdef TREE_CODE
===================================================================
@@ -369,8 +369,11 @@ struct rs6000_reg_addr {
enum insn_code reload_fpr_gpr; /* INSN to move from FPR to GPR. */
enum insn_code reload_gpr_vsx; /* INSN to move from GPR to VSX. */
enum insn_code reload_vsx_gpr; /* INSN to move from VSX to GPR. */
+ enum insn_form default_insn_form; /* Default format for offsets. */
+ enum insn_form insn_form[(int)N_RELOAD_REG]; /* Register insn format. */
addr_mask_type addr_mask[(int)N_RELOAD_REG]; /* Valid address masks. */
bool scalar_in_vmx_p; /* Scalar value can go in VMX. */
+ bool prefixed_memory_p; /* We can use prefixed memory. */
};
static struct rs6000_reg_addr reg_addr[NUM_MACHINE_MODES];
@@ -2053,6 +2056,28 @@ rs6000_debug_vector_unit (enum rs6000_vector v)
return ret;
}
+/* Return a character that can be printed out to describe an instruction
+ format. */
+
+DEBUG_FUNCTION char
+rs6000_debug_insn_form (enum insn_form iform)
+{
+ char ret;
+
+ switch (iform)
+ {
+ case INSN_FORM_UNKNOWN: ret = '-'; break;
+ case INSN_FORM_D: ret = 'd'; break;
+ case INSN_FORM_DS: ret = 's'; break;
+ case INSN_FORM_DQ: ret = 'q'; break;
+ case INSN_FORM_X: ret = 'x'; break;
+ case INSN_FORM_PREFIXED: ret = 'p'; break;
+ default: ret = '?'; break;
+ }
+
+ return ret;
+}
+
/* Inner function printing just the address mask for a particular reload
register class. */
DEBUG_FUNCTION char *
@@ -2115,6 +2140,12 @@ rs6000_debug_print_mode (ssize_t m)
fprintf (stderr, " %s: %s", reload_reg_map[rc].name,
rs6000_debug_addr_mask (reg_addr[m].addr_mask[rc], true));
+ fprintf (stderr, " Format: %c:%c%c%c",
+ rs6000_debug_insn_form (reg_addr[m].default_insn_form),
+ rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_GPR]),
+ rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_FPR]),
+ rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_VMX]));
+
if ((reg_addr[m].reload_store != CODE_FOR_nothing)
|| (reg_addr[m].reload_load != CODE_FOR_nothing))
{
@@ -2668,6 +2699,153 @@ rs6000_setup_reg_addr_masks (void)
}
}
+/* Set up the instruction format for each mode and register type from the
+ addr_mask. */
+
+static void
+setup_insn_form (void)
+{
+ for (ssize_t m = 0; m < NUM_MACHINE_MODES; ++m)
+ {
+ machine_mode scalar_mode = (machine_mode) m;
+
+ /* Convert complex and IBM double double/_Decimal128 into their scalar
+ parts that the registers will be split into for doing load or
+ store. */
+ if (COMPLEX_MODE_P (scalar_mode))
+ scalar_mode = GET_MODE_INNER (scalar_mode);
+
+ if (FLOAT128_2REG_P (scalar_mode))
+ scalar_mode = DFmode;
+
+ for (ssize_t rc = FIRST_RELOAD_REG_CLASS; rc <= LAST_RELOAD_REG_CLASS; rc++)
+ {
+ machine_mode single_reg_mode = scalar_mode;
+ size_t msize = GET_MODE_SIZE (scalar_mode);
+ addr_mask_type addr_mask = reg_addr[scalar_mode].addr_mask[rc];
+ enum insn_form iform = INSN_FORM_UNKNOWN;
+
+ /* Is the mode permitted in the GPR/FPR/Altivec registers? */
+ if ((addr_mask & RELOAD_REG_VALID) != 0)
+ {
+ /* The addr_mask does not have the offsettable or indexed bits
+ set for modes that are split into multiple registers (like
+ IFmode). It doesn't need this set, since typically by time it
+ is used in secondary reload, the modes are split into
+ component parts.
+
+ The instruction format however can be used earlier in the
+ compilation, so we need to setup what kind of instruction can
+ be generated for the modes that are split. */
+ if ((addr_mask & (RELOAD_REG_MULTIPLE
+ | RELOAD_REG_OFFSET
+ | RELOAD_REG_INDEXED)) == RELOAD_REG_MULTIPLE)
+ {
+ /* Multiple register types in GPRs depend on whether we can
+ use DImode in a single register or SImode. */
+ if (rc == RELOAD_REG_GPR)
+ {
+ if (TARGET_POWERPC64)
+ {
+ gcc_assert ((msize % 8) == 0);
+ single_reg_mode = DImode;
+ }
+
+ else
+ {
+ gcc_assert ((msize % 4) == 0);
+ single_reg_mode = SImode;
+ }
+ }
+
+ /* Multiple VSX vector sized data items will use a single
+ vector type as an instruction format. */
+ else if (TARGET_VSX)
+ {
+ gcc_assert ((rc == RELOAD_REG_FPR)
+ || (rc == RELOAD_REG_VMX));
+
+ if ((msize % 16) == 0)
+ single_reg_mode = V2DImode;
+ }
+
+ /* Multiple Altivec vector sized data items will use a single
+ vector type as an instruction format. */
+ else if (TARGET_ALTIVEC && rc == RELOAD_REG_VMX
+ && (msize % 16) == 0
+ && VECTOR_MODE_P (single_reg_mode))
+ single_reg_mode = V4SImode;
+
+ /* If we only have the traditional floating point unit, use
+ DFmode as the base type. */
+ else if (!TARGET_VSX && TARGET_HARD_FLOAT
+ && rc == RELOAD_REG_FPR && (msize % 8) == 0)
+ single_reg_mode = DFmode;
+
+ /* Get the information for the register mode used after
+ splitting. */
+ addr_mask = reg_addr[single_reg_mode].addr_mask[rc];
+ msize = GET_MODE_SIZE (single_reg_mode);
+ }
+
+ /* Figure out the instruction format of each mode.
+
+ For offsettable addresses that aren't specifically quad mode,
+ see if the default form is D or DS. GPR 64-bit offsettable
+ addresses are DS format. Likewise, all Altivec offsettable
+ adddresses are DS format. */
+ if ((addr_mask & RELOAD_REG_OFFSET) != 0)
+ {
+ if ((addr_mask & RELOAD_REG_QUAD_OFFSET) != 0)
+ iform = INSN_FORM_DQ;
+
+ else if (rc == RELOAD_REG_VMX
+ || (rc == RELOAD_REG_GPR && TARGET_POWERPC64
+ && (msize >= 8)))
+ iform = INSN_FORM_DS;
+
+ else
+ iform = INSN_FORM_D;
+ }
+
+ else if ((addr_mask & RELOAD_REG_INDEXED) != 0)
+ iform = INSN_FORM_X;
+ }
+
+ reg_addr[m].insn_form[rc] = iform;
+ }
+
+ /* Figure out the default insn format that is used for offsettable memory
+ instructions. For scalar floating point use the FPR addressing, for
+ vectors and IEEE 128-bit use a suitable vector register type, and
+ otherwise use GPRs. */
+ ssize_t def_rc;
+ if (TARGET_VSX
+ && (VECTOR_MODE_P (scalar_mode) || FLOAT128_IEEE_P (scalar_mode)))
+ {
+ if ((reg_addr[m].addr_mask[RELOAD_REG_FPR] & RELOAD_REG_VALID) != 0)
+ def_rc = RELOAD_REG_FPR;
+ else
+ def_rc = RELOAD_REG_VMX;
+ }
+
+ else if (TARGET_ALTIVEC && !TARGET_VSX && VECTOR_MODE_P (scalar_mode))
+ def_rc = RELOAD_REG_VMX;
+
+ else if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (scalar_mode))
+ def_rc = RELOAD_REG_FPR;
+
+ else
+ def_rc = RELOAD_REG_GPR;
+
+ reg_addr[m].default_insn_form = reg_addr[m].insn_form[def_rc];
+
+ /* Don't enable prefixed memory support until all of the infrastructure
+ changes are in. */
+ reg_addr[m].prefixed_memory_p = false;
+ }
+}
+
/* Initialize the various global tables that are based on register size. */
static void
@@ -3181,6 +3359,9 @@ rs6000_init_hard_regno_mode_ok (bool global_init_p
use. */
rs6000_setup_reg_addr_masks ();
+ /* Update the instruction formats. */
+ setup_insn_form ();
+
if (global_init_p || TARGET_DEBUG_TARGET)
{
if (TARGET_DEBUG_REG)
@@ -13070,30 +13251,22 @@ print_operand (FILE *file, rtx x, int code)
void
print_operand_address (FILE *file, rtx x)
{
+ pcrel_info_type pcrel_info;
+
if (REG_P (x))
fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
/* Is it a pc-relative address? */
- else if (pcrel_address (x, Pmode))
+ else if (pcrel_addr_p (x, true, true, &pcrel_info))
{
- HOST_WIDE_INT offset;
+ output_addr_const (file, pcrel_info.base_addr);
- if (GET_CODE (x) == CONST)
- x = XEXP (x, 0);
+ if (pcrel_info.offset)
+ fprintf (file, "%+" PRId64, pcrel_info.offset);
- if (GET_CODE (x) == PLUS)
- {
- offset = INTVAL (XEXP (x, 1));
- x = XEXP (x, 0);
- }
- else
- offset = 0;
+ if (pcrel_info.external_p)
+ fputs ("@got", file);
- output_addr_const (file, x);
-
- if (offset)
- fprintf (file, "%+" PRId64, offset);
-
fputs ("@pcrel", file);
}
else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
@@ -13579,70 +13752,204 @@ rs6000_pltseq_template (rtx *operands, int which)
return str;
}
#endif
+
+/* Return true if the address ADDR is a prefixed address either with a large
+ offset, an offset that does not fit in the normal instruction form, or a
+ pc-relative address to a local symbol.
-/* Helper function to return whether a MODE can do prefixed loads/stores.
- VOIDmode is used when we are loading the pc-relative address into a base
- register, but we are not using it as part of a memory operation. As modes
- add support for prefixed memory, they will be added here. */
+ MODE is the mode of the memory.
-static bool
-mode_supports_prefixed_address_p (machine_mode mode)
-{
- return mode == VOIDmode;
-}
+ IFORM is used to determine if the traditional address is either DS format or
+ DQ format and the bottom bits of the offset are non-zero. */
-/* Function to return true if ADDR is a valid prefixed memory address that uses
- mode MODE. */
-
bool
-rs6000_prefixed_address_mode_p (rtx addr, machine_mode mode)
+prefixed_local_addr_p (rtx addr, machine_mode mode, enum insn_form iform)
{
- if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
+ if (!reg_addr[mode].prefixed_memory_p)
return false;
- /* Check for PC-relative addresses. */
- if (pcrel_address (addr, Pmode))
- return true;
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
- /* Check for prefixed memory addresses that have a large numeric offset,
- or an offset that can't be used for a DS/DQ-form memory operation. */
+ /* Single register, not prefixed. */
+ if (REG_P (addr) || SUBREG_P (addr))
+ return false;
+
+ /* Register + offset. */
+ else if (GET_CODE (addr) == PLUS
+ && (REG_P (XEXP (addr, 0)) || SUBREG_P (XEXP (addr, 0)))
+ && CONST_INT_P (XEXP (addr, 1)))
+ {
+ HOST_WIDE_INT offset = INTVAL (XEXP (addr, 1));
+
+ /* Prefixed instructions can only access 34-bits. Fail if the value
+ is larger than that. */
+ if (!SIGNED_34BIT_OFFSET_P (offset))
+ return false;
+
+ /* For small offsets see whether it might be a DS or DQ instruction where
+ the bottom bits non-zero. This would require using a prefixed
+ address. If the offset is larger than 16 bits, then the instruction
+ must be prefixed. */
+ if (SIGNED_16BIT_OFFSET_P (offset))
+ {
+ /* Use default if we don't know the precise instruction format. */
+ if (iform == INSN_FORM_UNKNOWN)
+ iform = reg_addr[mode].default_insn_form;
+
+ if (iform == INSN_FORM_DS)
+ return (offset & 3) != 0;
+
+ else if (iform == INSN_FORM_DQ)
+ return (offset & 15) != 0;
+
+ else if (iform != INSN_FORM_PREFIXED)
+ return false;
+ }
+
+ return true;
+ }
+
+ else if (!TARGET_PCREL)
+ return false;
+
if (GET_CODE (addr) == PLUS)
{
- rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
- if (!base_reg_operand (op0, Pmode) || !CONST_INT_P (op1))
+ if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
return false;
- HOST_WIDE_INT value = INTVAL (op1);
- if (!SIGNED_34BIT_OFFSET_P (value))
+ addr = XEXP (addr, 0);
+ }
+
+ /* Local pc-relative symbols/labels. */
+ return (LABEL_REF_P (addr)
+ || (SYMBOL_REF_P (addr) && SYMBOL_REF_LOCAL_P (addr)));
+}
+
+/* Return true if the address ADDR is a prefixed address that is a pc-relative
+ reference either to a local symbol or to an external symbol. We break apart
+ the address and return the parts. LOCAL_SYMBOL_P and EXTERNAL_SYMBOL_P says
+ whether local and external pc-relative symbols are allowed. P_INFO points
+ to a structure that returns the broken out component parts if desired. */
+
+bool
+pcrel_addr_p (rtx addr,
+ bool local_symbol_p,
+ bool external_symbol_p,
+ pcrel_info_type *p_info)
+{
+ rtx base_addr = NULL_RTX;
+ HOST_WIDE_INT offset = 0;
+ bool was_external_p = false;
+
+ if (p_info)
+ {
+ p_info->base_addr = NULL_RTX;
+ p_info->offset = 0;
+ p_info->external_p = false;
+ }
+
+ if (!TARGET_PCREL)
+ return false;
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+
+ /* Pc-relative symbols/labels without offsets. */
+ if (SYMBOL_REF_P (addr))
+ {
+ base_addr = addr;
+ was_external_p = !SYMBOL_REF_LOCAL_P (addr);
+ }
+
+ else if (LABEL_REF_P (addr))
+ base_addr = addr;
+
+ /* Pc-relative symbols with offsets. */
+ else if (GET_CODE (addr) == PLUS
+ && SYMBOL_REF_P (XEXP (addr, 0))
+ && CONST_INT_P (XEXP (addr, 1)))
+ {
+ base_addr = XEXP (addr, 0);
+ offset = INTVAL (XEXP (addr, 1));
+ was_external_p = !SYMBOL_REF_LOCAL_P (base_addr);
+
+ if (!SIGNED_34BIT_OFFSET_P (offset))
return false;
+ }
- /* Offset larger than 16-bits? */
- if (!SIGNED_16BIT_OFFSET_P (value))
- return true;
+ else
+ return false;
- /* DQ instruction (bottom 4 bits must be 0) for vectors. */
- HOST_WIDE_INT mask;
- if (GET_MODE_SIZE (mode) >= 16)
- mask = 15;
+ if (was_external_p && !external_symbol_p)
+ return false;
- /* DS instruction (bottom 2 bits must be 0). For 32-bit integers, we
- need to use DS instructions if we are sign-extending the value with
- LWA. For 32-bit floating point, we need DS instructions to load and
- store values to the traditional Altivec registers. */
- else if (GET_MODE_SIZE (mode) >= 4)
- mask = 3;
+ if (!was_external_p && !local_symbol_p)
+ return false;
- /* QImode/HImode has no restrictions. */
+ if (p_info)
+ {
+ p_info->base_addr = base_addr;
+ p_info->offset = offset;
+ p_info->external_p = was_external_p;
+ }
+
+ return true;
+}
+
+/* Given a register and a mode, return the instruction format for that
+ register. If the register is a pseudo register, use the default format.
+ Otherwise if it is hard register, look to see exactly what type of
+ addressing is used. */
+
+enum insn_form
+reg_to_insn_form (rtx reg, machine_mode mode)
+{
+ enum insn_form iform;
+
+ /* Handle UNSPECs, such as the special UNSPEC_SF_FROM_SI and
+ UNSPEC_SI_FROM_SF UNSPECs, which are used to hide SF/SI interactions.
+ Look at the first argument, and if it is a register, use that. */
+ if (GET_CODE (reg) == UNSPEC || GET_CODE (reg) == UNSPEC_VOLATILE)
+ {
+ rtx op0 = XVECEXP (reg, 0, 0);
+ if (REG_P (op0) || SUBREG_P (op0))
+ reg = op0;
+ }
+
+ /* If it isn't a register, use the defaults. */
+ if (!REG_P (reg) && !SUBREG_P (reg))
+ iform = reg_addr[mode].default_insn_form;
+
+ else
+ {
+ unsigned int r = reg_or_subregno (reg);
+
+ /* If we have a pseudo, use the default instruction format. */
+ if (r >= FIRST_PSEUDO_REGISTER)
+ iform = reg_addr[mode].default_insn_form;
+
+ /* If we have a hard register, use the address format of that hard
+ register. */
else
- return true;
+ {
+ if (IN_RANGE (r, FIRST_GPR_REGNO, LAST_GPR_REGNO))
+ iform = reg_addr[mode].insn_form[RELOAD_REG_GPR];
- /* Return true if we must use a prefixed instruction. */
- return (value & mask) != 0;
+ else if (IN_RANGE (r, FIRST_FPR_REGNO, LAST_FPR_REGNO))
+ iform = reg_addr[mode].insn_form[RELOAD_REG_FPR];
+
+ else if (IN_RANGE (r, FIRST_ALTIVEC_REGNO, LAST_ALTIVEC_REGNO))
+ iform = reg_addr[mode].insn_form[RELOAD_REG_VMX];
+
+ else
+ gcc_unreachable ();
+ }
}
- return false;
+ return iform;
}
#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
===================================================================
@@ -252,6 +252,23 @@
;; Is copying of this instruction disallowed?
(define_attr "cannot_copy" "no,yes" (const_string "no"))
+;; Enumeration of the PowerPC instruction formats. We only list the
+;; instruction formats that are used by the code, and not every possible
+;; instruction format that the machine supports.
+
+;; The main use for this enumeration is to determine if a particular
+;; offsettable instruction has a valid offset field for a traditional
+;; instruction, or whether a prefixed instruction might be needed to hold the
+;; offset. For DS/DQ format instructions, if we have an offset that has the
+;; bottom bits non-zero, we can use a prefixed instruction instead of pushing
+;; the offset to an index register.
+(define_enum "insn_form" [unknown ; Unknown format
+ d ; Offset addressing uses 16 bits
+ ds ; Offset addressing uses 14 bits
+ dq ; Offset addressing uses 12 bits
+ x ; Indexed addressing
+ prefixed]) ; Prefixed instruction
+
;; Length of the instruction (in bytes).
(define_attr "length" "" (const_int 4))