This patch reworks the prefixed and pc-relative memory matching functions. As I said in the intro message, I do not re-use the address mask bits in reg_addr, but instead, I have a separate function that takes an address and decodes it into the various different flavors (single register address, D-form 16-bit address, X-form indexed address, numeric 34-bit offset, local pc-relative address, etc.). The caller then decides whether the address matches what they are looking for.
I have two enumerations that I added to this series: 1) insn_form: This is the address format (D, DS, DQ, X, etc.); 2) non_prefixed: This is a limited enum that just describes the format of the non-prefixed instruction to decide if an address needs to be prefixed or not. Originally, I was trying to re-use the same insn_form enumeration for both the output and the input to say what the traditional instruction uses, but I ultimately separated them to make it clearer. This is an infrastructure patch. It needs the second patch to enable basic pc-relative support. 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/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. * config/rs6000/rs6000.c (print_operand_address): Check for either pc-relative local symbols or pc-relative external symbols. (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. Index: gcc/config/rs6000/predicates.md =================================================================== --- gcc/config/rs6000/predicates.md (revision 275903) +++ gcc/config/rs6000/predicates.md (working copy) @@ -1625,82 +1625,7 @@ (define_predicate "small_toc_ref" 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); -}) - + ;; Match the first insn (addis) in fusing the combination of addis and loads to ;; GPR registers on power8. (define_predicate "fusion_gpr_addis" @@ -1857,3 +1782,31 @@ (define_predicate "fusion_addis_mem_comb return 0; }) + + +;; Return true if the operand is a pc-relative address to 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 an 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" + (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_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL); +}) Index: gcc/config/rs6000/rs6000-protos.h =================================================================== --- gcc/config/rs6000/rs6000-protos.h (revision 275903) +++ gcc/config/rs6000/rs6000-protos.h (working copy) @@ -154,7 +154,41 @@ extern align_flags rs6000_loop_align (rt 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 the + 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, /* Base register + 16-bit numeric offset. */ + INSN_FORM_DS, /* Base register + 14-bit offset + 00. */ + INSN_FORM_DQ, /* Base register + 12-bit offset + 0000. */ + INSN_FORM_X, /* Base register + index register. */ + INSN_FORM_UPDATE, /* Address udpates base register. */ + INSN_FORM_LO_SUM, /* Special offset instruction. */ + INSN_FORM_PREFIXED_NUMERIC, /* Base register + 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 { + 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); #endif /* RTX_CODE */ #ifdef TREE_CODE Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (revision 275903) +++ gcc/config/rs6000/rs6000.c (working copy) @@ -13079,7 +13079,7 @@ print_operand_address (FILE *file, rtx x fprintf (file, "0(%s)", reg_names[ REGNO (x) ]); /* Is it a pc-relative address? */ - else if (pcrel_address (x, Pmode)) + else if (TARGET_PCREL && pcrel_local_or_external_address (x, VOIDmode)) { HOST_WIDE_INT offset; @@ -13099,6 +13099,9 @@ print_operand_address (FILE *file, rtx x if (offset) fprintf (file, "%+" PRId64, offset); + if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x)) + fputs ("@got", file); + fputs ("@pcrel", file); } else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST @@ -13584,71 +13587,6 @@ rs6000_pltseq_template (rtx *operands, i 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; -} #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO /* Emit an assembler directive to set symbol visibility for DECL to @@ -24627,6 +24565,158 @@ rs6000_pcrel_p (struct function *fn) return rs6000_fndecl_pcrel_p (fn->decl); } + +/* Given an address (ADDR), a mode (MODE), and what the format of the + non-prefixed address (NON_PREFIXED_INSN) is, return the instruction format + for the address. */ + +enum insn_form +address_to_insn_form (rtx addr, + machine_mode mode, + enum non_prefixed non_prefixed_insn) +{ + rtx op0, op1; + + /* Single register is easy. */ + if (REG_P (addr) || SUBREG_P (addr)) + return INSN_FORM_BASE_REG; + + /* If we don't support offset addressing, make sure only indexed addressing + is allowed. We special case SDmode so that the register allocator does + try to move SDmode through GPR registers, but instead uses the 32-bit + integer read/write instructions for the floating point registers. */ + if (non_prefixed_insn == NON_PREFIXED_X || mode == SDmode) + { + if (GET_CODE (addr) != PLUS) + return INSN_FORM_BAD; + + op0 = XEXP (addr, 0); + 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)) + return (SYMBOL_REF_LOCAL_P (addr) + ? INSN_FORM_PCREL_LOCAL + : INSN_FORM_PCREL_EXTERNAL); + + if (LABEL_REF_P (addr)) + return INSN_FORM_PCREL_LOCAL; + } + + /* Check whether this is an offsettable address. Deal with LO_SUM addresses + used with TOC and 32-bit addressing and with indexed addresses. */ + if (GET_CODE (addr) == CONST) + addr = XEXP (addr, 0); + + if (GET_CODE (addr) != PLUS) + return GET_CODE (addr) == LO_SUM ? INSN_FORM_LO_SUM : INSN_FORM_BAD; + + op0 = XEXP (addr, 0); + op1 = XEXP (addr, 1); + + if (REG_P (op1) || SUBREG_P (op1)) + return INSN_FORM_X; + + 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)) + return (SYMBOL_REF_LOCAL_P (op0) + ? INSN_FORM_PCREL_LOCAL + : INSN_FORM_PCREL_EXTERNAL); + + if (LABEL_REF_P (op0)) + return INSN_FORM_PCREL_LOCAL; + } + + /* If it isn't pc-relative, check for 16-bit D/DS/DQ-form. */ + if (!REG_P (op0) && !SUBREG_P (op0)) + return INSN_FORM_BAD; + + /* Large offsets must be prefixed. */ + if (!SIGNED_16BIT_OFFSET_P (offset)) + return (TARGET_PREFIXED_ADDR + ? INSN_FORM_PREFIXED_NUMERIC + : INSN_FORM_BAD); + + /* 16-bit offset, see what default instruction format to use. */ + if (non_prefixed_insn == NON_PREFIXED_DEFAULT) + { + unsigned size = GET_MODE_SIZE (mode); + + /* On 64-bit systems, assume 64-bit integers need to use DS form + addresses. VSX vectors need to use DQ form addresses. */ + if (TARGET_POWERPC64 && size >= 8 && GET_MODE_CLASS (mode) == MODE_INT) + non_prefixed_insn = NON_PREFIXED_DS; + + else if (TARGET_VSX && size >= 16 + && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) + non_prefixed_insn = NON_PREFIXED_DQ; + + else + non_prefixed_insn = NON_PREFIXED_D; + } + + /* Classify the D/DS/DQ-form addresses. */ + switch (non_prefixed_insn) + { + /* 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; +} + + #ifdef HAVE_GAS_HIDDEN # define USE_HIDDEN_LINKONCE 1 #else -- 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