+2019-09-30 Michael Meissner <meissner@linux.ibm.com>
+
+ * config/rs6000/predicates.md (pcrel_address): Delete predicate.
+ (pcrel_local_address): Replace pcrel_address predicate, use the
+ new function address_to_insn_form.
+ (pcrel_external_address): Replace with new implementation using
+ address_to_insn_form..
+ (prefixed_mem_operand): Delete predicate which is now unused.
+ (pcrel_external_mem_operand): Delete predicate which is now
+ unused.
+ * config/rs6000/rs6000-protos.h (enum insn_form): New
+ enumeration.
+ (enum non_prefixed): New enumeration.
+ (address_to_insn_form): New declaration.
+ (prefixed_load_p): New declaration.
+ (prefixed_store_p): New declaration.
+ (prefixed_paddi_p): New declaration.
+ (rs6000_asm_output_opcode): New declaration.
+ (rs6000_final_prescan_insn): Move declaration and update calling
+ signature.
+ (address_is_prefixed): New helper inline function.
+ * config/rs6000/rs6000.c(print_operand_address): Check for either
+ PC-relative local symbols or PC-relative external symbols.
+ (rs6000_emit_move): Support loading PC-relative addresses.
+ (mode_supports_prefixed_address_p): Delete, no longer used.
+ (rs6000_prefixed_address_mode_p): Delete, no longer used.
+ (address_to_insn_form): New function to decode an address format.
+ (reg_to_non_prefixed): New function to identify what the
+ non-prefixed memory instruction format is for a register.
+ (prefixed_load_p): New function to identify prefixed loads.
+ (prefixed_store_p): New function to identify prefixed stores.
+ (prefixed_paddi_p): New function to identify prefixed load
+ immediates.
+ (next_insn_prefixed_p): New static state variable.
+ (rs6000_final_prescan_insn): New function to determine if an insn
+ uses a prefixed instruction.
+ (rs6000_asm_output_opcode): New function to emit 'p' in front of a
+ prefixed instruction.
+ * config/rs6000/rs6000.h (FINAL_PRESCAN_INSN): New target hook.
+ (ASM_OUTPUT_OPCODE): New target hook.
+ * config/rs6000/rs6000.md (prefixed): New insn attribute for
+ prefixed instructions.
+ (prefixed_length): New insn attribute for the size of prefixed
+ instructions.
+ (non_prefixed_length): New insn attribute for the size of
+ non-prefixed instructions.
+ (pcrel_local_addr): New insn to load up a local PC-relative
+ address.
+ (pcrel_extern_addr): New insn to load up an external PC-relative
+ address.
+ (mov<mode>_64bit_dm): Split the alternatives for loading 0.0 to a
+ GPR and loading a 128-bit floating point type to a GPR.
+
2019-09-30 Richard Biener <rguenther@suse.de>
* gimple.c (gimple_get_lhs): For PHIs return the result.
return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
})
-;; Return true if the operand is a pc-relative address.
-(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 true if the operand is an external symbol whose address can be loaded
-;; into a register using:
-;; PLD reg,label@pcrel@got
-;;
-;; The linker will either optimize this to either a PADDI if the label is
-;; defined locally in another module or a PLD of the address if the label is
-;; defined in another module.
-
-(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 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 1 if op is a memory operand to an external variable when we
-;; support pc-relative addressing and the PCREL_OPT relocation to
-;; optimize references to it.
-(define_predicate "pcrel_external_mem_operand"
- (match_code "mem")
-{
- return pcrel_external_address (XEXP (op, 0), Pmode);
-})
-
+\f
;; Match the first insn (addis) in fusing the combination of addis and loads to
;; GPR registers on power8.
(define_predicate "fusion_gpr_addis"
return 0;
})
+
+\f
+;; Return true if the operand is a PC-relative address of a local symbol or a
+;; label that can be used directly in a memory operation.
+(define_predicate "pcrel_local_address"
+ (match_code "label_ref,symbol_ref,const")
+{
+ enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
+ return iform == INSN_FORM_PCREL_LOCAL;
+})
+
+;; Return true if the operand is a PC-relative external symbol whose address
+;; can be loaded into a register.
+(define_predicate "pcrel_external_address"
+ (match_code "symbol_ref,const")
+{
+ enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
+ return iform == INSN_FORM_PCREL_EXTERNAL;
+})
+
+;; Return true if the address is PC-relative and the symbol is either local or
+;; external.
+(define_predicate "pcrel_local_or_external_address"
+ (ior (match_operand 0 "pcrel_local_address")
+ (match_operand 0 "pcrel_external_address")))
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);
+
+/* Different PowerPC instruction formats that are used by GCC. There are
+ various other instruction formats used by the PowerPC hardware, but these
+ formats are not currently used by GCC. */
+
+enum insn_form {
+ INSN_FORM_BAD, /* Bad instruction format. */
+ INSN_FORM_BASE_REG, /* Base register only. */
+ INSN_FORM_D, /* Reg + 16-bit numeric offset. */
+ INSN_FORM_DS, /* Reg + offset, bottom 2 bits must be 0. */
+ INSN_FORM_DQ, /* Reg + offset, bottom 4 bits must be 0. */
+ INSN_FORM_X, /* Base register + index register. */
+ INSN_FORM_UPDATE, /* Address updates base register. */
+ INSN_FORM_LO_SUM, /* Reg + offset using symbol. */
+ INSN_FORM_PREFIXED_NUMERIC, /* Reg + 34 bit numeric offset. */
+ INSN_FORM_PCREL_LOCAL, /* PC-relative local symbol. */
+ INSN_FORM_PCREL_EXTERNAL /* PC-relative external symbol. */
+};
+
+/* Instruction format for the non-prefixed version of a load or store. This is
+ used to determine if a 16-bit offset is valid to be used with a non-prefixed
+ (traditional) instruction or if the bottom bits of the offset cannot be used
+ with a DS or DQ instruction format, and GCC has to use a prefixed
+ instruction for the load or store. */
+
+enum non_prefixed_form {
+ NON_PREFIXED_DEFAULT, /* Use the default. */
+ NON_PREFIXED_D, /* All 16-bits are valid. */
+ NON_PREFIXED_DS, /* Bottom 2 bits must be 0. */
+ NON_PREFIXED_DQ, /* Bottom 4 bits must be 0. */
+ NON_PREFIXED_X /* No offset memory form exists. */
+};
+
+extern enum insn_form address_to_insn_form (rtx, machine_mode,
+ enum non_prefixed_form);
+extern bool prefixed_load_p (rtx_insn *);
+extern bool prefixed_store_p (rtx_insn *);
+extern bool prefixed_paddi_p (rtx_insn *);
+extern void rs6000_asm_output_opcode (FILE *);
+extern void rs6000_final_prescan_insn (rtx_insn *, rtx [], int);
+
+/* Return true if the address can be used for a prefixed load, store, or add
+ immediate instructions that cannot be used with a non-prefixed instruction.
+ For example, using a numeric offset that is not valid for the non-prefixed
+ instruction or a PC-relative reference to a local symbol would return true,
+ but an address with an offset of 64 would not return true.
+
+ References to external PC-relative symbols aren't allowed, because GCC has
+ to load the address into a register and then issue a separate load or
+ store. */
+
+static inline bool
+address_is_prefixed (rtx addr,
+ machine_mode mode,
+ enum non_prefixed_form non_prefixed)
+{
+ enum insn_form iform = address_to_insn_form (addr, mode, non_prefixed);
+ return (iform == INSN_FORM_PREFIXED_NUMERIC
+ || iform == INSN_FORM_PCREL_LOCAL);
+}
#endif /* RTX_CODE */
#ifdef TREE_CODE
const char * rs6000_xcoff_strip_dollar (const char *);
#endif
-void rs6000_final_prescan_insn (rtx_insn *, rtx *operand, int num_operands);
-
extern unsigned char rs6000_class_max_nregs[][LIM_REG_CLASSES];
extern unsigned char rs6000_hard_regno_nregs[][FIRST_PSEUDO_REGISTER];
return;
}
+ /* Use the default pattern for loading up PC-relative addresses. */
+ if (TARGET_PCREL && mode == Pmode
+ && pcrel_local_or_external_address (operands[1], Pmode))
+ {
+ emit_insn (gen_rtx_SET (operands[0], operands[1]));
+ return;
+ }
+
if (DEFAULT_ABI == ABI_V4
&& mode == Pmode && mode == SImode
&& flag_pic == 1 && got_operand (operands[1], mode))
if (REG_P (x))
fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
- /* Is it a pc-relative address? */
- else if (pcrel_address (x, Pmode))
+ /* Is it a PC-relative address? */
+ else if (TARGET_PCREL && pcrel_local_or_external_address (x, VOIDmode))
{
HOST_WIDE_INT offset;
if (offset)
fprintf (file, "%+" PRId64, offset);
- fputs ("@pcrel", file);
+ if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x))
+ fprintf (file, "@got");
+
+ fprintf (file, "@pcrel");
}
else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
|| GET_CODE (x) == LABEL_REF)
return str;
}
#endif
-
-/* 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. */
-
-static bool
-mode_supports_prefixed_address_p (machine_mode mode)
-{
- return mode == VOIDmode;
-}
-
-/* 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)
-{
- if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
- return false;
-
- /* Check for PC-relative addresses. */
- if (pcrel_address (addr, Pmode))
- return true;
-
- /* 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. */
- 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))
- return false;
-
- HOST_WIDE_INT value = INTVAL (op1);
- if (!SIGNED_34BIT_OFFSET_P (value))
- return false;
-
- /* Offset larger than 16-bits? */
- if (!SIGNED_16BIT_OFFSET_P (value))
- return true;
-
- /* DQ instruction (bottom 4 bits must be 0) for vectors. */
- HOST_WIDE_INT mask;
- if (GET_MODE_SIZE (mode) >= 16)
- mask = 15;
-
- /* 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;
-
- /* QImode/HImode has no restrictions. */
- else
- return true;
-
- /* Return true if we must use a prefixed instruction. */
- return (value & mask) != 0;
- }
-
- return false;
-}
\f
#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
/* Emit an assembler directive to set symbol visibility for DECL to
return rs6000_fndecl_pcrel_p (fn->decl);
}
+\f
+/* Given an address (ADDR), a mode (MODE), and what the format of the
+ non-prefixed address (NON_PREFIXED_FORMAT) is, return the instruction format
+ for the address. */
+
+enum insn_form
+address_to_insn_form (rtx addr,
+ machine_mode mode,
+ enum non_prefixed_form non_prefixed_format)
+{
+ /* Single register is easy. */
+ if (REG_P (addr) || SUBREG_P (addr))
+ return INSN_FORM_BASE_REG;
+
+ /* If the non prefixed instruction format doesn't support offset addressing,
+ make sure only indexed addressing is allowed.
+
+ We special case SDmode so that the register allocator does not try to move
+ SDmode through GPR registers, but instead uses the 32-bit integer load and
+ store instructions for the floating point registers. */
+ if (non_prefixed_format == NON_PREFIXED_X || (mode == SDmode && TARGET_DFP))
+ {
+ if (GET_CODE (addr) != PLUS)
+ return INSN_FORM_BAD;
+
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+ if (!REG_P (op0) && !SUBREG_P (op0))
+ return INSN_FORM_BAD;
+
+ if (!REG_P (op1) && !SUBREG_P (op1))
+ return INSN_FORM_BAD;
+
+ return INSN_FORM_X;
+ }
+
+ /* Deal with update forms. */
+ if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC)
+ return INSN_FORM_UPDATE;
+
+ /* Handle PC-relative symbols and labels. Check for both local and external
+ symbols. Assume labels are always local. */
+ if (TARGET_PCREL)
+ {
+ if (SYMBOL_REF_P (addr) && !SYMBOL_REF_LOCAL_P (addr))
+ return INSN_FORM_PCREL_EXTERNAL;
+
+ if (SYMBOL_REF_P (addr) || LABEL_REF_P (addr))
+ return INSN_FORM_PCREL_LOCAL;
+ }
+
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+
+ /* Recognize LO_SUM addresses used with TOC and 32-bit addressing. */
+ if (GET_CODE (addr) == LO_SUM)
+ return INSN_FORM_LO_SUM;
+
+ /* Everything below must be an offset address of some form. */
+ if (GET_CODE (addr) != PLUS)
+ return INSN_FORM_BAD;
+
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ /* Check for indexed addresses. */
+ if (REG_P (op1) || SUBREG_P (op1))
+ {
+ if (REG_P (op0) || SUBREG_P (op0))
+ return INSN_FORM_X;
+
+ return INSN_FORM_BAD;
+ }
+
+ if (!CONST_INT_P (op1))
+ return INSN_FORM_BAD;
+
+ HOST_WIDE_INT offset = INTVAL (op1);
+ if (!SIGNED_34BIT_OFFSET_P (offset))
+ return INSN_FORM_BAD;
+
+ /* Check for local and external PC-relative addresses. Labels are always
+ local. */
+ if (TARGET_PCREL)
+ {
+ if (SYMBOL_REF_P (op0) && !SYMBOL_REF_LOCAL_P (op0))
+ return INSN_FORM_PCREL_EXTERNAL;
+
+ if (SYMBOL_REF_P (op0) || LABEL_REF_P (op0))
+ return INSN_FORM_PCREL_LOCAL;
+ }
+
+ /* If it isn't PC-relative, the address must use a base register. */
+ if (!REG_P (op0) && !SUBREG_P (op0))
+ return INSN_FORM_BAD;
+
+ /* Large offsets must be prefixed. */
+ if (!SIGNED_16BIT_OFFSET_P (offset))
+ {
+ if (TARGET_PREFIXED_ADDR)
+ return INSN_FORM_PREFIXED_NUMERIC;
+
+ return INSN_FORM_BAD;
+ }
+
+ /* We have a 16-bit offset, see what default instruction format to use. */
+ if (non_prefixed_format == NON_PREFIXED_DEFAULT)
+ {
+ unsigned size = GET_MODE_SIZE (mode);
+
+ /* On 64-bit systems, assume 64-bit integers need to use DS form
+ addresses (for LD/STD). VSX vectors need to use DQ form addresses
+ (for LXV and STXV). TImode is problematical in that its normal usage
+ is expected to be GPRs where it wants a DS instruction format, but if
+ it goes into the vector registers, it wants a DQ instruction
+ format. */
+ if (TARGET_POWERPC64 && size >= 8 && GET_MODE_CLASS (mode) == MODE_INT)
+ non_prefixed_format = NON_PREFIXED_DS;
+
+ else if (TARGET_VSX && size >= 16
+ && (VECTOR_MODE_P (mode) || FLOAT128_VECTOR_P (mode)))
+ non_prefixed_format = NON_PREFIXED_DQ;
+
+ else
+ non_prefixed_format = NON_PREFIXED_D;
+ }
+
+ /* Classify the D/DS/DQ-form addresses. */
+ switch (non_prefixed_format)
+ {
+ /* Instruction format D, all 16 bits are valid. */
+ case NON_PREFIXED_D:
+ return INSN_FORM_D;
+
+ /* Instruction format DS, bottom 2 bits must be 0. */
+ case NON_PREFIXED_DS:
+ if ((offset & 3) == 0)
+ return INSN_FORM_DS;
+
+ else if (TARGET_PREFIXED_ADDR)
+ return INSN_FORM_PREFIXED_NUMERIC;
+
+ else
+ return INSN_FORM_BAD;
+
+ /* Instruction format DQ, bottom 4 bits must be 0. */
+ case NON_PREFIXED_DQ:
+ if ((offset & 15) == 0)
+ return INSN_FORM_DQ;
+
+ else if (TARGET_PREFIXED_ADDR)
+ return INSN_FORM_PREFIXED_NUMERIC;
+
+ else
+ return INSN_FORM_BAD;
+
+ default:
+ break;
+ }
+
+ return INSN_FORM_BAD;
+}
+
+/* Helper function to take a REG and a MODE and turn it into the non-prefixed
+ instruction format (D/DS/DQ) used for offset memory. */
+
+static enum non_prefixed_form
+reg_to_non_prefixed (rtx reg, machine_mode mode)
+{
+ /* If it isn't a register, use the defaults. */
+ if (!REG_P (reg) && !SUBREG_P (reg))
+ return NON_PREFIXED_DEFAULT;
+
+ unsigned int r = reg_or_subregno (reg);
+
+ /* If we have a pseudo, use the default instruction format. */
+ if (!HARD_REGISTER_NUM_P (r))
+ return NON_PREFIXED_DEFAULT;
+
+ unsigned size = GET_MODE_SIZE (mode);
+
+ /* FPR registers use D-mode for scalars, and DQ-mode for vectors, IEEE
+ 128-bit floating point, and 128-bit integers. */
+ if (FP_REGNO_P (r))
+ {
+ if (mode == SFmode || size == 8 || FLOAT128_2REG_P (mode))
+ return NON_PREFIXED_D;
+
+ else if (size < 8)
+ return NON_PREFIXED_X;
+
+ else if (TARGET_VSX && size >= 16
+ && (VECTOR_MODE_P (mode)
+ || FLOAT128_VECTOR_P (mode)
+ || mode == TImode || mode == CTImode))
+ return NON_PREFIXED_DQ;
+
+ else
+ return NON_PREFIXED_DEFAULT;
+ }
+
+ /* Altivec registers use DS-mode for scalars, and DQ-mode for vectors, IEEE
+ 128-bit floating point, and 128-bit integers. */
+ else if (ALTIVEC_REGNO_P (r))
+ {
+ if (mode == SFmode || size == 8 || FLOAT128_2REG_P (mode))
+ return NON_PREFIXED_DS;
+
+ else if (size < 8)
+ return NON_PREFIXED_X;
+
+ else if (TARGET_VSX && size >= 16
+ && (VECTOR_MODE_P (mode)
+ || FLOAT128_VECTOR_P (mode)
+ || mode == TImode || mode == CTImode))
+ return NON_PREFIXED_DQ;
+
+ else
+ return NON_PREFIXED_DEFAULT;
+ }
+
+ /* GPR registers use DS-mode for 64-bit items on 64-bit systems, and D-mode
+ otherwise. Assume that any other register, such as LR, CRs, etc. will go
+ through the GPR registers for memory operations. */
+ else if (TARGET_POWERPC64 && size >= 8)
+ return NON_PREFIXED_DS;
+
+ return NON_PREFIXED_D;
+}
+
+\f
+/* Whether a load instruction is a prefixed instruction. This is called from
+ the prefixed attribute processing. */
+
+bool
+prefixed_load_p (rtx_insn *insn)
+{
+ /* Validate the insn to make sure it is a normal load insn. */
+ extract_insn_cached (insn);
+ if (recog_data.n_operands < 2)
+ return false;
+
+ rtx reg = recog_data.operand[0];
+ rtx mem = recog_data.operand[1];
+
+ if (!REG_P (reg) && !SUBREG_P (reg))
+ return false;
+
+ if (!MEM_P (mem))
+ return false;
+
+ /* Prefixed load instructions do not support update or indexed forms. */
+ if (get_attr_indexed (insn) == INDEXED_YES
+ || get_attr_update (insn) == UPDATE_YES)
+ return false;
+
+ /* LWA uses the DS format instead of the D format that LWZ uses. */
+ enum non_prefixed_form non_prefixed;
+ machine_mode reg_mode = GET_MODE (reg);
+ machine_mode mem_mode = GET_MODE (mem);
+
+ if (mem_mode == SImode && reg_mode == DImode
+ && get_attr_sign_extend (insn) == SIGN_EXTEND_YES)
+ non_prefixed = NON_PREFIXED_DS;
+
+ else
+ non_prefixed = reg_to_non_prefixed (reg, mem_mode);
+
+ return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed);
+}
+
+/* Whether a store instruction is a prefixed instruction. This is called from
+ the prefixed attribute processing. */
+
+bool
+prefixed_store_p (rtx_insn *insn)
+{
+ /* Validate the insn to make sure it is a normal store insn. */
+ extract_insn_cached (insn);
+ if (recog_data.n_operands < 2)
+ return false;
+
+ rtx mem = recog_data.operand[0];
+ rtx reg = recog_data.operand[1];
+
+ if (!REG_P (reg) && !SUBREG_P (reg))
+ return false;
+
+ if (!MEM_P (mem))
+ return false;
+
+ /* Prefixed store instructions do not support update or indexed forms. */
+ if (get_attr_indexed (insn) == INDEXED_YES
+ || get_attr_update (insn) == UPDATE_YES)
+ return false;
+
+ machine_mode mem_mode = GET_MODE (mem);
+ enum non_prefixed_form non_prefixed = reg_to_non_prefixed (reg, mem_mode);
+ return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed);
+}
+
+/* Whether a load immediate or add instruction is a prefixed instruction. This
+ is called from the prefixed attribute processing. */
+
+bool
+prefixed_paddi_p (rtx_insn *insn)
+{
+ rtx set = single_set (insn);
+ if (!set)
+ return false;
+
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ if (!REG_P (dest) && !SUBREG_P (dest))
+ return false;
+
+ /* Is this a load immediate that can't be done with a simple ADDI or
+ ADDIS? */
+ if (CONST_INT_P (src))
+ return (satisfies_constraint_eI (src)
+ && !satisfies_constraint_I (src)
+ && !satisfies_constraint_L (src));
+
+ /* Is this a PADDI instruction that can't be done with a simple ADDI or
+ ADDIS? */
+ if (GET_CODE (src) == PLUS)
+ {
+ rtx op1 = XEXP (src, 1);
+
+ return (CONST_INT_P (op1)
+ && satisfies_constraint_eI (op1)
+ && !satisfies_constraint_I (op1)
+ && !satisfies_constraint_L (op1));
+ }
+
+ /* If not, is it a load of a PC-relative address? */
+ if (!TARGET_PCREL || GET_MODE (dest) != Pmode)
+ return false;
+
+ if (!SYMBOL_REF_P (src) && !LABEL_REF_P (src) && GET_CODE (src) != CONST)
+ return false;
+
+ enum insn_form iform = address_to_insn_form (src, Pmode,
+ NON_PREFIXED_DEFAULT);
+
+ return (iform == INSN_FORM_PCREL_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL);
+}
+
+/* Whether the next instruction needs a 'p' prefix issued before the
+ instruction is printed out. */
+static bool next_insn_prefixed_p;
+
+/* Define FINAL_PRESCAN_INSN if some processing needs to be done before
+ outputting the assembler code. On the PowerPC, we remember if the current
+ insn is a prefixed insn where we need to emit a 'p' before the insn.
+
+ In addition, if the insn is part of a PC-relative reference to an external
+ label optimization, this is recorded also. */
+void
+rs6000_final_prescan_insn (rtx_insn *insn, rtx [], int)
+{
+ next_insn_prefixed_p = (get_attr_prefixed (insn) != PREFIXED_NO);
+ return;
+}
+
+/* Define ASM_OUTPUT_OPCODE to do anything special before emitting an opcode.
+ We use it to emit a 'p' for prefixed insns that is set in
+ FINAL_PRESCAN_INSN. */
+void
+rs6000_asm_output_opcode (FILE *stream)
+{
+ if (next_insn_prefixed_p)
+ fprintf (stream, "p");
+
+ return;
+}
+
+\f
#ifdef HAVE_GAS_HIDDEN
# define USE_HIDDEN_LINKONCE 1
#else
IN_RANGE ((VALUE), \
-(HOST_WIDE_INT_1 << 33), \
(HOST_WIDE_INT_1 << 33) - 1 - (EXTRA))
+
+/* Define this if some processing needs to be done before outputting the
+ assembler code. On the PowerPC, we remember if the current insn is a normal
+ prefixed insn where we need to emit a 'p' before the insn. */
+#define FINAL_PRESCAN_INSN(INSN, OPERANDS, NOPERANDS) \
+do \
+ { \
+ if (TARGET_PREFIXED_ADDR) \
+ rs6000_final_prescan_insn (INSN, OPERANDS, NOPERANDS); \
+ } \
+while (0)
+
+/* Do anything special before emitting an opcode. We use it to emit a 'p' for
+ prefixed insns that is set in FINAL_PRESCAN_INSN. */
+#define ASM_OUTPUT_OPCODE(STREAM, OPCODE) \
+ do \
+ { \
+ if (TARGET_PREFIXED_ADDR) \
+ rs6000_asm_output_opcode (STREAM); \
+ } \
+ while (0)
;; Is copying of this instruction disallowed?
(define_attr "cannot_copy" "no,yes" (const_string "no"))
-;; Length of the instruction (in bytes).
-(define_attr "length" "" (const_int 4))
+
+;; Whether an insn is a prefixed insn, and an initial 'p' should be printed
+;; before the instruction. A prefixed instruction has a prefix instruction
+;; word that extends the immediate value of the instructions from 12-16 bits to
+;; 34 bits. The macro ASM_OUTPUT_OPCODE emits a leading 'p' for prefixed
+;; insns. The default "length" attribute will also be adjusted by default to
+;; be 12 bytes.
+(define_attr "prefixed" "no,yes"
+ (cond [(ior (match_test "!TARGET_PREFIXED_ADDR")
+ (match_test "!NONJUMP_INSN_P (insn)"))
+ (const_string "no")
+
+ (eq_attr "type" "load,fpload,vecload")
+ (if_then_else (match_test "prefixed_load_p (insn)")
+ (const_string "yes")
+ (const_string "no"))
+
+ (eq_attr "type" "store,fpstore,vecstore")
+ (if_then_else (match_test "prefixed_store_p (insn)")
+ (const_string "yes")
+ (const_string "no"))
+
+ (eq_attr "type" "integer,add")
+ (if_then_else (match_test "prefixed_paddi_p (insn)")
+ (const_string "yes")
+ (const_string "no"))]
+
+ (const_string "no")))
+
+;; Length in bytes of instructions that use prefixed addressing and length in
+;; bytes of instructions that does not use prefixed addressing. This allows
+;; both lengths to be defined as constants, and the length attribute can pick
+;; the size as appropriate.
+(define_attr "prefixed_length" "" (const_int 12))
+(define_attr "non_prefixed_length" "" (const_int 4))
+
+;; Length of the instruction (in bytes). Prefixed insns are 8 bytes, but the
+;; assembler might issue need to issue a NOP so that the prefixed instruction
+;; does not cross a cache boundary, which makes them possibly 12 bytes.
+(define_attr "length" ""
+ (if_then_else (eq_attr "prefixed" "yes")
+ (attr "prefixed_length")
+ (attr "non_prefixed_length")))
;; Processor type -- this attribute must exactly match the processor_type
;; enumeration in rs6000-opts.h.
;; not swapped like they are for TImode or TFmode. Subregs therefore are
;; problematical. Don't allow direct move for this case.
+;; FPR load FPR store FPR move FPR zero GPR load
+;; GPR zero GPR store GPR move MFVSRD MTVSRD
+
(define_insn_and_split "*mov<mode>_64bit_dm"
- [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand" "=m,d,d,d,Y,r,r,r,d")
- (match_operand:FMOVE128_FPR 1 "input_operand" "d,m,d,<zero_fp>,r,<zero_fp>Y,r,d,r"))]
+ [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand"
+ "=m, d, d, d, Y,
+ r, r, r, r, d")
+
+ (match_operand:FMOVE128_FPR 1 "input_operand"
+ "d, m, d, <zero_fp>, r,
+ <zero_fp>, Y, r, d, r"))]
+
"TARGET_HARD_FLOAT && TARGET_POWERPC64 && FLOAT128_2REG_P (<MODE>mode)
&& (<MODE>mode != TDmode || WORDS_BIG_ENDIAN)
&& (gpc_reg_operand (operands[0], <MODE>mode)
"&& reload_completed"
[(pc)]
{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
- [(set_attr "length" "8,8,8,8,12,12,8,8,8")
- (set_attr "isa" "*,*,*,*,*,*,*,p8v,p8v")])
+ [(set_attr "length" "8")
+ (set_attr "isa" "*,*,*,*,*,*,*,*,p8v,p8v")])
(define_insn_and_split "*movtd_64bit_nodm"
[(set (match_operand:TD 0 "nonimmediate_operand" "=m,d,d,Y,r,r")
operands[6] = gen_rtx_PARALLEL (VOIDmode, p);
})
\f
+;; Load up a PC-relative address. Print_operand_address will append a @pcrel
+;; to the symbol or label.
+(define_insn "*pcrel_local_addr"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+ (match_operand:DI 1 "pcrel_local_address"))]
+ "TARGET_PCREL"
+ "la %0,%a1"
+ [(set_attr "prefixed" "yes")])
+
+;; Load up a PC-relative address to an external symbol. If the symbol and the
+;; program are both defined in the main program, the linker will optimize this
+;; to a PADDI. Otherwise, it will create a GOT address that is relocated by
+;; the dynamic linker and loaded up. Print_operand_address will append a
+;; @got@pcrel to the symbol.
+(define_insn "*pcrel_extern_addr"
+ [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+ (match_operand:DI 1 "pcrel_external_address"))]
+ "TARGET_PCREL"
+ "ld %0,%a1"
+ [(set_attr "prefixed" "yes")
+ (set_attr "type" "load")])
+
;; TOC register handling.
;; Code to initialize the TOC register...