From patchwork Sat Oct 27 04:37:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stafford Horne X-Patchwork-Id: 989828 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-488446-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="nV9Zdsz0"; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pG9yCr7Y"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42hp4k2TGtz9s3T for ; Sat, 27 Oct 2018 15:37:42 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=MX8ElGNI4VaHH6q5fRfABSonBbS1WIYH2AW5eUasL4jANzuHdeGj1 fXoOWHgtRH2kepIhDjp01jhvb8gt2DdrdBToQ9g8pcaGRWvolnOLnWPAs7x/OxIY vjdR7VRNXEpWAmhOq/xX67xeYQVmlZ5jzIp9fUbGDK3AIUItja2SfA= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=nwK5LobNv3fXVkIDjqZMlIvRG5s=; b=nV9Zdsz0vleZZoZTPzCh 3azPh24NO11p9gXu9UYEc1PX7ey5tIVOsafXXxRZjZtfAQQi66CU7YxOXn7pixPN 1yJTuDCclAW9fTEdZdy1R+glVptfJbwVW+N+kQ+rEnJpXmcdP4Z9+LjtATazgvoE REezQKV/0zDaLpF5JtCr6lU= Received: (qmail 83294 invoked by alias); 27 Oct 2018 04:37:29 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 83116 invoked by uid 89); 27 Oct 2018 04:37:28 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS, TIME_LIMIT_EXCEEDED autolearn=unavailable version=3.3.2 spammy=appropriately, HX-Received:sk:z2-v6mr, lj, quot X-HELO: mail-pf1-f179.google.com Received: from mail-pf1-f179.google.com (HELO mail-pf1-f179.google.com) (209.85.210.179) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 27 Oct 2018 04:37:18 +0000 Received: by mail-pf1-f179.google.com with SMTP id j23-v6so1480126pfi.4 for ; Fri, 26 Oct 2018 21:37:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=OU/UuBS/UJWuONW//+3CqEXi06dJtaZOHC85nPAgkP4=; b=pG9yCr7YOs1LRAW2rHP/F/Qt0r/yIUo4anzRmaU7BNXkUuk6iDxYYfOSz/0YeGq5IT myIErb/qcBX6XXjSFJLsvllZWEWdrkIuZ7U1EYM/9e5fD5qEjasNPGi1w87OkRcsawux brAKsg1JaFAmLvutchFRdzIjYKB6zSKZRIbM9U8/GDdK2P8MnWJX2f2Zu16J25gDHcOL anG0JrjDzZN5ics7LlfnDpXKJjBiUCheKIR73Rxg8MOfRgx09IxKEkK7UBhohvVLkiXN 8FivjwBIMwKCmKffTXZpNPrxRqbFq56Pf7LHCyd5xN9du8C6xCmJwNVTsySnpZgdgbW1 StvQ== Received: from localhost (g106.218-225-177.ppp.wakwak.ne.jp. [218.225.177.106]) by smtp.gmail.com with ESMTPSA id t4-v6sm13547862pfh.45.2018.10.26.21.37.15 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 26 Oct 2018 21:37:15 -0700 (PDT) From: Stafford Horne To: GCC patches Cc: Openrisc , Richard Henderson , Jeff Law , Joseph Myers , Joel Sherrill , Sebastian Huber , Stafford Horne Subject: [PATCH v3 1/3] or1k: libgcc: initial support for openrisc Date: Sat, 27 Oct 2018 13:37:00 +0900 Message-Id: <20181027043702.18414-2-shorne@gmail.com> In-Reply-To: <20181027043702.18414-1-shorne@gmail.com> References: <20181027043702.18414-1-shorne@gmail.com> X-IsSubscribed: yes yyyy-mm-dd Stafford Horne Richard Henderson libgcc/ChangeLog: * config.host: Add OpenRISC support. * config/or1k/*: New. --- libgcc/config.host | 13 ++ libgcc/config/or1k/crti.S | 33 +++++ libgcc/config/or1k/crtn.S | 1 + libgcc/config/or1k/lib1funcs.S | 223 ++++++++++++++++++++++++++++++ libgcc/config/or1k/linux-unwind.h | 87 ++++++++++++ libgcc/config/or1k/sfp-machine.h | 54 ++++++++ libgcc/config/or1k/t-or1k | 22 +++ 7 files changed, 433 insertions(+) create mode 100644 libgcc/config/or1k/crti.S create mode 100644 libgcc/config/or1k/crtn.S create mode 100644 libgcc/config/or1k/lib1funcs.S create mode 100644 libgcc/config/or1k/linux-unwind.h create mode 100644 libgcc/config/or1k/sfp-machine.h create mode 100644 libgcc/config/or1k/t-or1k diff --git a/libgcc/config.host b/libgcc/config.host index 029f6569caf..e32b2541ea1 100644 --- a/libgcc/config.host +++ b/libgcc/config.host @@ -165,6 +165,9 @@ nds32*-*) nios2*-*-*) cpu_type=nios2 ;; +or1k*-*-*) + cpu_type=or1k + ;; powerpc*-*-*) cpu_type=rs6000 ;; @@ -1039,6 +1042,16 @@ nios2-*-*) tmake_file="$tmake_file nios2/t-nios2 t-softfp-sfdf t-softfp-excl t-softfp" extra_parts="$extra_parts crti.o crtn.o" ;; +or1k-*-linux*) + tmake_file="$tmake_file or1k/t-or1k" + tmake_file="$tmake_file t-softfp-sfdf t-softfp" + md_unwind_header=or1k/linux-unwind.h + ;; +or1k-*-*) + tmake_file="$tmake_file or1k/t-or1k" + tmake_file="$tmake_file t-softfp-sfdf t-softfp" + extra_parts="$extra_parts crti.o crtn.o" + ;; pdp11-*-*) tmake_file="pdp11/t-pdp11 t-fdpbit" ;; diff --git a/libgcc/config/or1k/crti.S b/libgcc/config/or1k/crti.S new file mode 100644 index 00000000000..9fcf6ae5995 --- /dev/null +++ b/libgcc/config/or1k/crti.S @@ -0,0 +1,33 @@ +/* Copyright (C) 2012-2018 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Here _init and _fini are empty because .init_array/.fini_array are used + exclusively. However, the functions are still needed as required when + linking. */ + .align 4 + .global _init + .type _init,@function +_init: + .global _fini + .type _fini,@function +_fini: + l.jr r9 + l.nop diff --git a/libgcc/config/or1k/crtn.S b/libgcc/config/or1k/crtn.S new file mode 100644 index 00000000000..ca6ee7b6fba --- /dev/null +++ b/libgcc/config/or1k/crtn.S @@ -0,0 +1 @@ +/* crtn.S is empty because .init_array/.fini_array are used exclusively. */ diff --git a/libgcc/config/or1k/lib1funcs.S b/libgcc/config/or1k/lib1funcs.S new file mode 100644 index 00000000000..354aadae8c4 --- /dev/null +++ b/libgcc/config/or1k/lib1funcs.S @@ -0,0 +1,223 @@ +/* Copyright (C) 2018 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifdef L__mulsi3 + .balign 4 + .globl __mulsi3 + .type __mulsi3, @function +__mulsi3: + l.movhi r11, 0 /* initial r */ + + /* Given R = X * Y ... */ +1: l.sfeq r4, r0 /* while (y != 0) */ + l.bf 2f + l.andi r5, r4, 1 /* if (y & 1) ... */ + l.add r12, r11, r3 + l.sfne r5, r0 +#if defined(__or1k_cmov__) + l.cmov r11, r12, r11 /* ... r += x. */ + l.srli r4, r4, 1 /* y >>= 1 */ +#else + l.bnf 3f + l.srli r4, r4, 1 /* y >>= 1 */ + l.ori r11, r12, 0 +3: +#endif + l.j 1b + l.add r3, r3, r3 /* x <<= 1 */ + +2: l.jr r9 + l.nop + + .size __mulsi3, . - __mulsi3 +#endif + +#if defined(L__udivsi3) || defined(L__umodsi3) \ + || defined(L__divsi3) || defined(L__modsi3) + .global __udivmodsi3_internal + .hidden __udivmodsi3_internal + .type __udivmodsi3_internal, @function +#endif + +#ifdef L__udivsi3 + .balign 4 + .global __udivsi3 + .type __udivsi3, @function +__udivsi3: +__udivmodsi3_internal: + /* Note that the other division routines assume that r13 + is not clobbered by this routine, and use that as to + save a return address without creating a stack frame. */ + + l.sfeqi r4, 0 /* division by zero; return 0. */ + l.ori r11, r0, 0 /* initial quotient */ + l.bf 9f + l.ori r12, r3, 0 /* initial remainder */ + + /* Given X/Y, shift Y left until Y >= X. */ + l.ori r6, r0, 1 /* mask = 1 */ +1: l.sfltsi r4, 0 /* y has msb set */ + l.bf 2f + l.sfltu r4, r12 /* y < x */ + l.add r4, r4, r4 /* y <<= 1 */ + l.bnf 1b + l.add r6, r6, r6 /* mask <<= 1 */ + + /* Shift Y back to the right again, subtracting from X. */ +2: l.add r7, r11, r6 /* tmp1 = quot + mask */ +3: l.srli r6, r6, 1 /* mask >>= 1 */ + l.sub r8, r12, r4 /* tmp2 = x - y */ + l.sfleu r4, r12 /* y <= x */ + l.srli r4, r4, 1 /* y >>= 1 */ +#if defined(__or1k_cmov__) + l.cmov r11, r7, r11 /* if (y <= x) quot = tmp1 */ + l.cmov r12, r8, r12 /* if (y <= x) x = tmp2 */ +#else + l.bnf 4f + l.nop + l.ori r11, r7, 0 + l.ori r12, r8, 0 +4: +#endif + l.sfne r6, r0 /* loop until mask == 0 */ + l.bf 3b + l.add r7, r11, r6 /* delay fill from loop start */ + +9: l.jr r9 + l.nop + + .size __udivsi3, . - __udivsi3 + .size __udivmodsi3_internal, . - __udivmodsi3_internal +#endif + +#ifdef L__umodsi3 + .balign 4 + .global __umodsi3 + .type __umodsi3, @function + .cfi_startproc +__umodsi3: + /* Know that __udivmodsi3_internal does not clobber r13. */ + l.ori r13, r9, 0 + .cfi_register 9, 13 + l.jal __udivmodsi3_internal + l.nop + l.jr r13 /* return to saved lr */ + l.ori r11, r12, 0 /* move remainder to rv */ + + .cfi_endproc + .size __umodsi3, . - __umodsi3 +#endif + +/* For signed division we do: + * + * -x / y = x / -y = -(x / y) + * -x % y = -(x % y) + * x % -y = x % b + * + * which has the property that (x/y)*y + (x%y) = x. + */ + +#ifdef L__divsi3 + .balign 4 + .global __divsi3 + .type __divsi3, @function + .cfi_startproc +__divsi3: + l.xor r6, r3, r4 /* need result negate? */ + + l.sflts r3, r0 /* abs(x) */ +#if defined(__or1k_cmov__) + l.sub r5, r0, r3 + l.cmov r3, r5, r3 +#else + l.bnf 1f + l.sub r5, r0, r3 + l.ori r3, r5, 0 +1: +#endif + l.sflts r4, r0 /* abs(y) */ +#if defined(__or1k_cmov__) + l.sub r5, r0, r4 + l.cmov r4, r5, r4 +#else + l.bnf 2f + l.sub r5, r0, r4 + l.ori r4, r5, 0 +2: +#endif + + /* If the result will not require sign flip, tail call. */ + l.sflts r6, r0 + l.bnf __udivmodsi3_internal + l.ori r13, r9, 0 /* save lr */ + + /* Otherwise, know that __udivmodsi3_internal does not clobber r13. + Perform a normal call, then negate and return via saved lr. */ + .cfi_register 9, 13 + l.jal __udivmodsi3_internal + l.nop + l.jr r13 + l.sub r11, r0, r11 + + .cfi_endproc + .size __divsi3, . - __divsi3 +#endif + +#ifdef L__modsi3 + .balign 4 + .global __modsi3 + .type __modsi3, @function + .cfi_startproc +__modsi3: + l.sflts r4, r0 /* abs(y) */ +#if defined(__or1k_cmov__) + l.sub r5, r0, r4 + l.cmov r4, r5, r4 +#else + l.bnf 2f + l.sub r5, r0, r4 + l.ori r4, r5, 0 +2: +#endif + + l.sflts r3, r0 /* x negative? */ + l.bf 1f + l.ori r13, r9, 0 /* save lr */ + + /* Know that __udivmodsi3_internal does not clobber r13. */ + .cfi_register 9, 13 + + /* X positive; no negate of the result required. */ + l.jal __udivmodsi3_internal + l.nop + l.jr r13 /* return to saved lr */ + l.ori r11, r12, 0 /* move remainder to rv */ + + /* X negative; negate both X and the result. */ +1: l.jal __udivmodsi3_internal + l.sub r3, r0, r3 + l.jr r13 /* return to saved lr */ + l.sub r11, r0, r12 /* negate remainder to rv */ + + .cfi_endproc + .size __modsi3, .- __modsi3 +#endif diff --git a/libgcc/config/or1k/linux-unwind.h b/libgcc/config/or1k/linux-unwind.h new file mode 100644 index 00000000000..e80e9e0f309 --- /dev/null +++ b/libgcc/config/or1k/linux-unwind.h @@ -0,0 +1,87 @@ +/* DWARF2 EH unwinding support for OpenRISC Linux. + Copyright (C) 2018 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#ifndef inhibit_libc +/* Do code reading to identify a signal frame, and set the frame + state data appropriately. See unwind-dw2.c for the structs. */ + +#include +#include + +#define MD_FALLBACK_FRAME_STATE_FOR or1k_fallback_frame_state + +static _Unwind_Reason_Code +or1k_fallback_frame_state (struct _Unwind_Context *context, + _Unwind_FrameState *fs) +{ + unsigned int *pc = context->ra; + struct rt_sigframe { + siginfo_t info; + ucontext_t uc; + } *rt; + struct sigcontext *sc; + long new_cfa; + int i; + + if (pc[0] != 0xa960008b /* l.ori r11, r0, NR_rt_sigreturn */ + || pc[1] != 0x20000001) /* l.sys 1 */ + return _URC_END_OF_STACK; + if (context->cfa == 0) + return _URC_END_OF_STACK; + + rt = context->cfa; + sc = &rt->uc.uc_mcontext; + + new_cfa = sc->regs.gpr[1]; + fs->regs.cfa_how = CFA_REG_OFFSET; + fs->regs.cfa_reg = 1; + fs->regs.cfa_offset = new_cfa - (long) context->cfa; + for (i = 2; i < 32; ++i) + { + fs->regs.reg[i].how = REG_SAVED_OFFSET; + fs->regs.reg[i].loc.offset = (long) &sc->regs.gpr[i] - new_cfa; + } + fs->regs.reg[32].how = REG_SAVED_OFFSET; + fs->regs.reg[32].loc.offset = (long)&sc->regs.pc - new_cfa; + fs->retaddr_column = 32; + fs->signal_frame = 1; + + return _URC_NO_REASON; +} + +#define MD_FROB_UPDATE_CONTEXT or1k_frob_update_context + +/* Fix up for signal handlers that don't have S flag set. */ + +static void +or1k_frob_update_context (struct _Unwind_Context *context, + _Unwind_FrameState *fs ATTRIBUTE_UNUSED) +{ + unsigned int *pc = context->ra; + + if (pc[0] == 0xa960008b /* l.ori r11, r0, NR_rt_sigreturn */ + && pc[1] == 0x20000001) /* l.sys 1 */ + _Unwind_SetSignalFrame (context, 1); +} +#endif diff --git a/libgcc/config/or1k/sfp-machine.h b/libgcc/config/or1k/sfp-machine.h new file mode 100644 index 00000000000..5da9e84990d --- /dev/null +++ b/libgcc/config/or1k/sfp-machine.h @@ -0,0 +1,54 @@ +#define _FP_W_TYPE_SIZE 32 +#define _FP_W_TYPE unsigned long +#define _FP_WS_TYPE signed long +#define _FP_I_TYPE long + +#define _FP_MUL_MEAT_S(R,X,Y) \ + _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_S,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_D(R,X,Y) \ + _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_Q(R,X,Y) \ + _FP_MUL_MEAT_4_wide(_FP_WFRACBITS_Q,R,X,Y,umul_ppmm) + +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_loop(S,R,X,Y) +#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv(D,R,X,Y) +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_4_udiv(Q,R,X,Y) + +#define _FP_NANFRAC_S ((_FP_QNANBIT_S << 1) - 1) +#define _FP_NANFRAC_D ((_FP_QNANBIT_D << 1) - 1), -1 +#define _FP_NANFRAC_Q ((_FP_QNANBIT_Q << 1) - 1), -1, -1, -1 +#define _FP_NANSIGN_S 0 +#define _FP_NANSIGN_D 0 +#define _FP_NANSIGN_Q 0 + +#define _FP_KEEPNANFRACP 1 +#define _FP_QNANNEGATEDP 0 + +/* Someone please check this. */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ + do { \ + if ((_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs) \ + && !(_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs)) \ + { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + } \ + else \ + { \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R,X); \ + } \ + R##_c = FP_CLS_NAN; \ + } while (0) + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 + +#define __BYTE_ORDER __BIG_ENDIAN + +#define _FP_TININESS_AFTER_ROUNDING 0 + +/* Define ALIASNAME as a strong alias for NAME. */ +# define strong_alias(name, aliasname) _strong_alias(name, aliasname) +# define _strong_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((alias (#name))); diff --git a/libgcc/config/or1k/t-or1k b/libgcc/config/or1k/t-or1k new file mode 100644 index 00000000000..73a703ac58d --- /dev/null +++ b/libgcc/config/or1k/t-or1k @@ -0,0 +1,22 @@ +# Libgcc Makefile fragment for OpenRISC +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by Stafford Horne. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 3, or (at your +# option) any later version. +# +# GCC is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +LIB1ASMSRC = or1k/lib1funcs.S +LIB1ASMFUNCS = __mulsi3 __udivsi3 __divsi3 __umodsi3 __modsi3 From patchwork Sat Oct 27 04:37:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stafford Horne X-Patchwork-Id: 989829 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-488447-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="W1Zg51j3"; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="iAtl8ef1"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42hp4y4Vgzz9s3T for ; Sat, 27 Oct 2018 15:37:54 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=YsG48tqGoK62QAcWjcJ9OCqSIPCtI6D7zcvL7dbsArENadm8KhEYh OZoQFkT5rXuzjTsmBRlwxFkXuICG26IuiawDeuCRfGbI0CRsx2t6rW90PqQYB5VL ASIZycWDUGJasVsKdPAtxUBeyFFSBtS1XoqTPza0K9i9yAETE94OI0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=JM0tl5mJ52dhZ/phbEePIFywYWs=; b=W1Zg51j3flNM/ILKuHyz iPvrrPTGPhwAK+ZbvIuumbdzAoi2RjmW7WkGwP4ADSiRnhLdcLUxGbN58ka62DVn lH0NFuvG7EdnG97JN1OSlINcLvung4F9atwuK030lOUUy2oB46h/xx589SZvI6Oy z4xMqVaTVxyyg3Zfs/lHFiA= Received: (qmail 83356 invoked by alias); 27 Oct 2018 04:37:30 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 83168 invoked by uid 89); 27 Oct 2018 04:37:28 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=structs, ansi, U*rth X-HELO: mail-pl1-f193.google.com Received: from mail-pl1-f193.google.com (HELO mail-pl1-f193.google.com) (209.85.214.193) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 27 Oct 2018 04:37:21 +0000 Received: by mail-pl1-f193.google.com with SMTP id p5-v6so1366635plq.8 for ; Fri, 26 Oct 2018 21:37:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8iotcnkFHsN0MJi7e+d0iidMkZUYglmxsLSBJRPILLU=; b=iAtl8ef1y5kDKUxvj1VjjFEtj/xQMUjvrtKGT6futguAhu1kV9ulA/IM2eIiRNKqQ5 q6wgkAs0Vem3jx9JM2wNLvg0c4p2CWdI82JnlS4Duv2G66XqGZrzqcLl0zUG18+QaacS 9hIZX5bxIsz0f1yEeIfMbD1yenRSvgbiU6tkmbXohxWOWFY9aeZh4RS286XC4UPZ8Qyc yQddWW7YjoGbFzVj97q1h/g8VhXCnR5Y99L94Y4tLb66bALCwbdfJ6yL8sXOT0s/H+h7 cxV2TknrKZxM8ojYytdwlBBO01v2JI2+i2NJdQ+gAFjme0n2C8V8AX3FgTXoW1KYu3zM nPpg== Received: from localhost (g106.218-225-177.ppp.wakwak.ne.jp. [218.225.177.106]) by smtp.gmail.com with ESMTPSA id x186-v6sm14481627pfx.152.2018.10.26.21.37.17 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 26 Oct 2018 21:37:18 -0700 (PDT) From: Stafford Horne To: GCC patches Cc: Openrisc , Richard Henderson , Jeff Law , Joseph Myers , Joel Sherrill , Sebastian Huber , Stafford Horne Subject: [PATCH v3 2/3] or1k: testsuite: initial support for openrisc Date: Sat, 27 Oct 2018 13:37:01 +0900 Message-Id: <20181027043702.18414-3-shorne@gmail.com> In-Reply-To: <20181027043702.18414-1-shorne@gmail.com> References: <20181027043702.18414-1-shorne@gmail.com> X-IsSubscribed: yes yyyy-mm-dd Stafford Horne Richard Henderson gcc/testsuite/ChangeLog: * gcc.c-torture/execute/20101011-1.c: Adjust for OpenRISC. * gcc.dg/20020312-2.c: Likewise. * gcc.dg/attr-alloc_size-11.c: Likewise. * gcc.dg/builtin-apply2.c: Likewise. * gcc.dg/nop.h: Likewise. * gcc.dg/torture/stackalign/builtin-apply-2.c: Likewise. * gcc.dg/tree-ssa/20040204-1.c: Likewise. * gcc.dg/tree-ssa/reassoc-33.c: Likewise. * gcc.dg/tree-ssa/reassoc-34.c: Likewise. * gcc.dg/tree-ssa/reassoc-35.c: Likewise. * gcc.dg/tree-ssa/reassoc-36.c: Likewise. * lib/target-supports.exp (check_effective_target_logical_op_short_circuit): Add or1k*-*-*. * gcc.target/or1k/*: New. --- .../gcc.c-torture/execute/20101011-1.c | 3 ++ gcc/testsuite/gcc.dg/20020312-2.c | 2 + gcc/testsuite/gcc.dg/attr-alloc_size-11.c | 4 +- gcc/testsuite/gcc.dg/builtin-apply2.c | 2 +- gcc/testsuite/gcc.dg/nop.h | 2 + .../torture/stackalign/builtin-apply-2.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/20040204-1.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/reassoc-34.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/reassoc-35.c | 2 +- gcc/testsuite/gcc.dg/tree-ssa/reassoc-36.c | 2 +- gcc/testsuite/gcc.target/or1k/args-1.c | 19 +++++++++ gcc/testsuite/gcc.target/or1k/args-2.c | 15 +++++++ gcc/testsuite/gcc.target/or1k/cmov-1.c | 8 ++++ gcc/testsuite/gcc.target/or1k/cmov-2.c | 9 ++++ gcc/testsuite/gcc.target/or1k/div-mul-1.c | 9 ++++ gcc/testsuite/gcc.target/or1k/div-mul-2.c | 9 ++++ gcc/testsuite/gcc.target/or1k/or1k.exp | 41 +++++++++++++++++++ gcc/testsuite/gcc.target/or1k/return-1.c | 10 +++++ gcc/testsuite/gcc.target/or1k/return-2.c | 19 +++++++++ gcc/testsuite/gcc.target/or1k/return-3.c | 19 +++++++++ gcc/testsuite/gcc.target/or1k/return-4.c | 19 +++++++++ gcc/testsuite/gcc.target/or1k/ror-1.c | 8 ++++ gcc/testsuite/gcc.target/or1k/ror-2.c | 9 ++++ gcc/testsuite/gcc.target/or1k/ror-3.c | 8 ++++ gcc/testsuite/gcc.target/or1k/shftimm-1.c | 8 ++++ gcc/testsuite/gcc.target/or1k/shftimm-2.c | 8 ++++ gcc/testsuite/gcc.target/or1k/sibcall-1.c | 18 ++++++++ gcc/testsuite/lib/target-supports.exp | 1 + 29 files changed, 253 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/gcc.target/or1k/args-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/args-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/cmov-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/cmov-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/div-mul-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/div-mul-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/or1k.exp create mode 100644 gcc/testsuite/gcc.target/or1k/return-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/return-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/return-3.c create mode 100644 gcc/testsuite/gcc.target/or1k/return-4.c create mode 100644 gcc/testsuite/gcc.target/or1k/ror-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/ror-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/ror-3.c create mode 100644 gcc/testsuite/gcc.target/or1k/shftimm-1.c create mode 100644 gcc/testsuite/gcc.target/or1k/shftimm-2.c create mode 100644 gcc/testsuite/gcc.target/or1k/sibcall-1.c diff --git a/gcc/testsuite/gcc.c-torture/execute/20101011-1.c b/gcc/testsuite/gcc.c-torture/execute/20101011-1.c index 8261b796a47..d2beeb52a0e 100644 --- a/gcc/testsuite/gcc.c-torture/execute/20101011-1.c +++ b/gcc/testsuite/gcc.c-torture/execute/20101011-1.c @@ -100,6 +100,9 @@ __aeabi_idiv0 (int return_value) #elif defined (__moxie__) /* Not all moxie configurations may raise exceptions. */ # define DO_TEST 0 +#elif defined (__or1k__) + /* On OpenRISC division by zero does not trap. */ +# define DO_TEST 0 #else # define DO_TEST 1 #endif diff --git a/gcc/testsuite/gcc.dg/20020312-2.c b/gcc/testsuite/gcc.dg/20020312-2.c index 1a8afd81506..e72a5b261ae 100644 --- a/gcc/testsuite/gcc.dg/20020312-2.c +++ b/gcc/testsuite/gcc.dg/20020312-2.c @@ -117,6 +117,8 @@ extern void abort (void); # if defined (__CK807__) || defined (__CK810__) # define PIC_REG "r28" # endif +#elif defined (__or1k__) +/* No pic register. */ #else # error "Modify the test for your target." #endif diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-11.c b/gcc/testsuite/gcc.dg/attr-alloc_size-11.c index 3ec44dc1463..6bb904f4794 100644 --- a/gcc/testsuite/gcc.dg/attr-alloc_size-11.c +++ b/gcc/testsuite/gcc.dg/attr-alloc_size-11.c @@ -47,8 +47,8 @@ typedef __SIZE_TYPE__ size_t; /* The following tests fail because of missing range information. The xfail exclusions are PR79356. */ -TEST (signed char, SCHAR_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for signed char" { xfail { ! { aarch64*-*-* arm*-*-* avr-*-* alpha*-*-* ia64-*-* mips*-*-* powerpc*-*-* sparc*-*-* s390*-*-* visium-*-* } } } } */ -TEST (short, SHRT_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for short" { xfail { ! { aarch64*-*-* arm*-*-* alpha*-*-* avr-*-* ia64-*-* mips*-*-* powerpc*-*-* sparc*-*-* s390x-*-* visium-*-* } } } } */ +TEST (signed char, SCHAR_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for signed char" { xfail { ! { aarch64*-*-* arm*-*-* avr-*-* alpha*-*-* ia64-*-* mips*-*-* powerpc*-*-* sparc*-*-* s390*-*-* visium-*-* or1k*-*-* } } } } */ +TEST (short, SHRT_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" "missing range info for short" { xfail { ! { aarch64*-*-* arm*-*-* alpha*-*-* avr-*-* ia64-*-* mips*-*-* powerpc*-*-* sparc*-*-* s390x-*-* visium-*-* or1k*-*-* } } } } */ TEST (int, INT_MIN + 2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ TEST (int, -3, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ TEST (int, -2, ALLOC_MAX); /* { dg-warning "argument 1 range \\\[13, \[0-9\]+\\\] exceeds maximum object size 12" } */ diff --git a/gcc/testsuite/gcc.dg/builtin-apply2.c b/gcc/testsuite/gcc.dg/builtin-apply2.c index 3768caa5d5a..dd521973cae 100644 --- a/gcc/testsuite/gcc.dg/builtin-apply2.c +++ b/gcc/testsuite/gcc.dg/builtin-apply2.c @@ -1,7 +1,7 @@ /* { dg-do run } */ /* { dg-require-effective-target untyped_assembly } */ /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { "avr-*-* nds32*-*-*" } } */ -/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "riscv*-*-*" } } */ +/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "riscv*-*-* or1k*-*-*" } } */ /* { dg-skip-if "Variadic funcs use Base AAPCS. Normal funcs use VFP variant." { arm*-*-* && arm_hf_eabi } } */ /* PR target/12503 */ diff --git a/gcc/testsuite/gcc.dg/nop.h b/gcc/testsuite/gcc.dg/nop.h index a0c19a34414..23491a603f5 100644 --- a/gcc/testsuite/gcc.dg/nop.h +++ b/gcc/testsuite/gcc.dg/nop.h @@ -2,6 +2,8 @@ #define NOP "nop 0" #elif defined (__MMIX__) #define NOP "swym 0" +#elif defined (__or1k__) +#define NOP "l.nop" #else #define NOP "nop" #endif diff --git a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c index d033010dc7c..3f8d350ba8f 100644 --- a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c +++ b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c @@ -9,7 +9,7 @@ /* arm_hf_eabi: Variadic funcs use Base AAPCS. Normal funcs use VFP variant. avr: Variadic funcs don't pass arguments in registers, while normal funcs do. */ -/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { avr-*-* riscv*-*-* } } } */ +/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { avr-*-* riscv*-*-* or1k*-*-* } } } */ /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { nds32*-*-* } } */ /* { dg-require-effective-target untyped_assembly } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20040204-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20040204-1.c index a1237cf839b..bc486e32586 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/20040204-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/20040204-1.c @@ -33,4 +33,4 @@ void test55 (int x, int y) that the && should be emitted (based on BRANCH_COST). Fix this by teaching dom to look through && and register all components as true. */ -/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail { ! "alpha*-*-* arm*-*-* aarch64*-*-* powerpc*-*-* cris-*-* crisv32-*-* hppa*-*-* i?86-*-* mmix-*-* mips*-*-* m68k*-*-* moxie-*-* nds32*-*-* s390*-*-* sh*-*-* sparc*-*-* spu-*-* visium-*-* x86_64-*-* riscv*-*-*" } } } } */ +/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail { ! "alpha*-*-* arm*-*-* aarch64*-*-* powerpc*-*-* cris-*-* crisv32-*-* hppa*-*-* i?86-*-* mmix-*-* mips*-*-* m68k*-*-* moxie-*-* nds32*-*-* s390*-*-* sh*-*-* sparc*-*-* spu-*-* visium-*-* x86_64-*-* riscv*-*-* or1k*-*-*" } } } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c index 5572df4ae24..243508c872c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-33.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-*"} } } */ +/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-* or1k-*-*-*"} } } */ /* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-34.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-34.c index 9b45f1cd9be..24070046ef2 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-34.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-34.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-*"} } } */ +/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-* or1k*-*-*"} } } */ /* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-35.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-35.c index 9ee3abca04e..e5ba101e001 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-35.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-35.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-*"} } } */ +/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-* or1k*-*-*"} } } */ /* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-36.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-36.c index ac3a04291b7..4df5840859c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-36.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-36.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-*"} } } */ +/* { dg-do run { target { ! "m68k*-*-* mmix*-*-* bfin*-*-* v850*-*-* moxie*-*-* cris*-*-* m32c*-*-* fr30*-*-* mcore*-*-* powerpc*-*-* xtensa*-*-* hppa*-*-* nios2*-*-* or1k*-*-*"} } } */ /* { dg-options "-O2 -fno-inline -fdump-tree-reassoc1-details" } */ /* { dg-additional-options "-mbranch-cost=2" { target branch_cost } } */ diff --git a/gcc/testsuite/gcc.target/or1k/args-1.c b/gcc/testsuite/gcc.target/or1k/args-1.c new file mode 100644 index 00000000000..7538705d07f --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/args-1.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +struct a { + long x; + long y; + long z; +}; + +int passlibstruct (int b, struct a aa); + +int main() { + struct a aa = { 55, 66, 77 }; + + return passlibstruct(-1, aa); +} + +/* Ensure we pass a stack reference in the second arg. */ +/* { dg-final { scan-assembler-times "r4, r1, " 1 } } */ diff --git a/gcc/testsuite/gcc.target/or1k/args-2.c b/gcc/testsuite/gcc.target/or1k/args-2.c new file mode 100644 index 00000000000..362f7c0c9e9 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/args-2.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +struct a { + long x; + long y; + long z; +}; + +int passstruct (int b, struct a aa) { + return aa.z + aa.y + b; +} + +/* Ensure our struct reads are offset from the address in arg 2. */ +/* { dg-final { scan-assembler-times "l.lwz\\s+r\\d+, \\d+.r4." 2 } } */ diff --git a/gcc/testsuite/gcc.target/or1k/cmov-1.c b/gcc/testsuite/gcc.target/or1k/cmov-1.c new file mode 100644 index 00000000000..c66b67c07c7 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/cmov-1.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-mcmov -O2" } */ + +int cond (int a, int b) { + return a > b; +} + +/* { dg-final { scan-assembler "l.cmov" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/cmov-2.c b/gcc/testsuite/gcc.target/or1k/cmov-2.c new file mode 100644 index 00000000000..9b3b5529740 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/cmov-2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { *-*-* } { "-mcmov" } { "" } } */ + +int cond (int a, int b) { + return a > b; +} + +/* { dg-final { scan-assembler-not "l.cmov" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/div-mul-1.c b/gcc/testsuite/gcc.target/or1k/div-mul-1.c new file mode 100644 index 00000000000..a5e8d286545 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/div-mul-1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msoft-mul -msoft-div" } */ + +int calc (int a, int b, int c) { + return a * b / c; +} + +/* { dg-final { scan-assembler-not "l.mul" } } */ +/* { dg-final { scan-assembler-not "l.div" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/div-mul-2.c b/gcc/testsuite/gcc.target/or1k/div-mul-2.c new file mode 100644 index 00000000000..a567d7d9f50 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/div-mul-2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msoft-div" } */ + +int calc (int a, int b, int c) { + return a * b / c; +} + +/* { dg-final { scan-assembler "l.mul" } } */ +/* { dg-final { scan-assembler-not "l.div" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/or1k.exp b/gcc/testsuite/gcc.target/or1k/or1k.exp new file mode 100644 index 00000000000..1a4d53a9f79 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/or1k.exp @@ -0,0 +1,41 @@ +# Copyright (C) 2017-2018 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Exit immediately if this isn't an OpenRISC target. +if ![istarget or1k*-*-*] then { + return +} + +# Load support procs. +load_lib gcc-dg.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " -ansi -pedantic-errors" +} + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ + "" $DEFAULT_CFLAGS + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.target/or1k/return-1.c b/gcc/testsuite/gcc.target/or1k/return-1.c new file mode 100644 index 00000000000..6dd04197164 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/return-1.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +long long calc (long long a, long long b) { + return a * b + 5; +} + +/* Ensure our return value is set in the r11, r12 pair. */ +/* { dg-final { scan-assembler "r11," } } */ +/* { dg-final { scan-assembler "r12," } } */ diff --git a/gcc/testsuite/gcc.target/or1k/return-2.c b/gcc/testsuite/gcc.target/or1k/return-2.c new file mode 100644 index 00000000000..c072ae23142 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/return-2.c @@ -0,0 +1,19 @@ +/* Large structs are returned at a memory address passed in r3. */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +struct a { + long x; + long y; + long z; +}; + +struct a getstruct (long aa) { + struct a as = { 22, aa, -5 }; + return as; +} + +/* Ensure our return value is returned on stack. */ +/* { dg-final { scan-assembler-not "r12," } } */ +/* { dg-final { scan-assembler "l.or\\s+r11, r3, r3" } } */ +/* { dg-final { scan-assembler-times "l.sw\\s+\\d+.r3.," 3 } } */ diff --git a/gcc/testsuite/gcc.target/or1k/return-3.c b/gcc/testsuite/gcc.target/or1k/return-3.c new file mode 100644 index 00000000000..5c2e5f5ad91 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/return-3.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +struct a { + long x; + long y; + long z; +}; + +struct a getlibstruct (long aa); + +int main() { + struct a rs = getlibstruct(123); + + return rs.x; +} + +/* Ensure our return value is read from memory. */ +/* { dg-final { scan-assembler "l.lwz\\s+r11," } } */ diff --git a/gcc/testsuite/gcc.target/or1k/return-4.c b/gcc/testsuite/gcc.target/or1k/return-4.c new file mode 100644 index 00000000000..b866f58a307 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/return-4.c @@ -0,0 +1,19 @@ +/* Test to ensure small structs are returned in memory too. */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +struct a { + long x; + long y; +}; + +struct a getlibstruct (long aa); + +int main() { + struct a rs = getlibstruct(123); + + return rs.x; +} + +/* Ensure our return value is read from memory. */ +/* { dg-final { scan-assembler "l.lwz\\s+r11," } } */ diff --git a/gcc/testsuite/gcc.target/or1k/ror-1.c b/gcc/testsuite/gcc.target/or1k/ror-1.c new file mode 100644 index 00000000000..df55a6adfef --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/ror-1.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-mror -O2" } */ + +unsigned int rotate (unsigned int a, int b) { + return ( a >> b ) | ( a << ( 32 - b ) ); +} + +/* { dg-final { scan-assembler "l.ror" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/ror-2.c b/gcc/testsuite/gcc.target/or1k/ror-2.c new file mode 100644 index 00000000000..9cd7f35d285 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/ror-2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { *-*-* } { "-mror" } { "" } } */ + +unsigned int rotate (unsigned int a, int b) { + return ( a >> b ) | ( a << ( 32 - b ) ); +} + +/* { dg-final { scan-assembler-not "l.ror" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/ror-3.c b/gcc/testsuite/gcc.target/or1k/ror-3.c new file mode 100644 index 00000000000..b0a73b3064d --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/ror-3.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-mror -O2" } */ + +unsigned int rotate6 (unsigned int a) { + return ( a >> 6 ) | ( a << ( 32 - 6 ) ); +} + +/* { dg-final { scan-assembler-not "l.rori" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/shftimm-1.c b/gcc/testsuite/gcc.target/or1k/shftimm-1.c new file mode 100644 index 00000000000..be8d9e8b895 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/shftimm-1.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-mror -mshftimm -O2" } */ + +unsigned int rotate6 (unsigned int a) { + return ( a >> 6 ) | ( a << ( 32 - 6 ) ); +} + +/* { dg-final { scan-assembler "l.rori" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/shftimm-2.c b/gcc/testsuite/gcc.target/or1k/shftimm-2.c new file mode 100644 index 00000000000..ef9b52f2820 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/shftimm-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-mshftimm -O2" } */ + +unsigned int shift6 (unsigned int a) { + return a >> 6; +} + +/* { dg-final { scan-assembler "l.srli" } } */ diff --git a/gcc/testsuite/gcc.target/or1k/sibcall-1.c b/gcc/testsuite/gcc.target/or1k/sibcall-1.c new file mode 100644 index 00000000000..8134f0cfe35 --- /dev/null +++ b/gcc/testsuite/gcc.target/or1k/sibcall-1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +/* Just create some dummy call that should trigger sibcall, no + stack logic. */ +int calc (int a, int b, int c) { + if (c <= 0) return a; + return calc (a * b, b, --c); +} + +int main() { + return calc (4, 3, 4); +} + +/* Ensure sibcalls do not need to manipulate the stack. */ +/* { dg-final { scan-assembler-not "r1," } } */ +/* Ensure sibcall maintains the body of the function. */ +/* { dg-final { scan-assembler "l.mul" } } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index fd74c04d092..528e336752b 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -8394,6 +8394,7 @@ proc check_effective_target_logical_op_short_circuit {} { || [istarget riscv*-*-*] || [istarget v850*-*-*] || [istarget visium-*-*] + || [istarget or1k*-*-*] || [check_effective_target_arm_cortex_m] } { return 1 } From patchwork Sat Oct 27 04:37:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stafford Horne X-Patchwork-Id: 989830 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-488448-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Hfgn55G/"; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="rn/fumNk"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42hp5L1NZkz9s3T for ; Sat, 27 Oct 2018 15:38:14 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=mF5JQWfVSGx64avsAcp9LrJDteJUhR6WYHMA1yZ3qbnVhKn9DtfEt oKsyQBLB7TQIahw4+74Y4X/6mhPXma7I+1nlGXdrm+4d9jOztZY1I9gNJJ0zE0Wl tHEPAFog8eiPwzog1aRjNkg3geEBXjjZbrqtkRMHzwDtMZ5mPsa8AI= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=7ejXE2mx6wOQY79dEBUY8IuznXU=; b=Hfgn55G/4DhTFWlWi0SD r9bjfo922ejltmW+dbUAKGf2zwfANLOJ4Ua/zEJq3TIi7LIDPmmKiVzGkEAbry0q oheM6xfhvdAevaaBaszAnPt8hu4lnXJY1H5DiCO4T83WsMikuQMEXIbfHrapfkLy qvtKZwzXLqUPSCEA2+A8nAw= Received: (qmail 84260 invoked by alias); 27 Oct 2018 04:37:38 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 84122 invoked by uid 89); 27 Oct 2018 04:37:37 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS, TIME_LIMIT_EXCEEDED autolearn=unavailable version=3.3.2 spammy=Sign, cum, attempted, mr X-HELO: mail-pf1-f180.google.com Received: from mail-pf1-f180.google.com (HELO mail-pf1-f180.google.com) (209.85.210.180) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 27 Oct 2018 04:37:26 +0000 Received: by mail-pf1-f180.google.com with SMTP id j23-v6so1480218pfi.4 for ; Fri, 26 Oct 2018 21:37:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=eF+T1pqOn81kMX4nR1TsYo0RCxKuy1IirqX2w1qUYy4=; b=rn/fumNkbgnRsa5gFXTmbcuB+kPa+v7QzhmXvAv42YZBHgS1AuvhF14v9xlqVlKwH2 KeBZ7N4BBFsDSLSpiVZfdkn3hKZRIBgBLX+WWLLOybC/vhzKcWdgVsqHW/9N4zlyLm7b ZzpilujaC3jDrGEKisRtahQ7V6Li0ynz763LnnN4KCoS/m7Aj4B20VUlTmuyACFi4ypW hIggBO2ephW6ln0XMzyIoOCstf9PnioFF5Nj+KGkU/473lYJEk35SkbEjTBiVdtGuxvH sCFTPzAXpwCmGtNc+KabvAo8OPk+wR4RLwk7NejjVaxS5Tp3dHOLOFfqtI7GrnYWrN0B ThFQ== Received: from localhost (g106.218-225-177.ppp.wakwak.ne.jp. [218.225.177.106]) by smtp.gmail.com with ESMTPSA id s80-v6sm15289892pfa.114.2018.10.26.21.37.20 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 26 Oct 2018 21:37:21 -0700 (PDT) From: Stafford Horne To: GCC patches Cc: Openrisc , Richard Henderson , Jeff Law , Joseph Myers , Joel Sherrill , Sebastian Huber , Stafford Horne Subject: [PATCH v3 3/3] or1k: gcc: initial support for openrisc Date: Sat, 27 Oct 2018 13:37:02 +0900 Message-Id: <20181027043702.18414-4-shorne@gmail.com> In-Reply-To: <20181027043702.18414-1-shorne@gmail.com> References: <20181027043702.18414-1-shorne@gmail.com> X-IsSubscribed: yes yyyy-mm-dd Stafford Horne Richard Henderson Joel Sherrill gcc/ChangeLog: * common/config/or1k/or1k-common.c: New file. * config/or1k/*: New. * config.gcc (or1k*-*-*): New. * configure.ac (or1k*-*-*): New test for openrisc tls. * configure: Regenerated. * doc/install.texi: Document OpenRISC triplets. * doc/invoke.texi: Document OpenRISC arguments. * doc/md.texi: Document OpenRISC. --- gcc/common/config/or1k/or1k-common.c | 41 + gcc/config.gcc | 45 + gcc/config/or1k/constraints.md | 55 + gcc/config/or1k/elf.h | 42 + gcc/config/or1k/elf.opt | 33 + gcc/config/or1k/linux.h | 44 + gcc/config/or1k/or1k-protos.h | 38 + gcc/config/or1k/or1k.c | 2186 ++++++++++++++++++++++++++ gcc/config/or1k/or1k.h | 392 +++++ gcc/config/or1k/or1k.md | 907 +++++++++++ gcc/config/or1k/or1k.opt | 67 + gcc/config/or1k/predicates.md | 84 + gcc/config/or1k/rtems.h | 30 + gcc/config/or1k/t-or1k | 22 + gcc/config/or1k/t-rtems | 3 + gcc/configure | 12 + gcc/configure.ac | 12 + gcc/doc/install.texi | 19 + gcc/doc/invoke.texi | 68 + gcc/doc/md.texi | 25 + 20 files changed, 4125 insertions(+) create mode 100644 gcc/common/config/or1k/or1k-common.c create mode 100644 gcc/config/or1k/constraints.md create mode 100644 gcc/config/or1k/elf.h create mode 100644 gcc/config/or1k/elf.opt create mode 100644 gcc/config/or1k/linux.h create mode 100644 gcc/config/or1k/or1k-protos.h create mode 100644 gcc/config/or1k/or1k.c create mode 100644 gcc/config/or1k/or1k.h create mode 100644 gcc/config/or1k/or1k.md create mode 100644 gcc/config/or1k/or1k.opt create mode 100644 gcc/config/or1k/predicates.md create mode 100644 gcc/config/or1k/rtems.h create mode 100644 gcc/config/or1k/t-or1k create mode 100644 gcc/config/or1k/t-rtems diff --git a/gcc/common/config/or1k/or1k-common.c b/gcc/common/config/or1k/or1k-common.c new file mode 100644 index 00000000000..044e843fd19 --- /dev/null +++ b/gcc/common/config/or1k/or1k-common.c @@ -0,0 +1,41 @@ +/* Common hooks for OpenRISC + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "diagnostic-core.h" +#include "tm.h" +#include "common/common-target.h" +#include "common/common-target-def.h" +#include "opts.h" +#include "flags.h" + +/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ +static const struct default_options or1k_option_optimization_table[] = + { + /* Enable section anchors by default at -O1 or higher. */ + { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +#undef TARGET_OPTION_OPTIMIZATION_TABLE +#define TARGET_OPTION_OPTIMIZATION_TABLE or1k_option_optimization_table + +struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER; diff --git a/gcc/config.gcc b/gcc/config.gcc index 71f62a2aba2..0dc2ac4b879 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -468,6 +468,9 @@ nios2-*-*) nvptx-*-*) cpu_type=nvptx ;; +or1k*-*-*) + cpu_type=or1k + ;; powerpc*-*-*spe*) cpu_type=powerpcspe extra_headers="ppc-asm.h altivec.h spe.h ppu_intrinsics.h paired.h spu2vmx.h vec_types.h si2vmx.h htmintrin.h htmxlintrin.h" @@ -2464,6 +2467,48 @@ nvptx-*) tm_file="${tm_file} nvptx/offload.h" fi ;; +or1k*-*-*) + tm_file="elfos.h ${tm_file}" + tmake_file="${tmake_file} or1k/t-or1k" + # Force .init_array support. The configure script cannot always + # automatically detect that GAS supports it, yet we require it. + gcc_cv_initfini_array=yes + + # Handle --with-multilib-list=... + or1k_multilibs="${with_multilib_list}" + if test "$or1k_multilibs" = "default"; then + or1k_multilibs="mcmov,msoft-mul,msoft-div" + fi + or1k_multilibs=`echo $or1k_multilibs | sed -e 's/,/ /g'` + for or1k_multilib in ${or1k_multilibs}; do + case ${or1k_multilib} in + mcmov | msext | msfimm | \ + mhard-div | mhard-mul | \ + msoft-div | msoft-mul ) + TM_MULTILIB_CONFIG="${TM_MULTILIB_CONFIG},${or1k_multilib}" + ;; + *) + echo "--with-multilib-list=${with_multilib_list} not supported." + exit 1 + esac + done + TM_MULTILIB_CONFIG=`echo $TM_MULTILIB_CONFIG | sed 's/^,//'` + + case ${target} in + or1k*-*-linux*) + tm_file="${tm_file} gnu-user.h linux.h glibc-stdint.h" + tm_file="${tm_file} or1k/linux.h" + ;; + or1k*-*-elf*) + tm_file="${tm_file} newlib-stdint.h or1k/elf.h" + extra_options="${extra_options} or1k/elf.opt" + ;; + or1k*-*-rtems*) + tm_file="${tm_file} newlib-stdint.h or1k/rtems.h rtems.h" + tmake_file="${tmake_file} or1k/t-rtems" + ;; + esac + ;; pdp11-*-*) tm_file="${tm_file} newlib-stdint.h" use_gcc_stdint=wrap diff --git a/gcc/config/or1k/constraints.md b/gcc/config/or1k/constraints.md new file mode 100644 index 00000000000..aa321bc0f86 --- /dev/null +++ b/gcc/config/or1k/constraints.md @@ -0,0 +1,55 @@ +;; Constraint definitions for OpenRISC +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by Stafford Horne + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. + +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; . + +;; ------------------------------------------------------------------------- +;; Constraints +;; ------------------------------------------------------------------------- + +; We use: +; c - sibcall registers +; I - constant signed 16-bit +; K - constant unsigned 16-bit +; M - constant signed 16-bit shifted left 16-bits (l.movhi) +; O - constant zero + +(define_register_constraint "c" "SIBCALL_REGS" + "Registers which can hold a sibling call address") + +;; Immediates +(define_constraint "I" + "A signed 16-bit immediate in the range -32768 to 32767." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -32768, 32767)"))) + +(define_constraint "K" + "An unsigned 16-bit immediate in the range 0 to 0xffff." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 0, 65535)"))) + +(define_constraint "M" + "A shifted signed 16-bit constant suitable for l.movhi." + (and (match_code "const_int") + (match_test "(ival & 0xffff) == 0 + && (ival >> 31 == -1 || ival >> 31 == 0)"))) + +(define_constraint "O" + "The constant zero" + (and (match_code "const_int") + (match_test "ival == 0"))) diff --git a/gcc/config/or1k/elf.h b/gcc/config/or1k/elf.h new file mode 100644 index 00000000000..901f2b94388 --- /dev/null +++ b/gcc/config/or1k/elf.h @@ -0,0 +1,42 @@ +/* Target Newlib Definitions for OpenRISC. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Stafford Horne. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#ifndef GCC_OR1K_ELF_H +#define GCC_OR1K_ELF_H + +#undef LIB_SPEC +#define LIB_SPEC "--start-group -lc -lor1k " \ + "%{mboard=*:-lboard-%*; :-lboard-or1ksim} --end-group" + +#undef LINK_SPEC +#define LINK_SPEC "%{h*} \ + %{static:-Bstatic} \ + %{shared:-shared} \ + %{symbolic:-Bsymbolic} \ + %{!static:%{rdynamic:-export-dynamic}} \ + --entry=0x100" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt0.o%s crti.o%s crtbegin.o%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +#endif /* GCC_OR1K_ELF_H */ diff --git a/gcc/config/or1k/elf.opt b/gcc/config/or1k/elf.opt new file mode 100644 index 00000000000..956d395003d --- /dev/null +++ b/gcc/config/or1k/elf.opt @@ -0,0 +1,33 @@ +; OpenRISC command line options for newlib binaries + +; Copyright (C) 2010-2018 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License as published by the Free +; Software Foundation; either version 3, or (at your option) any later +; version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; . + +; See the GCC internals manual (options.texi) for a description of +; this file's format. + +; Please try to keep this file in ASCII collating order. + +mboard= +Target RejectNegative Joined +Configure board specific runtime. + +mnewlib +Target RejectNegative +For compatibility, it's always newlib for elf now. + diff --git a/gcc/config/or1k/linux.h b/gcc/config/or1k/linux.h new file mode 100644 index 00000000000..6dfa546f837 --- /dev/null +++ b/gcc/config/or1k/linux.h @@ -0,0 +1,44 @@ +/* Linux Definitions for OpenRISC. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Stafford Horne. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#ifndef GCC_OR1K_LINUX_H +#define GCC_OR1K_LINUX_H + +/* elfos.h should have already been included. Now just override + any conflicting definitions and add any extras. */ + +#define TARGET_OS_CPP_BUILTINS() \ + GNU_USER_TARGET_OS_CPP_BUILTINS () + +#define GLIBC_DYNAMIC_LINKER "/lib/ld-linux-or1k.so.1" + +#undef MUSL_DYNAMIC_LINKER +#define MUSL_DYNAMIC_LINKER "/lib/ld-musl-or1k.so.1" + +#undef LINK_SPEC +#define LINK_SPEC "%{h*} \ + %{static:-Bstatic} \ + %{shared:-shared} \ + %{symbolic:-Bsymbolic} \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!shared:-dynamic-linker " GNU_USER_DYNAMIC_LINKER "}}" + +#endif /* GCC_OR1K_LINUX_H */ diff --git a/gcc/config/or1k/or1k-protos.h b/gcc/config/or1k/or1k-protos.h new file mode 100644 index 00000000000..e18383ae781 --- /dev/null +++ b/gcc/config/or1k/or1k-protos.h @@ -0,0 +1,38 @@ +/* Prototypes for OpenRISC functions used in the md file & elsewhere. + Copyright (C) 2018 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +extern HOST_WIDE_INT or1k_initial_elimination_offset (int, int); +extern void or1k_expand_prologue (void); +extern void or1k_expand_epilogue (void); +extern void or1k_expand_eh_return (rtx); +extern rtx or1k_initial_frame_addr (void); +extern rtx or1k_dynamic_chain_addr (rtx); +extern rtx or1k_return_addr (int, rtx); +extern void or1k_expand_move (machine_mode, rtx, rtx); +extern void or1k_expand_compare (rtx *); +extern void or1k_expand_call (rtx, rtx, rtx, bool); + +#ifdef RTX_CODE +void or1k_expand_atomic_compare_and_swap (rtx operands[]); +void or1k_expand_atomic_compare_and_swap_qihi (rtx operands[]); +void or1k_expand_atomic_exchange (rtx operands[]); +void or1k_expand_atomic_exchange_qihi (rtx operands[]); +void or1k_expand_atomic_op (rtx_code, rtx, rtx, rtx, rtx); +void or1k_expand_atomic_op_qihi (rtx_code, rtx, rtx, rtx, rtx); +#endif diff --git a/gcc/config/or1k/or1k.c b/gcc/config/or1k/or1k.c new file mode 100644 index 00000000000..5d9ede41759 --- /dev/null +++ b/gcc/config/or1k/or1k.c @@ -0,0 +1,2186 @@ +/* Target Code for OpenRISC + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Stafford Horne based on other ports. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#define IN_TARGET_CODE 1 + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "stringpool.h" +#include "attribs.h" +#include "df.h" +#include "regs.h" +#include "memmodel.h" +#include "emit-rtl.h" +#include "diagnostic-core.h" +#include "output.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "expr.h" +#include "builtins.h" +#include "optabs.h" +#include "explow.h" +#include "cfgrtl.h" +#include "alias.h" + +/* These 4 are needed to allow using satisfies_constraint_J. */ +#include "insn-config.h" +#include "recog.h" +#include "tm_p.h" +#include "tm-constrs.h" + +/* This file should be included last. */ +#include "target-def.h" + +/* Per-function machine data. */ +struct GTY(()) machine_function +{ + /* Number of bytes saved on the stack for callee saved registers. */ + HOST_WIDE_INT callee_saved_reg_size; + + /* Number of bytes saved on the stack for local variables. */ + HOST_WIDE_INT local_vars_size; + + /* Number of bytes saved on the stack for outgoing/sub-fucntion args. */ + HOST_WIDE_INT args_size; + + /* The sum of sizes: locals vars, called saved regs, stack pointer + * and an optional frame pointer. + * Used in expand_prologue () and expand_epilogue(). */ + HOST_WIDE_INT total_size; + + /* Remember where the set_got_placeholder is located. */ + rtx_insn *set_got_insn; +}; + +/* Zero initialization is OK for all current fields. */ + +static struct machine_function * +or1k_init_machine_status (void) +{ + return ggc_cleared_alloc (); +} + + +/* Worker for TARGET_OPTION_OVERRIDE. + We currently only use this to setup init_machine_status. */ + +static void +or1k_option_override (void) +{ + /* Set the per-function-data initializer. */ + init_machine_status = or1k_init_machine_status; +} + +/* Returns true if REGNO must be saved for the current function. */ + +static bool +callee_saved_regno_p (int regno) +{ + /* Check call-saved registers. */ + if (!call_used_regs[regno] && df_regs_ever_live_p (regno)) + return true; + + switch (regno) + { + case HARD_FRAME_POINTER_REGNUM: + return frame_pointer_needed; + + case LR_REGNUM: + /* Always save LR if we are saving HFP, producing a walkable + stack chain with -fno-omit-frame-pointer. */ + return (frame_pointer_needed + || !crtl->is_leaf + || crtl->uses_pic_offset_table + || df_regs_ever_live_p (regno)); + + case HW_TO_GCC_REGNO (25): + case HW_TO_GCC_REGNO (27): + case HW_TO_GCC_REGNO (29): + case HW_TO_GCC_REGNO (31): + /* See EH_RETURN_DATA_REGNO. */ + return crtl->calls_eh_return; + + default: + return false; + } +} + +/* Worker for TARGET_COMPUTE_FRAME_LAYOUT. + Compute and populate machine specific function attributes which are globally + accessible via cfun->machine. These include the sizes needed for + stack stored local variables, callee saved registers and space for stack + arguments which may be passed to a next function. The values are used for + the epilogue, prologue and eliminations. + + OpenRISC stack grows downwards and contains: + + ---- previous frame -------- + current func arg[n] + current func arg[0] <-- r2 [HFP,AP] + ---- current stack frame --- ^ ---\ + return address r9 | | + old frame pointer r2 (+) |-- machine->total_size + callee saved regs | | > machine->callee_saved_reg_size + local variables | | > machine->local_vars_size <-FP + next function args <-- r1 [SP]---/ > machine->args_size + ---------------------------- | + (-) + (future) | + V + + All of these contents are optional. */ + +static void +or1k_compute_frame_layout (void) +{ + HOST_WIDE_INT local_vars_size, args_size, save_reg_size; + + local_vars_size = get_frame_size (); + local_vars_size = ROUND_UP (local_vars_size, UNITS_PER_WORD); + + args_size = crtl->outgoing_args_size; + args_size = ROUND_UP (args_size, UNITS_PER_WORD); + + save_reg_size = 0; + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (callee_saved_regno_p (regno)) + save_reg_size += UNITS_PER_WORD; + + cfun->machine->local_vars_size = local_vars_size; + cfun->machine->args_size = args_size; + cfun->machine->callee_saved_reg_size = save_reg_size; + cfun->machine->total_size = save_reg_size + local_vars_size + args_size; +} + +/* Emit rtl to save register REGNO contents to stack memory at the given OFFSET + from the current stack pointer. */ + +static void +or1k_save_reg (int regno, HOST_WIDE_INT offset) +{ + rtx reg = gen_rtx_REG (Pmode, regno); + rtx mem = gen_frame_mem (SImode, plus_constant (Pmode, stack_pointer_rtx, + offset)); + rtx insn = emit_move_insn (mem, reg); + RTX_FRAME_RELATED_P (insn) = 1; +} + +/* Emit rtl to restore register REGNO contents from stack memory at the given + OFFSET from the current stack pointer. */ + +static rtx +or1k_restore_reg (int regno, HOST_WIDE_INT offset, rtx cfa_restores) +{ + rtx reg = gen_rtx_REG (Pmode, regno); + rtx mem = gen_frame_mem (SImode, plus_constant (Pmode, stack_pointer_rtx, + offset)); + emit_move_insn (reg, mem); + return alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores); +} + +/* Expand the "prologue" pattern. */ + +void +or1k_expand_prologue (void) +{ + HOST_WIDE_INT sp_offset = -cfun->machine->total_size; + HOST_WIDE_INT reg_offset, this_offset; + rtx insn; + + if (flag_stack_usage_info) + current_function_static_stack_size = -sp_offset; + + /* Early exit for frameless functions. */ + if (sp_offset == 0) + goto fini; + + /* Adjust the stack pointer. For large stack offsets we will + do this in multiple parts, before and after saving registers. */ + reg_offset = (sp_offset + cfun->machine->local_vars_size + + cfun->machine->args_size); + this_offset = MAX (sp_offset, -32764); + reg_offset -= this_offset; + sp_offset -= this_offset; + + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (this_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + + /* Save callee-saved registers. */ + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (regno != HARD_FRAME_POINTER_REGNUM + && regno != LR_REGNUM + && callee_saved_regno_p (regno)) + { + or1k_save_reg (regno, reg_offset); + reg_offset += UNITS_PER_WORD; + } + + /* Save and update frame pointer. */ + if (callee_saved_regno_p (HARD_FRAME_POINTER_REGNUM)) + { + or1k_save_reg (HARD_FRAME_POINTER_REGNUM, reg_offset); + if (frame_pointer_needed) + { + insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, + GEN_INT (-this_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + } + reg_offset += UNITS_PER_WORD; + } + + /* Save the link register. */ + if (callee_saved_regno_p (LR_REGNUM)) + { + or1k_save_reg (LR_REGNUM, reg_offset); + reg_offset += UNITS_PER_WORD; + } + gcc_assert (reg_offset + this_offset == 0); + + /* Allocate the rest of the stack frame, if any. */ + if (sp_offset != 0) + { + if (sp_offset < 2 * -32768) + { + /* For very large offsets, we need a temporary register. */ + rtx tmp = gen_rtx_REG (Pmode, PE_TMP_REGNUM); + emit_move_insn (tmp, GEN_INT (sp_offset)); + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, tmp)); + if (!frame_pointer_needed) + { + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (stack_pointer_rtx, + plus_constant (Pmode, + stack_pointer_rtx, + sp_offset))); + } + } + else + { + /* Otherwise, emit one or two sequential subtracts. */ + do + { + this_offset = MAX (sp_offset, -32768); + sp_offset -= this_offset; + + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (this_offset))); + if (!frame_pointer_needed) + RTX_FRAME_RELATED_P (insn) = 1; + } + while (sp_offset != 0); + } + } + + fini: + /* Fix up, or remove, the insn that initialized the pic register. */ + rtx_insn *set_got_insn = cfun->machine->set_got_insn; + if (crtl->uses_pic_offset_table) + { + rtx reg = SET_DEST (PATTERN (set_got_insn)); + rtx_insn *insn = emit_insn_before (gen_set_got (reg), set_got_insn); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL_RTX); + } + delete_insn (set_got_insn); +} + +/* Expand the "epilogue" pattern. */ + +void +or1k_expand_epilogue (void) +{ + HOST_WIDE_INT reg_offset, sp_offset; + rtx insn, cfa_restores = NULL; + + sp_offset = cfun->machine->total_size; + if (sp_offset == 0) + return; + + reg_offset = cfun->machine->local_vars_size + cfun->machine->args_size; + + if (sp_offset >= 32768 || cfun->calls_alloca) + { + /* The saved registers are out of range of the stack pointer. + We need to partially deallocate the stack frame now. */ + if (frame_pointer_needed) + { + /* Reset the stack pointer to the bottom of the saved regs. */ + sp_offset -= reg_offset; + reg_offset = 0; + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, + hard_frame_pointer_rtx, + GEN_INT (-sp_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_DEF_CFA, + plus_constant (Pmode, stack_pointer_rtx, sp_offset)); + } + else if (sp_offset >= 3 * 32768) + { + /* For very large offsets, we need a temporary register. */ + rtx tmp = gen_rtx_REG (Pmode, PE_TMP_REGNUM); + emit_move_insn (tmp, GEN_INT (reg_offset)); + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, tmp)); + sp_offset -= reg_offset; + reg_offset = 0; + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_DEF_CFA, + plus_constant (Pmode, stack_pointer_rtx, sp_offset)); + } + else + { + /* Otherwise, emit one or two sequential additions. */ + do + { + HOST_WIDE_INT this_offset = MIN (reg_offset, 32764); + reg_offset -= this_offset; + sp_offset -= this_offset; + + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (this_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_DEF_CFA, + plus_constant (Pmode, stack_pointer_rtx, + sp_offset)); + } + while (sp_offset >= 32768); + } + } + + /* Restore callee-saved registers. */ + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (regno != HARD_FRAME_POINTER_REGNUM + && regno != LR_REGNUM + && callee_saved_regno_p (regno)) + { + cfa_restores = or1k_restore_reg (regno, reg_offset, cfa_restores); + reg_offset += UNITS_PER_WORD; + } + + /* Restore frame pointer. */ + if (callee_saved_regno_p (HARD_FRAME_POINTER_REGNUM)) + { + cfa_restores = or1k_restore_reg (HARD_FRAME_POINTER_REGNUM, + reg_offset, cfa_restores); + reg_offset += UNITS_PER_WORD; + } + + /* Restore link register. */ + if (callee_saved_regno_p (LR_REGNUM)) + { + cfa_restores = or1k_restore_reg (LR_REGNUM, reg_offset, cfa_restores); + reg_offset += UNITS_PER_WORD; + } + gcc_assert (reg_offset == sp_offset); + + /* Restore stack pointer. */ + insn = emit_insn (gen_frame_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (sp_offset))); + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = cfa_restores; + add_reg_note (insn, REG_CFA_DEF_CFA, stack_pointer_rtx); + + /* Move up to the stack frame of an exception handler. */ + if (crtl->calls_eh_return) + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); +} + +/* Worker for TARGET_INIT_PIC_REG. + Initialize the cfun->machine->set_got_insn rtx and insert it at the entry + of the current function. The rtx is just a temporary placeholder for + the GOT and will be replaced or removed during or1k_expand_prologue. */ + +static void +or1k_init_pic_reg (void) +{ + start_sequence (); + + cfun->machine->set_got_insn + = emit_insn (gen_set_got_tmp (pic_offset_table_rtx)); + + rtx_insn *seq = get_insns (); + end_sequence (); + + edge entry_edge = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + insert_insn_on_edge (seq, entry_edge); + commit_one_edge_insertion (entry_edge); +} + +#undef TARGET_INIT_PIC_REG +#define TARGET_INIT_PIC_REG or1k_init_pic_reg +#undef TARGET_USE_PSEUDO_PIC_REG +#define TARGET_USE_PSEUDO_PIC_REG hook_bool_void_true + +/* Worker for INITIAL_FRAME_ADDRESS_RTX. + Returns the RTX representing the address of the initial stack frame. */ + +rtx +or1k_initial_frame_addr () +{ + /* Use this to force a stack frame for the current function. */ + crtl->accesses_prior_frames = 1; + return arg_pointer_rtx; +} + +/* Worker for DYNAMIC_CHAIN_ADDRESS. + Returns the RTX representing the address of where the caller's frame pointer + may be stored on the stack. */ + +rtx +or1k_dynamic_chain_addr (rtx frame) +{ + return plus_constant (Pmode, frame, -2 * UNITS_PER_WORD); +} + +/* Worker for RETURN_ADDR_RTX. + Returns the RTX representing the address of where the link register may be + stored on the stack. */ + +rtx +or1k_return_addr (int, rtx frame) +{ + return gen_frame_mem (Pmode, plus_constant (Pmode, frame, -UNITS_PER_WORD)); +} + +/* Worker for TARGET_FRAME_POINTER_REQUIRED. + Returns true if the current function must use a frame pointer. */ + +static bool +or1k_frame_pointer_required () +{ + /* ??? While IRA checks accesses_prior_frames, reload does not. + We do want the frame pointer for this case. */ + return (crtl->accesses_prior_frames || crtl->profile); +} + +/* Expand the "eh_return" pattern. + Used for defining __builtin_eh_return, this will emit RTX to override the + current function's return address stored on the stack. The emitted RTX is + inserted before the epilogue so we can't just update the link register. + This is used when handling exceptions to jump into the exception handler + catch block upon return from _Unwind_RaiseException. */ + +void +or1k_expand_eh_return (rtx eh_addr) +{ + rtx lraddr; + + lraddr = gen_frame_mem (Pmode, plus_constant (Pmode, + arg_pointer_rtx, + -UNITS_PER_WORD)); + /* Set address to volitile to ensure the store doesn't get optimized out. */ + MEM_VOLATILE_P (lraddr) = true; + emit_move_insn (lraddr, eh_addr); +} + +/* Helper for defining INITIAL_ELIMINATION_OFFSET. + We allow the following eliminiations: + FP -> HARD_FP or SP + AP -> HARD_FP or SP + + HFP and AP are the same which is handled below. */ + +HOST_WIDE_INT +or1k_initial_elimination_offset (int from, int to) +{ + HOST_WIDE_INT offset; + + /* Set OFFSET to the offset from the stack pointer. */ + switch (from) + { + /* Incoming args are all the way up at the previous frame. */ + case ARG_POINTER_REGNUM: + offset = cfun->machine->total_size; + break; + + /* Local args grow downward from the saved registers. */ + case FRAME_POINTER_REGNUM: + offset = cfun->machine->args_size + cfun->machine->local_vars_size; + break; + + default: + gcc_unreachable (); + } + + if (to == HARD_FRAME_POINTER_REGNUM) + offset -= cfun->machine->total_size; + + return offset; +} + +/* Worker for TARGET_LEGITIMATE_ADDRESS_P. + Returns true if X is a legitimate address RTX on OpenRISC. */ + +static bool +or1k_legitimate_address_p (machine_mode, rtx x, bool strict_p) +{ + rtx base, addend; + + switch (GET_CODE (x)) + { + case REG: + base = x; + break; + + case PLUS: + base = XEXP (x, 0); + addend = XEXP (x, 1); + if (!REG_P (base)) + return false; + /* Register elimination is going to adjust all of these offsets. + We might as well keep them as a unit until then. */ + if (!strict_p && virtual_frame_reg_operand (base, VOIDmode)) + return CONST_INT_P (addend); + if (!satisfies_constraint_I (addend)) + return false; + break; + + case LO_SUM: + base = XEXP (x, 0); + if (!REG_P (base)) + return false; + x = XEXP (x, 1); + switch (GET_CODE (x)) + { + case CONST: + case SYMBOL_REF: + case LABEL_REF: + /* Assume legitimize_address properly categorized + the symbol. Continue to check the base. */ + break; + + case UNSPEC: + switch (XINT (x, 1)) + { + case UNSPEC_GOT: + case UNSPEC_GOTOFF: + case UNSPEC_TPOFF: + case UNSPEC_GOTTPOFF: + /* Assume legitimize_address properly categorized + the symbol. Continue to check the base. */ + break; + default: + return false; + } + break; + + default: + return false; + } + break; + + default: + return false; + } + + unsigned regno = REGNO (base); + if (regno >= FIRST_PSEUDO_REGISTER) + { + if (strict_p) + regno = reg_renumber[regno]; + else + return true; + } + if (strict_p) + return regno <= 31; + else + return REGNO_OK_FOR_BASE_P (regno); +} + +/* Return the TLS type for TLS symbols, 0 otherwise. */ + +static tls_model +or1k_tls_symbolic_operand (rtx op) +{ + rtx sym, addend; + split_const (op, &sym, &addend); + if (SYMBOL_REF_P (sym)) + return SYMBOL_REF_TLS_MODEL (sym); + return TLS_MODEL_NONE; +} + +/* Get a reference to the '__tls_get_addr' symbol. */ + +static GTY(()) rtx gen_tls_tga; + +static rtx +gen_tls_get_addr (void) +{ + if (!gen_tls_tga) + gen_tls_tga = init_one_libfunc ("__tls_get_addr"); + return gen_tls_tga; +} + +/* Emit a call to '__tls_get_addr'. */ + +static void +or1k_tls_call (rtx dest, rtx arg) +{ + emit_library_call_value (gen_tls_get_addr (), dest, LCT_CONST, + Pmode, arg, Pmode); +} + +/* Helper for or1k_legitimize_address_1. Wrap X in an unspec. */ + +static rtx +gen_sym_unspec (rtx x, int kind) +{ + return gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), kind); +} + +/* Worker for TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT. + Split an out-of-range address displacement into hi and lo parts. + The hi part will have to be loaded into a register separately, + but the low part will be folded into the memory operand. */ + +static bool +or1k_legitimize_address_displacement (rtx *off1, rtx *off2, + poly_int64 poly_offset, machine_mode) +{ + HOST_WIDE_INT orig_offset = poly_offset; + HOST_WIDE_INT lo, hi; + + /* If the displacement is within range of 2 addi insns, prefer that. + Otherwise split as per normal, at which point the register allocator + will see that OFF1 is not a valid add3 operand and load it into + a register, as desired. */ + if (orig_offset >= 0 && orig_offset < 2 * 32767) + { + hi = 32767; + lo = orig_offset - hi; + } + else if (orig_offset < 0 && orig_offset >= 2 * -32768) + { + hi = -32768; + lo = orig_offset - hi; + } + else + { + lo = sext_hwi (orig_offset, 16); + hi = orig_offset - lo; + } + + *off1 = GEN_INT (hi); + *off2 = GEN_INT (lo); + return true; +} + +#undef TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT +#define TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT \ + or1k_legitimize_address_displacement + +/* Helper function to implement both TARGET_LEGITIMIZE_ADDRESS and expand the + patterns "movqi", "movqi" and "movsi". Returns an valid OpenRISC RTX that + represents the argument X which is an invalid address RTX. The argument + SCRATCH may be used as a temporary when building addresses. */ + +static rtx +or1k_legitimize_address_1 (rtx x, rtx scratch) +{ + rtx base, addend, t1, t2; + tls_model tls_kind = TLS_MODEL_NONE; + bool is_local = true; + + split_const(x, &base, &addend); + switch (GET_CODE (base)) + { + default: + gcc_assert (can_create_pseudo_p ()); + base = force_reg (Pmode, base); + break; + + case REG: + case SUBREG: + break; + + case SYMBOL_REF: + tls_kind = SYMBOL_REF_TLS_MODEL (base); + is_local = SYMBOL_REF_LOCAL_P (base); + /* FALLTHRU */ + + case LABEL_REF: + switch (tls_kind) + { + case TLS_MODEL_NONE: + t1 = can_create_pseudo_p () ? gen_reg_rtx (Pmode) : scratch; + if (!flag_pic) + { + emit_insn (gen_rtx_SET (t1, gen_rtx_HIGH (Pmode, x))); + return gen_rtx_LO_SUM (Pmode, t1, x); + } + else if (is_local) + { + crtl->uses_pic_offset_table = 1; + t2 = gen_sym_unspec (x, UNSPEC_GOTOFF); + emit_insn (gen_rtx_SET (t1, gen_rtx_HIGH (Pmode, t2))); + emit_insn (gen_add3_insn (t1, t1, pic_offset_table_rtx)); + return gen_rtx_LO_SUM (Pmode, t1, copy_rtx (t2)); + } + else + { + base = gen_sym_unspec (base, UNSPEC_GOT); + crtl->uses_pic_offset_table = 1; + t2 = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, base); + t2 = gen_const_mem (Pmode, t2); + emit_insn (gen_rtx_SET (t1, t2)); + base = t1; + } + break; + + case TLS_MODEL_GLOBAL_DYNAMIC: + case TLS_MODEL_LOCAL_DYNAMIC: + /* TODO: For now, treat LD as GD. */ + t1 = gen_reg_rtx (Pmode); + base = gen_sym_unspec (base, UNSPEC_TLSGD); + emit_insn (gen_rtx_SET (t1, gen_rtx_HIGH (Pmode, base))); + emit_insn (gen_rtx_SET (t1, gen_rtx_LO_SUM (Pmode, t1, base))); + crtl->uses_pic_offset_table = 1; + emit_insn (gen_add3_insn (t1, t1, pic_offset_table_rtx)); + base = gen_reg_rtx (Pmode); + or1k_tls_call (base, t1); + break; + + case TLS_MODEL_INITIAL_EXEC: + t1 = gen_reg_rtx (Pmode); + t2 = gen_reg_rtx (Pmode); + base = gen_sym_unspec (base, UNSPEC_GOTTPOFF); + emit_insn (gen_rtx_SET (t1, gen_rtx_HIGH (Pmode, base))); + crtl->uses_pic_offset_table = 1; + emit_insn (gen_add3_insn (t1, t1, pic_offset_table_rtx)); + t1 = gen_rtx_LO_SUM (Pmode, t1, base); + emit_move_insn (t2, gen_const_mem (Pmode, t1)); + t1 = gen_rtx_REG (Pmode, TLS_REGNUM); + emit_insn (gen_add3_insn (t2, t2, t1)); + base = t2; + break; + + case TLS_MODEL_LOCAL_EXEC: + x = gen_sym_unspec (x, UNSPEC_TPOFF); + t1 = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (t1, gen_rtx_HIGH (Pmode, x))); + t2 = gen_rtx_REG (Pmode, TLS_REGNUM); + emit_insn (gen_add3_insn (t1, t1, t2)); + return gen_rtx_LO_SUM (Pmode, t1, x); + + default: + gcc_unreachable (); + } + break; + + /* + * Accept what we may have already emitted. + */ + + case LO_SUM: + case UNSPEC: + return x; + } + + /* If we get here, we still have addend outstanding. */ + gcc_checking_assert (register_operand (base, Pmode)); + if (addend == const0_rtx) + return base; + if (satisfies_constraint_I (addend) + || virtual_frame_reg_operand (base, VOIDmode)) + return gen_rtx_PLUS (Pmode, base, addend); + else + { + rtx hi, lo; + bool ok = (or1k_legitimize_address_displacement + (&hi, &lo, INTVAL (addend), SImode)); + gcc_assert (ok); + + t2 = can_create_pseudo_p () ? gen_reg_rtx (Pmode) : scratch; + if (satisfies_constraint_I (hi)) + emit_insn (gen_addsi3 (t2, base, hi)); + else + { + t1 = can_create_pseudo_p () ? gen_reg_rtx (Pmode) : scratch; + emit_move_insn (t1, hi); + emit_insn (gen_add3_insn (t2, base, t1)); + } + if (lo == const0_rtx) + return t2; + else + return gen_rtx_PLUS (Pmode, t2, lo); + } +} + +/* Worker for TARGET_LEGITIMIZE_ADDRESS. + This delegates implementation to or1k_legitimize_address_1. */ + +static rtx +or1k_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, machine_mode) +{ + return or1k_legitimize_address_1 (x, NULL_RTX); +} + +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS or1k_legitimize_address + +/* Worker for TARGET_DELEGITIMIZE_ADDRESS. + In the name of slightly smaller debug output, and to cater to + general assembler lossage, recognize PIC+GOTOFF and turn it back + into a direct symbol reference. */ + +static rtx +or1k_delegitimize_address (rtx x) +{ + if (GET_CODE (x) == UNSPEC) + { + /* The LO_SUM to which X was attached has been stripped. + Since the only legitimate address we could have been computing + is that of the symbol, assume that's what we've done. */ + if (XINT (x, 1) == UNSPEC_GOTOFF) + return XVECEXP (x, 0, 0); + } + else if (MEM_P (x)) + { + rtx addr = XEXP (x, 0); + if (GET_CODE (addr) == LO_SUM + && XEXP (addr, 0) == pic_offset_table_rtx) + { + rtx inner = XEXP (addr, 1); + if (GET_CODE (inner) == UNSPEC + && XINT (inner, 1) == UNSPEC_GOT) + return XVECEXP (inner, 0, 0); + } + } + return delegitimize_mem_from_attrs (x); +} + +#undef TARGET_DELEGITIMIZE_ADDRESS +#define TARGET_DELEGITIMIZE_ADDRESS or1k_delegitimize_address + +/* Worker for TARGET_CANNOT_FORCE_CONST_MEM. + Primarily this is required for TLS symbols, but given that our move + patterns *ought* to be able to handle any symbol at any time, we + should never be spilling symbolic operands to the constant pool, ever. */ + +static bool +or1k_cannot_force_const_mem (machine_mode, rtx x) +{ + rtx_code code = GET_CODE (x); + return (code == SYMBOL_REF + || code == LABEL_REF + || code == CONST + || code == HIGH); +} + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM or1k_cannot_force_const_mem + +/* Worker for TARGET_LEGITIMATE_CONSTANT_P. + Returns true is the RTX X represents a constant that can be used as an + immediate operand in OpenRISC. */ + +static bool +or1k_legitimate_constant_p (machine_mode, rtx x) +{ + switch (GET_CODE (x)) + { + case CONST_INT: + case CONST_WIDE_INT: + case HIGH: + /* We construct these, rather than spilling to memory. */ + return true; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + /* These may need to be split and not reconstructed. */ + return or1k_tls_symbolic_operand (x) == TLS_MODEL_NONE; + + default: + return false; + } +} + +#undef TARGET_LEGITIMATE_CONSTANT_P +#define TARGET_LEGITIMATE_CONSTANT_P or1k_legitimate_constant_p + +/* Worker for TARGET_PASS_BY_REFERENCE. + Returns true if an argument of TYPE in MODE should be passed by reference + as required by the OpenRISC ABI. On OpenRISC structures, unions and + arguments larger than 64-bits are passed by reference. */ + +static bool +or1k_pass_by_reference (cumulative_args_t, machine_mode mode, + const_tree type, bool) +{ + HOST_WIDE_INT size; + if (type) + { + if (AGGREGATE_TYPE_P (type)) + return true; + size = int_size_in_bytes (type); + } + else + size = GET_MODE_SIZE (mode); + return size < 0 || size > 8; +} + +/* Worker for TARGET_FUNCTION_VALUE. + Returns an RTX representing the location where function return values will + be stored. On OpenRISC this is the register r11. 64-bit return value's + upper 32-bits are returned in r12, this is automatically done by GCC. */ + +static rtx +or1k_function_value (const_tree valtype, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (TYPE_MODE (valtype), RV_REGNUM); +} + +/* Worker for TARGET_LIBCALL_VALUE. + Returns an RTX representing the location where function return values to + external libraries will be stored. On OpenRISC this the same as local + function calls. */ + +static rtx +or1k_libcall_value (machine_mode mode, + const_rtx fun ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, RV_REGNUM); +} + + +/* Worker for TARGET_FUNCTION_VALUE_REGNO_P. + Returns true if REGNO is a valid register for storing a function return + value. */ + +static bool +or1k_function_value_regno_p (const unsigned int regno) +{ + return (regno == RV_REGNUM); +} + +/* Worker for TARGET_STRICT_ARGUMENT_NAMING. + The final named argument in a variatic function is named. */ + +static bool +or1k_strict_argument_naming (cumulative_args_t ca ATTRIBUTE_UNUSED) +{ + return true; +} + +#undef TARGET_STRICT_ARGUMENT_NAMING +#define TARGET_STRICT_ARGUMENT_NAMING or1k_strict_argument_naming + +/* Worker for TARGET_FUNCTION_ARG. + Return the next register to be used to hold a function argument or NULL_RTX + if there's no more space. Arugment CUM_V represents the current argument + offset, zero for the first function argument. OpenRISC function arguments + maybe be passed in registers r3 to r8. */ + +static rtx +or1k_function_arg (cumulative_args_t cum_v, machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, + bool named) +{ + /* VOIDmode is passed as a special flag for "last argument". */ + if (mode == VOIDmode) + return NULL_RTX; + + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int nreg = CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD); + + /* Note that all large arguments are passed by reference. */ + gcc_assert (nreg <= 2); + if (named && *cum + nreg <= 6) + return gen_rtx_REG (mode, *cum + 3); + else + return NULL_RTX; +} + +/* Worker for TARGET_FUNCTION_ARG_ADVANCE. + Update the cumulative args descriptor CUM_V to advance past the next function + argument. Note, this is not called for arguments passed on the stack. */ + +static void +or1k_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, bool named) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + int nreg = CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD); + + /* Note that all large arguments are passed by reference. */ + gcc_assert (nreg <= 2); + if (named) + *cum += nreg; +} + +/* worker function for TARGET_RETURN_IN_MEMORY. + Returns true if the argument of TYPE should be returned in memory. On + OpenRISC this is any value larger than 64-bits. */ + +static bool +or1k_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +{ + const HOST_WIDE_INT size = int_size_in_bytes (type); + return (size == -1 || size > (2 * UNITS_PER_WORD)); +} + +/* Print reloc(x + add). */ + +static void +output_addr_reloc (FILE *stream, rtx x, HOST_WIDE_INT add, const char *reloc) +{ + if (*reloc) + { + fputs (reloc, stream); + fputc ('(', stream); + } + output_addr_const (stream, x); + if (add) + { + if (add > 0) + fputc ('+', stream); + fprintf (stream, HOST_WIDE_INT_PRINT_DEC, add); + } + if (*reloc) + fputc (')', stream); +} + +enum reloc_kind +{ + RKIND_LO, + RKIND_HI, + RKIND_MAX +}; + +enum reloc_type +{ + RTYPE_DIRECT, + RTYPE_GOT, + RTYPE_GOTOFF, + RTYPE_TPOFF, + RTYPE_GOTTPOFF, + RTYPE_TLSGD, + RTYPE_MAX +}; + +static void +print_reloc (FILE *stream, rtx x, HOST_WIDE_INT add, reloc_kind kind) +{ + /* All data relocations. A NULL in this table indicates a form that + we expect to never generate, while "" indicates a form that requires + no special markup. */ + static const char * const relocs[RKIND_MAX][RTYPE_MAX] = { + { "lo", "got", "gotofflo", "tpofflo", "gottpofflo", "tlsgdlo" }, + { "ha", NULL, "gotoffha", "tpoffha", "gottpoffha", "tlsgdhi" }, + }; + reloc_type type = RTYPE_DIRECT; + + if (GET_CODE (x) == UNSPEC) + { + switch (XINT (x, 1)) + { + case UNSPEC_GOT: + type = RTYPE_GOT; + break; + case UNSPEC_GOTOFF: + type = RTYPE_GOTOFF; + break; + case UNSPEC_TPOFF: + type = RTYPE_TPOFF; + break; + case UNSPEC_GOTTPOFF: + type = RTYPE_GOTTPOFF; + break; + case UNSPEC_TLSGD: + type = RTYPE_TLSGD; + break; + default: + output_operand_lossage("invalid relocation"); + return; + } + x = XVECEXP (x, 0, 0); + } + + const char *reloc = relocs[kind][type]; + if (reloc == NULL) + output_operand_lossage("invalid relocation"); + else + output_addr_reloc (stream, x, add, reloc); +} + +/* Worker for TARGET_PRINT_OPERAND_ADDRESS. + Prints the argument ADDR, an address RTX, to the file FILE. The output is + formed as expected by the OpenRISC assembler. Examples: + + RTX OUTPUT + (reg:SI 3) 0(r3) + (plus:SI (reg:SI 3) (const_int 4)) 0x4(r3) + (lo_sum:SI (reg:SI 3) (symbol_ref:SI ("x")))) lo(x)(r3) */ + +static void +or1k_print_operand_address (FILE *file, machine_mode, rtx addr) +{ + rtx offset; + + switch (GET_CODE (addr)) + { + case REG: + fputc ('0', file); + break; + + case PLUS: + offset = XEXP (addr, 1); + addr = XEXP (addr, 0); + gcc_assert (CONST_INT_P (offset)); + if (GET_CODE (addr) == LO_SUM) + { + print_reloc (file, XEXP (addr, 1), INTVAL (offset), RKIND_LO); + addr = XEXP (addr, 0); + } + else + output_addr_const (file, offset); + break; + + case LO_SUM: + offset = XEXP (addr, 1); + addr = XEXP (addr, 0); + print_reloc (file, offset, 0, RKIND_LO); + break; + + default: + output_addr_const (file, addr); + return; + } + + fprintf (file, "(%s)", reg_names[REGNO (addr)]); +} + +/* Worker for TARGET_PRINT_OPERAND. + Print operand X, an RTX, to the file FILE. The output is formed as expected + by the OpenRISC assember. CODE is the letter following a '%' in an + instrunction template used to control the RTX output. Example(s): + + CODE RTX OUTPUT COMMENT + 0 (reg:SI 3) r3 output an operand + r (reg:SI 3) r3 output a register or const zero + H (reg:SI 3) r4 output the high pair register + h (symbol_ref:SI ("x")) ha(x) output a signed high relocation + L (symbol_ref:SI ("x")) lo(x) output a low relocation + + Note, '#' is a special code used to fill the branch delay slot with an l.nop + instruction. The l.nop (no-op) instruction is only outputted when the delay + slot has not been filled. */ + +static void +or1k_print_operand (FILE *file, rtx x, int code) +{ + rtx operand = x; + + switch (code) + { + case '#': + /* Conditionally add a nop in unfilled delay slot. */ + if (final_sequence == NULL) + fputs ("\n\t l.nop\n", file); + break; + + case 'r': + if (REG_P (x)) + fprintf (file, "%s", reg_names[REGNO (operand)]); + else if (x == CONST0_RTX (GET_MODE (x))) + fprintf (file, "r0"); + else + output_operand_lossage ("invalid %%r value"); + break; + + case 'H': + if (REG_P (x)) + fprintf (file, "%s", reg_names[REGNO (operand) + 1]); + else + output_operand_lossage ("invalid %%H value"); + break; + + case 'h': + print_reloc (file, x, 0, RKIND_HI); + break; + case 'L': + print_reloc (file, x, 0, RKIND_LO); + break; + case 'P': + if (!flag_pic || SYMBOL_REF_LOCAL_P (x)) + output_addr_const (file, x); + else + output_addr_reloc (file, x, 0, "plt"); + break; + + case 0: + /* Print an operand as without a modifier letter. */ + switch (GET_CODE (operand)) + { + case REG: + if (REGNO (operand) > 31) + internal_error ("internal error: bad register: %d", + REGNO (operand)); + fprintf (file, "%s", reg_names[REGNO (operand)]); + break; + + case MEM: + output_address (GET_MODE (XEXP (operand, 0)), XEXP (operand, 0)); + break; + + case CODE_LABEL: + case LABEL_REF: + output_asm_label (operand); + break; + + default: + /* No need to handle all strange variants, let output_addr_const + do it for us. */ + if (CONSTANT_P (operand)) + output_addr_const (file, operand); + else + internal_error ("unexpected operand: %d", GET_CODE (operand)); + break; + } + break; + + default: + output_operand_lossage ("unknown operand letter: '%c'", code); + break; + } +} + +/* Worker for TARGET_TRAMPOLINE_INIT. + This is called to initialize a trampoline. The argument M_TRAMP is an RTX + for the memory block to be initialized with trampoline code. The argument + FNDECL contains the definition of the nested function to be called, we use + this to get the function's address. The argument CHAIN is an RTX for the + static chain value to be passed to the nested function. */ + +static void +or1k_trampoline_init (rtx m_tramp, tree fndecl, rtx chain) +{ + const unsigned movhi_r13 = (0x06u << 26) | (13 << 21); + const unsigned movhi_r11 = (0x06u << 26) | (11 << 21); + const unsigned ori_r13_r13 = (0x2a << 26) | (13 << 21) | (13 << 16); + const unsigned ori_r11_r11 = (0x2a << 26) | (11 << 21) | (11 << 16); + const unsigned jr_r13 = (0x11 << 26) | (13 << 11); + rtx tramp[5], fnaddr, f_hi, f_lo, c_hi, c_lo; + + fnaddr = force_operand (XEXP (DECL_RTL (fndecl), 0), NULL); + f_hi = expand_binop (SImode, lshr_optab, fnaddr, GEN_INT (16), + NULL, true, OPTAB_DIRECT); + f_lo = expand_binop (SImode, and_optab, fnaddr, GEN_INT (0xffff), + NULL, true, OPTAB_DIRECT); + + chain = force_operand (chain, NULL); + c_hi = expand_binop (SImode, lshr_optab, chain, GEN_INT (16), + NULL, true, OPTAB_DIRECT); + c_lo = expand_binop (SImode, and_optab, chain, GEN_INT (0xffff), + NULL, true, OPTAB_DIRECT); + + /* We want to generate + * + * l.movhi r13,hi(nested_func) + * l.movhi r11,hi(static_chain) + * l.ori r13,r13,lo(nested_func) + * l.jr r13 + * l.ori r11,r11,lo(static_chain) + */ + tramp[0] = expand_binop (SImode, ior_optab, f_hi, + gen_int_mode (movhi_r13, SImode), + f_hi, true, OPTAB_DIRECT); + tramp[1] = expand_binop (SImode, ior_optab, c_hi, + gen_int_mode (movhi_r11, SImode), + c_hi, true, OPTAB_DIRECT); + tramp[2] = expand_binop (SImode, ior_optab, f_lo, + gen_int_mode (ori_r13_r13, SImode), + f_lo, true, OPTAB_DIRECT); + tramp[4] = expand_binop (SImode, ior_optab, c_lo, + gen_int_mode (ori_r11_r11, SImode), + c_lo, true, OPTAB_DIRECT); + tramp[3] = gen_int_mode (jr_r13, SImode); + + for (int i = 0; i < 5; ++i) + { + rtx mem = adjust_address (m_tramp, SImode, i * 4); + emit_move_insn (mem, tramp[i]); + } + + /* Flushing the trampoline from the instruction cache needs + to be done here. */ +} + +/* Worker for TARGET_HARD_REGNO_MODE_OK. + Returns true if the hard register REGNO is ok for storing values of mode + MODE. */ + +static bool +or1k_hard_regno_mode_ok (unsigned int regno, machine_mode mode) +{ + /* For OpenRISC, GENERAL_REGS can hold anything, while + FLAG_REGS are really single bits within SP[SR]. */ + if (REGNO_REG_CLASS (regno) == FLAG_REGS) + return mode == BImode; + return true; +} + +#undef TARGET_HARD_REGNO_MODE_OK +#define TARGET_HARD_REGNO_MODE_OK or1k_hard_regno_mode_ok + +/* Worker for TARGET_CAN_CHANGE_MODE_CLASS. + Returns true if its ok to change a register in class RCLASS from mode FROM to + mode TO. In general OpenRISC registers, other than special flags, handle all + supported classes. */ + +static bool +or1k_can_change_mode_class (machine_mode from, machine_mode to, + reg_class_t rclass) +{ + if (rclass == FLAG_REGS) + return from == to; + return true; +} + +#undef TARGET_CAN_CHANGE_MODE_CLASS +#define TARGET_CAN_CHANGE_MODE_CLASS or1k_can_change_mode_class + +/* Expand the patterns "movqi", "movqi" and "movsi". The argument OP0 is the + destination and OP1 is the source. This expands to set OP0 to OP1. OpenRISC + cannot do memory to memory assignments so for those cases we force one + argument to a register. Constants that can't fit into a 16-bit immediate are + split. Symbols are lagitimized using split relocations. */ + +void +or1k_expand_move (machine_mode mode, rtx op0, rtx op1) +{ + if (MEM_P (op0)) + { + if (!const0_operand(op1, mode)) + op1 = force_reg (mode, op1); + } + else if (mode == QImode || mode == HImode) + { + /* ??? Maybe promote MEMs and CONST_INT to SImode, + and then squish back with gen_lowpart. */ + } + else + { + switch (GET_CODE (op1)) + { + case CONST_INT: + if (!input_operand (op1, mode)) + { + HOST_WIDE_INT i = INTVAL (op1); + HOST_WIDE_INT lo = i & 0xffff; + HOST_WIDE_INT hi = i ^ lo; + rtx subtarget = op0; + + if (!cse_not_expected && can_create_pseudo_p ()) + subtarget = gen_reg_rtx (SImode); + emit_insn (gen_rtx_SET (subtarget, GEN_INT (hi))); + emit_insn (gen_iorsi3 (op0, subtarget, GEN_INT (lo))); + return; + } + break; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + op1 = or1k_legitimize_address_1 (op1, op0); + break; + + default: + break; + } + } + emit_insn (gen_rtx_SET (op0, op1)); +} + +/* Used to expand patterns "movsicc", "movqicc", "movhicc", "cstoresi4" and + "cbranchsi4". + Expands a comparison where OPERANDS is an array of RTX describing the + comparison. The first argument OPERANDS[0] is the operator and OPERANDS[1] + and OPERANDS[2] are the operands. Split out the compare into SR[F] and + return a new operation in OPERANDS[0]. The inputs OPERANDS[1] and + OPERANDS[2] are not directly used, only overridden. */ + +void +or1k_expand_compare (rtx *operands) +{ + rtx sr_f = gen_rtx_REG (BImode, SR_F_REGNUM); + + /* The RTL may receive an immediate in argument 1 of the compare, this is not + supported unless we have l.sf*i instructions, force them into registers. */ + if (!TARGET_SFIMM) + XEXP (operands[0], 1) = force_reg (SImode, XEXP (operands[0], 1)); + + /* Emit the given comparison into the Flag bit. */ + PUT_MODE (operands[0], BImode); + emit_insn (gen_rtx_SET (sr_f, operands[0])); + + /* Adjust the operands for use in the caller. */ + operands[0] = gen_rtx_NE (VOIDmode, sr_f, const0_rtx); + operands[1] = sr_f; + operands[2] = const0_rtx; +} + +/* Expand the patterns "call", "sibcall", "call_value" and "sibcall_value". + Expands a function call where argument RETVAL is an optional RTX providing + return value storage, the argument FNADDR is and RTX describing the function + to call, the argument CALLARG1 is the number or registers used as operands + and the argument SIBCALL should be true if this is a nested function call. + If FNADDR is a non local symbol and FLAG_PIC is enabled this will generate + a PLT call. */ + +void +or1k_expand_call (rtx retval, rtx fnaddr, rtx callarg1, bool sibcall) +{ + rtx call, use = NULL; + + /* Calls via the PLT require the PIC register. */ + if (flag_pic + && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF + && !SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0))) + { + crtl->uses_pic_offset_table = 1; + rtx hard_pic = gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM); + emit_move_insn (hard_pic, pic_offset_table_rtx); + use_reg (&use, hard_pic); + } + + if (!call_insn_operand (XEXP (fnaddr, 0), Pmode)) + { + fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0)); + fnaddr = gen_rtx_MEM (SImode, fnaddr); + } + + call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1); + if (retval) + call = gen_rtx_SET (retval, call); + + /* Normal calls clobber LR. This is required in order to + prevent e.g. a prologue store of LR being placed into + the delay slot of the call, after it has been updated. */ + if (!sibcall) + { + rtx clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNUM)); + call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, call, clob)); + } + call = emit_call_insn (call); + + CALL_INSN_FUNCTION_USAGE (call) = use; +} + +/* Worker for TARGET_FUNCTION_OK_FOR_SIBCALL. + Returns true if the function declared by DECL is ok for calling as a nested + function. */ + +static bool +or1k_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) +{ + /* We can sibcall to any function if not PIC. */ + if (!flag_pic) + return true; + + /* We can sibcall any indirect function. */ + if (decl == NULL) + return true; + + /* If the call may go through the PLT, we need r16 live. */ + return targetm.binds_local_p (decl); +} + +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL or1k_function_ok_for_sibcall + +/* Worker for TARGET_RTX_COSTS. */ + +static bool +or1k_rtx_costs (rtx x, machine_mode mode, int outer_code, + int opno ATTRIBUTE_UNUSED, int *total, + bool speed ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (x)) + { + case CONST_INT: + if (x == const0_rtx) + *total = 0; + else if ((outer_code == PLUS || outer_code == XOR || outer_code == MULT) + && satisfies_constraint_I (x)) + *total = 0; + else if ((outer_code == AND || outer_code == IOR) + && satisfies_constraint_K (x)) + *total = 0; + else if (satisfies_constraint_I (x) + || satisfies_constraint_K (x) + || satisfies_constraint_M (x)) + *total = 2; + else + *total = COSTS_N_INSNS (2); + return true; + + case CONST_DOUBLE: + *total = (x == CONST0_RTX (mode) ? 0 : COSTS_N_INSNS (2)); + return true; + + case HIGH: + /* This is effectively an 'M' constraint. */ + *total = 2; + return true; + + case LO_SUM: + /* This is effectively an 'I' constraint. */ + *total = (outer_code == MEM ? 0 : 2); + return true; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (outer_code == LO_SUM || outer_code == HIGH) + *total = 0; + else + { + /* ??? Extra cost for GOT or TLS symbols. */ + *total = COSTS_N_INSNS (1 + (outer_code != MEM)); + } + return true; + + case PLUS: + if (outer_code == MEM) + *total = 0; + break; + + default: + break; + } + return false; +} + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS or1k_rtx_costs + + +/* A subroutine of the atomic operation splitters. Jump to LABEL if + COND is true. Mark the jump as unlikely to be taken. */ + +static void +emit_unlikely_jump (rtx_code code, rtx label) +{ + rtx x; + + x = gen_rtx_REG (BImode, SR_F_REGNUM); + x = gen_rtx_fmt_ee (code, VOIDmode, x, const0_rtx); + x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label, pc_rtx); + emit_jump_insn (gen_rtx_SET (pc_rtx, x)); + + // Disable this for now -- producing verify_cfg failures on probabilities. + // int very_unlikely = REG_BR_PROB_BASE / 100 - 1; + // add_int_reg_note (insn, REG_BR_PROB, very_unlikely); +} + +/* A subroutine of the atomic operation splitters. + Emit a raw comparison for A CODE B. */ + +static void +emit_compare (rtx_code code, rtx a, rtx b) +{ + emit_insn (gen_rtx_SET (gen_rtx_REG (BImode, SR_F_REGNUM), + gen_rtx_fmt_ee (code, BImode, a, b))); +} + +/* A subroutine of the atomic operation splitters. + Emit a load-locked instruction in MODE. */ + +static void +emit_load_locked (machine_mode mode, rtx reg, rtx mem) +{ + gcc_assert (mode == SImode); + emit_insn (gen_load_locked_si (reg, mem)); +} + +/* A subroutine of the atomic operation splitters. + Emit a store-conditional instruction in MODE. */ + +static void +emit_store_conditional (machine_mode mode, rtx mem, rtx val) +{ + gcc_assert (mode == SImode); + emit_insn (gen_store_conditional_si (mem, val)); +} + +/* A subroutine of the various atomic expanders. For sub-word operations, + we must adjust things to operate on SImode. Given the original MEM, + return a new aligned memory. Also build and return the quantities by + which to shift and mask. */ + +static rtx +or1k_adjust_atomic_subword (rtx orig_mem, rtx *pshift, rtx *pmask) +{ + rtx addr, align, shift, mask, mem; + machine_mode mode = GET_MODE (orig_mem); + + addr = XEXP (orig_mem, 0); + addr = force_reg (Pmode, addr); + + /* Aligned memory containing subword. Generate a new memory. We + do not want any of the existing MEM_ATTR data, as we're now + accessing memory outside the original object. */ + align = expand_binop (Pmode, and_optab, addr, GEN_INT (-4), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + mem = gen_rtx_MEM (SImode, align); + MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem); + if (MEM_ALIAS_SET (orig_mem) == ALIAS_SET_MEMORY_BARRIER) + set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER); + + /* Shift amount for subword relative to aligned word. */ + rtx mode_mask = GEN_INT (mode == QImode ? 3 : 2); + shift = expand_binop (SImode, and_optab, gen_lowpart (SImode, addr), + mode_mask, NULL_RTX, 1, OPTAB_LIB_WIDEN); + if (BYTES_BIG_ENDIAN) + shift = expand_binop (SImode, xor_optab, shift, mode_mask, + shift, 1, OPTAB_LIB_WIDEN); + shift = expand_binop (SImode, ashl_optab, shift, GEN_INT (3), + shift, 1, OPTAB_LIB_WIDEN); + *pshift = shift; + + /* Mask for insertion. */ + mask = expand_binop (SImode, ashl_optab, GEN_INT (GET_MODE_MASK (mode)), + shift, NULL_RTX, 1, OPTAB_LIB_WIDEN); + *pmask = mask; + + return mem; +} + +/* A subroutine of the various atomic expanders. For sub-word operations, + complete the operation by shifting result to the lsb of the SImode + temporary and then extracting the result in MODE with a SUBREG. */ + +static void +or1k_finish_atomic_subword (machine_mode mode, rtx o, rtx n, rtx shift) +{ + n = expand_binop (SImode, lshr_optab, n, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + emit_move_insn (o, gen_lowpart (mode, n)); +} + +/* Expand an atomic compare and swap operation. + Emits the RTX to perform a compare and swap operation. This function takes + 8 RTX arguments in the OPERANDS array. The compare and swap operation + loads a value from memory (OPERANDS[2]) and compares it with an expected + value (OPERANDS[3]), if the values are equal it stores a new value + (OPERANDS[4]) to memory. The argument OPERANDS[0] represents a boolean + result which will be set to true if the operation succeeds. A return value + (OPERANDS[1]) will be set to what was loaded from memory. The argument + OPERAND[5] is used to indicate if the compare and swap is to be treated as + weak. OpenRISC does not use OPERANDS[5] or OPERANDS[6] which provide memory + model details. + For OpenRISC this emits RTX which will translate to assembly using the + 'l.lwa' (load word atomic) and 'l.swa' (store word atomic) instructions. */ + +void +or1k_expand_atomic_compare_and_swap (rtx operands[]) +{ + rtx boolval, retval, mem, oldval, newval; + rtx label1, label2; + machine_mode mode; + bool is_weak; + + boolval = operands[0]; + retval = operands[1]; + mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + is_weak = (INTVAL (operands[5]) != 0); + mode = GET_MODE (mem); + + if (reg_overlap_mentioned_p (retval, oldval)) + oldval = copy_to_reg (oldval); + + label1 = NULL_RTX; + /* If strong, create a label to try again. */ + if (!is_weak) + { + label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label1, 0)); + } + label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + + emit_load_locked (mode, retval, mem); + emit_compare (EQ, retval, oldval); + emit_unlikely_jump (EQ, label2); + emit_store_conditional (mode, mem, newval); + + /* If strong, jump back to try again on fails. */ + if (!is_weak) + emit_unlikely_jump (EQ, label1); + emit_label (XEXP (label2, 0)); + + /* In all cases, SR_F contains 1 on success, and 0 on failure. */ + emit_insn (gen_sne_sr_f (boolval)); +} + +void +or1k_expand_atomic_compare_and_swap_qihi (rtx operands[]) +{ + rtx boolval, orig_retval, retval, scratch, mem, oldval, newval; + rtx label1, label2, mask, shift; + machine_mode mode; + bool is_weak; + + boolval = operands[0]; + orig_retval = operands[1]; + mem = operands[2]; + oldval = operands[3]; + newval = operands[4]; + is_weak = (INTVAL (operands[5]) != 0); + mode = GET_MODE (mem); + + mem = or1k_adjust_atomic_subword (mem, &shift, &mask); + + /* Shift and mask OLDVAL and NEWVAL into position with the word. */ + if (oldval != const0_rtx) + { + oldval = convert_modes (SImode, mode, oldval, 1); + oldval = expand_binop (SImode, ashl_optab, oldval, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } + if (newval != const0_rtx) + { + newval = convert_modes (SImode, mode, newval, 1); + newval = expand_binop (SImode, ashl_optab, newval, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } + + label1 = NULL_RTX; + if (!is_weak) + { + label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label1, 0)); + } + label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + + scratch = gen_reg_rtx (SImode); + emit_load_locked (SImode, scratch, mem); + + retval = expand_binop (SImode, and_optab, scratch, mask, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + scratch = expand_binop (SImode, xor_optab, scratch, retval, + scratch, 1, OPTAB_LIB_WIDEN); + + emit_compare (EQ, retval, oldval); + emit_unlikely_jump (EQ, label2); + + if (newval != const0_rtx) + scratch = expand_binop (SImode, ior_optab, scratch, newval, + scratch, 1, OPTAB_LIB_WIDEN); + + emit_store_conditional (SImode, mem, scratch); + + if (!is_weak) + emit_unlikely_jump (EQ, label1); + emit_label (XEXP (label2, 0)); + + or1k_finish_atomic_subword (mode, orig_retval, retval, shift); + + /* In all cases, SR_F contains 1 on success, and 0 on failure. */ + emit_insn (gen_sne_sr_f (boolval)); +} + +/* Expand an atomic exchange operation. + Emits the RTX to perform an exchange operation. This function takes 4 RTX + arguments in the OPERANDS array. The exchange operation atomically loads a + value from memory (OPERANDS[1]) to a return value (OPERANDS[0]) and stores a + new value (OPERANDS[2]) back to the memory location. + Another argument (OPERANDS[3]) is used to indicate the memory model and + is not used by OpenRISC. + For OpenRISC this emits RTX which will translate to assembly using the + 'l.lwa' (load word atomic) and 'l.swa' (store word atomic) instructions. */ + +void +or1k_expand_atomic_exchange (rtx operands[]) +{ + rtx retval, mem, val, label; + machine_mode mode; + + retval = operands[0]; + mem = operands[1]; + val = operands[2]; + mode = GET_MODE (mem); + + if (reg_overlap_mentioned_p (retval, val)) + val = copy_to_reg (val); + + label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label, 0)); + + emit_load_locked (mode, retval, mem); + emit_store_conditional (mode, mem, val); + emit_unlikely_jump (EQ, label); +} + +void +or1k_expand_atomic_exchange_qihi (rtx operands[]) +{ + rtx orig_retval, retval, mem, val, scratch; + rtx label, mask, shift; + machine_mode mode; + + orig_retval = operands[0]; + mem = operands[1]; + val = operands[2]; + mode = GET_MODE (mem); + + mem = or1k_adjust_atomic_subword (mem, &shift, &mask); + + /* Shift and mask VAL into position with the word. */ + if (val != const0_rtx) + { + val = convert_modes (SImode, mode, val, 1); + val = expand_binop (SImode, ashl_optab, val, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } + + label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label, 0)); + + scratch = gen_reg_rtx (SImode); + emit_load_locked (SImode, scratch, mem); + + retval = expand_binop (SImode, and_optab, scratch, mask, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + scratch = expand_binop (SImode, xor_optab, scratch, retval, + scratch, 1, OPTAB_LIB_WIDEN); + if (val != const0_rtx) + scratch = expand_binop (SImode, ior_optab, scratch, val, + scratch, 1, OPTAB_LIB_WIDEN); + + emit_store_conditional (SImode, mem, scratch); + emit_unlikely_jump (EQ, label); + + or1k_finish_atomic_subword (mode, orig_retval, retval, shift); +} + +/* Expand an atomic fetch-and-operate pattern. CODE is the binary operation + to perform (with MULT as a stand-in for NAND). MEM is the memory on which + to operate. VAL is the second operand of the binary operator. BEFORE and + AFTER are optional locations to return the value of MEM either before of + after the operation. */ + +void +or1k_expand_atomic_op (rtx_code code, rtx mem, rtx val, + rtx orig_before, rtx orig_after) +{ + machine_mode mode = GET_MODE (mem); + rtx before = orig_before, after = orig_after; + rtx label; + + label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label, 0)); + + if (before == NULL_RTX) + before = gen_reg_rtx (mode); + + emit_load_locked (mode, before, mem); + + if (code == MULT) + { + after = expand_binop (mode, and_optab, before, val, + after, 1, OPTAB_LIB_WIDEN); + after = expand_unop (mode, one_cmpl_optab, after, after, 1); + } + else + after = expand_simple_binop (mode, code, before, val, + after, 1, OPTAB_LIB_WIDEN); + + emit_store_conditional (mode, mem, after); + emit_unlikely_jump (EQ, label); + + if (orig_before) + emit_move_insn (orig_before, before); + if (orig_after) + emit_move_insn (orig_after, after); +} + +void +or1k_expand_atomic_op_qihi (rtx_code code, rtx mem, rtx val, + rtx orig_before, rtx orig_after) +{ + machine_mode mode = GET_MODE (mem); + rtx label, mask, shift, x; + rtx before, after, scratch; + + mem = or1k_adjust_atomic_subword (mem, &shift, &mask); + + /* Shift and mask VAL into position with the word. */ + val = convert_modes (SImode, mode, val, 1); + val = expand_binop (SImode, ashl_optab, val, shift, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + switch (code) + { + case IOR: + case XOR: + /* We've already zero-extended VAL. That is sufficient to + make certain that it does not affect other bits. */ + break; + + case AND: + case MULT: /* NAND */ + /* If we make certain that all of the other bits in VAL are + set, that will be sufficient to not affect other bits. */ + x = expand_unop (SImode, one_cmpl_optab, mask, NULL_RTX, 1); + val = expand_binop (SImode, ior_optab, val, x, + val, 1, OPTAB_LIB_WIDEN); + break; + + case PLUS: + case MINUS: + /* These will all affect bits outside the field and need + adjustment via MASK within the loop. */ + break; + + default: + gcc_unreachable (); + } + + label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + emit_label (XEXP (label, 0)); + + before = scratch = gen_reg_rtx (SImode); + emit_load_locked (SImode, before, mem); + + switch (code) + { + case IOR: + case XOR: + case AND: + after = expand_simple_binop (SImode, code, before, val, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + scratch = after; + break; + + case PLUS: + case MINUS: + before = expand_binop (SImode, and_optab, scratch, mask, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + scratch = expand_binop (SImode, xor_optab, scratch, before, + scratch, 1, OPTAB_LIB_WIDEN); + after = expand_simple_binop (SImode, code, before, val, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + after = expand_binop (SImode, and_optab, after, mask, + after, 1, OPTAB_LIB_WIDEN); + scratch = expand_binop (SImode, ior_optab, scratch, after, + scratch, 1, OPTAB_LIB_WIDEN); + break; + + case MULT: /* NAND */ + after = expand_binop (SImode, and_optab, before, val, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + after = expand_binop (SImode, xor_optab, after, mask, + after, 1, OPTAB_LIB_WIDEN); + scratch = after; + break; + + default: + gcc_unreachable (); + } + + emit_store_conditional (SImode, mem, scratch); + emit_unlikely_jump (EQ, label); + + if (orig_before) + or1k_finish_atomic_subword (mode, orig_before, before, shift); + if (orig_after) + or1k_finish_atomic_subword (mode, orig_after, after, shift); +} + +/* Worker for TARGET_ASM_OUTPUT_MI_THUNK. + Output the assembler code for a thunk function. THUNK_DECL is the + declaration for the thunk function itself, FUNCTION is the decl for + the target function. DELTA is an immediate constant offset to be + added to THIS. If VCALL_OFFSET is nonzero, the word at address + (*THIS + VCALL_OFFSET) should be additionally added to THIS. */ + +static void +or1k_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) +{ + rtx this_rtx, funexp; + rtx_insn *insn; + + reload_completed = 1; + epilogue_completed = 1; + + emit_note (NOTE_INSN_PROLOGUE_END); + + /* Find the "this" pointer. Normally in r3, but if the function + returns a structure, the structure return pointer is in r3 and + the "this" pointer is in r4 instead. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + this_rtx = gen_rtx_REG (Pmode, 4); + else + this_rtx = gen_rtx_REG (Pmode, 3); + + /* Add DELTA. When possible use a plain add, otherwise load it + into a register first. */ + if (delta) + { + rtx delta_rtx = GEN_INT (delta); + + if (!satisfies_constraint_I (delta_rtx)) + { + rtx scratch = gen_rtx_REG (Pmode, PE_TMP_REGNUM); + emit_move_insn (scratch, delta_rtx); + delta_rtx = scratch; + } + + /* THIS_RTX += DELTA. */ + emit_insn (gen_add2_insn (this_rtx, delta_rtx)); + } + + /* Add the word at address (*THIS_RTX + VCALL_OFFSET). */ + if (vcall_offset) + { + rtx scratch = gen_rtx_REG (Pmode, PE_TMP_REGNUM); + HOST_WIDE_INT lo = sext_hwi(vcall_offset, 16); + HOST_WIDE_INT hi = vcall_offset - lo; + rtx tmp; + + /* SCRATCH = *THIS_RTX. */ + tmp = gen_rtx_MEM (Pmode, this_rtx); + emit_move_insn (scratch, tmp); + + if (hi != 0) + { + rtx scratch2 = gen_rtx_REG (Pmode, RV_REGNUM); + emit_move_insn (scratch2, GEN_INT (hi)); + emit_insn (gen_add2_insn (scratch, scratch2)); + } + + /* SCRATCH = *(*THIS_RTX + VCALL_OFFSET). */ + tmp = plus_constant (Pmode, scratch, lo); + tmp = gen_rtx_MEM (Pmode, tmp); + emit_move_insn (scratch, tmp); + + /* THIS_RTX += *(*THIS_RTX + VCALL_OFFSET). */ + emit_insn (gen_add2_insn (this_rtx, scratch)); + } + + /* Generate a tail call to the target function. */ + if (! TREE_USED (function)) + { + assemble_external (function); + TREE_USED (function) = 1; + } + funexp = XEXP (DECL_RTL (function), 0); + + /* The symbol will be a local alias and therefore always binds local. */ + gcc_assert (SYMBOL_REF_LOCAL_P (funexp)); + + funexp = gen_rtx_MEM (FUNCTION_MODE, funexp); + insn = emit_call_insn (gen_sibcall (funexp, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + emit_barrier (); + + /* Run just enough of rest_of_compilation to get the insns emitted. + There's not really enough bulk here to make other passes such as + instruction scheduling worth while. Note that use_thunk calls + assemble_start_function and assemble_end_function. */ + insn = get_insns (); + shorten_branches (insn); + final_start_function (insn, file, 1); + final (insn, file, 1); + final_end_function (); + + reload_completed = 0; + epilogue_completed = 0; +} + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK or1k_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK \ + hook_bool_const_tree_hwi_hwi_const_tree_true + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE or1k_option_override + +#undef TARGET_COMPUTE_FRAME_LAYOUT +#define TARGET_COMPUTE_FRAME_LAYOUT or1k_compute_frame_layout + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P or1k_legitimate_address_p + +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS true + +#undef TARGET_HAVE_SPECULATION_SAFE_VALUE +#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed + +/* Calling Conventions. */ +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE or1k_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE or1k_libcall_value +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P or1k_function_value_regno_p +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG or1k_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE or1k_function_arg_advance +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY or1k_return_in_memory +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE or1k_pass_by_reference +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT or1k_trampoline_init +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED or1k_frame_pointer_required +#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS +#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 + +/* Assembly generation. */ +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND or1k_print_operand +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS or1k_print_operand_address + +/* Section anchor support. */ +#undef TARGET_MIN_ANCHOR_OFFSET +#define TARGET_MIN_ANCHOR_OFFSET -32768 +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET 32767 + +struct gcc_target targetm = TARGET_INITIALIZER; + +#include "gt-or1k.h" diff --git a/gcc/config/or1k/or1k.h b/gcc/config/or1k/or1k.h new file mode 100644 index 00000000000..90a7c96cefa --- /dev/null +++ b/gcc/config/or1k/or1k.h @@ -0,0 +1,392 @@ +/* Target Definitions for OpenRISC. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Stafford Horne. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#ifndef GCC_OR1K_H +#define GCC_OR1K_H + +/* Names to predefine in the preprocessor for this target machine. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__OR1K__"); \ + builtin_define ("__OR1K_DELAY__"); \ + builtin_define ("__or1k__"); \ + if (TARGET_CMOV) \ + builtin_define ("__or1k_cmov__"); \ + builtin_assert ("cpu=or1k"); \ + builtin_assert ("machine=or1k"); \ + } \ + while (0) + +/* Storage layout. */ + +#define DEFAULT_SIGNED_CHAR 1 +#define BITS_BIG_ENDIAN 0 +#define BYTES_BIG_ENDIAN 1 +#define WORDS_BIG_ENDIAN 1 +#define BITS_PER_WORD 32 +#define UNITS_PER_WORD 4 +#define POINTER_SIZE 32 +#define BIGGEST_ALIGNMENT 32 +#define STRICT_ALIGNMENT 1 +#define FUNCTION_BOUNDARY 32 +#define PARM_BOUNDARY 32 +#define STACK_BOUNDARY 32 +#define PREFERRED_STACK_BOUNDARY 32 +#define MAX_FIXED_MODE_SIZE 64 + +/* Layout of source language data types. */ + +#define INT_TYPE_SIZE 32 +#define SHORT_TYPE_SIZE 16 +#define LONG_TYPE_SIZE 32 +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE 64 +#define WCHAR_TYPE_SIZE 32 + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "unsigned int" + +/* Describing Relative Costs of Operations. */ +#define MOVE_MAX 4 +#define SLOW_BYTE_ACCESS 1 + +/* Register usage, class and contents. */ + +/* In OpenRISC there are 32 general purpose registers with the following + designations: + + r0 always 0 + r1 stack pointer + r2 frame pointer (optional) + r3 arg 0 + r4 arg 1 + r5 arg 2 + r6 arg 3 + r7 arg 4 + r8 arg 5 + r9 function call return link address + r10 thread local storage + r11 function return value & static chain + r12 function return value high (upper 64-bit) + r13 temporary (used in prologue and epilogue) + r14 callee saved + r15 temporary + r16 callee saved & pic base register + r17 temporary + r18 callee saved + r19 temporary + r20 callee saved + r21 temporary + r22 callee saved + r23 temporary + r24 callee saved + r25 temporary + r26 callee saved + r27 temporary + r28 callee saved + r29 temporary + r30 callee saved + r31 temporary + + r32 soft argument pointer + r33 soft frame pointer + r34 SR[F] (bit) register + + This ABI has no adjacent call-saved register, which means that + DImode/DFmode pseudos cannot be call-saved and will always be + spilled across calls. To solve this without changing the ABI, + remap the compiler internal register numbers to place the even + call-saved registers r16-r30 in 24-31, and the odd call-clobbered + registers r17-r31 in 16-23. */ + +#define FIRST_PSEUDO_REGISTER 35 + +#define HW_TO_GCC_REGNO(X) \ + ((X) < 16 || (X) > 31 ? (X) \ + : (X) & 1 ? ((X) - 16) / 2 + 16 \ + : ((X) - 16) / 2 + 24) + +#define GCC_TO_HW_REGNO(X) \ + ((X) < 16 || (X) > 31 ? (X) \ + : (X) < 24 ? ((X) - 16) * 2 + 17 \ + : ((X) - 24) * 2 + 16) + +#define DBX_REGISTER_NUMBER(X) GCC_TO_HW_REGNO(X) + +#define REGISTER_NAMES { \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \ + "r17", "r19", "r21", "r23", "r25", "r27", "r29", "r31", \ + "r16", "r18", "r20", "r22", "r24", "r26", "r28", "r30", \ + "?ap", "?fp", "?sr_f" } + +#define FIXED_REGISTERS \ +{ 1, 1, 0, 0, 0, 0, 0, 0, \ + 0, 0, 1, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1 } + +/* Caller saved/temporary registers + args + fixed */ +#define CALL_USED_REGISTERS \ +{ 1, 1, 0, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 0, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1 } + +/* List the order in which to allocate registers. Each register must + be listed once, even those in FIXED_REGISTERS. + + ??? Note that placing REAL_PIC_OFFSET_TABLE_REGNUM (r16 = 24) first + happens to make it most likely selected *as* the pic register when + compiling without optimization, simply because the pic pseudo happens + to be allocated with the lowest pseudo regno. */ + +#define REG_ALLOC_ORDER { \ + 16, 17, 18, 19, 20, 21, 22, 23, /* r17-r31 (odd), non-saved */ \ + 13, 15, /* non-saved */ \ + 12, 11, /* non-saved return values */ \ + 8, 7, 6, 5, 4, 3, /* non-saved argument regs */ \ + 24, /* r16, saved, pic reg */ \ + 25, 26, 27, 28, 29, 30, 31, /* r18-r31 (even), saved */ \ + 14, /* r14, saved */ \ + 2, /* saved hard frame pointer */ \ + 9, /* saved return address */ \ + 0, /* fixed zero reg */ \ + 1, /* fixed stack pointer */ \ + 10, /* fixed thread pointer */ \ + 32, 33, 34, /* fixed ap, fp, sr[f], */ \ +} + +enum reg_class +{ + NO_REGS, + SIBCALL_REGS, + GENERAL_REGS, + FLAG_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES { \ + "NO_REGS", \ + "SIBCALL_REGS", \ + "GENERAL_REGS", \ + "FLAG_REGS", \ + "ALL_REGS" } + +/* The SIBCALL_REGS must be call-clobbered, and not used as a temporary + in the epilogue. This excludes R9 (LR), R11 (STATIC_CHAIN), and + R13 (PE_TMP_REGNUM). */ +#define SIBCALL_REGS_MASK 0x00ff95f8u + +#define REG_CLASS_CONTENTS \ +{ { 0x00000000, 0x00000000 }, \ + { SIBCALL_REGS_MASK, 0 }, \ + { 0xffffffff, 0x00000003 }, \ + { 0x00000000, 0x00000004 }, \ + { 0xffffffff, 0x00000007 } \ +} + +/* A C expression whose value is a register class containing hard + register REGNO. In general there is more that one such class; + choose a class which is "minimal", meaning that no smaller class + also contains the register. */ +#define REGNO_REG_CLASS(REGNO) \ + ((REGNO) >= SR_F_REGNUM ? FLAG_REGS \ + : (REGNO) < 32 && ((SIBCALL_REGS_MASK >> (REGNO)) & 1) ? SIBCALL_REGS \ + : GENERAL_REGS) + +#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \ +do { \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ + (MODE) = word_mode; \ +} while (0) + +/* A macro whose definition is the name of the class to which a valid + base register must belong. A base register is one used in an + address which is the register value plus a displacement. */ +#define BASE_REG_CLASS GENERAL_REGS + +#define INDEX_REG_CLASS NO_REGS + +/* Assembly definitions. */ + +#define ASM_APP_ON "" +#define ASM_APP_OFF "" + +#define ASM_COMMENT_START "# " + +#define GLOBAL_ASM_OP "\t.global\t" +#define TEXT_SECTION_ASM_OP "\t.section\t.text" +#define DATA_SECTION_ASM_OP "\t.section\t.data" +#define BSS_SECTION_ASM_OP "\t.section\t.bss" +#define SBSS_SECTION_ASM_OP "\t.section\t.sbss" + +/* This is how to output an assembler line + that says to advance the location counter + to a multiple of 2**LOG bytes. */ +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + do \ + { \ + if ((LOG) != 0) \ + fprintf (FILE, "\t.align %d\n", 1 << (LOG)); \ + } \ + while (0) + +/* This is used in crtstuff to create call stubs in the + _init() and _fini() functions. Defining this here saves + a few bytes created by the dummy call_xxx() functions. */ +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP "\n" \ +" l.jal " #FUNC "\n" \ +" l.nop\n" \ +" .previous"); + + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) (code == '#') + +/* Calling convention definitions. */ +#define CUMULATIVE_ARGS int +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ + do { (CUM) = 0; } while (0) + + +/* Trampolines, for nested functions */ +#define TRAMPOLINE_SIZE 20 +#define TRAMPOLINE_ALIGNMENT 32 + +/* Pointer mode */ +#define Pmode SImode +#define FUNCTION_MODE SImode +#define STACK_POINTER_REGNUM SP_REGNUM +#define FRAME_POINTER_REGNUM SFP_REGNUM +#define HARD_FRAME_POINTER_REGNUM HFP_REGNUM +#define STATIC_CHAIN_REGNUM RV_REGNUM + +/* The register number of the arg pointer register, which is used to + access the function's argument list. */ +#define ARG_POINTER_REGNUM AP_REGNUM + +/* Position Independent Code. See or1k_init_pic_reg. */ +#define REAL_PIC_OFFSET_TABLE_REGNUM HW_TO_GCC_REGNO (16) + +/* ??? Follow i386 in working around gimple costing estimation, which + happens without properly initializing the pic_offset_table pseudo. */ +#define PIC_OFFSET_TABLE_REGNUM \ + (pic_offset_table_rtx ? INVALID_REGNUM : REAL_PIC_OFFSET_TABLE_REGNUM) + +/* A C expression that is nonzero if REGNO is the number of a hard + register in which function arguments are sometimes passed. */ +#define FUNCTION_ARG_REGNO_P(r) (r >= 3 && r <= 8) + +#define MAX_REGS_PER_ADDRESS 1 + +/* The ELIMINABLE_REGS macro specifies a table of register pairs used to + eliminate unneeded registers that point into the stack frame. Note, + the only elimination attempted by the compiler is to replace references + to the frame pointer with references to the stack pointer. */ + +#define ELIMINABLE_REGS \ +{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + do { \ + (OFFSET) = or1k_initial_elimination_offset ((FROM), (TO)); \ + } while (0) + +#define REGNO_OK_FOR_INDEX_P(REGNO) 0 +#define REGNO_OK_FOR_BASE_P(REGNO) ((REGNO) <= SFP_REGNUM) + +/* If defined, the maximum amount of space required for outgoing + arguments will be computed and placed into the variable + 'crtl->outgoing_args_size'. No space will be pushed + onto the stack for each call; instead, the function prologue + should increase the stack frame size by this amount. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* Stack layout and stack pointer usage. */ + +/* This plus ARG_POINTER_REGNUM points to the first word of incoming args. */ +#define FIRST_PARM_OFFSET(FNDECL) (0) + +/* This plus STACK_POINTER_REGNUM points to the first work of outgoing args. */ +#define STACK_POINTER_OFFSET (0) + +/* Define this macro if pushing a word onto the stack moves the stack + pointer to a smaller address. */ +#define STACK_GROWS_DOWNWARD 1 + +#define FRAME_GROWS_DOWNWARD 1 + +/* An alias for a machine mode name. This is the machine mode that + elements of a jump-table should have. */ +#define CASE_VECTOR_MODE SImode + +#define STORE_FLAG_VALUE 1 + +/* Indicates how loads of narrow mode values are loaded into words. */ +#define LOAD_EXTEND_OP(MODE) (ZERO_EXTEND) + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. */ +#define EXIT_IGNORE_STACK 1 + +/* Macros related to the access of the stack frame chain. */ +#define INITIAL_FRAME_ADDRESS_RTX or1k_initial_frame_addr () +#define DYNAMIC_CHAIN_ADDRESS or1k_dynamic_chain_addr +#define RETURN_ADDR_RTX or1k_return_addr + +/* Always pass the SYMBOL_REF for direct calls to the expanders. */ +#define NO_FUNCTION_CSE 1 + +/* Profiling */ +#define FUNCTION_PROFILER(FILE,LABELNO) (abort (), 0) + +/* Dwarf 2 Support */ +#define DWARF2_DEBUGGING_INFO 1 +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, LR_REGNUM) +#define DWARF_FRAME_RETURN_COLUMN LR_REGNUM + +/* Describe how we implement __builtin_eh_return. */ +#define EH_RETURN_REGNUM HW_TO_GCC_REGNO (23) +/* Use r25, r27, r29 and r31 (clobber regs) for exception data. + Recall that these are remapped consecutively. */ +#define EH_RETURN_DATA_REGNO(N) \ + ((N) < 4 ? HW_TO_GCC_REGNO (25) + (N) : INVALID_REGNUM) +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, EH_RETURN_REGNUM) + +#endif /* GCC_OR1K_H */ diff --git a/gcc/config/or1k/or1k.md b/gcc/config/or1k/or1k.md new file mode 100644 index 00000000000..48c40228af0 --- /dev/null +++ b/gcc/config/or1k/or1k.md @@ -0,0 +1,907 @@ +;; Machine description for OpenRISC +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by Stafford Horne + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. + +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; . + +;; ------------------------------------------------------------------------- +;; OpenRISC specific constraints, predicates and attributes +;; ------------------------------------------------------------------------- + +(include "constraints.md") +(include "predicates.md") + +;; Register numbers +(define_constants + [(SP_REGNUM 1) + (HFP_REGNUM 2) + (LR_REGNUM 9) + (TLS_REGNUM 10) + (RV_REGNUM 11) + (PE_TMP_REGNUM 13) + (AP_REGNUM 32) + (SFP_REGNUM 33) + (SR_F_REGNUM 34)] +) + +(define_c_enum "unspec" [ + UNSPEC_SET_GOT + UNSPEC_GOT + UNSPEC_GOTOFF + UNSPEC_TPOFF + UNSPEC_GOTTPOFF + UNSPEC_TLSGD + UNSPEC_MSYNC +]) + +(define_c_enum "unspecv" [ + UNSPECV_SET_GOT + UNSPECV_LL + UNSPECV_SC +]) + +;; Instruction scheduler + +; Most instructions are 4 bytes long. +(define_attr "length" "" (const_int 4)) + +(define_attr "type" + "alu,st,ld,control,multi" + (const_string "alu")) + +(define_attr "insn_support" "class1,sext,sfimm,shftimm" (const_string "class1")) + +(define_attr "enabled" "" + (cond [(eq_attr "insn_support" "class1") (const_int 1) + (and (eq_attr "insn_support" "sext") + (ne (symbol_ref "TARGET_SEXT") (const_int 0))) (const_int 1) + (and (eq_attr "insn_support" "sfimm") + (ne (symbol_ref "TARGET_SFIMM") (const_int 0))) (const_int 1) + (and (eq_attr "insn_support" "shftimm") + (ne (symbol_ref "TARGET_SHFTIMM") (const_int 0))) (const_int 1)] + (const_int 0))) + +;; Describe a user's asm statement. +(define_asm_attributes + [(set_attr "type" "multi")]) + +(define_automaton "or1k") +(define_cpu_unit "cpu" "or1k") +(define_insn_reservation "alu" 1 + (eq_attr "type" "alu") + "cpu") +(define_insn_reservation "st" 1 + (eq_attr "type" "st") + "cpu") +(define_insn_reservation "ld" 3 + (eq_attr "type" "st") + "cpu") +(define_insn_reservation "control" 1 + (eq_attr "type" "control") + "cpu") + +; Define delay slots for any branch +(define_delay (eq_attr "type" "control") + [(eq_attr "type" "alu,st,ld") (nil) (nil)]) + +;; ------------------------------------------------------------------------- +;; nop instruction +;; ------------------------------------------------------------------------- + +(define_insn "nop" + [(const_int 0)] + "" + "l.nop") + +;; ------------------------------------------------------------------------- +;; Arithmetic instructions +;; ------------------------------------------------------------------------- + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_s16_operand" " r,I")))] + "" + "@ + l.add\t%0, %1, %2 + l.addi\t%0, %1, %2") + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mult:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_s16_operand" " r,I")))] + "!TARGET_SOFT_MUL" + "@ + l.mul\t%0, %1, %2 + l.muli\t%0, %1, %2") + +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (div:SI + (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")))] + "!TARGET_SOFT_DIV" + "l.div\t%0, %1, %2") + +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (udiv:SI + (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")))] + "!TARGET_SOFT_DIV" + "l.divu\t%0, %1, %2") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI + (match_operand:SI 1 "reg_or_0_operand" "rO") + (match_operand:SI 2 "register_operand" "r")))] + "" + "l.sub\t%0, %r1, %2") + +;; ------------------------------------------------------------------------- +;; Logical operators +;; ------------------------------------------------------------------------- + +(define_code_iterator SHIFT [ashift ashiftrt lshiftrt]) +(define_code_attr shift_op [(ashift "ashl") (ashiftrt "ashr") + (lshiftrt "lshr")]) +(define_code_attr shift_asm [(ashift "sll") (ashiftrt "sra") + (lshiftrt "srl")]) + +(define_insn "si3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (SHIFT:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_u6_operand" "r,n")))] + "" + "@ + l.\t%0, %1, %2 + l.i\t%0, %1, %2" + [(set_attr "insn_support" "*,shftimm")]) + +(define_insn "rotrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (rotatert:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_u6_operand" "r,n")))] + "TARGET_ROR" + "@ + l.ror\t%0, %1, %2 + l.rori\t%0, %1, %2" + [(set_attr "insn_support" "*,shftimm")]) + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (and:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_u16_operand" " r,K")))] + "" + "@ + l.and\t%0, %1, %2 + l.andi\t%0, %1, %2") + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (xor:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_s16_operand" " r,I")))] + "" + "@ + l.xor\t%0, %1, %2 + l.xori\t%0, %1, %2") + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ior:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_u16_operand" " r,K")))] + "" + "@ + l.or\t%0, %1, %2 + l.ori\t%0, %1, %2") + +(define_expand "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "") + (xor:SI (match_operand:SI 1 "register_operand" "") (const_int -1)))] + "" + "") + +;; ------------------------------------------------------------------------- +;; Move instructions +;; ------------------------------------------------------------------------- + +(define_mode_iterator I [QI HI SI]) +(define_mode_iterator I12 [QI HI]) + +(define_mode_attr ldst [(QI "b") (HI "h") (SI "w")]) + +(define_expand "mov" + [(set (match_operand:I 0 "nonimmediate_operand" "") + (match_operand:I 1 "general_operand" ""))] + "" +{ + or1k_expand_move (mode, operands[0], operands[1]); + DONE; +}) + +;; 8-bit, 16-bit and 32-bit moves + +(define_insn "*mov_internal" + [(set (match_operand:I 0 "nonimmediate_operand" "=r,r,r,r, m,r") + (match_operand:I 1 "input_operand" " r,M,K,I,rO,m"))] + "register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)" + "@ + l.or\t%0, %1, %1 + l.movhi\t%0, hi(%1) + l.ori\t%0, r0, %1 + l.xori\t%0, r0, %1 + l.s\t%0, %r1 + l.lz\t%0, %1" + [(set_attr "type" "alu,alu,alu,alu,st,ld")]) + +;; Hi/Low moves for constant and symbol loading + +(define_insn "movsi_high" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand:SI 1 "high_operand" "")))] + "" + "l.movhi\t%0, %h1" + [(set_attr "type" "alu")]) + +(define_insn "*movsi_lo_sum_iori" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "losum_ior_operand" "")))] + "" + "l.ori\t%0, %1, %L2" + [(set_attr "type" "alu")]) + +(define_insn "*movsi_lo_sum_addi" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "losum_add_operand" "")))] + "" + "l.addi\t%0, %1, %L2" + [(set_attr "type" "alu")]) + +;; 64-bit moves +;; ??? The clobber that emit_move_multi_word emits is arguably incorrect. +;; Consider gcc.c-torture/execute/20030222-1.c, where a reg-reg DImode +;; move gets register allocated to a no-op move. At which point the +;; we actively clobber the input. + +(define_expand "movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" +{ + if (MEM_P (operands[0]) && !const0_operand(operands[1], DImode)) + operands[1] = force_reg (DImode, operands[1]); +}) + +(define_insn_and_split "*movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,o,r") + (match_operand:DI 1 "general_operand" " r,o,rO,n"))] + "register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode)" + "#" + "" + [(const_int 0)] +{ + rtx l0 = operand_subword (operands[0], 0, 0, DImode); + rtx l1 = operand_subword (operands[1], 0, 0, DImode); + rtx h0 = operand_subword (operands[0], 1, 0, DImode); + rtx h1 = operand_subword (operands[1], 1, 0, DImode); + + if (reload_completed && reg_overlap_mentioned_p (l0, h1)) + { + gcc_assert (!reg_overlap_mentioned_p (h0, l1)); + emit_move_insn (h0, h1); + emit_move_insn (l0, l1); + } + else + { + emit_move_insn (l0, l1); + emit_move_insn (h0, h1); + } + DONE; +}) + +;; ------------------------------------------------------------------------- +;; Sign Extending +;; ------------------------------------------------------------------------- + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))] + "" + "@ + l.exthz\t%0, %1 + l.lhz\t%0, %1" + [(set_attr "insn_support" "sext,*")]) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] + "" + "@ + l.extbz\t%0, %1 + l.lbz\t%0, %1" + [(set_attr "insn_support" "sext,*")]) + +;; Sign extension patterns + +;; We can do memory extensions with a single load +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))] + "" + "@ + l.exths\t%0, %1 + l.lhs\t%0, %1" + [(set_attr "insn_support" "sext,*")]) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] + "" + "@ + l.extbs\t%0, %1 + l.lbs\t%0, %1" + [(set_attr "insn_support" "sext,*")]) + +;; ------------------------------------------------------------------------- +;; Compare instructions +;; ------------------------------------------------------------------------- + +;; OpenRISC supports these integer comparisons: +;; +;; l.sfeq[i] - equality, r r or r i +;; l.sfne[i] - not equal, r r or r i +;; l.sflt{s,u}[i] - less than, signed or unsigned, r r or r i +;; l.sfle{s,u}[i] - less than or equal, signed or unsigned, r r or r i +;; l.sfgt{s,u}[i] - greater than, signed or unsigned, r r or r i +;; l.sfge{s,u}[i] - greater than or equal, signed or unsigned, r r or r i +;; +;; EQ,NE,LT,LTU,LE,LEU,GT,GTU,GE,GEU +;; We iterate through all of these +;; + +(define_code_iterator intcmpcc [ne eq lt ltu gt gtu ge le geu leu]) +(define_code_attr insn [(ne "ne") (eq "eq") (lt "lts") (ltu "ltu") + (gt "gts") (gtu "gtu") (ge "ges") (le "les") + (geu "geu") (leu "leu") ]) + +(define_insn "*sf_insn" + [(set (reg:BI SR_F_REGNUM) + (intcmpcc:BI (match_operand:SI 0 "reg_or_0_operand" "rO,rO") + (match_operand:SI 1 "reg_or_s16_operand" "r,I")))] + "" + "@ + l.sf\t%r0, %1 + l.sfi\t%r0, %1" + [(set_attr "insn_support" "*,sfimm")]) + +;; ------------------------------------------------------------------------- +;; Conditional Store instructions +;; ------------------------------------------------------------------------- + +(define_expand "cstoresi4" + [(set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI + (match_operator 1 "comparison_operator" + [(match_operand:SI 2 "reg_or_0_operand" "") + (match_operand:SI 3 "reg_or_s16_operand" "")]) + (match_dup 0) + (const_int 0)))] + "" +{ + or1k_expand_compare (operands + 1); + PUT_MODE (operands[1], SImode); + emit_insn (gen_rtx_SET (operands[0], operands[1])); + DONE; +}) + +;; Being able to "copy" SR_F to a general register is helpful for +;; the atomic insns, wherein the usual usage is to test the success +;; of the compare-and-swap. Representing the operation in this way, +;; rather than exposing the cmov immediately, allows the optimizers +;; to propagate the use of SR_F directly into a branch. + +(define_expand "sne_sr_f" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (reg:BI SR_F_REGNUM) (const_int 0)))] + "") + +(define_insn_and_split "*scc" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 1 "equality_comparison_operator" + [(reg:BI SR_F_REGNUM) (const_int 0)]))] + "" + "#" + "reload_completed" + [(set (match_dup 0) (const_int 1)) + (set (match_dup 0) + (if_then_else:SI (match_dup 1) + (match_dup 0) + (const_int 0)))] + "") + +(define_expand "movcc" + [(set (match_operand:I 0 "register_operand" "") + (if_then_else:I (match_operand 1 "comparison_operator" "") + (match_operand:I 2 "reg_or_0_operand" "") + (match_operand:I 3 "reg_or_0_operand" "")))] + "" +{ + rtx xops[3] = { operands[1], XEXP (operands[1], 0), XEXP (operands[1], 1) }; + or1k_expand_compare (xops); + operands[1] = xops[0]; +}) + +(define_insn_and_split "*cmov" + [(set (match_operand:I 0 "register_operand" "=r") + (if_then_else:I + (match_operator 3 "equality_comparison_operator" + [(reg:BI SR_F_REGNUM) (const_int 0)]) + (match_operand:I 1 "reg_or_0_operand" "rO") + (match_operand:I 2 "reg_or_0_operand" "rO")))] + "" +{ + return (GET_CODE (operands[3]) == NE + ? "l.cmov\t%0, %r1, %r2" + : "l.cmov\t%0, %r2, %r1"); +} + "!TARGET_CMOV" + [(const_int 0)] +{ + rtx x; + rtx label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + + /* Generated a *cbranch pattern. */ + if (rtx_equal_p (operands[0], operands[2])) + { + PUT_CODE (operands[3], (GET_CODE (operands[3]) == NE) ? EQ : NE); + x = gen_rtx_IF_THEN_ELSE (VOIDmode, operands[3], label, pc_rtx); + emit_jump_insn (gen_rtx_SET (pc_rtx, x)); + emit_move_insn (operands[0], operands[1]); + } + else + { + x = gen_rtx_IF_THEN_ELSE (VOIDmode, operands[3], label, pc_rtx); + emit_move_insn (operands[0], operands[1]); + emit_jump_insn (gen_rtx_SET (pc_rtx, x)); + emit_move_insn (operands[0], operands[2]); + } + + emit_label (XEXP (label, 0)); + DONE; +}) + +;; ------------------------------------------------------------------------- +;; Branch instructions +;; ------------------------------------------------------------------------- + +(define_expand "cbranchsi4" + [(set (pc) + (if_then_else + (match_operator 0 "comparison_operator" + [(match_operand:SI 1 "reg_or_0_operand" "") + (match_operand:SI 2 "reg_or_s16_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" +{ + or1k_expand_compare (operands); +}) + +(define_insn "*cbranch" + [(set (pc) + (if_then_else + (match_operator 1 "equality_comparison_operator" + [(reg:BI SR_F_REGNUM) (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" +{ + return (GET_CODE (operands[1]) == NE + ? "l.bf\t%0%#" + : "l.bnf\t%0%#"); +} + [(set_attr "type" "control")]) + +;; ------------------------------------------------------------------------- +;; Jump instructions +;; ------------------------------------------------------------------------- + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "l.j\t%0%#" + [(set_attr "type" "control")]) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "r"))] + "" + "l.jr\t%0%#" + [(set_attr "type" "control")]) + +;; ------------------------------------------------------------------------- +;; Prologue & Epilogue +;; ------------------------------------------------------------------------- + +(define_expand "prologue" + [(const_int 1)] + "" +{ + or1k_expand_prologue (); + DONE; +}) + +;; Expand epilogue as RTL +(define_expand "epilogue" + [(return)] + "" +{ + or1k_expand_epilogue (); + emit_jump_insn (gen_simple_return ()); + DONE; +}) + +(define_expand "sibcall_epilogue" + [(return)] + "" +{ + or1k_expand_epilogue (); + /* Placing a USE of LR here, rather than as a REG_USE on the + sibcall itself, means that LR is not unnecessarily live + within the function itself, which would force creation of + a stack frame. */ + emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, LR_REGNUM))); + DONE; +}) + +(define_expand "simple_return" + [(parallel [(simple_return) (use (match_dup 0))])] + "" +{ + operands[0] = gen_rtx_REG (Pmode, LR_REGNUM); +}) + +(define_insn "*simple_return" + [(simple_return) + (use (match_operand:SI 0 "register_operand" "r"))] + "" + "l.jr\t%0%#" + [(set_attr "type" "control")]) + +(define_expand "eh_return" + [(use (match_operand 0 "general_operand"))] + "" +{ + or1k_expand_eh_return (operands[0]); + DONE; +}) + +;; This is a placeholder, during RA, in order to create the PIC regiter. +;; We do this so that we don't unconditionally mark the LR register as +;; clobbered. It is replaced during prologue generation with the proper +;; set_got pattern below. This works because the set_got_tmp insn is the +;; first insn in the stream and that it isn't moved during RA. +(define_insn "set_got_tmp" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(const_int 0)] UNSPECV_SET_GOT))] + "" +{ + gcc_unreachable (); +}) + +;; The insn to initialize the GOT. +(define_insn "set_got" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_SET_GOT)) + (clobber (reg:SI LR_REGNUM))] + "" +{ + return ("l.jal\t8\;" + " l.movhi\t%0, gotpchi(_GLOBAL_OFFSET_TABLE_-4)\;" + "l.ori\t%0, %0, gotpclo(_GLOBAL_OFFSET_TABLE_+0)\;" + "l.add\t%0, %0, r9"); +} + [(set_attr "length" "16") + (set_attr "type" "multi")]) + +;; Block memory operations from being scheduled across frame (de)allocation. +(define_insn "frame_addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI + (match_operand:SI 1 "register_operand" "%r,r") + (match_operand:SI 2 "reg_or_s16_operand" " r,I"))) + (clobber (mem:BLK (scratch)))] + "reload_completed" + "@ + l.add\t%0, %1, %2 + l.addi\t%0, %1, %2") + +;; ------------------------------------------------------------------------- +;; Atomic Operations +;; ------------------------------------------------------------------------- + +;; Note that MULT stands in for the non-existant NAND rtx_code. +(define_code_iterator FETCHOP [plus minus ior xor and mult]) + +(define_code_attr fetchop_name + [(plus "add") + (minus "sub") + (ior "or") + (xor "xor") + (and "and") + (mult "nand")]) + +(define_code_attr fetchop_pred + [(plus "reg_or_s16_operand") + (minus "register_operand") + (ior "reg_or_u16_operand") + (xor "reg_or_s16_operand") + (and "reg_or_u16_operand") + (mult "reg_or_u16_operand")]) + +(define_expand "mem_thread_fence" + [(match_operand:SI 0 "const_int_operand" "")] ;; model + "" +{ + memmodel model = memmodel_base (INTVAL (operands[0])); + if (model != MEMMODEL_RELAXED) + emit_insn (gen_msync ()); + DONE; +}) + +(define_expand "msync" + [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_MSYNC))] + "" +{ + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; +}) + +(define_insn "*msync" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_MSYNC))] + "" + "l.msync") + +(define_insn "load_locked_si" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI + [(match_operand:SI 1 "memory_operand" "m")] UNSPECV_LL))] + "" + "l.lwa\t%0,%1" + [(set_attr "type" "ld")]) + +(define_insn "store_conditional_si" + [(set (reg:BI SR_F_REGNUM) + (unspec_volatile:BI [(const_int 0)] UNSPECV_SC)) + (set (match_operand:SI 0 "memory_operand" "=m") + (match_operand:SI 1 "reg_or_0_operand" "rO"))] + "" + "l.swa\t%0,%r1" + [(set_attr "type" "st")]) + +(define_expand "atomic_compare_and_swapsi" + [(match_operand:SI 0 "register_operand") ;; bool output + (match_operand:SI 1 "register_operand") ;; val output + (match_operand:SI 2 "memory_operand") ;; memory + (match_operand:SI 3 "reg_or_s16_operand") ;; expected + (match_operand:SI 4 "reg_or_0_operand") ;; desired + (match_operand:SI 5 "const_int_operand") ;; is_weak + (match_operand:SI 6 "const_int_operand") ;; mod_s + (match_operand:SI 7 "const_int_operand")] ;; mod_f + "" +{ + or1k_expand_atomic_compare_and_swap (operands); + DONE; +}) + +(define_expand "atomic_compare_and_swap" + [(match_operand:SI 0 "register_operand") ;; bool output + (match_operand:I12 1 "register_operand") ;; val output + (match_operand:I12 2 "memory_operand") ;; memory + (match_operand:I12 3 "register_operand") ;; expected + (match_operand:I12 4 "reg_or_0_operand") ;; desired + (match_operand:SI 5 "const_int_operand") ;; is_weak + (match_operand:SI 6 "const_int_operand") ;; mod_s + (match_operand:SI 7 "const_int_operand")] ;; mod_f + "" +{ + or1k_expand_atomic_compare_and_swap_qihi (operands); + DONE; +}) + +(define_expand "atomic_exchangesi" + [(match_operand:SI 0 "register_operand") ;; output + (match_operand:SI 1 "memory_operand") ;; memory + (match_operand:SI 2 "reg_or_0_operand") ;; input + (match_operand:SI 3 "const_int_operand")] ;; model + "" +{ + or1k_expand_atomic_exchange (operands); + DONE; +}) + +(define_expand "atomic_exchange" + [(match_operand:I12 0 "register_operand") ;; output + (match_operand:I12 1 "memory_operand") ;; memory + (match_operand:I12 2 "reg_or_0_operand") ;; input + (match_operand:SI 3 "const_int_operand")] ;; model + "" +{ + or1k_expand_atomic_exchange_qihi (operands); + DONE; +}) + +(define_expand "atomic_si" + [(match_operand:SI 0 "memory_operand") ;; memory + (FETCHOP:SI (match_dup 0) + (match_operand:SI 1 "")) ;; operand + (match_operand:SI 2 "const_int_operand")] ;; model + "" +{ + or1k_expand_atomic_op (, operands[0], operands[1], NULL, NULL); + DONE; +}) + +(define_expand "atomic_" + [(match_operand:I12 0 "memory_operand") ;; memory + (FETCHOP:I12 (match_dup 0) + (match_operand:I12 1 "register_operand")) ;; operand + (match_operand:SI 2 "const_int_operand")] ;; model + "" +{ + or1k_expand_atomic_op_qihi (, operands[0], operands[1], NULL, NULL); + DONE; +}) + +(define_expand "atomic_fetch_si" + [(match_operand:SI 0 "register_operand" "") ;; output + (match_operand:SI 1 "memory_operand" "") ;; memory + (FETCHOP:SI (match_dup 1) + (match_operand:SI 2 "" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model + "" +{ + or1k_expand_atomic_op (, operands[1], operands[2], operands[0], NULL); + DONE; +}) + +(define_expand "atomic_fetch_" + [(match_operand:I12 0 "register_operand" "") ;; output + (match_operand:I12 1 "memory_operand" "") ;; memory + (FETCHOP:I12 (match_dup 1) + (match_operand:I12 2 "" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model + "" +{ + or1k_expand_atomic_op_qihi (, operands[1], operands[2], + operands[0], NULL); + DONE; +}) + +(define_expand "atomic__fetchsi" + [(match_operand:SI 0 "register_operand" "") ;; output + (match_operand:SI 1 "memory_operand" "") ;; memory + (FETCHOP:SI (match_dup 1) + (match_operand:SI 2 "" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model + "" +{ + or1k_expand_atomic_op (, operands[1], operands[2], NULL, operands[0]); + DONE; +}) + +(define_expand "atomic__fetch" + [(match_operand:I12 0 "register_operand" "") ;; output + (match_operand:I12 1 "memory_operand" "") ;; memory + (FETCHOP:I12 (match_dup 1) + (match_operand:I12 2 "" "")) ;; operand + (match_operand:SI 3 "const_int_operand" "")] ;; model + "" +{ + or1k_expand_atomic_op_qihi (, operands[1], operands[2], + NULL, operands[0]); + DONE; +}) + +;; ------------------------------------------------------------------------- +;; Call Instructions +;; ------------------------------------------------------------------------- + +;; Leave these to last, as the modeless operand for call_value +;; interferes with normal patterns. + +(define_expand "call" + [(call (match_operand 0) (match_operand 1))] + "" +{ + or1k_expand_call (NULL, operands[0], operands[1], false); + DONE; +}) + +(define_expand "sibcall" + [(call (match_operand 0) (match_operand 1))] + "" +{ + or1k_expand_call (NULL, operands[0], operands[1], true); + DONE; +}) + +(define_expand "call_value" + [(set (match_operand 0) (call (match_operand 1) (match_operand 2)))] + "" +{ + or1k_expand_call (operands[0], operands[1], operands[2], false); + DONE; +}) + +(define_expand "sibcall_value" + [(set (match_operand 0) (call (match_operand 1) (match_operand 2)))] + "" +{ + or1k_expand_call (operands[0], operands[1], operands[2], true); + DONE; +}) + +(define_insn "*call" + [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "r,s")) + (match_operand 1)) + (clobber (reg:SI LR_REGNUM))] + "!SIBLING_CALL_P (insn)" + "@ + l.jalr\t%0%# + l.jal\t%P0%#" + [(set_attr "type" "control")]) + +(define_insn "*sibcall" + [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "c,s")) + (match_operand 1))] + "SIBLING_CALL_P (insn)" + "@ + l.jr\t%0%# + l.j\t%P0%#" + [(set_attr "type" "control")]) + +(define_insn "*call_value" + [(set (match_operand 0) + (call (mem:SI (match_operand:SI 1 "call_insn_operand" "r,s")) + (match_operand 2))) + (clobber (reg:SI LR_REGNUM))] + "!SIBLING_CALL_P (insn)" + "@ + l.jalr\t%1%# + l.jal\t%P1%#" + [(set_attr "type" "control")]) + +(define_insn "*sibcall_value" + [(set (match_operand 0) + (call (mem:SI (match_operand:SI 1 "call_insn_operand" "c,s")) + (match_operand 2)))] + "SIBLING_CALL_P (insn)" + "@ + l.jr\t%1%# + l.j\t%P1%#" + [(set_attr "type" "control")]) diff --git a/gcc/config/or1k/or1k.opt b/gcc/config/or1k/or1k.opt new file mode 100644 index 00000000000..3cc9422f756 --- /dev/null +++ b/gcc/config/or1k/or1k.opt @@ -0,0 +1,67 @@ +; OpenRISC command line options + +; Copyright (C) 2010-2018 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License as published by the Free +; Software Foundation; either version 3, or (at your option) any later +; version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; . + +; See the GCC internals manual (options.texi) for a description of +; this file's format. + +; Please try to keep this file in ASCII collating order. + +mhard-div +Target RejectNegative InverseMask(SOFT_DIV) +Use hardware divide instructions, use -msoft-div for emulation. + +mhard-mul +Target RejectNegative InverseMask(SOFT_MUL). +Use hardware multiply instructions, use -msoft-mul for emulation. + +mcmov +Target RejectNegative Mask(CMOV) +Allows generation of binaries which use the l.cmov instruction. If your target +does not support this the compiler will generate the equivalent using set and +branch. + +mror +Target RejectNegative Mask(ROR) +Allows generation of binaries which use the l.rori instructions. + +msext +Target RejectNegative Mask(SEXT) +Allows generation of binaries which use sign-extension instructions. If your +target does not support this the compiler will use memory loads to perform sign +extension. + +msfimm +Target RejectNegative Mask(SFIMM) +Allows generation of binaries which use l.sf*i instructions. If your target +does not support this the compiler will generate instructions to store the +immediate to a register first. + +mshftimm +Target RejectNegative Mask(SHFTIMM) +Allows generation of binaries which support shifts and rotate instructions +supporting immediate arguments, for example l.rori. + +msoft-div +Target RejectNegative Mask(SOFT_DIV) +Use divide emulation. + +msoft-mul +Target RejectNegative Mask(SOFT_MUL). +Use multiply emulation. diff --git a/gcc/config/or1k/predicates.md b/gcc/config/or1k/predicates.md new file mode 100644 index 00000000000..10e39de5d08 --- /dev/null +++ b/gcc/config/or1k/predicates.md @@ -0,0 +1,84 @@ +;; Predicate definitions for OpenRISC +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by Stafford Horne + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. + +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; . + +;; ------------------------------------------------------------------------- +;; Predicates +;; ------------------------------------------------------------------------- + +(define_predicate "input_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "memory_operand") + (and (match_code "const_int") + (match_test "satisfies_constraint_I (op) + || satisfies_constraint_K (op) + || satisfies_constraint_M (op)")))) + +(define_predicate "const0_operand" + (and (match_code "const_int,const_wide_int,const_double,const_vector") + (match_test "op == CONST0_RTX (mode)"))) + +(define_predicate "reg_or_0_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "const0_operand"))) + +(define_predicate "reg_or_u6_operand" + (if_then_else (match_code "const_int") + (match_test "INTVAL (op) >= 0 && INTVAL (op) <= 0x3f") + (match_operand 0 "register_operand"))) + +(define_predicate "reg_or_u16_operand" + (if_then_else (match_code "const_int") + (match_test "INTVAL (op) >= 0 && INTVAL (op) <= 0xffff") + (match_operand 0 "register_operand"))) + +(define_predicate "reg_or_s16_operand" + (if_then_else (match_code "const_int") + (match_test "INTVAL (op) >= -32768 && INTVAL (op) <= 32767") + (match_operand 0 "register_operand"))) + +(define_predicate "call_insn_operand" + (ior (match_code "symbol_ref") + (match_operand 0 "register_operand"))) + +(define_predicate "high_operand" + (match_code "symbol_ref,label_ref,const,unspec")) + +;; Return true for relocations that must use MOVHI+ADDI +(define_predicate "losum_add_operand" + (match_code "symbol_ref,label_ref,const,unspec")) + +;; Return true for relocations that must use MOVHI+ORI +(define_predicate "losum_ior_operand" + (and (match_code "unspec") + (match_test "XINT(op, 1) == UNSPEC_TLSGD"))) + +;; Return true for a "virtual" or "soft" register that will be +;; adjusted to a "soft" or "hard" register during elimination. +(define_predicate "virtual_frame_reg_operand" + (match_code "reg") +{ + unsigned regno = REGNO (op); + return (regno != STACK_POINTER_REGNUM + && regno != HARD_FRAME_POINTER_REGNUM + && REGNO_PTR_FRAME_P (regno)); +}) + +(define_predicate "equality_comparison_operator" + (match_code "ne,eq")) diff --git a/gcc/config/or1k/rtems.h b/gcc/config/or1k/rtems.h new file mode 100644 index 00000000000..0c3d39cc9c4 --- /dev/null +++ b/gcc/config/or1k/rtems.h @@ -0,0 +1,30 @@ +/* Target Newlib Definitions for OpenRISC. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Joel Sherrill (joel.sherrill@OARcorp.com). + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +/* Target OS builtins. */ +#undef TARGET_OS_CPP_BUILTINS +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__rtems__"); \ + builtin_define ("__USE_INIT_FINI__"); \ + builtin_assert ("system=rtems"); \ + } \ + while (0) diff --git a/gcc/config/or1k/t-or1k b/gcc/config/or1k/t-or1k new file mode 100644 index 00000000000..6771c82da08 --- /dev/null +++ b/gcc/config/or1k/t-or1k @@ -0,0 +1,22 @@ +# Target Makefile Fragment for OpenRISC +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by Stafford Horne. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 3, or (at your +# option) any later version. +# +# GCC is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +comma=, +MULTILIB_OPTIONS = $(subst $(comma), ,$(TM_MULTILIB_CONFIG)) diff --git a/gcc/config/or1k/t-rtems b/gcc/config/or1k/t-rtems new file mode 100644 index 00000000000..e4e5e1dca97 --- /dev/null +++ b/gcc/config/or1k/t-rtems @@ -0,0 +1,3 @@ +# RTEMS OR1K multilibs + +# No custom multilibs defined diff --git a/gcc/configure b/gcc/configure index c49b4b264e8..9e9dcb1b33f 100755 --- a/gcc/configure +++ b/gcc/configure @@ -24331,6 +24331,18 @@ foo: .long 25 tls_first_minor=20 tls_as_opt='--fatal-warnings' ;; + or1k*-*-*) + conftest_s=' + .section ".tdata","awT",@progbits +foo: .long 25 + .text + l.movhi r3, tpoffha(foo) + l.add r3, r3, r10 + l.lwz r4, tpofflo(foo)(r3)' + tls_first_major=2 + tls_first_minor=30 + tls_as_opt=--fatal-warnings + ;; powerpc-ibm-aix*) conftest_s=' .extern __get_tpointer diff --git a/gcc/configure.ac b/gcc/configure.ac index f43af0b8d20..b79a604061c 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3460,6 +3460,18 @@ foo: .long 25 tls_first_minor=20 tls_as_opt='--fatal-warnings' ;; + or1k*-*-*) + conftest_s=' + .section ".tdata","awT",@progbits +foo: .long 25 + .text + l.movhi r3, tpoffha(foo) + l.add r3, r3, r10 + l.lwz r4, tpofflo(foo)(r3)' + tls_first_major=2 + tls_first_minor=30 + tls_as_opt=--fatal-warnings + ;; powerpc-ibm-aix*) conftest_s=' .extern __get_tpointer diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index 069b71922d2..5263253780a 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -3272,6 +3272,10 @@ information have to. @item @uref{#nvptx-x-none,,nvptx-*-none} @item +@uref{#or1k-x-elf,,or1k-*-elf} +@item +@uref{#or1k-x-linux,,or1k-*-linux} +@item @uref{#powerpc-x-x,,powerpc*-*-*} @item @uref{#powerpc-x-darwin,,powerpc-*-darwin*} @@ -4239,6 +4243,21 @@ the GCC sources. Use the @option{--disable-sjlj-exceptions} and @option{--enable-newlib-io-long-long} options when configuring. +@html +
+@end html +@anchor{or1k-x-elf} +@heading or1k-*-elf +The OpenRISC 1000 32-bit processor with delay slots. +This configuration is intended for embedded systems. + +@html +
+@end html +@anchor{or1k-x-linux} +@heading or1k-*-linux +The OpenRISC 1000 32-bit processor with delay slots. + @html
@end html diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 57491f1033c..b3b9336b298 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1005,6 +1005,11 @@ Objective-C and Objective-C++ Dialects}. @emph{Nvidia PTX Options} @gccoptlist{-m32 -m64 -mmainkernel -moptimize} +@emph{OpenRISC Options} +@gccoptlist{-mboard=@var{name} -mnewlib -mhard-mul -mhard-div @gol +-msoft-mul -msoft-div @gol +-mcmov -mror -msext -msfimm -mshftimm} + @emph{PDP-11 Options} @gccoptlist{-mfpu -msoft-float -mac0 -mno-ac0 -m40 -m45 -m10 @gol -mint32 -mno-int16 -mint16 -mno-int32 @gol @@ -14934,6 +14939,7 @@ platform. * NDS32 Options:: * Nios II Options:: * Nvidia PTX Options:: +* OpenRISC Options:: * PDP-11 Options:: * picoChip Options:: * PowerPC Options:: @@ -22688,6 +22694,68 @@ Generate code for use in OpenMP offloading: enables @option{-msoft-stack} and @end table +@node OpenRISC Options +@subsection OpenRISC Options +@cindex OpenRISC Options + +These options are defined for OpenRISC: + +@table @gcctabopt + +@item -mboard=@var{name} +@opindex mboard +Configure a board specific runtime. This will be passed to the linker for +newlib board library linking. The default is @code{or1ksim}. + +@item -mnewlib +@opindex mnewlib +For compatibility, it's always newlib for elf now. + +@item -mhard-div +@opindex mhard-div +Generate code for hardware which supports divide instructions. This is the +default. + +@item -mhard-mul +@opindex mhard-mul +Generate code for hardware which supports multiply instructions. This is the +default. + +@item -mcmov +@opindex mcmov +Generate code for hardware which supports the conditional move (@code{l.cmov}) +instruction. + +@item -mror +@opindex mror +Generate code for hardware which supports rotate right instructions. + +@item -msext +@opindex msext +Generate code for hardware which supports sign-extension instructions. + +@item -msfimm +@opindex msfimm +Generate code for hardware which supports set flag immediate (@code{l.sf*i}) +instructions. + +@item -mshftimm +@opindex mshftimm +Generate code for hardware which supports shift immediate related instructions +(i.e. @code{l.srai}, @code{l.srli}, @code{l.slli}, @code{1.rori}). Note, to +enable generation of the @code{l.rori} instruction the @option{-mror} flag must +also be specified. + +@item -msoft-div +@opindex msoft-div +Generate code for hardware which requires divide instruction emulation. + +@item -msoft-mul +@opindex msoft-mul +Generate code for hardware which requires multiply instruction emulation. + +@end table + @node PDP-11 Options @subsection PDP-11 Options @cindex PDP-11 Options diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 360b36b862f..46806bbea37 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -3003,6 +3003,31 @@ representing a supported PIC or TLS relocation. @end table +@item OpenRISC---@file{config/or1k/constraints.md} +@table @code +@item I +Integer that is valid as an immediate operand in an +instruction taking a signed 16-bit number. Range +@minus{}32768 to 32767. + +@item K +Integer that is valid as an immediate operand in an +instruction taking an unsigned 16-bit number. Range +0 to 65535. + +@item M +Signed 16-bit constant shifted left 16 bits. (Used with @code{l.movhi}) + +@item O +Zero + +@ifset INTERNALS +@item c +Register usable for sibcalls. +@end ifset + +@end table + @item PDP-11---@file{config/pdp11/constraints.md} @table @code @item a