From 26ca7d1b24478324969eae0d171ad698926c8541 Mon Sep 17 00:00:00 2001 From: Michael Meissner Date: Mon, 30 Sep 2019 13:49:13 +0000 Subject: [PATCH] Add initial support for prefixed/PC-relative addressing. 2019-09-30 Michael Meissner * 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_64bit_dm): Split the alternatives for loading 0.0 to a GPR and loading a 128-bit floating point type to a GPR. From-SVN: r276300 --- gcc/ChangeLog | 53 ++++ gcc/config/rs6000/predicates.md | 102 ++----- gcc/config/rs6000/rs6000-protos.h | 63 +++- gcc/config/rs6000/rs6000.c | 461 +++++++++++++++++++++++++----- gcc/config/rs6000/rs6000.h | 21 ++ gcc/config/rs6000/rs6000.md | 84 +++++- 6 files changed, 631 insertions(+), 153 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7583de8ac5b..74f08cc6dd3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,56 @@ +2019-09-30 Michael Meissner + + * 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_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 * gimple.c (gimple_get_lhs): For PHIs return the result. diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md index 9368bdd5a8f..345d9c337af 100644 --- a/gcc/config/rs6000/predicates.md +++ b/gcc/config/rs6000/predicates.md @@ -1625,82 +1625,7 @@ 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,28 @@ return 0; }) + + +;; 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"))) diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index 06e40d94b17..c51b768d964 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -154,7 +154,66 @@ 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); + +/* 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 @@ -234,8 +293,6 @@ extern void rs6000_d_target_versions (void); 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]; diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 1eb13176dd0..d6e1fea8426 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -9640,6 +9640,14 @@ rs6000_emit_move (rtx dest, rtx source, machine_mode mode) 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)) @@ -13082,8 +13090,8 @@ print_operand_address (FILE *file, rtx x) 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; @@ -13103,7 +13111,10 @@ print_operand_address (FILE *file, rtx x) 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) @@ -13588,71 +13599,6 @@ rs6000_pltseq_template (rtx *operands, int which) 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 @@ -24617,6 +24563,385 @@ 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_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; +} + + +/* 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; +} + + #ifdef HAVE_GAS_HIDDEN # define USE_HIDDEN_LINKONCE 1 #else diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index 8f5c70e5d8e..0156448f572 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -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) diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 46167e5f20f..4f27f13772b 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -256,8 +256,49 @@ ;; 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. @@ -7713,9 +7754,18 @@ ;; 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_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,,r,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, , r, + , Y, r, d, r"))] + "TARGET_HARD_FLOAT && TARGET_POWERPC64 && FLOAT128_2REG_P (mode) && (mode != TDmode || WORDS_BIG_ENDIAN) && (gpc_reg_operand (operands[0], mode) @@ -7724,8 +7774,8 @@ "&& 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") @@ -9874,6 +9924,28 @@ 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... -- 2.30.2