This patch adds the "prefixed" insn attribute that says whether or not the insn generates a prefixed instruction or not.
The attributes "prefixed_length" and "non_prefixed_length" give then length in bytes (12 and 4 by default) of the insn if it is prefixed or not. The "length" attribute is set based on the "prefixed" attribute. I use the target hooks ASM_OUTPUT_OPCODE and FINAL_PRESCAN_INSN to decide whether to emit a leading "p" before the insn. There are functions (prefixed_load_p, prefixed_store_p, and prefixed_paddi_p) that given an insn type, say whether that particular insn type is prefixed or not. In addition, this patch adds the support in rs6000_emit_move to load up pc-relative addresses, both local addresses defined in the same compilation unit, and external addresses that might be need to be loaded from a .GOT address table. I have done a bootstrap build with all of the patches applied, and there were no regressions in the test suite. After posting these patches, I will start a job to build each set of patches in turn just to make sure there are no extra warnings. Can I commit this patch to the trunk? 2019-09-18 Michael Meissner <meiss...@linux.ibm.com> * config/rs6000/rs6000-protos.h (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 (rs6000_emit_move): Support loading pc-relative addresses. (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. Index: gcc/config/rs6000/rs6000-protos.h =================================================================== --- gcc/config/rs6000/rs6000-protos.h (revision 275908) +++ gcc/config/rs6000/rs6000-protos.h (working copy) @@ -189,6 +189,30 @@ enum non_prefixed { extern enum insn_form address_to_insn_form (rtx, machine_mode, enum non_prefixed); +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 is a prefixed instruction that can be directly + used in a memory instruction (i.e. using numeric offset or a pc-relative + reference to a local symbol). + + 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 non_prefixed_insn) +{ + enum insn_form iform = address_to_insn_form (addr, mode, + non_prefixed_insn); + return (iform == INSN_FORM_PREFIXED_NUMERIC + || iform == INSN_FORM_PCREL_LOCAL); +} #endif /* RTX_CODE */ #ifdef TREE_CODE @@ -268,8 +292,6 @@ extern void rs6000_d_target_versions (vo 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]; Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (revision 275908) +++ gcc/config/rs6000/rs6000.c (working copy) @@ -9639,6 +9639,22 @@ rs6000_emit_move (rtx dest, rtx source, return; } + /* Use the default pattern for loading up pc-relative addresses. */ + if (TARGET_PCREL && mode == Pmode + && (SYMBOL_REF_P (operands[1]) || LABEL_REF_P (operands[1]) + || GET_CODE (operands[1]) == CONST)) + { + enum insn_form iform = address_to_insn_form (operands[1], mode, + NON_PREFIXED_DEFAULT); + + if (iform == INSN_FORM_PCREL_LOCAL + || iform == INSN_FORM_PCREL_EXTERNAL) + { + 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)) @@ -24716,6 +24732,203 @@ address_to_insn_form (rtx addr, 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 +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 (r >= FIRST_PSEUDO_REGISTER) + return NON_PREFIXED_DEFAULT; + + unsigned size = GET_MODE_SIZE (mode); + + /* FPR registers use D-mode for scalars, and DQ-mode for vectors. */ + 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 && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) + return NON_PREFIXED_DQ; + + else + return NON_PREFIXED_DEFAULT; + } + + /* Altivec registers use DS-mode for scalars, and DQ-mode for vectors. */ + 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 && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) + 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; +} + + +/* 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; + + /* LWA uses the DS format instead of the D format that LWZ uses. */ + enum non_prefixed non_prefixed_insn; + 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_insn = NON_PREFIXED_DS; + + else + non_prefixed_insn = reg_to_non_prefixed (reg, mem_mode); + + return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed_insn); +} + +/* 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; + + machine_mode mem_mode = GET_MODE (mem); + enum non_prefixed non_prefixed_insn = reg_to_non_prefixed (reg, mem_mode); + return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed_insn); +} + +/* 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) + fputc ('p', stream); + + return; +} + #ifdef HAVE_GAS_HIDDEN # define USE_HIDDEN_LINKONCE 1 Index: gcc/config/rs6000/rs6000.h =================================================================== --- gcc/config/rs6000/rs6000.h (revision 275894) +++ gcc/config/rs6000/rs6000.h (working copy) @@ -2547,3 +2547,24 @@ typedef struct GTY(()) machine_function 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) Index: gcc/config/rs6000/rs6000.md =================================================================== --- gcc/config/rs6000/rs6000.md (revision 275894) +++ gcc/config/rs6000/rs6000.md (working copy) @@ -256,8 +256,52 @@ (define_attr "var_shift" "no,yes" ;; 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 (and (eq_attr "indexed" "no") + (eq_attr "update" "no") + (match_test "prefixed_load_p (insn)")) + (const_string "yes") + (const_string "no")) + + (eq_attr "type" "store,fpstore,vecstore") + (if_then_else (and (eq_attr "indexed" "no") + (eq_attr "update" "no") + (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. @@ -9875,6 +9919,28 @@ (define_expand "restore_stack_nonlocal" operands[6] = gen_rtx_PARALLEL (VOIDmode, p); }) +;; 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... -- Michael Meissner, IBM IBM, M/S 2506R, 550 King Street, Littleton, MA 01460-6245, USA email: meiss...@linux.ibm.com, phone: +1 (978) 899-4797