From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by sourceware.org (Postfix) with ESMTP id EDE723858281 for ; Tue, 19 Jul 2022 13:09:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org EDE723858281 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=loongson.cn Received: from 5.5.5 (unknown [10.2.5.5]) by mail.loongson.cn (Coremail) with SMTP id AQAAf9DxD9HnrNZiAIYoAA--.35902S3; Tue, 19 Jul 2022 21:09:04 +0800 (CST) From: Lulu Cheng To: gcc-patches@gcc.gnu.org Cc: xry111@xry111.site, i@xen0n.name, xuchenghua@loongson.cn, Lulu Cheng Subject: [PATCH v1 1/2] LoongArch: Modify the method of obtaining symbolic addresses. Date: Tue, 19 Jul 2022 21:08:51 +0800 Message-Id: <20220719130852.2011955-2-chenglulu@loongson.cn> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20220719130852.2011955-1-chenglulu@loongson.cn> References: <20220719130852.2011955-1-chenglulu@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CM-TRANSID: AQAAf9DxD9HnrNZiAIYoAA--.35902S3 X-Coremail-Antispam: 1UD129KBjvAXoWDZr4kJF1UZrWDur4ktr15twb_yoW7tr4rCo WrAF4kZr18Gryjg393KFnxXryDXF4jy3yxAa43Zw1Y9a1ktrWDJrW7Ga15Z3y3X397WrWU J348Wa93AFZrJF4xn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUYA7AC8VAFwI0_Gr0_Xr1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l82xGYIkIc2x26280x7IE14v26r18M28IrcIa0x kI8VCY1x0267AKxVWUXVWUCwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK021l84AC jcxK6xIIjxv20xvE14v26F1j6w1UM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4UJVWxJr 1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_GcCE3s1l e2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI 8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwAC jcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lc2xSY4AK6svPMxAIw28Icx kI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2Iq xVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVWUAVWUtwCIc40Y0x0EwIxGrwCI42 IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI42IY 6xAIw20EY4v20xvaj40_JFI_Gr1lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcVC2z280aV CY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7VUbKii3UUUUU== X-CM-SenderInfo: xfkh0wpoxo3qxorr0wxvrqhubq/ X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, KAM_STOCKGEN, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Jul 2022 13:09:14 -0000 1. The original LA macro instruction is split into two instructions to obtain the address of the symbol if enable '-mexplicit-relocs'. 2. Currently, '-mcmodel=' only supports 'normal' mode, because other mode behaviors are not yet determined. This function is gradually improved by the subsequent handling. 3. Modify the method that calls global functions. From 'la.global + jirl' to 'bl'. gcc/ChangeLog: * common/config/loongarch/loongarch-common.cc: Enable '-fsection-anchors' when O1 and more advanced optimization. * config/loongarch/constraints.md (a): Delete the constraint. (b): A constant call not local address. (h): Delete the constraint. (t): Delete the constraint. * config/loongarch/genopts/loongarch.opt.in: Add new options '-mexplicit-relocs', and enable by default. * config/loongarch/loongarch-protos.h (enum loongarch_symbol_type): Add new symbol type 'SYMBOL_PCREL', 'SYMBOL_TLS_IE' and 'SYMBOL_TLS_LE'. (loongarch_split_move_insn_p): Delete function declaration. (loongarch_split_move_insn): Delete function declaration. * config/loongarch/loongarch.cc (enum loongarch_address_type): Add new address type 'ADDRESS_LO_SUM'. (loongarch_symbol_binds_local_p): (loongarch_classify_symbol): Return symbol type. If symbol is a label, or symbol is a local symbol return SYMBOL_PCREL. If is a tls symbol, return SYMBOL_TLS. If is a not local symbol return SYMBOL_GOT_DISP. (loongarch_classify_symbolic_expression): New function definitions. Classify the base of symbolic expression X, given that X appears in context CONTEXT. (loongarch_symbolic_constant_p): Add handling of 'SYMBOL_TLS_IE' 'SYMBOL_TLS_LE' and 'SYMBOL_PCREL'. (loongarch_symbol_insns): Add handling of 'SYMBOL_TLS_IE' 'SYMBOL_TLS_LE' and 'SYMBOL_PCREL'. (loongarch_split_symbol_type): New function definitions. Determines whether the symbol load should be split into two instructions. (loongarch_valid_lo_sum_p): New function definitions. Return true if a LO_SUM can address a value of mode MODE when the LO_SUM symbol has type SYMBOL_TYPE. (loongarch_classify_address): Add handling of 'LO_SUM'. (loongarch_address_insns): Add handling of 'ADDRESS_LO_SUM'. (loongarch_12bit_offset_address_p): Return true if address type is ADDRESS_LO_SUM. (loongarch_const_insns): Add handling of 'HIGH'. (loongarch_split_move_insn_p): Add the static attribute to the function. (loongarch_emit_set): New function definitions. (loongarch_call_tls_get_addr): Add symbol handling when defining the TARGET_EXPLICIT_RELOCS macro. (loongarch_legitimize_tls_address): Add symbol handling when defining the TARGET_EXPLICIT_RELOCS macro. (loongarch_split_symbol): New function definitions. Split symbol. (loongarch_legitimize_address): Add codes see if the address can split into a high part and a LO_SUM. (loongarch_legitimize_const_move): Add codes split moves of symbolic constants into high and low. (loongarch_split_move_insn): Delete function definitions. (loongarch_output_move): (loongarch_print_operand_reloc): New function definitions. Print symbolic operand OP, which is part of a HIGH or LO_SUM in context CONTEXT. (loongarch_print_operand): Rearrange alphabetical order and add H and L to support HIGH and LOW output. (loongarch_print_operand_address): Add handling of 'ADDRESS_LO_SUM'. (TARGET_MIN_ANCHOR_OFFSET): Define macro to -IMM_REACH/2. (TARGET_MAX_ANCHOR_OFFSET): Define macro to IMM_REACH/2-1. * config/loongarch/loongarch.h (LARCH_U12BIT_OFFSET_P): Rename to LARCH_12BIT_OFFSET_P. (LARCH_12BIT_OFFSET_P): New macro. * config/loongarch/loongarch.md (movti): Delete the template. (*movti): Delete the template. (movtf): Delete the template. (*movtf): Delete the template. (*low): New template of normal symbol low address. (@tls_low): New template of tls symbol low address. (@ld_from_got): New template load address from got table. (@ori_l_lo12): New template. * config/loongarch/loongarch.opt: Update. * config/loongarch/predicates.md (is_const_call_weak_symbol): Delete the predicate. (is_const_call_plt_symbol): Delete the predicate. (is_const_call_global_noplt_symbol): Delete the predicate. (is_const_call_no_local_symbol): New predicate, Determines whether it is a local symbol or label. --- .../config/loongarch/loongarch-common.cc | 1 + gcc/config/loongarch/constraints.md | 24 +- gcc/config/loongarch/genopts/loongarch.opt.in | 4 + gcc/config/loongarch/loongarch-protos.h | 9 +- gcc/config/loongarch/loongarch.cc | 660 +++++++++++++----- gcc/config/loongarch/loongarch.h | 2 +- gcc/config/loongarch/loongarch.md | 401 +++-------- gcc/config/loongarch/loongarch.opt | 4 + gcc/config/loongarch/predicates.md | 56 +- 9 files changed, 628 insertions(+), 533 deletions(-) diff --git a/gcc/common/config/loongarch/loongarch-common.cc b/gcc/common/config/loongarch/loongarch-common.cc index ed3730fce8b..f8b4660fabf 100644 --- a/gcc/common/config/loongarch/loongarch-common.cc +++ b/gcc/common/config/loongarch/loongarch-common.cc @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see static const struct default_options loongarch_option_optimization_table[] = { { OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 }, + { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } }; diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md index d0bfddbd5a9..43cb7b5f0f5 100644 --- a/gcc/config/loongarch/constraints.md +++ b/gcc/config/loongarch/constraints.md @@ -20,14 +20,14 @@ ;; Register constraints -;; "a" "A constant call global and noplt address." -;; "b" <-----unused +;; "a" <-----unused +;; "b" "A constant call not local address." ;; "c" "A constant call local address." ;; "d" <-----unused ;; "e" JIRL_REGS ;; "f" FP_REGS ;; "g" <-----unused -;; "h" "A constant call plt address." +;; "h" <-----unused ;; "i" "Matches a general integer constant." (Global non-architectural) ;; "j" SIBCALL_REGS ;; "k" "A memory operand whose address is formed by a base register and @@ -42,7 +42,7 @@ ;; "q" CSR_REGS ;; "r" GENERAL_REGS (Global non-architectural) ;; "s" "Matches a symbolic integer constant." (Global non-architectural) -;; "t" "A constant call weak address" +;; "t" <-----unused ;; "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)" ;; "v" "A signed 64-bit constant and low 44-bit is zero (for logic instructions)." ;; "w" "Matches any valid memory." @@ -89,10 +89,10 @@ ;; "<" "Matches a pre-dec or post-dec operand." (Global non-architectural) ;; ">" "Matches a pre-inc or post-inc operand." (Global non-architectural) -(define_constraint "a" +(define_constraint "b" "@internal - A constant call global and noplt address." - (match_operand 0 "is_const_call_global_noplt_symbol")) + A constant call no local address." + (match_operand 0 "is_const_call_no_local_symbol")) (define_constraint "c" "@internal @@ -105,11 +105,6 @@ (define_register_constraint "e" "JIRL_REGS" (define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS" "A floating-point register (if available).") -(define_constraint "h" - "@internal - A constant call plt address." - (match_operand 0 "is_const_call_plt_symbol")) - (define_register_constraint "j" "SIBCALL_REGS" "@internal") @@ -134,11 +129,6 @@ (define_memory_constraint "m" (define_register_constraint "q" "CSR_REGS" "A general-purpose register except for $r0 and $r1 for lcsr.") -(define_constraint "t" - "@internal - A constant call weak address." - (match_operand 0 "is_const_call_weak_symbol")) - (define_constraint "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)." (and (match_code "const_int") diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in index 61e7d72a0a1..6f39500935d 100644 --- a/gcc/config/loongarch/genopts/loongarch.opt.in +++ b/gcc/config/loongarch/genopts/loongarch.opt.in @@ -154,6 +154,10 @@ mmax-inline-memcpy-size= Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024) -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024. +mexplicit-relocs +Target Var(TARGET_EXPLICIT_RELOCS) Init(1) +Use %reloc() assembly operators. + ; The code model option names for -mcmodel. Enum Name(cmodel) Type(int) diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h index 2287fd3763c..5bbc43d92fb 100644 --- a/gcc/config/loongarch/loongarch-protos.h +++ b/gcc/config/loongarch/loongarch-protos.h @@ -27,9 +27,13 @@ along with GCC; see the file COPYING3. If not see SYMBOL_GOT_DISP The symbol's value will be loaded directly from the GOT. + SYMBOL_PCREL + The symbol's value will be loaded directly from data section. + SYMBOL_TLS A thread-local symbol. + SYMBOL_TLS_IE SYMBOL_TLSGD SYMBOL_TLSLDM UNSPEC wrappers around SYMBOL_TLS, corresponding to the @@ -37,7 +41,10 @@ along with GCC; see the file COPYING3. If not see */ enum loongarch_symbol_type { SYMBOL_GOT_DISP, + SYMBOL_PCREL, SYMBOL_TLS, + SYMBOL_TLS_IE, + SYMBOL_TLS_LE, SYMBOL_TLSGD, SYMBOL_TLSLDM, }; @@ -71,8 +78,6 @@ extern rtx loongarch_legitimize_call_address (rtx); extern rtx loongarch_subword (rtx, bool); extern bool loongarch_split_move_p (rtx, rtx); extern void loongarch_split_move (rtx, rtx, rtx); -extern bool loongarch_split_move_insn_p (rtx, rtx); -extern void loongarch_split_move_insn (rtx, rtx, rtx); extern const char *loongarch_output_move (rtx, rtx); extern bool loongarch_cfun_has_cprestore_slot_p (void); #ifdef RTX_CODE diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc index 8b0d7f459e0..3b28b6cef46 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -100,6 +100,10 @@ along with GCC; see the file COPYING3. If not see ADDRESS_REG_REG A base register indexed by (optionally scaled) register. + ADDRESS_LO_SUM + A LO_SUM rtx. The first operand is a valid base register and the second + operand is a symbolic address. + ADDRESS_CONST_INT A signed 16-bit constant address. @@ -109,24 +113,13 @@ enum loongarch_address_type { ADDRESS_REG, ADDRESS_REG_REG, + ADDRESS_LO_SUM, ADDRESS_CONST_INT, ADDRESS_SYMBOLIC }; -/* Information about an address described by loongarch_address_type. - - ADDRESS_CONST_INT - No fields are used. - - ADDRESS_REG - REG is the base register and OFFSET is the constant offset. - - ADDRESS_REG_REG - A base register indexed by (optionally scaled) register. - - ADDRESS_SYMBOLIC - SYMBOL_TYPE is the type of symbol that the address references. */ +/* Information about an address described by loongarch_address_type. */ struct loongarch_address_info { enum loongarch_address_type type; @@ -1617,11 +1610,12 @@ loongarch_weak_symbol_p (const_rtx x) bool loongarch_symbol_binds_local_p (const_rtx x) { - if (LABEL_REF_P (x)) + if (SYMBOL_REF_P (x)) + return (SYMBOL_REF_DECL (x) + ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) + : SYMBOL_REF_LOCAL_P (x)); + else return false; - - return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) - : SYMBOL_REF_LOCAL_P (x)); } /* Return true if rtx constants of mode MODE should be put into a small @@ -1640,17 +1634,31 @@ static enum loongarch_symbol_type loongarch_classify_symbol (const_rtx x) { if (LABEL_REF_P (x)) - return SYMBOL_GOT_DISP; - - gcc_assert (SYMBOL_REF_P (x)); + return SYMBOL_PCREL; if (SYMBOL_REF_TLS_MODEL (x)) return SYMBOL_TLS; - if (SYMBOL_REF_P (x)) + if (SYMBOL_REF_P (x) + && !loongarch_symbol_binds_local_p (x)) return SYMBOL_GOT_DISP; - return SYMBOL_GOT_DISP; + return SYMBOL_PCREL; +} + +/* Classify the base of symbolic expression X, given that X appears in + context CONTEXT. */ + +static enum loongarch_symbol_type +loongarch_classify_symbolic_expression (rtx x) +{ + rtx offset; + + split_const (x, &x, &offset); + if (UNSPEC_ADDRESS_P (x)) + return UNSPEC_ADDRESS_TYPE (x); + + return loongarch_classify_symbol (x); } /* Return true if X is a symbolic constant. If it is, @@ -1683,9 +1691,15 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type) relocations. */ switch (*symbol_type) { - case SYMBOL_GOT_DISP: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: case SYMBOL_TLSGD: case SYMBOL_TLSLDM: + case SYMBOL_PCREL: + /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */ + return sext_hwi (INTVAL (offset), 32) == INTVAL (offset); + + case SYMBOL_GOT_DISP: case SYMBOL_TLS: return false; } @@ -1702,14 +1716,19 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode) case SYMBOL_GOT_DISP: /* The constant will have to be loaded from the GOT before it is used in an address. */ - if (mode != MAX_MACHINE_MODE) + if (!TARGET_EXPLICIT_RELOCS && mode != MAX_MACHINE_MODE) return 0; return 3; + case SYMBOL_PCREL: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: + return 2; + case SYMBOL_TLSGD: case SYMBOL_TLSLDM: - return 1; + return 3; case SYMBOL_TLS: /* We don't treat a bare TLS symbol as a constant. */ @@ -1815,6 +1834,84 @@ loongarch_valid_offset_p (rtx x, machine_mode mode) return true; } +/* Should a symbol of type SYMBOL_TYPE should be split in two? */ + +bool +loongarch_split_symbol_type (enum loongarch_symbol_type symbol_type) +{ + switch (symbol_type) + { + case SYMBOL_PCREL: + case SYMBOL_GOT_DISP: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + return true; + + case SYMBOL_TLS: + return false; + + default: + gcc_unreachable (); + } +} + +/* Return true if a LO_SUM can address a value of mode MODE when the + LO_SUM symbol has type SYMBOL_TYPE. */ + +static bool +loongarch_valid_lo_sum_p (enum loongarch_symbol_type symbol_type, + machine_mode mode, rtx x) +{ + int align, size; + + /* Check that symbols of type SYMBOL_TYPE can be used to access values + of mode MODE. */ + if (loongarch_symbol_insns (symbol_type, mode) == 0) + return false; + + /* Check that there is a known low-part relocation. */ + if (!loongarch_split_symbol_type (symbol_type)) + return false; + + /* We can't tell size or alignment when we have BLKmode, so try extracing a + decl from the symbol if possible. */ + if (mode == BLKmode) + { + rtx offset; + + /* Extract the symbol from the LO_SUM operand, if any. */ + split_const (x, &x, &offset); + + /* Might be a CODE_LABEL. We can compute align but not size for that, + so don't bother trying to handle it. */ + if (!SYMBOL_REF_P (x)) + return false; + + /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ + align = (SYMBOL_REF_DECL (x) + ? DECL_ALIGN (SYMBOL_REF_DECL (x)) + : 1); + size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) + ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) + : 2*BITS_PER_WORD); + } + else + { + align = GET_MODE_ALIGNMENT (mode); + size = GET_MODE_BITSIZE (mode); + } + + /* We may need to split multiword moves, so make sure that each word + can be accessed without inducing a carry. */ + if (size > BITS_PER_WORD + && (!TARGET_STRICT_ALIGN || size > align)) + return false; + + return true; +} + static bool loongarch_valid_index_p (struct loongarch_address_info *info, rtx x, machine_mode mode, bool strict_p) @@ -1881,6 +1978,26 @@ loongarch_classify_address (struct loongarch_address_info *info, rtx x, info->offset = XEXP (x, 1); return (loongarch_valid_base_register_p (info->reg, mode, strict_p) && loongarch_valid_offset_p (info->offset, mode)); + + case LO_SUM: + info->type = ADDRESS_LO_SUM; + info->reg = XEXP (x, 0); + info->offset = XEXP (x, 1); + /* We have to trust the creator of the LO_SUM to do something vaguely + sane. Target-independent code that creates a LO_SUM should also + create and verify the matching HIGH. Target-independent code that + adds an offset to a LO_SUM must prove that the offset will not + induce a carry. Failure to do either of these things would be + a bug, and we are not required to check for it here. The MIPS + backend itself should only create LO_SUMs for valid symbolic + constants, with the high part being either a HIGH or a copy + of _gp. */ + info->symbol_type + = loongarch_classify_symbolic_expression (info->offset); + return (loongarch_valid_base_register_p (info->reg, mode, strict_p) + && loongarch_valid_lo_sum_p (info->symbol_type, mode, + info->offset)); + default: return false; } @@ -1937,14 +2054,13 @@ loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p) switch (addr.type) { case ADDRESS_REG: - return factor; - case ADDRESS_REG_REG: - return factor; - case ADDRESS_CONST_INT: return factor; + case ADDRESS_LO_SUM: + return factor + 1; + case ADDRESS_SYMBOLIC: return factor * loongarch_symbol_insns (addr.symbol_type, mode); } @@ -1972,7 +2088,8 @@ loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits, return loongarch_unsigned_immediate_p (x, bits, shift); } -/* Return true if X is a legitimate address with a 12-bit offset. +/* Return true if X is a legitimate address with a 12-bit offset + or addr.type is ADDRESS_LO_SUM. MODE is the mode of the value being accessed. */ bool @@ -1981,9 +2098,10 @@ loongarch_12bit_offset_address_p (rtx x, machine_mode mode) struct loongarch_address_info addr; return (loongarch_classify_address (&addr, x, mode, false) - && addr.type == ADDRESS_REG - && CONST_INT_P (addr.offset) - && LARCH_U12BIT_OFFSET_P (INTVAL (addr.offset))); + && ((addr.type == ADDRESS_REG + && CONST_INT_P (addr.offset) + && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset))) + || addr.type == ADDRESS_LO_SUM)); } /* Return true if X is a legitimate address with a 14-bit offset shifted 2. @@ -2001,6 +2119,9 @@ loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode) && LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset))); } +/* Return true if X is a legitimate address with base and index. + MODE is the mode of the value being accessed. */ + bool loongarch_base_index_address_p (rtx x, machine_mode mode) { @@ -2022,6 +2143,14 @@ loongarch_const_insns (rtx x) switch (GET_CODE (x)) { + case HIGH: + if (!loongarch_symbolic_constant_p (XEXP (x, 0), &symbol_type) + || !loongarch_split_symbol_type (symbol_type)) + return 0; + + /* This is simply a PCALAU12I. */ + return 1; + case CONST_INT: return loongarch_integer_cost (INTVAL (x)); @@ -2082,6 +2211,8 @@ loongarch_split_const_insns (rtx x) return low + high; } +static bool loongarch_split_move_insn_p (rtx dest, rtx src); + /* Return the number of instructions needed to implement INSN, given that it loads from or stores to MEM. */ @@ -2199,6 +2330,15 @@ loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type) return loongarch_unspec_address_offset (base, offset, symbol_type); } +/* Emit an instruction of the form (set TARGET SRC). */ + +static rtx +loongarch_emit_set (rtx target, rtx src) +{ + emit_insn (gen_rtx_SET (target, src)); + return target; +} + /* If OP is an UNSPEC address, return the address to which it refers, otherwise return OP itself. */ @@ -2280,6 +2420,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) { rtx loc, a0; rtx_insn *insn; + rtx tmp = gen_reg_rtx (Pmode); a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); @@ -2290,12 +2431,22 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) start_sequence (); - if (type == SYMBOL_TLSLDM) - emit_insn (loongarch_got_load_tls_ld (a0, loc)); - else if (type == SYMBOL_TLSGD) - emit_insn (loongarch_got_load_tls_gd (a0, loc)); + if (TARGET_EXPLICIT_RELOCS) + { + /* Split tls symbol to high and low. */ + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (loc)); + high = loongarch_force_temporary (tmp, high); + emit_insn (gen_tls_low (Pmode, a0, high, loc)); + } else - gcc_unreachable (); + { + if (type == SYMBOL_TLSLDM) + emit_insn (loongarch_got_load_tls_ld (a0, loc)); + else if (type == SYMBOL_TLSGD) + emit_insn (loongarch_got_load_tls_gd (a0, loc)); + else + gcc_unreachable (); + } insn = emit_call_insn (gen_call_value_internal (v0, loongarch_tls_symbol, const0_rtx)); @@ -2315,7 +2466,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) static rtx loongarch_legitimize_tls_address (rtx loc) { - rtx dest, tp, tmp; + rtx dest, tp, tmp, tmp1, tmp2, tmp3; enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); rtx_insn *insn; @@ -2336,21 +2487,45 @@ loongarch_legitimize_tls_address (rtx loc) break; case TLS_MODEL_INITIAL_EXEC: - /* la.tls.ie; tp-relative add */ - tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); - tmp = gen_reg_rtx (Pmode); - emit_insn (loongarch_got_load_tls_ie (tmp, loc)); - dest = gen_reg_rtx (Pmode); - emit_insn (gen_add3_insn (dest, tmp, tp)); + { + /* la.tls.ie; tp-relative add. */ + tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); + tmp1 = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); + if (TARGET_EXPLICIT_RELOCS) + { + tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_IE); + tmp3 = gen_reg_rtx (Pmode); + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); + high = loongarch_force_temporary (tmp3, high); + emit_insn (gen_ld_from_got (Pmode, tmp1, high, tmp2)); + } + else + emit_insn (loongarch_got_load_tls_ie (tmp1, loc)); + emit_insn (gen_add3_insn (dest, tmp1, tp)); + } break; case TLS_MODEL_LOCAL_EXEC: - /* la.tls.le; tp-relative add */ - tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); - tmp = gen_reg_rtx (Pmode); - emit_insn (loongarch_got_load_tls_le (tmp, loc)); - dest = gen_reg_rtx (Pmode); - emit_insn (gen_add3_insn (dest, tmp, tp)); + { + /* la.tls.le; tp-relative add. */ + tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); + tmp1 = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); + + if (TARGET_EXPLICIT_RELOCS) + { + tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_LE); + tmp3 = gen_reg_rtx (Pmode); + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); + high = loongarch_force_temporary (tmp3, high); + emit_insn (gen_ori_l_lo12 (Pmode, tmp1, high, tmp2)); + } + else + emit_insn (loongarch_got_load_tls_le (tmp1, loc)); + emit_insn (gen_add3_insn (dest, tmp1, tp)); + + } break; default: @@ -2399,6 +2574,68 @@ loongarch_force_address (rtx x, machine_mode mode) return x; } +/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise + it appears in a MEM of that mode. Return true if ADDR is a legitimate + constant in that context and can be split into high and low parts. + If so, and if LOW_OUT is nonnull, emit the high part and store the + low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise. + + Return false if build with '-mno-explicit-relocs'. + + TEMP is as for loongarch_force_temporary and is used to load the high + part into a register. + + When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be + a legitimize SET_SRC for an .md pattern, otherwise the low part + is guaranteed to be a legitimate address for mode MODE. */ + +bool +loongarch_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out) +{ + enum loongarch_symbol_type symbol_type; + rtx high; + + /* If build with '-mno-explicit-relocs', don't split symbol. */ + if (!TARGET_EXPLICIT_RELOCS) + return false; + + if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE) + || !loongarch_symbolic_constant_p (addr, &symbol_type) + || loongarch_symbol_insns (symbol_type, mode) == 0 + || !loongarch_split_symbol_type (symbol_type)) + return false; + + if (temp == NULL) + temp = gen_reg_rtx (Pmode); + + /* Get the 12-31 bits of the address. */ + high = gen_rtx_HIGH (Pmode, copy_rtx (addr)); + high = loongarch_force_temporary (temp, high); + + if (low_out) + switch (symbol_type) + { + case SYMBOL_PCREL: + *low_out = gen_rtx_LO_SUM (Pmode, high, addr); + break; + + case SYMBOL_GOT_DISP: + /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */ + { + rtx low = gen_rtx_LO_SUM (Pmode, high, addr); + rtx mem = gen_rtx_MEM (Pmode, low); + *low_out = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, mem), + UNSPEC_LOAD_FROM_GOT); + break; + } + + default: + gcc_unreachable (); + } + + return true; +} + /* This function is used to implement LEGITIMIZE_ADDRESS. If X can be legitimized in a way that the generic machinery might not expect, return a new address, otherwise return NULL. MODE is the mode of @@ -2414,6 +2651,10 @@ loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, if (loongarch_tls_symbol_p (x)) return loongarch_legitimize_tls_address (x); + /* See if the address can split into a high part and a LO_SUM. */ + if (loongarch_split_symbol (NULL, x, mode, &addr)) + return loongarch_force_address (addr, mode); + /* Handle BASE + OFFSET using loongarch_add_offset. */ loongarch_split_plus (x, &base, &offset); if (offset != 0) @@ -2501,6 +2742,13 @@ loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src) return; } + /* Split moves of symbolic constants into high and low. */ + if (loongarch_split_symbol (dest, src, MAX_MACHINE_MODE, &src)) + { + loongarch_emit_set (dest, src); + return; + } + /* Generate the appropriate access sequences for TLS symbols. */ if (loongarch_tls_symbol_p (src)) { @@ -3243,21 +3491,12 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_) /* Return true if a move from SRC to DEST in INSN should be split. */ -bool +static bool loongarch_split_move_insn_p (rtx dest, rtx src) { return loongarch_split_move_p (dest, src); } -/* Split a move from SRC to DEST in INSN, given that - loongarch_split_move_insn_p holds. */ - -void -loongarch_split_move_insn (rtx dest, rtx src, rtx insn) -{ - loongarch_split_move (dest, src, insn); -} - /* Implement TARGET_CONSTANT_ALIGNMENT. */ static HOST_WIDE_INT @@ -3371,12 +3610,16 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "st.h\t%z1,%0"; case 4: - if (const_arith_operand (offset, Pmode)) + /* Matching address type with a 12bit offset and + ADDRESS_LO_SUM. */ + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "st.w\t%z1,%0"; else return "stptr.w\t%z1,%0"; case 8: - if (const_arith_operand (offset, Pmode)) + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "st.d\t%z1,%0"; else return "stptr.d\t%z1,%0"; @@ -3409,12 +3652,16 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "ld.hu\t%0,%1"; case 4: - if (const_arith_operand (offset, Pmode)) + /* Matching address type with a 12bit offset and + ADDRESS_LO_SUM. */ + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "ld.w\t%0,%1"; else return "ldptr.w\t%0,%1"; case 8: - if (const_arith_operand (offset, Pmode)) + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "ld.d\t%0,%1"; else return "ldptr.d\t%0,%1"; @@ -3423,6 +3670,21 @@ loongarch_output_move (rtx dest, rtx src) } } + if (src_code == HIGH) + { + rtx offset, x; + split_const (XEXP (src, 0), &x, &offset); + enum loongarch_symbol_type type = SYMBOL_PCREL; + + if (UNSPEC_ADDRESS_P (x)) + type = UNSPEC_ADDRESS_TYPE (x); + + if (type == SYMBOL_TLS_LE) + return "lu12i.w\t%0,%h1"; + else + return "pcalau12i\t%0,%h1"; + } + if (src_code == CONST_INT) { if (LU12I_INT (src)) @@ -3436,56 +3698,17 @@ loongarch_output_move (rtx dest, rtx src) else gcc_unreachable (); } + } - if (symbolic_operand (src, VOIDmode)) - { - if ((TARGET_CMODEL_TINY && (!loongarch_global_symbol_p (src) - || loongarch_symbol_binds_local_p (src))) - || (TARGET_CMODEL_TINY_STATIC && !loongarch_weak_symbol_p (src))) - { - /* The symbol must be aligned to 4 byte. */ - unsigned int align; - - if (LABEL_REF_P (src)) - align = 32 /* Whatever. */; - else if (CONSTANT_POOL_ADDRESS_P (src)) - align = GET_MODE_ALIGNMENT (get_pool_mode (src)); - else if (TREE_CONSTANT_POOL_ADDRESS_P (src)) - { - tree exp = SYMBOL_REF_DECL (src); - align = TYPE_ALIGN (TREE_TYPE (exp)); - align = loongarch_constant_alignment (exp, align); - } - else if (SYMBOL_REF_DECL (src)) - align = DECL_ALIGN (SYMBOL_REF_DECL (src)); - else if (SYMBOL_REF_HAS_BLOCK_INFO_P (src) - && SYMBOL_REF_BLOCK (src) != NULL) - align = SYMBOL_REF_BLOCK (src)->alignment; - else - align = BITS_PER_UNIT; - - if (align % (4 * 8) == 0) - return "pcaddi\t%0,%%pcrel(%1)>>2"; - } - if (TARGET_CMODEL_TINY - || TARGET_CMODEL_TINY_STATIC - || TARGET_CMODEL_NORMAL - || TARGET_CMODEL_LARGE) - { - if (!loongarch_global_symbol_p (src) - || loongarch_symbol_binds_local_p (src)) - return "la.local\t%0,%1"; - else - return "la.global\t%0,%1"; - } - if (TARGET_CMODEL_EXTREME) - { - sorry ("Normal symbol loading not implemented in extreme mode."); - gcc_unreachable (); - } - - } + if (!TARGET_EXPLICIT_RELOCS + && dest_code == REG && symbolic_operand (src, VOIDmode)) + { + if (loongarch_classify_symbol (src) == SYMBOL_PCREL) + return "la.local\t%0,%1"; + else + return "la.global\t%0,%1"; } + if (src_code == REG && FP_REG_P (REGNO (src))) { if (dest_code == REG && FP_REG_P (REGNO (dest))) @@ -3503,6 +3726,7 @@ loongarch_output_move (rtx dest, rtx src) return dbl_p ? "fst.d\t%1,%0" : "fst.s\t%1,%0"; } } + if (dest_code == REG && FP_REG_P (REGNO (dest))) { if (src_code == MEM) @@ -3517,6 +3741,7 @@ loongarch_output_move (rtx dest, rtx src) return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1"; } } + gcc_unreachable (); } @@ -4345,29 +4570,75 @@ loongarch_memmodel_needs_release_fence (enum memmodel model) } } +/* Print symbolic operand OP, which is part of a HIGH or LO_SUM + in context CONTEXT. HI_RELOC indicates a high-part reloc. */ + +static void +loongarch_print_operand_reloc (FILE *file, rtx op, bool hi_reloc) +{ + const char *reloc; + + switch (loongarch_classify_symbolic_expression (op)) + { + case SYMBOL_PCREL: + reloc = hi_reloc ? "%pc_hi20" : "%pc_lo12"; + break; + + case SYMBOL_GOT_DISP: + reloc = hi_reloc ? "%got_pc_hi20" : "%got_pc_lo12"; + break; + + case SYMBOL_TLS_IE: + reloc = hi_reloc ? "%ie_pc_hi20" : "%ie_pc_lo12"; + break; + + case SYMBOL_TLS_LE: + reloc = hi_reloc ? "%le_hi20" : "%le_lo12"; + break; + + case SYMBOL_TLSGD: + reloc = hi_reloc ? "%gd_pc_hi20" : "%got_pc_lo12"; + break; + + case SYMBOL_TLSLDM: + reloc = hi_reloc ? "%ld_pc_hi20" : "%got_pc_lo12"; + break; + + default: + gcc_unreachable (); + } + + fprintf (file, "%s(", reloc); + output_addr_const (file, loongarch_strip_unspec_address (op)); + fputc (')', file); +} + /* Implement TARGET_PRINT_OPERAND. The LoongArch-specific operand codes are: - 'X' Print CONST_INT OP in hexadecimal format. - 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. + 'A' Print a _DB suffix if the memory model requires a release. + 'b' Print the address of a memory operand, without offset. + 'C' Print the integer branch condition for comparison OP. 'd' Print CONST_INT OP in decimal. + 'F' Print the FPU branch condition for comparison OP. + 'G' Print a DBAR insn if the memory model requires a release. + 'H' Print address 52-61bit relocation associated with OP. + 'h' Print the high-part relocation associated with OP. + 'i' Print i if the operand is not a register. + 'L' Print the low-part relocation associated with OP. 'm' Print one less than CONST_INT OP in decimal. - 'y' Print exact log2 of CONST_INT OP in decimal. - 'C' Print the integer branch condition for comparison OP. 'N' Print the inverse of the integer branch condition for comparison OP. - 'F' Print the FPU branch condition for comparison OP. - 'W' Print the inverse of the FPU branch condition for comparison OP. 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...), 'z' for (eq:?I ...), 'n' for (ne:?I ...). 't' Like 'T', but with the EQ/NE cases reversed - 'Y' Print loongarch_fp_conditions[INTVAL (OP)] - 'Z' Print OP and a comma for 8CC, otherwise print nothing. - 'z' Print $0 if OP is zero, otherwise print OP normally. - 'b' Print the address of a memory operand, without offset. 'V' Print exact log2 of CONST_INT OP element 0 of a replicated CONST_VECTOR in decimal. - 'A' Print a _DB suffix if the memory model requires a release. - 'G' Print a DBAR insn if the memory model requires a release. - 'i' Print i if the operand is not a register. */ + 'W' Print the inverse of the FPU branch condition for comparison OP. + 'X' Print CONST_INT OP in hexadecimal format. + 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. + 'Y' Print loongarch_fp_conditions[INTVAL (OP)] + 'y' Print exact log2 of CONST_INT OP in decimal. + 'Z' Print OP and a comma for 8CC, otherwise print nothing. + 'z' Print $0 if OP is zero, otherwise print OP normally. */ static void loongarch_print_operand (FILE *file, rtx op, int letter) @@ -4385,18 +4656,13 @@ loongarch_print_operand (FILE *file, rtx op, int letter) switch (letter) { - case 'X': - if (CONST_INT_P (op)) - fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'A': + if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) + fputs ("_db", file); break; - case 'x': - if (CONST_INT_P (op)) - fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'C': + loongarch_print_int_branch_condition (file, code, letter); break; case 'd': @@ -4406,6 +4672,34 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; + case 'F': + loongarch_print_float_branch_condition (file, code, letter); + break; + + case 'G': + if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) + fputs ("dbar\t0", file); + break; + + case 'h': + if (code == HIGH) + op = XEXP (op, 0); + loongarch_print_operand_reloc (file, op, true /* hi_reloc */); + break; + + case 'H': + loongarch_print_operand_reloc (file, op, true /* hi_reloc */); + break; + + case 'i': + if (code != REG) + fputs ("i", file); + break; + + case 'L': + loongarch_print_operand_reloc (file, op, false /* lo_reloc */); + break; + case 'm': if (CONST_INT_P (op)) fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1); @@ -4413,17 +4707,21 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'y': - if (CONST_INT_P (op)) - { - int val = exact_log2 (INTVAL (op)); - if (val != -1) - fprintf (file, "%d", val); - else - output_operand_lossage ("invalid use of '%%%c'", letter); - } - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'N': + loongarch_print_int_branch_condition (file, reverse_condition (code), + letter); + break; + + case 'R': + loongarch_print_operand_reloc (file, op, false /* lo_reloc */); + break; + + case 't': + case 'T': + { + int truth = (code == NE) == (letter == 'T'); + fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); + } break; case 'V': @@ -4441,30 +4739,36 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'C': - loongarch_print_int_branch_condition (file, code, letter); - break; - - case 'N': - loongarch_print_int_branch_condition (file, reverse_condition (code), - letter); + case 'W': + loongarch_print_float_branch_condition (file, reverse_condition (code), + letter); break; - case 'F': - loongarch_print_float_branch_condition (file, code, letter); + case 'x': + if (CONST_INT_P (op)) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'W': - loongarch_print_float_branch_condition (file, reverse_condition (code), - letter); + case 'X': + if (CONST_INT_P (op)) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'T': - case 't': - { - int truth = (code == NE) == (letter == 'T'); - fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); - } + case 'y': + if (CONST_INT_P (op)) + { + int val = exact_log2 (INTVAL (op)); + if (val != -1) + fprintf (file, "%d", val); + else + output_operand_lossage ("invalid use of '%%%c'", letter); + } + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; case 'Y': @@ -4481,21 +4785,6 @@ loongarch_print_operand (FILE *file, rtx op, int letter) fputc (',', file); break; - case 'A': - if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) - fputs ("_db", file); - break; - - case 'G': - if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) - fputs ("dbar\t0", file); - break; - - case 'i': - if (code != REG) - fputs ("i", file); - break; - default: switch (code) { @@ -4555,6 +4844,11 @@ loongarch_print_operand_address (FILE *file, machine_mode /* mode */, rtx x) reg_names[REGNO (addr.offset)]); return; + case ADDRESS_LO_SUM: + fprintf (file, "%s,", reg_names[REGNO (addr.reg)]); + loongarch_print_operand_reloc (file, addr.offset, false /* hi_reloc */); + return; + case ADDRESS_CONST_INT: fprintf (file, "%s,", reg_names[GP_REG_FIRST]); output_addr_const (file, x); @@ -5929,6 +6223,12 @@ loongarch_starting_frame_offset (void) #undef TARGET_TRAMPOLINE_INIT #define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init +#undef TARGET_MIN_ANCHOR_OFFSET +#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2) + +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1) + #undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV #define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h index f9de9a6e4fb..89a5bd728fe 100644 --- a/gcc/config/loongarch/loongarch.h +++ b/gcc/config/loongarch/loongarch.h @@ -614,7 +614,7 @@ enum reg_class #define LU12I_INT(X) LU12I_OPERAND (INTVAL (X)) #define LU32I_INT(X) LU32I_OPERAND (INTVAL (X)) #define LU52I_INT(X) LU52I_OPERAND (INTVAL (X)) -#define LARCH_U12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047)) +#define LARCH_12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047)) #define LARCH_9BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -256, 255)) #define LARCH_16BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -32768, 32767)) #define LARCH_SHIFT_2_OFFSET_P(OFFSET) (((OFFSET) & 0x3) == 0) diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md index 5c0445dd879..6b6df22a5f1 100644 --- a/gcc/config/loongarch/loongarch.md +++ b/gcc/config/loongarch/loongarch.md @@ -57,6 +57,10 @@ (define_c_enum "unspec" [ ;; CRC UNSPEC_CRC UNSPEC_CRCC + + UNSPEC_LOAD_FROM_GOT + UNSPEC_ORI_L_LO12 + UNSPEC_TLS_LOW ]) (define_c_enum "unspecv" [ @@ -1743,73 +1747,6 @@ (define_insn "*movdf_softfloat" [(set_attr "move_type" "move,load,store") (set_attr "mode" "DF")]) - -;; 128-bit integer moves - -(define_expand "movti" - [(set (match_operand:TI 0) - (match_operand:TI 1))] - "TARGET_64BIT" -{ - if (loongarch_legitimize_move (TImode, operands[0], operands[1])) - DONE; -}) - -(define_insn "*movti" - [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,r,m") - (match_operand:TI 1 "move_operand" "r,i,m,rJ"))] - "TARGET_64BIT - && (register_operand (operands[0], TImode) - || reg_or_0_operand (operands[1], TImode))" - { return loongarch_output_move (operands[0], operands[1]); } - [(set_attr "move_type" "move,const,load,store") - (set (attr "mode") - (if_then_else (eq_attr "move_type" "imul") - (const_string "SI") - (const_string "TI")))]) - -;; 128-bit floating point moves - -(define_expand "movtf" - [(set (match_operand:TF 0) - (match_operand:TF 1))] - "TARGET_64BIT" -{ - if (loongarch_legitimize_move (TFmode, operands[0], operands[1])) - DONE; -}) - -;; This pattern handles both hard- and soft-float cases. -(define_insn "*movtf" - [(set (match_operand:TF 0 "nonimmediate_operand" "=r,r,m,f,r,f,m") - (match_operand:TF 1 "move_operand" "rG,m,rG,rG,f,m,f"))] - "TARGET_64BIT - && (register_operand (operands[0], TFmode) - || reg_or_0_operand (operands[1], TFmode))" - "#" - [(set_attr "move_type" "move,load,store,mgtf,mftg,fpload,fpstore") - (set_attr "mode" "TF")]) - -(define_split - [(set (match_operand:MOVE64 0 "nonimmediate_operand") - (match_operand:MOVE64 1 "move_operand"))] - "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])" - [(const_int 0)] -{ - loongarch_split_move_insn (operands[0], operands[1], curr_insn); - DONE; -}) - -(define_split - [(set (match_operand:MOVE128 0 "nonimmediate_operand") - (match_operand:MOVE128 1 "move_operand"))] - "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])" - [(const_int 0)] -{ - loongarch_split_move_insn (operands[0], operands[1], curr_insn); - DONE; -}) - ;; Emit a doubleword move in which exactly one of the operands is ;; a floating-point register. We can't just emit two normal moves ;; because of the constraints imposed by the FPU register model; @@ -1938,6 +1875,57 @@ (define_insn "lu52i_d" [(set_attr "type" "arith") (set_attr "mode" "DI")]) +;; Instructions for adding the low 12 bits of an address to a register. +;; Operand 2 is the address: loongarch_print_operand works out which relocation +;; should be applied. + +(define_insn "*low" + [(set (match_operand:P 0 "register_operand" "=r") + (lo_sum:P (match_operand:P 1 "register_operand" " r") + (match_operand:P 2 "symbolic_operand" "")))] + "TARGET_EXPLICIT_RELOCS" + "addi.\t%0,%1,%L2" + [(set_attr "type" "arith") + (set_attr "mode" "")]) + +(define_insn "@tls_low" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(mem:P (lo_sum:P (match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand" "")))] + UNSPEC_TLS_LOW))] + "TARGET_EXPLICIT_RELOCS" + "addi.\t%0,%1,%L2" + [(set_attr "type" "arith") + (set_attr "mode" "")]) + +;; Instructions for loading address from GOT entry. +;; operands[1] is pc plus the high half of the address difference with the got +;; entry; +;; operands[2] is low 12 bits for low 12 bit of the address difference with the +;; got entry. +;; loongarch_print_operand works out which relocation should be applied. + +(define_insn "@ld_from_got" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(mem:P (lo_sum:P + (match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand")))] + UNSPEC_LOAD_FROM_GOT))] + "TARGET_EXPLICIT_RELOCS" + "ld.\t%0,%1,%L2" + [(set_attr "type" "move")] +) + +(define_insn "@ori_l_lo12" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand")] + UNSPEC_ORI_L_LO12))] + "" + "ori\t%0,%1,%L2" + [(set_attr "type" "move")] +) + ;; Convert floating-point numbers to integers (define_insn "frint_" [(set (match_operand:ANYF 0 "register_operand" "=f") @@ -2844,48 +2832,14 @@ (define_expand "sibcall" }) (define_insn "sibcall_internal" - [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,a,t,h")) + [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,b")) (match_operand 1 "" ""))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%pcrel(%0+0x20000))>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "b\t%0"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%0"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "la.global\t$r12,%0\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "la.global\t$r12,%0\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "b\t%%plt(%0)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%0)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%0 + b\t%0 + b\t%%plt(%0)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "sibcall_value" [(parallel [(set (match_operand 0 "") @@ -2920,96 +2874,28 @@ (define_expand "sibcall_value" (define_insn "sibcall_value_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) (match_operand 2 "" "")))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%1"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%1+4)-((%%pcrel(%1+4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "b\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return " b\t%%plt(%1)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%1 + b\t%1 + b\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_insn "sibcall_value_multiple_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) (match_operand 2 "" ""))) (set (match_operand 3 "register_operand" "") (call (mem:SI (match_dup 1)) (match_dup 2)))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%1"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "b\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "b\t%%plt(%1)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%1 + b\t%1 + b\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "call" [(parallel [(call (match_operand 0 "") @@ -3025,50 +2911,15 @@ (define_expand "call" }) (define_insn "call_internal" - [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,a,t,h")) + [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,b")) (match_operand 1 "" "")) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%0,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%0+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%0"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%0"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%0)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%0)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%0,0 + bl\t%0 + bl\t%%plt(%0)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "call_value" [(parallel [(set (match_operand 0 "") @@ -3101,100 +2952,30 @@ (define_expand "call_value" (define_insn "call_value_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) (match_operand 2 "" ""))) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%1,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%1)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%1,0 + bl\t%1 + bl\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_insn "call_value_multiple_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) (match_operand 2 "" ""))) (set (match_operand 3 "register_operand" "") (call (mem:SI (match_dup 1)) (match_dup 2))) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%1,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0 "; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%1)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%1,0 + bl\t%1 + bl\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) ;; Call subroutine returning any type. diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt index 3ff0d860413..7a8c5b44418 100644 --- a/gcc/config/loongarch/loongarch.opt +++ b/gcc/config/loongarch/loongarch.opt @@ -161,6 +161,10 @@ mmax-inline-memcpy-size= Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024) -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024. +mexplicit-relocs +Target Var(TARGET_EXPLICIT_RELOCS) Init(1) +Use %reloc() assembly operators. + ; The code model option names for -mcmodel. Enum Name(cmodel) Type(int) diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md index edd74d4783d..cd3528c7c97 100644 --- a/gcc/config/loongarch/predicates.md +++ b/gcc/config/loongarch/predicates.md @@ -110,21 +110,30 @@ (define_predicate "low_bitmask_operand" (define_predicate "const_call_insn_operand" (match_code "const,symbol_ref,label_ref") { + /* Split symbol to high and low if return false. + If defined TARGET_CMODEL_LARGE, all symbol would be splited, + else if offset is not zero, the symbol would be splited. */ + enum loongarch_symbol_type symbol_type; + loongarch_symbolic_constant_p (op, &symbol_type); + + rtx offset, x = op; + split_const (x, &x, &offset); - if (!loongarch_symbolic_constant_p (op, &symbol_type)) + if (offset != const0_rtx) return false; switch (symbol_type) { - case SYMBOL_GOT_DISP: - /* Without explicit relocs, there is no special syntax for - loading the address of a call destination into a register. - Using "la.global JIRL_REGS,foo; jirl JIRL_REGS" would prevent the lazy - binding of "foo", so keep the address of global symbols with the jirl - macro. */ + case SYMBOL_PCREL: return 1; + case SYMBOL_GOT_DISP: + if (TARGET_CMODEL_LARGE || !flag_plt) + return false; + else + return 1; + default: return false; } @@ -140,22 +149,11 @@ (define_predicate "is_const_call_local_symbol" (match_test "loongarch_symbol_binds_local_p (op) != 0")) (match_test "CONSTANT_P (op)"))) -(define_predicate "is_const_call_weak_symbol" - (and (match_operand 0 "const_call_insn_operand") - (not (match_operand 0 "is_const_call_local_symbol")) - (match_test "loongarch_weak_symbol_p (op) != 0") - (match_test "CONSTANT_P (op)"))) - -(define_predicate "is_const_call_plt_symbol" - (and (match_operand 0 "const_call_insn_operand") - (match_test "flag_plt != 0") - (match_test "loongarch_global_symbol_noweak_p (op) != 0") - (match_test "CONSTANT_P (op)"))) - -(define_predicate "is_const_call_global_noplt_symbol" +(define_predicate "is_const_call_no_local_symbol" (and (match_operand 0 "const_call_insn_operand") - (match_test "flag_plt == 0") - (match_test "loongarch_global_symbol_noweak_p (op) != 0") + (ior (match_test "loongarch_global_symbol_p (op) != 0") + (match_test "loongarch_symbol_binds_local_p (op) == 0") + (match_test "loongarch_weak_symbol_p (op) != 0")) (match_test "CONSTANT_P (op)"))) ;; A legitimate CONST_INT operand that takes more than one instruction @@ -219,7 +217,19 @@ (define_predicate "move_operand" case CONST: case SYMBOL_REF: case LABEL_REF: - return (loongarch_symbolic_constant_p (op, &symbol_type)); + return (loongarch_symbolic_constant_p (op, &symbol_type) + && (!TARGET_EXPLICIT_RELOCS + || !loongarch_split_symbol_type (symbol_type))); + + case HIGH: + /* '-mno-explicit-relocs' don't generate high/low pairs. */ + if (!TARGET_EXPLICIT_RELOCS) + return false; + + op = XEXP (op, 0); + return (loongarch_symbolic_constant_p (op, &symbol_type) + && loongarch_split_symbol_type (symbol_type)); + default: return true; } -- 2.31.1