From patchwork Wed Oct 3 21:21:39 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleg Endo X-Patchwork-Id: 188931 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 253772C0085 for ; Thu, 4 Oct 2012 07:22:26 +1000 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1349904147; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Message-ID:Subject:From:To:Date:Content-Type:Mime-Version: Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:Sender:Delivered-To; bh=6Bzvb4GpzlhlvyraErne fkedqIE=; b=kJTd3y/UPB63fy0fT14GNQgDVT+DVEvJPbQFa3+Zm6B1X/cQTGGf YY/3Clu9F4H4rD8X3kstHbED8tjEg4Zy6Scn1RMgSk5lBHsZONuVuQWdpb4ijGu8 aHJJFY79S6EadTvQPpIQVN8i3ybnlAKG5QUY8PXZnng7HFenIkRqXtI= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:Received:Message-ID:Subject:From:To:Date:Content-Type:Mime-Version:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=D3jRAW8pp8z14/jb84tfcVSTmRIE4rj9cQN/tcfMbZMuLo4f119BH1vk4rVDrM R64KpmBsyZ7JjUBKHN54AZ54dW0u+80PhIZ46XI+nfsagaQgKeWL6qCll84bYvbw RCjDqSC9KnoQm4himRu2h4dR1zha6t2jROFCYBFB1Dd0E=; Received: (qmail 7347 invoked by alias); 3 Oct 2012 21:22:16 -0000 Received: (qmail 7283 invoked by uid 22791); 3 Oct 2012 21:22:09 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_NONE, RCVD_IN_HOSTKARMA_NO, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY X-Spam-Check-By: sourceware.org Received: from mailout07.t-online.de (HELO mailout07.t-online.de) (194.25.134.83) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 03 Oct 2012 21:21:55 +0000 Received: from fwd09.aul.t-online.de (fwd09.aul.t-online.de ) by mailout07.t-online.de with smtp id 1TJWO8-0000WB-Tc; Wed, 03 Oct 2012 23:21:52 +0200 Received: from [192.168.0.100] (JlSA9GZa8hmlJFu12LOWhuM6Z7Ts5th-6pVPy2T4Hwo7bBbPJUgZ2lQhWQd0Hi6g2N@[87.157.32.59]) by fwd09.t-online.de with esmtp id 1TJWNx-1R8awK0; Wed, 3 Oct 2012 23:21:41 +0200 Message-ID: <1349299299.9306.124.camel@yam-132-YW-E178-FTW> Subject: [SH] PR 54760 - Add thread pointer built-ins and GBR displacement addressing From: Oleg Endo To: gcc-patches Date: Wed, 03 Oct 2012 23:21:39 +0200 Mime-Version: 1.0 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 Hello, This adds the two common built-in functions __builtin_thread_pointer and __builtin_set_thread_pointer to the SH port. I've done it in a way so that hopefully it can be transitioned to target independent thread pointer built-ins easily, as suggested by Richard a while ago: http://gcc.gnu.org/ml/gcc-patches/2012-07/msg00946.html Originally I wanted to wait until the target independent bits are in, but somehow the thread mentioned above died and I got impatient. I've also added support for SH's GBR based displacement addressing modes. They are not used for general purpose mem loads/stores by the compiler, but rather when code accesses data behind the thread pointer (thread control block or something). The way GBR displacement address opportunities are discovered might not be the best way of doing this sort of thing, but it works. The insn walking could potentially slow down compile times, but it is only enabled for functions where the GBR is referenced, so it shouldn't be so bad. Alternatives and suggestions are highly appreciated. :) Tested on rev 191894 with 'make all' (c,c++) and 'make -k check-gcc RUNTESTFLAGS="sh.exp --target_board=sh-sim \{-m2/-ml,-m2/-mb,-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}"' For code that doesn't reference GBR there are no functional changes. TLS code that references GBR might trigger the 'sh_find_equiv_gbr_addr' function. Unfortunately TLS tests don't seem to work on sh-sim, so I could not test this part. Cheers, Oleg gcc/ChangeLog: PR target/54760 * config/sh/sh.md (define_constants): Add UNSPECV_GBR. (get_thread_pointer, set_thread_pointer): New expanders. (load_gbr): Rename to store_gbr. Remove GBR_REG use. (store_gbr): New insn. (*mov_gbr_load, *mov_gbr_store): New insns and accompanying unnamed splits. * config/sh/predicates.md (general_movsrc_operand, general_movdst_operand): Reject GBR addresses. * config/sh/sh-protos.h (sh_find_equiv_gbr_addr): New declaration. * config/sh/sh.c (prepare_move_operands): Use gen_store_gbr instead of gen_load_gbr in TLS_MODEL_LOCAL_EXEC case. (sh_address_cost, sh_legitimate_address_p, sh_secondary_reload): Handle GBR addresses. (builtin_description): Add is_enabled member. (shmedia_builtin, sh1_builtin): New functions. (signature_args): Add SH_BLTIN_VP. (bdesc): Use shmedia_builtin for existing built-ins. Add __builtin_thread_pointer and __builtin_set_thread_pointer as sh1_builtin. (sh_media_init_builtins, sh_init_builtins): Merge into single function sh_init_builtins. Add is_enabled checking. (sh_media_builtin_decl, sh_builtin_decl): Merge into single function sh_builtin_decl. Add is_enabled checking. (base_reg_disp): New class. (sh_find_base_reg_disp, sh_find_equiv_gbr_addr): New functions. testsuite/ChangeLog: PR target/54760 * gcc.target/sh/pr54706-1.c: New. * gcc.target/sh/pr54706-2.c: New. * gcc.target/sh/pr54706-3.c: New. Index: gcc/config/sh/sh.md =================================================================== --- gcc/config/sh/sh.md (revision 191894) +++ gcc/config/sh/sh.md (working copy) @@ -175,6 +175,7 @@ (UNSPECV_WINDOW_END 10) (UNSPECV_CONST_END 11) (UNSPECV_EH_RETURN 12) + (UNSPECV_GBR 13) ]) ;; ------------------------------------------------------------------------- @@ -10029,13 +10030,165 @@ DONE; }) -(define_insn "load_gbr" - [(set (match_operand:SI 0 "register_operand" "=r") (reg:SI GBR_REG)) - (use (reg:SI GBR_REG))] +;;------------------------------------------------------------------------------ +;; Thread pointer getter and setter. +;; +;; On SH the thread pointer is kept in the GBR. +;; These patterns are usually expanded from the respective built-in functions. +(define_expand "get_thread_pointer" + [(set (match_operand:SI 0 "register_operand") (reg:SI GBR_REG))] + "TARGET_SH1") + +(define_insn "store_gbr" + [(set (match_operand:SI 0 "register_operand" "=r") (reg:SI GBR_REG))] "" "stc gbr,%0" [(set_attr "type" "tls_load")]) +(define_expand "set_thread_pointer" + [(set (reg:SI GBR_REG) + (unspec_volatile:SI [(match_operand:SI 0 "register_operand")] + UNSPECV_GBR))] + "TARGET_SH1") + +(define_insn "load_gbr" + [(set (reg:SI GBR_REG) + (unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPECV_GBR))] + "TARGET_SH1" + "ldc %0,gbr" + [(set_attr "type" "move")]) + +;;------------------------------------------------------------------------------ +;; Thread pointer relative memory loads and stores. +;; +;; On SH there are GBR displacement address modes which can be utilized to +;; access memory behind the thread pointer. +;; Since we do not allow using GBR for general purpose memory accesses, these +;; GBR addressing modes are formed by the combine pass. +;; This could be done with fewer patterns than below by using a mem predicate +;; for the GBR mem, but then reload would try to reload addresses with a +;; zero displacement for some strange reason. + +(define_insn "*mov_gbr_load" + [(set (match_operand:QIHISI 0 "register_operand" "=z") + (mem:QIHISI (plus:SI (reg:SI GBR_REG) + (match_operand:QIHISI 1 "gbr_displacement"))))] + "TARGET_SH1" + "mov. @(%O1,gbr),%0" + [(set_attr "type" "load")]) + +(define_insn "*mov_gbr_load" + [(set (match_operand:QIHISI 0 "register_operand" "=z") + (mem:QIHISI (reg:SI GBR_REG)))] + "TARGET_SH1" + "mov. @(0,gbr),%0" + [(set_attr "type" "load")]) + +(define_insn "*mov_gbr_load" + [(set (match_operand:SI 0 "register_operand" "=z") + (sign_extend:SI + (mem:QIHI (plus:SI (reg:SI GBR_REG) + (match_operand:QIHI 1 "gbr_displacement")))))] + "TARGET_SH1" + "mov. @(%O1,gbr),%0" + [(set_attr "type" "load")]) + +(define_insn "*mov_gbr_load" + [(set (match_operand:SI 0 "register_operand" "=z") + (sign_extend:SI (mem:QIHI (reg:SI GBR_REG))))] + "TARGET_SH1" + "mov. @(0,gbr),%0" + [(set_attr "type" "load")]) + +(define_insn "*mov_gbr_store" + [(set (mem:QIHISI (plus:SI (reg:SI GBR_REG) + (match_operand:QIHISI 0 "gbr_displacement"))) + (match_operand:QIHISI 1 "register_operand" "z"))] + "TARGET_SH1" + "mov. %1,@(%O0,gbr)" + [(set_attr "type" "store")]) + +(define_insn "*mov_gbr_store" + [(set (mem:QIHISI (reg:SI GBR_REG)) + (match_operand:QIHISI 0 "register_operand" "z"))] + "TARGET_SH1" + "mov. %0,@(0,gbr)" + [(set_attr "type" "store")]) + +;; Sometimes memory accesses do not get combined with the store_gbr insn, +;; in particular when the displacements are in the range of the regular move +;; insns. Thus, in the first split pass after the combine pass we search +;; for missed opportunities and try to fix them up ourselves. +;; If an equivalent GBR address can be determined the load / store is split +;; into one of the GBR load / store patterns. +;; All of that must happen before reload (GBR address modes use R0 as the +;; other operand) and there's no point of doing it if the GBR is not +;; referenced in a function at all. +(define_split + [(set (match_operand:QIHISI 0 "register_operand") + (match_operand:QIHISI 1 "memory_operand"))] + "TARGET_SH1 && !reload_in_progress && !reload_completed + && df_regs_ever_live_p (GBR_REG)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]); + if (gbr_mem != NULL_RTX) + operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem); + else + FAIL; +}) + +(define_split + [(set (match_operand:SI 0 "register_operand") + (sign_extend:SI (match_operand:QIHI 1 "memory_operand")))] + "TARGET_SH1 && !reload_in_progress && !reload_completed + && df_regs_ever_live_p (GBR_REG)" + [(set (match_dup 0) (sign_extend:SI (match_dup 1)))] +{ + rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]); + if (gbr_mem != NULL_RTX) + operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem); + else + FAIL; +}) + +;; On SH2A we've got movu.b and movu.w for doing zero-extending mem loads. +;; Split those so that a GBR load can be used. +(define_split + [(set (match_operand:SI 0 "register_operand") + (zero_extend:SI (match_operand:QIHI 1 "memory_operand")))] + "TARGET_SH2A && !reload_in_progress && !reload_completed + && df_regs_ever_live_p (GBR_REG)" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (zero_extend:SI (match_dup 2)))] +{ + rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]); + if (gbr_mem != NULL_RTX) + { + operands[2] = gen_reg_rtx (GET_MODE (operands[1])); + operands[1] = change_address (operands[1], GET_MODE (operands[1]), + gbr_mem); + } + else + FAIL; +}) + +(define_split + [(set (match_operand:QIHISI 0 "memory_operand") + (match_operand:QIHISI 1 "register_operand"))] + "TARGET_SH1 && !reload_in_progress && !reload_completed + && df_regs_ever_live_p (GBR_REG)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[0]); + if (gbr_mem != NULL_RTX) + operands[0] = change_address (operands[0], GET_MODE (operands[0]), gbr_mem); + else + FAIL; +}) + +;;------------------------------------------------------------------------------ ;; case instruction for switch statements. ;; Operand 0 is index Index: gcc/config/sh/predicates.md =================================================================== --- gcc/config/sh/predicates.md (revision 191899) +++ gcc/config/sh/predicates.md (working copy) @@ -409,6 +409,14 @@ if (MEM_P (op)) { rtx inside = XEXP (op, 0); + + /* Disallow mems with GBR address here. They have to go through + separate special patterns. */ + if ((REG_P (inside) && REGNO (inside) == GBR_REG) + || (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0)) + && REGNO (XEXP (inside, 0)) == GBR_REG)) + return 0; + if (GET_CODE (inside) == CONST) inside = XEXP (inside, 0); @@ -466,6 +474,17 @@ if (t_reg_operand (op, mode)) return 0; + if (MEM_P (op)) + { + rtx inside = XEXP (op, 0); + /* Disallow mems with GBR address here. They have to go through + separate special patterns. */ + if ((REG_P (inside) && REGNO (inside) == GBR_REG) + || (GET_CODE (inside) == PLUS && REG_P (XEXP (inside, 0)) + && REGNO (XEXP (inside, 0)) == GBR_REG)) + return 0; + } + /* Only pre dec allowed. */ if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == POST_INC) return 0; Index: gcc/config/sh/sh-protos.h =================================================================== --- gcc/config/sh/sh-protos.h (revision 191899) +++ gcc/config/sh/sh-protos.h (working copy) @@ -153,7 +153,7 @@ extern bool sh_cfun_trap_exit_p (void); extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&, enum machine_mode mode = VOIDmode); - +extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem); #endif /* RTX_CODE */ extern const char *output_jump_label_table (void); Index: gcc/config/sh/sh.c =================================================================== --- gcc/config/sh/sh.c (revision 191899) +++ gcc/config/sh/sh.c (working copy) @@ -243,8 +243,6 @@ static void sh_init_builtins (void); static tree sh_builtin_decl (unsigned, bool); -static void sh_media_init_builtins (void); -static tree sh_media_builtin_decl (unsigned, bool); static rtx sh_expand_builtin (tree, rtx, rtx, enum machine_mode, int); static void sh_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static void sh_file_start (void); @@ -1880,7 +1878,7 @@ case TLS_MODEL_LOCAL_EXEC: tmp2 = gen_reg_rtx (Pmode); - emit_insn (gen_load_gbr (tmp2)); + emit_insn (gen_store_gbr (tmp2)); tmp = gen_reg_rtx (Pmode); emit_insn (gen_symTPOFF2reg (tmp, op1)); @@ -3603,6 +3601,10 @@ sh_address_cost (rtx x, enum machine_mode mode, addr_space_t as ATTRIBUTE_UNUSED, bool speed ATTRIBUTE_UNUSED) { + /* 'GBR + 0'. Account one more because of R0 restriction. */ + if (REG_P (x) && REGNO (x) == GBR_REG) + return 2; + /* Simple reg, post-inc, pre-dec addressing. */ if (REG_P (x) || GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC) return 1; @@ -3611,6 +3613,11 @@ if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1))) { + /* 'GBR + disp'. Account one more because of R0 restriction. */ + if (REGNO (XEXP (x, 0)) == GBR_REG + && gbr_displacement (XEXP (x, 1), mode)) + return 2; + const HOST_WIDE_INT offset = INTVAL (XEXP (x, 1)); if (offset == 0) @@ -10178,11 +10185,16 @@ REG+disp REG+r0 REG++ - --REG */ + --REG + GBR + GBR+disp */ static bool sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) { + if (REG_P (x) && REGNO (x) == GBR_REG) + return true; + if (MAYBE_BASE_REGISTER_RTX_P (x, strict)) return true; else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC) @@ -10195,6 +10207,9 @@ rtx xop0 = XEXP (x, 0); rtx xop1 = XEXP (x, 1); + if (REG_P (xop0) && REGNO (xop0) == GBR_REG) + return gbr_displacement (xop1, mode); + if (GET_MODE_SIZE (mode) <= 8 && MAYBE_BASE_REGISTER_RTX_P (xop0, strict) && sh_legitimate_index_p (mode, xop1, TARGET_SH2A, false)) @@ -11501,12 +11516,25 @@ struct builtin_description { + bool (* const is_enabled) (void); const enum insn_code icode; const char *const name; int signature; tree fndecl; }; +static bool +shmedia_builtin (void) +{ + return TARGET_SHMEDIA; +} + +static bool +sh1_builtin (void) +{ + return TARGET_SH1; +} + /* describe number and signedness of arguments; arg[0] == result (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */ /* 9: 64-bit pointer, 10: 32-bit pointer */ @@ -11564,6 +11592,8 @@ { 1, 1, 1, 1 }, #define SH_BLTIN_PV 23 { 0, 8 }, +#define SH_BLTIN_VP 24 + { 8, 0 } }; /* mcmv: operands considered unsigned. */ /* mmulsum_wq, msad_ubq: result considered unsigned long long. */ @@ -11573,103 +11603,195 @@ /* nsb: takes long long arg, returns unsigned char. */ static struct builtin_description bdesc[] = { - { CODE_FOR_absv2si2, "__builtin_absv2si2", SH_BLTIN_V2SI2, 0 }, - { CODE_FOR_absv4hi2, "__builtin_absv4hi2", SH_BLTIN_V4HI2, 0 }, - { CODE_FOR_addv2si3, "__builtin_addv2si3", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_addv4hi3, "__builtin_addv4hi3", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_alloco_i, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV, 0 }, - { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_mcmv, "__builtin_sh_media_MCMV", SH_BLTIN_UUUU, 0 }, - { CODE_FOR_mcnvs_lw, "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3, 0 }, - { CODE_FOR_mcnvs_wb, "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI, 0 }, - { CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI, 0 }, - { CODE_FOR_mextr1, "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr2, "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr3, "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr4, "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr5, "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr6, "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mextr7, "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI, 0 }, - { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI, 0 }, - { CODE_FOR_mulv2si3, "__builtin_mulv2si3", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_mulv4hi3, "__builtin_mulv4hi3", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_mmulfx_l, "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_mmulfx_w, "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI, 0 }, - { CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI, 0 }, - { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU, 0 }, - { CODE_FOR_mperm_w, "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI, 0 }, - { CODE_FOR_msad_ubq, "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU, 0 }, - { CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI, 0 }, - { CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI, 0 }, - { CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI, 0 }, - { CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI, 0 }, - { CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS, 0 }, - { CODE_FOR_mshfhi_b, "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mshfhi_l, "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_mshfhi_w, "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_mshflo_b, "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_mshflo_l, "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_mshflo_w, "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI, 0 }, - { CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI, 0 }, - { CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI, 0 }, - { CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI, 0 }, - { CODE_FOR_subv2si3, "__builtin_subv2si3", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_subv4hi3, "__builtin_subv4hi3", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3, 0 }, - { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3, 0 }, - { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3, 0 }, - { CODE_FOR_fcosa_s, "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF, 0 }, - { CODE_FOR_fsina_s, "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF, 0 }, - { CODE_FOR_fipr, "__builtin_sh_media_FIPR_S", SH_BLTIN_3, 0 }, - { CODE_FOR_ftrv, "__builtin_sh_media_FTRV_S", SH_BLTIN_3, 0 }, - { CODE_FOR_sqrtdf2, "__builtin_sh_media_FSQRT_D", SH_BLTIN_2, 0 }, - { CODE_FOR_sqrtsf2, "__builtin_sh_media_FSQRT_S", SH_BLTIN_2, 0 }, - { CODE_FOR_fsrra_s, "__builtin_sh_media_FSRRA_S", SH_BLTIN_2, 0 }, - { CODE_FOR_ldhi_l, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L, 0 }, - { CODE_FOR_ldhi_q, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q, 0 }, - { CODE_FOR_ldlo_l, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L, 0 }, - { CODE_FOR_ldlo_q, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q, 0 }, - { CODE_FOR_sthi_l, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L, 0 }, - { CODE_FOR_sthi_q, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q, 0 }, - { CODE_FOR_stlo_l, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L, 0 }, - { CODE_FOR_stlo_q, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q, 0 }, - { CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64, 0 }, - { CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64, 0 }, - { CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64, 0 }, - { CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64, 0 }, - { CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64, 0 }, - { CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64, 0 }, - { CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64, 0 }, - { CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64, 0 }, - { CODE_FOR_nsb, "__builtin_sh_media_NSB", SH_BLTIN_SU, 0 }, - { CODE_FOR_byterev, "__builtin_sh_media_BYTEREV", SH_BLTIN_2, 0 }, - { CODE_FOR_prefetch, "__builtin_sh_media_PREFO", SH_BLTIN_PSSV, 0 }, + { shmedia_builtin, + CODE_FOR_absv2si2, "__builtin_absv2si2", SH_BLTIN_V2SI2, 0 }, + { shmedia_builtin, + CODE_FOR_absv4hi2, "__builtin_absv4hi2", SH_BLTIN_V4HI2, 0 }, + { shmedia_builtin, + CODE_FOR_addv2si3, "__builtin_addv2si3", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_addv4hi3, "__builtin_addv4hi3", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_alloco_i, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_mcmv, "__builtin_sh_media_MCMV", SH_BLTIN_UUUU, 0 }, + { shmedia_builtin, + CODE_FOR_mcnvs_lw, "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3, 0 }, + { shmedia_builtin, + CODE_FOR_mcnvs_wb, "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI, 0 }, + { shmedia_builtin, + CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI, 0 }, + { shmedia_builtin, + CODE_FOR_mextr1, "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr2, "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr3, "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr4, "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr5, "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr6, "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mextr7, "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI, 0 }, + { shmedia_builtin, + CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI, 0 }, + { shmedia_builtin, + CODE_FOR_mulv2si3, "__builtin_mulv2si3", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_mulv4hi3, "__builtin_mulv4hi3", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_mmulfx_l, "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_mmulfx_w, "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI, 0 }, + { shmedia_builtin, + CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI, 0 }, + { shmedia_builtin, + CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU, 0 }, + { shmedia_builtin, + CODE_FOR_mperm_w, "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI, 0 }, + { shmedia_builtin, + CODE_FOR_msad_ubq, "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU, 0 }, + { shmedia_builtin, + CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI, 0 }, + { shmedia_builtin, + CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI, 0 }, + { shmedia_builtin, + CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI, 0 }, + { shmedia_builtin, + CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI, 0 }, + { shmedia_builtin, + CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS, 0 }, + { shmedia_builtin, + CODE_FOR_mshfhi_b, "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mshfhi_l, "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_mshfhi_w, "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_mshflo_b, "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_mshflo_l, "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_mshflo_w, "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI, 0 }, + { shmedia_builtin, + CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI, 0 }, + { shmedia_builtin, + CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI, 0 }, + { shmedia_builtin, + CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI, 0 }, + { shmedia_builtin, + CODE_FOR_subv2si3, "__builtin_subv2si3", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_subv4hi3, "__builtin_subv4hi3", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3, 0 }, + { shmedia_builtin, + CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3, 0 }, + { shmedia_builtin, + CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3, 0 }, + { shmedia_builtin, + CODE_FOR_fcosa_s, "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF, 0 }, + { shmedia_builtin, + CODE_FOR_fsina_s, "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF, 0 }, + { shmedia_builtin, + CODE_FOR_fipr, "__builtin_sh_media_FIPR_S", SH_BLTIN_3, 0 }, + { shmedia_builtin, + CODE_FOR_ftrv, "__builtin_sh_media_FTRV_S", SH_BLTIN_3, 0 }, + { shmedia_builtin, + CODE_FOR_sqrtdf2, "__builtin_sh_media_FSQRT_D", SH_BLTIN_2, 0 }, + { shmedia_builtin, + CODE_FOR_sqrtsf2, "__builtin_sh_media_FSQRT_S", SH_BLTIN_2, 0 }, + { shmedia_builtin, + CODE_FOR_fsrra_s, "__builtin_sh_media_FSRRA_S", SH_BLTIN_2, 0 }, + { shmedia_builtin, + CODE_FOR_ldhi_l, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L, 0 }, + { shmedia_builtin, + CODE_FOR_ldhi_q, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q, 0 }, + { shmedia_builtin, + CODE_FOR_ldlo_l, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L, 0 }, + { shmedia_builtin, + CODE_FOR_ldlo_q, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q, 0 }, + { shmedia_builtin, + CODE_FOR_sthi_l, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L, 0 }, + { shmedia_builtin, + CODE_FOR_sthi_q, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q, 0 }, + { shmedia_builtin, + CODE_FOR_stlo_l, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L, 0 }, + { shmedia_builtin, + CODE_FOR_stlo_q, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q, 0 }, + { shmedia_builtin, + CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64, 0 }, + { shmedia_builtin, + CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64, 0 }, + { shmedia_builtin, + CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64, 0 }, + { shmedia_builtin, + CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64, 0 }, + { shmedia_builtin, + CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64, 0 }, + { shmedia_builtin, + CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64, 0 }, + { shmedia_builtin, + CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64, 0 }, + { shmedia_builtin, + CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64, 0 }, + { shmedia_builtin, + CODE_FOR_nsb, "__builtin_sh_media_NSB", SH_BLTIN_SU, 0 }, + { shmedia_builtin, + CODE_FOR_byterev, "__builtin_sh_media_BYTEREV", SH_BLTIN_2, 0 }, + { shmedia_builtin, + CODE_FOR_prefetch, "__builtin_sh_media_PREFO", SH_BLTIN_PSSV, 0 }, + + { sh1_builtin, + CODE_FOR_get_thread_pointer, "__builtin_thread_pointer", SH_BLTIN_VP, 0 }, + { sh1_builtin, + CODE_FOR_set_thread_pointer, "__builtin_set_thread_pointer", + SH_BLTIN_PV, 0 }, }; static void -sh_media_init_builtins (void) +sh_init_builtins (void) { tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES]; - struct builtin_description *d; + memset (shared, 0, sizeof shared); - memset (shared, 0, sizeof shared); - for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++) + for (unsigned int di = 0; di < ARRAY_SIZE (bdesc); ++di) { - tree type, arg_type = 0; + builtin_description* d = &bdesc[di]; + + if (!d->is_enabled ()) + continue; + + tree type, arg_type = NULL_TREE; int signature = d->signature; - int i; if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature]) type = shared[signature]; @@ -11685,9 +11807,9 @@ if (! TARGET_FPU_ANY && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode)) continue; - for (i = 0; i < (int) ARRAY_SIZE (args); i++) + for (int i = 0; i < (int) ARRAY_SIZE (args); i++) args[i] = NULL_TREE; - for (i = 3; ; i--) + for (int i = 3; ; i--) { int arg = signature_args[signature][i]; int opno = i - 1 + has_result; @@ -11696,8 +11818,7 @@ arg_type = ptr_type_node; else if (arg) arg_type = (*lang_hooks.types.type_for_mode) - (insn_data[d->icode].operand[opno].mode, - (arg & 1)); + (insn_data[d->icode].operand[opno].mode, (arg & 1)); else if (i) continue; else @@ -11717,17 +11838,6 @@ } } -/* Returns the shmedia builtin decl for CODE. */ - -static tree -sh_media_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED) -{ - if (code >= ARRAY_SIZE (bdesc)) - return error_mark_node; - - return bdesc[code].fndecl; -} - /* Implements target hook vector_mode_supported_p. */ bool sh_vector_mode_supported_p (enum machine_mode mode) @@ -11773,22 +11883,18 @@ return DW_CC_normal; } -static void -sh_init_builtins (void) -{ - if (TARGET_SHMEDIA) - sh_media_init_builtins (); -} - /* Returns the sh builtin decl for CODE. */ static tree sh_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED) -{ - if (TARGET_SHMEDIA) - return sh_media_builtin_decl (code, initialize_p); - - return error_mark_node; +{ + if (code >= ARRAY_SIZE (bdesc)) + return error_mark_node; + + if (!bdesc[code].is_enabled ()) + return error_mark_node; + + return bdesc[code].fndecl; } /* Expand an expression EXP that calls a built-in function, @@ -11806,27 +11912,24 @@ const struct builtin_description *d = &bdesc[fcode]; enum insn_code icode = d->icode; int signature = d->signature; - enum machine_mode tmode = VOIDmode; - int nop = 0, i; + int nop = 0; rtx op[4]; - rtx pat = NULL_RTX; if (signature_args[signature][0]) { if (ignore) return NULL_RTX; - tmode = insn_data[icode].operand[0].mode; - if (! target - || GET_MODE (target) != tmode + enum machine_mode tmode = insn_data[icode].operand[0].mode; + if (! target || GET_MODE (target) != tmode || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) target = gen_reg_rtx (tmode); op[nop++] = target; } else - target = 0; + target = NULL_RTX; - for (i = 1; i <= 3; i++, nop++) + for (int i = 1; i <= 3; i++, nop++) { tree arg; enum machine_mode opmode, argmode; @@ -11855,6 +11958,8 @@ op[nop] = copy_to_mode_reg (opmode, op[nop]); } + rtx pat = NULL_RTX; + switch (nop) { case 1: @@ -12917,6 +13022,17 @@ { enum reg_class rclass = (enum reg_class) rclass_i; + if (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS + && REG_P (XEXP (XEXP (x, 0), 0)) + && REGNO (XEXP (XEXP (x, 0), 0)) == GBR_REG) + return rclass == R0_REGS ? NO_REGS : R0_REGS; + + if (MEM_P (x) && REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) == GBR_REG) + return rclass == R0_REGS ? NO_REGS : R0_REGS; + + if (REG_P (x) && REGNO (x) == GBR_REG) + return NO_REGS; + if (in_p) { if (REGCLASS_HAS_FP_REG (rclass) @@ -13143,7 +13259,149 @@ return false; return true; +} +/*------------------------------------------------------------------------------ + Address mode optimization support code +*/ + +typedef HOST_WIDE_INT disp_t; +static const disp_t MIN_DISP = HOST_WIDE_INT_MIN; +static const disp_t MAX_DISP = HOST_WIDE_INT_MAX; +static const disp_t INVALID_DISP = MAX_DISP; + +/* A memory reference which is described by a base register and a + displacement. */ +class base_reg_disp +{ +public: + base_reg_disp (rtx br, disp_t d); + + bool is_reg (void) const; + bool is_disp (void) const; + rtx reg (void) const; + disp_t disp (void) const; + +private: + rtx reg_; + disp_t disp_; +}; + +inline +base_reg_disp::base_reg_disp (rtx br, disp_t d) +: reg_ (br), disp_ (d) +{ } +inline bool +base_reg_disp::is_reg (void) const +{ + return reg_ != NULL_RTX && disp_ != INVALID_DISP; +} + +inline bool +base_reg_disp::is_disp (void) const +{ + return reg_ == NULL_RTX && disp_ != INVALID_DISP; +} + +inline rtx +base_reg_disp::reg (void) const +{ + return reg_; +} + +inline disp_t +base_reg_disp::disp (void) const +{ + return disp_; +} + +/* Find the base register and calculate the displacement for a given + address rtx 'x'. + This is done by walking the insn list backwards and following SET insns + that set the value of the specified reg 'x'. */ +static base_reg_disp +sh_find_base_reg_disp (rtx insn, rtx x, disp_t disp = 0, rtx base_reg = NULL) +{ + if (REG_P (x)) + { + if (REGNO (x) == GBR_REG) + return base_reg_disp (x, disp); + + /* We've reached a hard-reg. This is probably the point where + function args are copied to pseudos. Do not go any further and + stick to the pseudo. If the original mem addr was in a hard reg + from the beginning, it will become the base reg. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER) + return base_reg_disp (base_reg != NULL ? base_reg : x, disp); + + /* Try to find the previous insn that sets the reg. */ + for (rtx i = prev_nonnote_insn (insn); i != NULL; + i = prev_nonnote_insn (i)) + { + rtx p = PATTERN (i); + if (p != NULL && GET_CODE (p) == SET && REG_P (XEXP (p, 0)) + && REGNO (XEXP (p, 0)) == REGNO (x)) + { + /* If the recursion can't find out any more details about the + source of the set, then this reg becomes our new base reg. */ + return sh_find_base_reg_disp (i, XEXP (p, 1), disp, XEXP (p, 0)); + } + } + + /* When here, no previous insn was found that sets the reg. + The input reg is already the base reg. */ + return base_reg_disp (x, disp); + } + + else if (GET_CODE (x) == PLUS) + { + base_reg_disp left_val = sh_find_base_reg_disp (insn, XEXP (x, 0)); + base_reg_disp right_val = sh_find_base_reg_disp (insn, XEXP (x, 1)); + + /* Either left or right val must be a reg. + We don't handle the case of 'reg + reg' here. */ + if (left_val.is_reg () && right_val.is_disp ()) + return base_reg_disp (left_val.reg (), left_val.disp () + + right_val.disp () + disp); + else if (right_val.is_reg () && left_val.is_disp ()) + return base_reg_disp (right_val.reg (), right_val.disp () + + left_val.disp () + disp); + else + return base_reg_disp (base_reg, disp); + } + + else if (CONST_INT_P (x)) + return base_reg_disp (NULL, disp + INTVAL (x)); + + /* Didn't find anything useful. */ + return base_reg_disp (base_reg, disp); +} + +/* Given an insn and a memory operand, try to find an equivalent GBR + based memory address and return the corresponding new memory address. + Return NULL_RTX if not found. */ +rtx +sh_find_equiv_gbr_addr (rtx insn, rtx mem) +{ + if (!MEM_P (mem)) + return NULL_RTX; + + /* Leave post/pre inc/dec or any other side effect addresses alone. */ + if (side_effects_p (XEXP (mem, 0))) + return NULL_RTX; + + base_reg_disp gbr_disp = sh_find_base_reg_disp (insn, XEXP (mem, 0)); + + if (gbr_disp.is_reg () && REGNO (gbr_disp.reg ()) == GBR_REG) + { + rtx disp = GEN_INT (gbr_disp.disp ()); + if (gbr_displacement (disp, GET_MODE (mem))) + return gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, GBR_REG), disp); + } + + return NULL_RTX; +} + #include "gt-sh.h" Index: gcc/testsuite/gcc.target/sh/pr54706-1.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr54706-1.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr54706-1.c (revision 0) @@ -0,0 +1,20 @@ +/* Check that the __builtin_thread_pointer and __builtin_set_thread_pointer + built-in functions result in gbr store / load instructions. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */ +/* { dg-final { scan-assembler-times "ldc" 1 } } */ +/* { dg-final { scan-assembler-times "stc" 1 } } */ +/* { dg-final { scan-assembler-times "gbr" 2 } } */ + +void* +test00 (void) +{ + return __builtin_thread_pointer (); +} + +void +test01 (void* p) +{ + __builtin_set_thread_pointer (p); +} Index: gcc/testsuite/gcc.target/sh/pr54706-3.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr54706-3.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr54706-3.c (revision 0) @@ -0,0 +1,69 @@ +/* Check that these thread relative memory accesses play along with + surrounding code. + These should be moved to C torture tests once there are target + independent thread_pointer built-in functions available. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */ + +int +test00 (void* p, int x) +{ + int* tcb = (int*)__builtin_thread_pointer (); + int r = tcb[4]; + + __builtin_set_thread_pointer (p); + + tcb = (int*)__builtin_thread_pointer (); + return tcb[255] + r; +} + +int +test01 (void) +{ + unsigned short* tcb = (unsigned short*)__builtin_thread_pointer (); + return tcb[500]; +} + +void +test02 (int* x, int a, int b) +{ + int* tcb = (int*)__builtin_thread_pointer (); + tcb[50] = a; + + __builtin_set_thread_pointer (x); + + tcb = (int*)__builtin_thread_pointer (); + tcb[40] = b; +} + +int +test03 (const int* x, int c) +{ + volatile int* tcb = (volatile int*)__builtin_thread_pointer (); + + int s = 0; + int i; + for (i = 0; i < c; ++i) + s ^= x[i] + tcb[40]; + + return s; +} + +int +test04 (const int* x, int c, int** xx, int d) +{ + int s = 0; + int i; + for (i = 0; i < c; ++i) + { + volatile int* tcb = (volatile int*)__builtin_thread_pointer (); + tcb[20] = s; + + __builtin_set_thread_pointer (xx[i]); + + tcb = (volatile int*)__builtin_thread_pointer (); + s ^= x[i] + tcb[40] + d; + } + return s; +} Index: gcc/testsuite/gcc.target/sh/pr54706-2.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr54706-2.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr54706-2.c (revision 0) @@ -0,0 +1,223 @@ +/* Check that thread pointer relative memory accesses are converted to + gbr displacement address modes. If we see a gbr register store + instruction something is not working properly. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */ +/* { dg-final { scan-assembler-times "stc\tgbr" 0 } } */ + +/* --------------------------------------------------------------------------- + Simple GBR load. +*/ +#define func(name, type, disp)\ + int \ + name ## _tp_load (void) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + return tp[disp]; \ + } + +func (test00, int, 0) +func (test01, int, 5) +func (test02, int, 255) + +func (test03, short, 0) +func (test04, short, 5) +func (test05, short, 255) + +func (test06, char, 0) +func (test07, char, 5) +func (test08, char, 255) + +func (test09, unsigned int, 0) +func (test10, unsigned int, 5) +func (test11, unsigned int, 255) + +func (test12, unsigned short, 0) +func (test13, unsigned short, 5) +func (test14, unsigned short, 255) + +func (test15, unsigned char, 0) +func (test16, unsigned char, 5) +func (test17, unsigned char, 255) + +#undef func + +/* --------------------------------------------------------------------------- + Simple GBR store. +*/ +#define func(name, type, disp)\ + void \ + name ## _tp_store (int a) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + tp[disp] = (type)a; \ + } + +func (test00, int, 0) +func (test01, int, 5) +func (test02, int, 255) + +func (test03, short, 0) +func (test04, short, 5) +func (test05, short, 255) + +func (test06, char, 0) +func (test07, char, 5) +func (test08, char, 255) + +func (test09, unsigned int, 0) +func (test10, unsigned int, 5) +func (test11, unsigned int, 255) + +func (test12, unsigned short, 0) +func (test13, unsigned short, 5) +func (test14, unsigned short, 255) + +func (test15, unsigned char, 0) +func (test16, unsigned char, 5) +func (test17, unsigned char, 255) + +#undef func + +/* --------------------------------------------------------------------------- + Arithmetic on the result of a GBR load. +*/ +#define func(name, type, disp, op, opname)\ + int \ + name ## _tp_load_arith_ ##opname (int a) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + return tp[disp] op a; \ + } + +#define funcs(op, opname) \ + func (test00, int, 0, op, opname) \ + func (test01, int, 5, op, opname) \ + func (test02, int, 255, op, opname) \ + func (test03, short, 0, op, opname) \ + func (test04, short, 5, op, opname) \ + func (test05, short, 255, op, opname) \ + func (test06, char, 0, op, opname) \ + func (test07, char, 5, op, opname) \ + func (test08, char, 255, op, opname) \ + func (test09, unsigned int, 0, op, opname) \ + func (test10, unsigned int, 5, op, opname) \ + func (test11, unsigned int, 255, op, opname) \ + func (test12, unsigned short, 0, op, opname) \ + func (test13, unsigned short, 5, op, opname) \ + func (test14, unsigned short, 255, op, opname) \ + func (test15, unsigned char, 0, op, opname) \ + func (test16, unsigned char, 5, op, opname) \ + func (test17, unsigned char, 255, op, opname) \ + +funcs (+, plus) +funcs (-, minus) +funcs (*, mul) +funcs (&, and) +funcs (|, or) +funcs (^, xor) + +#undef funcs +#undef func + +/* --------------------------------------------------------------------------- + Arithmetic of the result of two GBR loads. +*/ +#define func(name, type, disp0, disp1, op, opname)\ + int \ + name ## _tp_load_load_arith_ ##opname (void) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + return tp[disp0] op tp[disp1]; \ + } + +#define funcs(op, opname) \ + func (test00, int, 0, 5, op, opname) \ + func (test02, int, 1, 255, op, opname) \ + func (test03, short, 0, 5, op, opname) \ + func (test05, short, 1, 255, op, opname) \ + func (test06, char, 0, 5, op, opname) \ + func (test08, char, 1, 255, op, opname) \ + func (test09, unsigned int, 0, 5, op, opname) \ + func (test11, unsigned int, 1, 255, op, opname) \ + func (test12, unsigned short, 0, 5, op, opname) \ + func (test14, unsigned short, 1, 255, op, opname) \ + func (test15, unsigned char, 0, 5, op, opname) \ + func (test17, unsigned char, 1, 255, op, opname) \ + +funcs (+, plus) +funcs (-, minus) +funcs (*, mul) +funcs (&, and) +funcs (|, or) +funcs (^, xor) + +#undef funcs +#undef func + +/* --------------------------------------------------------------------------- + GBR load GBR store copy. +*/ + +#define func(name, type, disp0, disp1)\ + void \ + name ## _tp_copy (void) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + tp[disp0] = tp[disp1]; \ + } + +func (test00, int, 0, 5) +func (test02, int, 1, 255) +func (test03, short, 0, 5) +func (test05, short, 1, 255) +func (test06, char, 0, 5) +func (test08, char, 1, 255) +func (test09, unsigned int, 0, 5) +func (test11, unsigned int, 1, 255) +func (test12, unsigned short, 0, 5) +func (test14, unsigned short, 1, 255) +func (test15, unsigned char, 0, 5) +func (test17, unsigned char, 1, 255) + +#undef func + +/* --------------------------------------------------------------------------- + GBR load, arithmetic, GBR store +*/ + +#define func(name, type, disp, op, opname)\ + void \ + name ## _tp_load_arith_store_ ##opname (int a) \ + { \ + type* tp = (type*)__builtin_thread_pointer (); \ + tp[disp] op a; \ + } + +#define funcs(op, opname) \ + func (test00, int, 0, op, opname) \ + func (test01, int, 5, op, opname) \ + func (test02, int, 255, op, opname) \ + func (test03, short, 0, op, opname) \ + func (test04, short, 5, op, opname) \ + func (test05, short, 255, op, opname) \ + func (test06, char, 0, op, opname) \ + func (test07, char, 5, op, opname) \ + func (test08, char, 255, op, opname) \ + func (test09, unsigned int, 0, op, opname) \ + func (test10, unsigned int, 5, op, opname) \ + func (test11, unsigned int, 255, op, opname) \ + func (test12, unsigned short, 0, op, opname) \ + func (test13, unsigned short, 5, op, opname) \ + func (test14, unsigned short, 255, op, opname) \ + func (test15, unsigned char, 0, op, opname) \ + func (test16, unsigned char, 5, op, opname) \ + func (test17, unsigned char, 255, op, opname) \ + +funcs (+=, plus) +funcs (-=, minus) +funcs (*=, mul) +funcs (&=, and) +funcs (|=, or) +funcs (^=, xor)