From patchwork Thu Aug 15 21:19:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Meissner X-Patchwork-Id: 1147850 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-507088-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="jyLrzMyy"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 468fVM1njRz9s3Z for ; Fri, 16 Aug 2019 07:19:53 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:references:mime-version:content-type :in-reply-to; q=dns; s=default; b=rZG1atHR5dOCt3exDLi+6UYGWg02nL ZOh2u7EvP4YQkVaqJu8T+uS9ZiTQcf8ospSMGvqXaqCwgAtMYKLh6hRQZATLdUj/ 4D62bL0/FRu2YI698zBOko/yi7sQEyof2HsMS42w9iieN3w+G3ZCXRiaNGs51b3q x9g4OeyYMsCZE= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:references:mime-version:content-type :in-reply-to; s=default; bh=NVM5oY+x/p4EO7olfWF0Q+OD5JM=; b=jyLr zMyyaFuc3iE5rVgBQ0u3EDsy2hErHWa4HlJ7hMrTlWvwLyQolV5ose9AOd23Wv4F a72KNKd2y8Xs/PzKvrv22SEFuEy4ulAn/qv9tRrWTUWraEz1RQqgg2mWzZ0/Wufw O09YVyNRr9pQzr4ffhtvss8NYtZUQCZAln8Mjk0= Received: (qmail 56346 invoked by alias); 15 Aug 2019 21:19:31 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 56265 invoked by uid 89); 15 Aug 2019 21:19:30 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-11.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_STOCKGEN, RCVD_IN_DNSWL_LOW, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.1 spammy= X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0b-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.158.5) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 15 Aug 2019 21:19:25 +0000 Received: from pps.filterd (m0098419.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x7FLD3eX101947; Thu, 15 Aug 2019 17:19:23 -0400 Received: from pps.reinject (localhost [127.0.0.1]) by mx0b-001b2d01.pphosted.com with ESMTP id 2uda2vve7d-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 15 Aug 2019 17:19:22 -0400 Received: from m0098419.ppops.net (m0098419.ppops.net [127.0.0.1]) by pps.reinject (8.16.0.27/8.16.0.27) with SMTP id x7FLG8CD109831; Thu, 15 Aug 2019 17:19:22 -0400 Received: from ppma02dal.us.ibm.com (a.bd.3ea9.ip4.static.sl-reverse.com [169.62.189.10]) by mx0b-001b2d01.pphosted.com with ESMTP id 2uda2vve6w-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 15 Aug 2019 17:19:22 -0400 Received: from pps.filterd (ppma02dal.us.ibm.com [127.0.0.1]) by ppma02dal.us.ibm.com (8.16.0.27/8.16.0.27) with SMTP id x7FL0FBF016047; Thu, 15 Aug 2019 21:19:21 GMT Received: from b03cxnp08027.gho.boulder.ibm.com (b03cxnp08027.gho.boulder.ibm.com [9.17.130.19]) by ppma02dal.us.ibm.com with ESMTP id 2u9nj66yhv-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 15 Aug 2019 21:19:20 +0000 Received: from b03ledav004.gho.boulder.ibm.com (b03ledav004.gho.boulder.ibm.com [9.17.130.235]) by b03cxnp08027.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x7FLJJ1M23921004 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 15 Aug 2019 21:19:19 GMT Received: from b03ledav004.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5B40F78067; Thu, 15 Aug 2019 21:19:19 +0000 (GMT) Received: from b03ledav004.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id B2AE97805C; Thu, 15 Aug 2019 21:19:18 +0000 (GMT) Received: from ibm-toto.the-meissners.org (unknown [9.32.77.177]) by b03ledav004.gho.boulder.ibm.com (Postfix) with ESMTPS; Thu, 15 Aug 2019 21:19:18 +0000 (GMT) Date: Thu, 15 Aug 2019 17:19:16 -0400 From: Michael Meissner To: gcc-patches@gcc.gnu.org, Segher Boessenkool , David Edelsohn , Michael Meissner , Alan Modra Subject: [PATCH], Patch #1 replacement (fix issues with future TLS patches) Message-ID: <20190815211916.GA19627@ibm-toto.the-meissners.org> Mail-Followup-To: Michael Meissner , gcc-patches@gcc.gnu.org, Segher Boessenkool , David Edelsohn , Alan Modra References: <20190814205732.GA11956@ibm-toto.the-meissners.org> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20190814205732.GA11956@ibm-toto.the-meissners.org> User-Agent: Mutt/1.5.21 (2010-09-15) After I submitted the patches, Aaron Sawdey tested the branch that has the patches on it, along with Alan's TLS patches. Alan's patch causes the functions that determine if the insn is prefixed or not to be run earlier that before. The compiler was dying because the virtual arg pointer and frame pointer registers weren't eliminated at that point, and because I was only checking if the regno was between 0 and 31 for GPRs. I rewrote the test in reg_to_insn_form to use INT_REGNO_P macro (which includes tests for arg pointer and frame pointer virtual registers). I used the other two macros (FP_REGNO_P and ALTIVEC_REGNO_P) for consistency. In addition, I removed the gcc_unreachable call if the register class is not a GPR, FPR, or VMX register, and used the GPR defaults. This is in case the function gets called in the middle of reload where the final moves are not done. This patch replaces patch #1. I have bootstrapped the compiler with these changes and verified it fixed the problem Aaron was seeing. Can I check this into the FSF trunk? 2019-08-15 Michael Meissner * config/rs6000/predicates.md (pcrel_address): Rewrite to use pcrel_addr_p. (pcrel_external_address): Rewrite to use pcrel_addr_p. (prefixed_mem_operand): Rewrite to use prefixed_local_addr_p. (pcrel_external_mem_operand): Rewrite to use pcrel_addr_p. * config/rs6000/rs6000-protos.h (reg_to_insn_form): New declaration. (pcrel_info_type): New declaration. (PCREL_NULL): New macro. (pcrel_addr_p): New declaration. (rs6000_prefixed_address_mode_p): Delete. * config/rs6000/rs6000.c (struct rs6000_reg_addr): Add fields for instruction format and prefixed memory support. (rs6000_debug_insn_form): New debug function. (rs6000_debug_print_mode): Print instruction formats. (setup_insn_form): New function. (rs6000_init_hard_regno_mode_ok): Call setup_insn_form. (print_operand_address): Call pcrel_addr_p instead of pcrel_address. Add support for external pc-relative labels. (mode_supports_prefixed_address_p): Delete. (rs6000_prefixed_address_mode_p): Delete, replace with prefixed_local_addr_p. (prefixed_local_addr_p): Replace rs6000_prefixed_address_mode_p. Add argument to specify the instruction format. (pcrel_addr_p): New function. (reg_to_insn_form): New function. * config/rs6000/rs6000.md (enum insn_form): New enumeration. Index: gcc/config/rs6000/predicates.md =================================================================== --- gcc/config/rs6000/predicates.md (revision 274172) +++ gcc/config/rs6000/predicates.md (working copy) @@ -1626,32 +1626,11 @@ (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. +;; 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_address" (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 "prefixed_mem_operand" (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 Index: gcc/config/rs6000/rs6000-protos.h =================================================================== --- gcc/config/rs6000/rs6000-protos.h (revision 274172) +++ gcc/config/rs6000/rs6000-protos.h (working copy) @@ -47,7 +47,19 @@ extern bool legitimate_indirect_address_ 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 (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); #endif /* RTX_CODE */ #ifdef TREE_CODE Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (revision 274172) +++ gcc/config/rs6000/rs6000.c (working copy) @@ -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_ve 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 glo 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,29 +13251,21 @@ print_operand (FILE *file, rtx x, int co 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; - - output_addr_const (file, x); - - if (offset) - fprintf (file, "%+" PRId64, offset); + if (pcrel_info.external_p) + fputs ("@got", file); fputs ("@pcrel", file); } @@ -13579,70 +13752,206 @@ rs6000_pltseq_template (rtx *operands, i 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. */ - -static bool -mode_supports_prefixed_address_p (machine_mode mode) -{ - return mode == VOIDmode; -} + MODE is the mode of the memory. -/* Function to return true if ADDR is a valid prefixed memory address that uses - mode MODE. */ + 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. */ 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); + + /* 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; - /* 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)) + 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; + + if (was_external_p && !external_symbol_p) + return false; + + if (!was_external_p && !local_symbol_p) + return false; - /* 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; + 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; - /* QImode/HImode has no restrictions. */ + /* 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 (INT_REGNO_P (r)) + iform = reg_addr[mode].insn_form[RELOAD_REG_GPR]; + + else if (FP_REGNO_P (r)) + iform = reg_addr[mode].insn_form[RELOAD_REG_FPR]; - /* Return true if we must use a prefixed instruction. */ - return (value & mask) != 0; + else if (ALTIVEC_REGNO_P (r)) + iform = reg_addr[mode].insn_form[RELOAD_REG_VMX]; + + /* For anything else (SPR, CA, etc.) assume the GPR registers will be + used to load or store the value. */ + else + iform = reg_addr[mode].insn_form[RELOAD_REG_GPR]; + } } - return false; + return iform; } #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO Index: gcc/config/rs6000/rs6000.md =================================================================== --- gcc/config/rs6000/rs6000.md (revision 274172) +++ gcc/config/rs6000/rs6000.md (working copy) @@ -252,6 +252,23 @@ (define_attr "var_shift" "no,yes" ;; 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))