From patchwork Tue Jun 8 12:57:03 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Modra X-Patchwork-Id: 54969 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 1443AB7D57 for ; Tue, 8 Jun 2010 22:57:28 +1000 (EST) Received: (qmail 18951 invoked by alias); 8 Jun 2010 12:57:27 -0000 Received: (qmail 18940 invoked by uid 22791); 8 Jun 2010 12:57:22 -0000 X-SWARE-Spam-Status: No, hits=-0.0 required=5.0 tests=AWL, BAYES_50, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, T_TO_NO_BRKTS_FREEMAIL X-Spam-Check-By: sourceware.org Received: from mail-pv0-f175.google.com (HELO mail-pv0-f175.google.com) (74.125.83.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 08 Jun 2010 12:57:13 +0000 Received: by pvc21 with SMTP id 21so165830pvc.20 for ; Tue, 08 Jun 2010 05:57:11 -0700 (PDT) Received: by 10.115.85.40 with SMTP id n40mr1221923wal.229.1276001831421; Tue, 08 Jun 2010 05:57:11 -0700 (PDT) Received: from bubble.grove.modra.org ([115.187.252.19]) by mx.google.com with ESMTPS id n29sm50285519wae.16.2010.06.08.05.57.06 (version=TLSv1/SSLv3 cipher=RC4-MD5); Tue, 08 Jun 2010 05:57:10 -0700 (PDT) Received: by bubble.grove.modra.org (Postfix, from userid 1000) id 30BCF170C47B; Tue, 8 Jun 2010 22:27:03 +0930 (CST) Date: Tue, 8 Jun 2010 22:27:03 +0930 From: Alan Modra To: gcc-patches@gcc.gnu.org Cc: David Edelsohn Subject: PowerPC64 large toc model Message-ID: <20100608125703.GI7312@bubble.grove.modra.org> Mail-Followup-To: gcc-patches@gcc.gnu.org, David Edelsohn MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) X-IsSubscribed: yes 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 PowerPC64 gcc support for a larger TOC via -mcmodel option. -mcmodel=small uses 16-bit toc offsets, ie. what we had before. -mcmodel=medium uses 32-bit toc offsets and addresses static data, string constants etc. relative to the toc pointer. Limits data to around 2G. -mcmodel=large uses 32-bit toc offsets, no limit on static data. Default is -mcmodel=medium unless you specify other toc options. For example, -mminimal-toc gets you -mcmodel=small. Bootstrapped and regression tested powerpc64-linux. Comments follow associated changelog entry. * doc/invoke.texi: Add mcmodel to powerpc options. * configure.ac: Add HAVE_LD_LARGE_TOC test. * configure: Regenerate. * config.in: Regenerate. * config/rs6000/linux64.opt (mcmodel): New. * config/rs6000/linux64.h (TARGET_USES_LINUX64_OPT): Define. (TARGET_CMODEL): Define. (SUBSUBTARGET_OVERRIDE_OPTIONS): Check user -mcmodel choice, select CMODEL_MEDIUM default. * config/rs6000/rs6000.h (enum rs6000_cmodel): New. (TARGET_CMODEL): Define default. * config/rs6000/rs6000.c (cmodel): New variable. (rs6000_explicit_options): Add cmodel field. (rs6000_handle_option): Handle -mcmodel. (create_TOC_reference): Add bigtoc_reg param. Generate high, lo_sum rtl for CMODEL_MEDIUM and CMODEL_LARGE. Update all callers. (rs6000_delegitimize_address): Recognise new toc reference rtl and minimal-toc rtl. (rs6000_legitimize_reload_address): Handle new toc references. (print_operand_address): Handle legitimate_constant_pool_address_p match before lo_sum. (rs6000_eliminate_indexed_memrefs): Tidy. (rs6000_emit_move): Tweak threshold for inlining constants. Keep rs6000_emit_allocate_stack large stack frame offsets loaded into r0 inline. (rs6000_generate_compare ): One more clobber. Basic big toc support. (tocrel_base, tocrel_offset): New variables. (toc_relative_expr_p): Set them here. (print_operand_address): Skip over any offset on constant pool address. (rs6000_output_addr_const_extra): Print tocrel_offset before @toc. I'm not too proud of this hack but can't see a better way of rearranging the assembly. (rs6000_mode_dependent_address ): False for new toc refs. Necessary to support insns using "o" constraint. (offsettable_ok_by_alignment): New function. (rs6000_emit_move): Address suitably aligned local symbol_refs relative to the toc pointer for -mcmodel=medium. We can't support toc pointer relative addressing for non-local vars since ELF shared library semantics require such variables to be overridable by the main executable or other shared libs. That would mean text relocs. (legitimate_constant_pool_address_p): Add strict param. Allow lo_sum version of addressing. Verify reg used for -mminimal-toc and -mcmodel != small. Update all callers. * config/rs6000/constraints.md: Update for above change. * config/rs6000/predicates.md: Likewise. Without properly checking the reg I found testsuite failures due to reload not giving the high part address a hard reg. I'm not sure why this problem didn't trigger with -mminimal-toc.. .../gcc.c-torture/compile/20051216-1.c:131:1: error: insn does not satisfy its constraints: (insn 105 562 666 27 ... (set (reg/v:DF 60 28 [orig:208 share_x ] [208]) (mem/u/c/i:DF (lo_sum:DI (mem/c:DI (plus:DI (reg/f:DI 1 1) (const_int 208 [0xd0])) [0 %sfp+208 S8 A64]) (const:DI (unspec:DI [ (symbol_ref/u:DI ("*.LC2") [flags 0x2]) ] 49))) [0 S8 A64])) 360 {*movdf_hardfloat64} (expr_list:REG_EQUAL (const_double:DF 0.0 [0x0.0p+0]) (nil))) * config/rs6000/rs6000.md (tls_gd_aix): Generate -mcmodel=medium/large code. (tls_gd): Split for -mcmodel=medium/large. (tls_gd_high, tls_gd_low): New. (tls_ld_aix, tls_ld, tls_ld_high, tls_ld_low): Similarly. (tls_got_dtprel, tls_got_dtprel_high, tls_got_dtprel_low): Similarly. (tls_got_tprel, tls_got_tprel_high, tls_got_tprel_low): Similarly. (bigtoc_high, bigtoc_low): New. (cmptf_internal2): Add clobber. * config/rs6000/rs6000-protos.h: Update. Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (revision 160429) +++ gcc/doc/invoke.texi (working copy) @@ -744,6 +744,7 @@ See RS/6000 and PowerPC Options. @emph{RS/6000 and PowerPC Options} @gccoptlist{-mcpu=@var{cpu-type} @gol -mtune=@var{cpu-type} @gol +-mcmodel=@var{code-model} @gol -mpower -mno-power -mpower2 -mno-power2 @gol -mpowerpc -mpowerpc64 -mno-powerpc @gol -maltivec -mno-altivec @gol @@ -14976,6 +14977,22 @@ values for @var{cpu_type} are used for @ architecture, registers, and mnemonics set by @option{-mcpu}, but the scheduling parameters set by @option{-mtune}. +@item -mcmodel=small +@opindex mcmodel=small +Generate PowerPC64 code for the small model: The TOC is limited to +64k. + +@item -mcmodel=medium +@opindex mcmodel=medium +Generate PowerPC64 code for the medium model: The TOC and other static +data may be up to a total of 4G in size. + +@item -mcmodel=large +@opindex mcmodel=large +Generate PowerPC64 code for the large model: The TOC may be up to 4G +in size. Other data and code is only limited by the 64-bit address +space. + @item -maltivec @itemx -mno-altivec @opindex maltivec Index: gcc/configure.ac =================================================================== --- gcc/configure.ac (revision 160429) +++ gcc/configure.ac (working copy) @@ -3996,6 +3996,36 @@ EOF AC_DEFINE(HAVE_LD_NO_DOT_SYMS, 1, [Define if your PowerPC64 linker only needs function descriptor syms.]) fi + + AC_CACHE_CHECK(linker large toc support, + gcc_cv_ld_large_toc, + [gcc_cv_ld_large_toc=no + if test $in_tree_ld = yes ; then + if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 21 -o "$gcc_cv_gld_major_version" -gt 2; then + gcc_cv_ld_large_toc=yes + fi + elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then + cat > conftest.s < /dev/null 2>&1 \ + && $gcc_cv_ld -melf64ppc --no-toc-sort -o conftest conftest.o > /dev/null 2>&1; then + gcc_cv_ld_large_toc=yes + fi + rm -f conftest conftest.o conftest.s + fi + ]) + if test x"$gcc_cv_ld_large_toc" = xyes; then + AC_DEFINE(HAVE_LD_LARGE_TOC, 1, + [Define if your PowerPC64 linker supports a large TOC.]) + fi ;; esac Index: gcc/config/rs6000/linux64.opt =================================================================== --- gcc/config/rs6000/linux64.opt (revision 160429) +++ gcc/config/rs6000/linux64.opt (working copy) @@ -22,3 +22,7 @@ mprofile-kernel Target Report Var(profile_kernel) Call mcount for profiling before a function prologue + +mcmodel= +Target RejectNegative Joined +Select code model Index: gcc/config/rs6000/linux64.h =================================================================== --- gcc/config/rs6000/linux64.h (revision 160429) +++ gcc/config/rs6000/linux64.h (working copy) @@ -63,6 +63,16 @@ extern int dot_symbols; #define TARGET_PROFILE_KERNEL profile_kernel +#define TARGET_USES_LINUX64_OPT 1 +#ifdef HAVE_LD_LARGE_TOC +extern enum rs6000_cmodel cmodel; +#undef TARGET_CMODEL +#define TARGET_CMODEL cmodel +#define SET_CMODEL(opt) cmodel = opt +#else +#define SET_CMODEL(opt) +#endif + #undef PROCESSOR_DEFAULT #define PROCESSOR_DEFAULT PROCESSOR_POWER6 #undef PROCESSOR_DEFAULT64 @@ -114,6 +124,23 @@ extern int dot_symbols; target_flags |= MASK_POWERPC64; \ error ("-m64 requires a PowerPC64 cpu"); \ } \ + if ((target_flags_explicit & MASK_MINIMAL_TOC) != 0) \ + { \ + if (rs6000_explicit_options.cmodel \ + && cmodel != CMODEL_SMALL) \ + error ("-mcmodel incompatible with other toc options"); \ + SET_CMODEL (CMODEL_SMALL); \ + } \ + else \ + { \ + if (!rs6000_explicit_options.cmodel) \ + SET_CMODEL (CMODEL_MEDIUM); \ + if (cmodel != CMODEL_SMALL) \ + { \ + TARGET_NO_FP_IN_TOC = 0; \ + TARGET_NO_SUM_IN_TOC = 0; \ + } \ + } \ } \ else \ { \ @@ -124,6 +151,11 @@ extern int dot_symbols; TARGET_PROFILE_KERNEL = 0; \ error (INVALID_32BIT, "profile-kernel"); \ } \ + if (rs6000_explicit_options.cmodel) \ + { \ + SET_CMODEL (CMODEL_SMALL); \ + error (INVALID_32BIT, "cmodel"); \ + } \ } \ } \ while (0) Index: gcc/config/rs6000/rs6000.h =================================================================== --- gcc/config/rs6000/rs6000.h (revision 160429) +++ gcc/config/rs6000/rs6000.h (working copy) @@ -293,6 +293,20 @@ extern const char *host_detect_local_cpu #define TARGET_SECURE_PLT 0 #endif +/* Code model for 64-bit linux. + small: 16-bit toc offsets. + medium: 32-bit toc offsets, static data and code within 2G of TOC pointer. + large: 32-bit toc offsets, no limit on static data and code. */ +enum rs6000_cmodel { + CMODEL_SMALL, + CMODEL_MEDIUM, + CMODEL_LARGE +}; + +#ifndef TARGET_CMODEL +#define TARGET_CMODEL CMODEL_SMALL +#endif + #define TARGET_32BIT (! TARGET_64BIT) #ifndef HAVE_AS_TLS Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (revision 160429) +++ gcc/config/rs6000/rs6000.c (working copy) @@ -279,6 +279,9 @@ static GTY(()) section *toc_section; /* String from -malign-XXXXX. */ int rs6000_alignment_flags; +/* Code model for 64-bit linux. */ +enum rs6000_cmodel cmodel; + /* True for any options that were explicitly set. */ static struct { bool aix_struct_ret; /* True if -maix-struct-ret was used. */ @@ -290,6 +293,7 @@ static struct { bool long_double; /* True if -mlong-double- was used. */ bool ieee; /* True if -mabi=ieee/ibmlongdouble used. */ bool vrsave; /* True if -mvrsave was used. */ + bool cmodel; /* True if -mcmodel was used. */ } rs6000_explicit_options; struct builtin_description @@ -3645,6 +3649,22 @@ rs6000_handle_option (size_t code, const break; #endif +#if defined (HAVE_LD_LARGE_TOC) && defined (TARGET_USES_LINUX64_OPT) + case OPT_mcmodel_: + if (strcmp (arg, "small") == 0) + cmodel = CMODEL_SMALL; + else if (strcmp (arg, "medium") == 0) + cmodel = CMODEL_MEDIUM; + else if (strcmp (arg, "large") == 0) + cmodel = CMODEL_LARGE; + else + { + error ("invalid option for -mcmodel: '%s'", arg); + return false; + } + rs6000_explicit_options.cmodel = true; +#endif + #ifdef TARGET_USES_AIX64_OPT case OPT_maix64: #else @@ -5098,26 +5118,29 @@ constant_pool_expr_p (rtx op) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode)); } +static rtx tocrel_base, tocrel_offset; + bool toc_relative_expr_p (rtx op) { - rtx base, offset; - if (GET_CODE (op) != CONST) return false; - split_const (op, &base, &offset); - return (GET_CODE (base) == UNSPEC - && XINT (base, 1) == UNSPEC_TOCREL); + split_const (op, &tocrel_base, &tocrel_offset); + return (GET_CODE (tocrel_base) == UNSPEC + && XINT (tocrel_base, 1) == UNSPEC_TOCREL); } bool -legitimate_constant_pool_address_p (rtx x) +legitimate_constant_pool_address_p (rtx x, bool strict) { return (TARGET_TOC - && GET_CODE (x) == PLUS + && (GET_CODE (x) == PLUS || GET_CODE (x) == LO_SUM) && GET_CODE (XEXP (x, 0)) == REG - && (TARGET_MINIMAL_TOC || REGNO (XEXP (x, 0)) == TOC_REGISTER) + && (REGNO (XEXP (x, 0)) == TOC_REGISTER + || ((TARGET_MINIMAL_TOC + || TARGET_CMODEL != CMODEL_SMALL) + && INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))) && toc_relative_expr_p (XEXP (x, 1))); } @@ -5146,7 +5169,7 @@ rs6000_legitimate_offset_address_p (enum return false; if (!reg_offset_addressing_ok_p (mode)) return virtual_stack_registers_memory_p (x); - if (legitimate_constant_pool_address_p (x)) + if (legitimate_constant_pool_address_p (x, strict)) return true; if (GET_CODE (XEXP (x, 1)) != CONST_INT) return false; @@ -5494,7 +5517,8 @@ rs6000_legitimize_address (rtx x, rtx ol && constant_pool_expr_p (x) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode)) { - return create_TOC_reference (x); + rtx reg = TARGET_CMODEL != CMODEL_SMALL ? gen_reg_rtx (Pmode) : NULL_RTX; + return create_TOC_reference (x, reg); } else return x; @@ -5585,10 +5609,13 @@ rs6000_delegitimize_address (rtx orig_x) if (MEM_P (x)) x = XEXP (x, 0); - if (GET_CODE (x) == PLUS - && GET_CODE (XEXP (x, 1)) == CONST + if ((GET_CODE (x) == PLUS + || GET_CODE (x) == LO_SUM) && GET_CODE (XEXP (x, 0)) == REG - && REGNO (XEXP (x, 0)) == TOC_REGISTER) + && (REGNO (XEXP (x, 0)) == TOC_REGISTER + || TARGET_MINIMAL_TOC + || TARGET_CMODEL != CMODEL_SMALL) + && GET_CODE (XEXP (x, 1)) == CONST) { y = XEXP (XEXP (x, 1), 0); if (GET_CODE (y) == UNSPEC @@ -5600,7 +5627,6 @@ rs6000_delegitimize_address (rtx orig_x) else return replace_equiv_address_nv (orig_x, y); } - return orig_x; } if (TARGET_MACHO @@ -5896,6 +5922,24 @@ rs6000_legitimize_reload_address (rtx x, } #endif + if (TARGET_CMODEL != CMODEL_SMALL + && GET_CODE (x) == LO_SUM + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && REGNO (XEXP (XEXP (x, 0), 0)) == TOC_REGISTER + && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH + && GET_CODE (XEXP (x, 1)) == CONST + && GET_CODE (XEXP (XEXP (x, 1), 0)) == UNSPEC + && XINT (XEXP (XEXP (x, 1), 0), 1) == UNSPEC_TOCREL + && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 1), 0), XEXP (x, 1))) + { + push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, + opnum, (enum reload_type) type); + *win = 1; + return x; + } + /* Force ld/std non-word aligned offset into base register by wrapping in offset 0. */ if (GET_CODE (x) == PLUS @@ -6021,7 +6065,11 @@ rs6000_legitimize_reload_address (rtx x, && constant_pool_expr_p (x) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode)) { - x = create_TOC_reference (x); + x = create_TOC_reference (x, NULL_RTX); + if (TARGET_CMODEL != CMODEL_SMALL) + push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, + opnum, (enum reload_type) type); *win = 1; return x; } @@ -6104,7 +6152,7 @@ rs6000_legitimate_address_p (enum machin return 1; if (reg_offset_p && legitimate_small_data_p (mode, x)) return 1; - if (reg_offset_p && legitimate_constant_pool_address_p (x)) + if (reg_offset_p && legitimate_constant_pool_address_p (x, reg_ok_strict)) return 1; /* If not REG_OK_STRICT (before reload) let pass any stack offset. */ if (! reg_ok_strict @@ -6212,7 +6260,9 @@ rs6000_mode_dependent_address (const_rtx break; case LO_SUM: - return true; + /* Anything in the constant pool is sufficiently aligned that + all bytes have the same high part address. */ + return !legitimate_constant_pool_address_p (addr, false); /* Auto-increment cases are now treated generically in recog.c. */ case PRE_MODIFY: @@ -6568,23 +6618,54 @@ rs6000_emit_set_long_const (rtx dest, HO static void rs6000_eliminate_indexed_memrefs (rtx operands[2]) { + if (reload_in_progress) + return; + if (GET_CODE (operands[0]) == MEM && GET_CODE (XEXP (operands[0], 0)) != REG - && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0)) - && ! reload_in_progress) + && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0), false)) operands[0] = replace_equiv_address (operands[0], copy_addr_to_reg (XEXP (operands[0], 0))); if (GET_CODE (operands[1]) == MEM && GET_CODE (XEXP (operands[1], 0)) != REG - && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0)) - && ! reload_in_progress) + && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0), false)) operands[1] = replace_equiv_address (operands[1], copy_addr_to_reg (XEXP (operands[1], 0))); } +/* Return true if memory accesses to DECL are known to never straddle + a 32k boundary. */ + +static bool +offsettable_ok_by_alignment (tree decl) +{ + unsigned HOST_WIDE_INT dsize; + + /* Presume any compiler generated symbol_ref is suitably aligned. */ + if (!decl) + return true; + + if (TREE_CODE (decl) != VAR_DECL + && TREE_CODE (decl) != PARM_DECL + && TREE_CODE (decl) != RESULT_DECL + && TREE_CODE (decl) != FIELD_DECL) + return true; + + if (!host_integerp (DECL_SIZE_UNIT (decl), 1)) + return false; + + dsize = tree_low_cst (DECL_SIZE_UNIT (decl), 1); + if (dsize <= 1) + return true; + if (dsize > 32768) + return false; + + return DECL_ALIGN_UNIT (decl) >= dsize; +} + /* Emit a move from SOURCE to DEST in mode MODE. */ void rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode) @@ -6898,25 +6979,43 @@ rs6000_emit_move (rtx dest, rtx source, /* If this is a SYMBOL_REF that refers to a constant pool entry, and we have put it in the TOC, we just need to make a TOC-relative reference to it. */ - if (TARGET_TOC - && GET_CODE (operands[1]) == SYMBOL_REF - && constant_pool_expr_p (operands[1]) - && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]), - get_pool_mode (operands[1]))) + if ((TARGET_TOC + && GET_CODE (operands[1]) == SYMBOL_REF + && constant_pool_expr_p (operands[1]) + && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]), + get_pool_mode (operands[1]))) + || (TARGET_CMODEL == CMODEL_MEDIUM + && GET_CODE (operands[1]) == SYMBOL_REF + && !CONSTANT_POOL_ADDRESS_P (operands[1]) + && SYMBOL_REF_LOCAL_P (operands[1]) + && offsettable_ok_by_alignment (SYMBOL_REF_DECL (operands[1])))) { - operands[1] = create_TOC_reference (operands[1]); + rtx reg = NULL_RTX; + if (TARGET_CMODEL != CMODEL_SMALL) + { + if (can_create_pseudo_p ()) + reg = gen_reg_rtx (Pmode); + else + reg = operands[0]; + } + operands[1] = create_TOC_reference (operands[1], reg); } else if (mode == Pmode && CONSTANT_P (operands[1]) && ((GET_CODE (operands[1]) != CONST_INT && ! easy_fp_constant (operands[1], mode)) || (GET_CODE (operands[1]) == CONST_INT - && num_insns_constant (operands[1], mode) > 2) + && (num_insns_constant (operands[1], mode) + > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2))) || (GET_CODE (operands[0]) == REG && FP_REGNO_P (REGNO (operands[0])))) && GET_CODE (operands[1]) != HIGH - && ! legitimate_constant_pool_address_p (operands[1]) - && ! toc_relative_expr_p (operands[1])) + && ! legitimate_constant_pool_address_p (operands[1], false) + && ! toc_relative_expr_p (operands[1]) + && (TARGET_CMODEL == CMODEL_SMALL + || can_create_pseudo_p () + || (REG_P (operands[0]) + && INT_REG_OK_FOR_BASE_P (operands[0], true)))) { #if TARGET_MACHO @@ -6962,9 +7061,17 @@ rs6000_emit_move (rtx dest, rtx source, get_pool_constant (XEXP (operands[1], 0)), get_pool_mode (XEXP (operands[1], 0)))) { - operands[1] - = gen_const_mem (mode, - create_TOC_reference (XEXP (operands[1], 0))); + rtx tocref; + rtx reg = NULL_RTX; + if (TARGET_CMODEL != CMODEL_SMALL) + { + if (can_create_pseudo_p ()) + reg = gen_reg_rtx (Pmode); + else + reg = operands[0]; + } + tocref = create_TOC_reference (XEXP (operands[1], 0), reg); + operands[1] = gen_const_mem (mode, tocref); set_mem_alias_set (operands[1], get_TOC_alias_set ()); } } @@ -15328,14 +15435,6 @@ print_operand_address (FILE *file, rtx x else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)", INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]); -#if TARGET_ELF - else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG - && CONSTANT_P (XEXP (x, 1))) - { - output_addr_const (file, XEXP (x, 1)); - fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); - } -#endif #if TARGET_MACHO else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG && CONSTANT_P (XEXP (x, 1))) @@ -15345,11 +15444,29 @@ print_operand_address (FILE *file, rtx x fprintf (file, ")(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); } #endif - else if (legitimate_constant_pool_address_p (x)) + else if (legitimate_constant_pool_address_p (x, true)) + { + /* This hack along with a corresponding hack in + rs6000_output_addr_const_extra arranges to output addends + where the assembler expects to find them. eg. + (lo_sum (reg 9) + . (const (plus (unspec [symbol_ref ("x") tocrel]) 8))) + without this hack would be output as "x@toc+8@l(9)". We + want "x+8@toc@l(9)". */ + output_addr_const (file, tocrel_base); + if (GET_CODE (x) == LO_SUM) + fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); + else + fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); + } +#if TARGET_ELF + else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG + && CONSTANT_P (XEXP (x, 1))) { output_addr_const (file, XEXP (x, 1)); - fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); + fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); } +#endif else gcc_unreachable (); } @@ -15363,9 +15480,14 @@ rs6000_output_addr_const_extra (FILE *fi switch (XINT (x, 1)) { case UNSPEC_TOCREL: - x = XVECEXP (x, 0, 0); - gcc_assert (GET_CODE (x) == SYMBOL_REF); - output_addr_const (file, x); + gcc_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF); + output_addr_const (file, XVECEXP (x, 0, 0)); + if (x == tocrel_base && tocrel_offset != const0_rtx) + { + if (INTVAL (tocrel_offset) >= 0) + fprintf (file, "+"); + output_addr_const (file, tocrel_offset); + } if (!TARGET_AIX || (TARGET_ELF && TARGET_MINIMAL_TOC)) { putc ('-', file); @@ -15689,7 +15811,7 @@ rs6000_generate_compare (rtx cmp, enum m && !TARGET_IEEEQUAD && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128) emit_insn (gen_rtx_PARALLEL (VOIDmode, - gen_rtvec (9, + gen_rtvec (10, gen_rtx_SET (VOIDmode, compare_result, gen_rtx_COMPARE (comp_mode, op0, op1)), @@ -15700,7 +15822,8 @@ rs6000_generate_compare (rtx cmp, enum m gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), - gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode))))); + gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), + gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (Pmode))))); else if (GET_CODE (op1) == UNSPEC && XINT (op1, 1) == UNSPEC_SP_TEST) { @@ -18259,8 +18382,10 @@ uses_TOC (void) #endif rtx -create_TOC_reference (rtx symbol) +create_TOC_reference (rtx symbol, rtx bigtoc_reg) { + rtx tocrel, tocreg; + if (TARGET_DEBUG_ADDR) { if (GET_CODE (symbol) == SYMBOL_REF) @@ -18276,10 +18401,23 @@ create_TOC_reference (rtx symbol) if (!can_create_pseudo_p ()) df_set_regs_ever_live (TOC_REGISTER, true); - return gen_rtx_PLUS (Pmode, - gen_rtx_REG (Pmode, TOC_REGISTER), - gen_rtx_CONST (Pmode, - gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol), UNSPEC_TOCREL))); + + tocrel = gen_rtx_CONST (Pmode, + gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol), + UNSPEC_TOCREL)); + tocreg = gen_rtx_REG (Pmode, TOC_REGISTER); + if (TARGET_CMODEL != CMODEL_SMALL) + { + rtx hi = gen_rtx_PLUS (Pmode, tocreg, gen_rtx_HIGH (Pmode, tocrel)); + if (bigtoc_reg != NULL) + { + emit_move_insn (bigtoc_reg, hi); + hi = bigtoc_reg; + } + return gen_rtx_LO_SUM (Pmode, hi, copy_rtx (tocrel)); + } + else + return gen_rtx_PLUS (Pmode, tocreg, tocrel); } /* Issue assembly directives that create a reference to the given DWARF Index: gcc/config/rs6000/constraints.md =================================================================== --- gcc/config/rs6000/constraints.md (revision 160429) +++ gcc/config/rs6000/constraints.md (working copy) @@ -166,7 +166,7 @@ (define_address_constraint "a" (define_constraint "R" "AIX TOC entry" - (match_test "legitimate_constant_pool_address_p (op)")) + (match_test "legitimate_constant_pool_address_p (op, false)")) ;; General constraints Index: gcc/config/rs6000/predicates.md =================================================================== --- gcc/config/rs6000/predicates.md (revision 160429) +++ gcc/config/rs6000/predicates.md (working copy) @@ -837,7 +837,7 @@ (define_predicate "input_operand" return 1; /* A SYMBOL_REF referring to the TOC is valid. */ - if (legitimate_constant_pool_address_p (op)) + if (legitimate_constant_pool_address_p (op, false)) return 1; /* A constant pool expression (relative to the TOC) is valid */ Index: gcc/config/rs6000/rs6000.md =================================================================== --- gcc/config/rs6000/rs6000.md (revision 160429) +++ gcc/config/rs6000/rs6000.md (working copy) @@ -11003,7 +11003,12 @@ (define_insn_and_split "tls_gd_aix" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") @@ -11052,13 +11060,47 @@ (define_insn_and_split "tls_gd_sysv" +(define_insn_and_split "*tls_gd" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] UNSPEC_TLSGD))] "HAVE_AS_TLS && TARGET_TLS_MARKERS" "addi %0,%1,%2@got@tlsgd" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (plus:TLSmode (match_dup 1) + (high:TLSmode + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_gd_high" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD))))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@tlsgd@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_gd_low" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addi %0,%1,%2@got@tlsgd@l" [(set_attr "length" "4")]) (define_insn "*tls_gd_call_aix" @@ -11101,7 +11143,12 @@ (define_insn_and_split "tls_ld_aix" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") @@ -11145,12 +11196,44 @@ (define_insn_and_split "tls_ld_sysv" +(define_insn_and_split "*tls_ld" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")] UNSPEC_TLSLD))] "HAVE_AS_TLS && TARGET_TLS_MARKERS" "addi %0,%1,%&@got@tlsld" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 2) + (plus:TLSmode (match_dup 1) + (high:TLSmode + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 2) + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))] + " +{ + operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_ld_high" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (high:TLSmode + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD))))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%&@got@tlsld@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_ld_low" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addi %0,%1,%&@got@tlsld@l" [(set_attr "length" "4")]) (define_insn "*tls_ld_call_aix" @@ -11207,13 +11290,48 @@ (define_insn "tls_dtprel_lo_" +(define_insn_and_split "tls_got_dtprel_" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] UNSPEC_TLSGOTDTPREL))] "HAVE_AS_TLS" - "l %0,%2@got@dtprel(%1)") + "l %0,%2@got@dtprel(%1)" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (plus:TLSmode (match_dup 1) + (high:TLSmode + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_got_dtprel_high" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTDTPREL))))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@dtprel@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_got_dtprel_low" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTDTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "l %0,%2@got@dtprel@l(%1)" + [(set_attr "length" "4")]) (define_insn "tls_tprel_" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") @@ -11242,13 +11360,48 @@ (define_insn "tls_tprel_lo_" +(define_insn_and_split "tls_got_tprel_" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] UNSPEC_TLSGOTTPREL))] "HAVE_AS_TLS" - "l %0,%2@got@tprel(%1)") + "l %0,%2@got@tprel(%1)" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (plus:TLSmode (match_dup 1) + (high:TLSmode + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_got_tprel_high" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTTPREL))))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@tprel@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_got_tprel_low" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "l %0,%2@got@tprel@l(%1)" + [(set_attr "length" "4")]) (define_insn "tls_tls_" [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") @@ -11257,7 +11410,6 @@ (define_insn "tls_tls_