From patchwork Tue May 5 22:30:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: feedable X-Patchwork-Id: 134516 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id AF6C04BA9008 for ; Tue, 5 May 2026 22:32:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AF6C04BA9008 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=XrH6drT/ X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by sourceware.org (Postfix) with ESMTPS id B188E4BA2E0B for ; Tue, 5 May 2026 22:31:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B188E4BA2E0B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B188E4BA2E0B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::332 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778020275; cv=none; b=UxmKUQXOS3YXPgnF0nBjUKKd7AfqKyPPCxWhc4oVXort3ibbynHlMjnzvdjTZ4OmOnHnOYHN4XkMLh1Hk9uxUeA09ZObAVoYtFz0rYNpkHpOpIgq9pI4ij/k/K6twvrtnT1ziZDEnoN4IG0vpCk/468UF+p+IdoMVNMk5DG3Cw8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778020275; c=relaxed/simple; bh=JgKXPP4fU332g85dnBKu4CxtRI+MHCrBa71GVxKv0Lo=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=wJM6aotmCfjyBLkBcgynzM/GHM8GrZQbi1cMHDYue/8s8fKU2M7TpresSrQLSDMPZbCKh2XlFAnB0wzDGFUu1RcEBIzSnmuiX72g1xsSApArVxRToOsVRcd6/foOOgzro1ZdXBk4iNk7cbd37KgCRoiGFJCMcnQKG5cEQQf8pEM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B188E4BA2E0B Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-48374014a77so68145385e9.3 for ; Tue, 05 May 2026 15:31:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778020274; x=1778625074; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=W3QSTVW1UGuUKD3F7Lu9UQBQNB46kPkQR2OrFYI4TMM=; b=XrH6drT/z4FdA8871XNdhz4AhnxkyQx5Qqp5Irt/sjMrrSAu0/6OJnZWANM8vT+vIb hYJ+b+Lfg6jacMRsQumLY3PCNlW3HfwIRShTgOEsOkb6KZik/kgHZick02sJvk5SxTPt nQxoCEkKfJst/z57kGMhNdnb472n2KNeHtb5QH7TH5P5zLrsTJiOvlfzDXzn/eHI8ssy NXoaXmFh/e+IWBhjwYiGn/JJFvvx+r6efr/YVwmnDXH/UpkR496uf7a9CKJnwv77T+Fk cX32Aeq8wZ+S3ZzhzWwcpOA1gsgngPT8YNEE60VJA9SK6WlocCesUxIYDX4xmsVOJq3p Un3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778020274; x=1778625074; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=W3QSTVW1UGuUKD3F7Lu9UQBQNB46kPkQR2OrFYI4TMM=; b=YBhGwhVqOdeawiQ5LobATkde1Pxi16/PfWpzxeQQet6rg6DxcQKn3f1PkAcAeqBH1s 7pWDsBVcHiG4dtKFDViK/7Z19PWn2Tbq0q/YQkKkaw2kXcMRVXOIQuZCiYbYXwym1t9c 1yXNXFfuB/EJamdjQelhCH6ULPaHgUikqUCFGkOYTeGPZhkWKLIu/6Kv+00jKqyCccP+ 5wBY5V6N5CbB8tL4MQ4i5+Z7Dg28VNS/II2GzmFzhSoGyzfVVkRxDWcnhMwaHcQlVRA2 LWFzHsqpSOWU0nT5tQ/JFQDf+LV+17lt2hpLLfH/9DInsWlgmhax1rgkwKxzJ3mRT8LM dvIA== X-Gm-Message-State: AOJu0YzPG1VhJdil/M/O8ZX+IqfP3rZxZk1GXQYmyAjR9WhI4TixMx0d Sl0v6Q+6HUHURDPT7QsVdbVvsDzNXJn/X1SnEIQ2Pm6L5YpyWtiKzjF0B2xqZA== X-Gm-Gg: AeBDiesq7wZ2i3F9+HUwoHvMaw+MVZg2X5zdDavdEST3SCAH3sdu4pI+3ACoziHDZON viM+P3StXalBrEwmqYjbowBw0AG6rtSiNZTRtMemR0nSHonfA7SuyKV2Gtm4arXQCAumV8UkJRg iECrTwjy+Tg5QIJo+p9diJns2gMDFRyyfjznQUQG90HRmGxlcGvzMIBOfZms1b1GDEZVAaHg9nu GoTO9hdM4/MMh6tDnHS0xi/g4ADpvbCSKWwZb9U3/CjT38FZzI+z9HjkFnR01ZeVXnSaLaKhrtO fcWFru+jleS4n9o/dn1u9x0Pchxz39y+yTZG6rDrWIBY7SPbpC7uyYvBEzsIrpuFIHtSVmHpgs4 PMTmjuRsTFXOEf6Qq4v4dlhEN0xZbLOj9LHdupRh5PlwxIAVlyAYQtPIbHCZZkZxcdPcjobVFEr 9Beqf6Wf9I5HJhvywgWKZXlzbwwBuyU8aMvtNgzGbTIYQt5hd3/QgMW7iyPxk= X-Received: by 2002:a05:600c:3b17:b0:48a:568f:ae6d with SMTP id 5b1f17b1804b1-48e51e15c37mr15996545e9.8.1778020273153; Tue, 05 May 2026 15:31:13 -0700 (PDT) Received: from 7a38.moduleworks.com ([2a02:8308:900b:fc00:1c70:a43b:cde8:2b29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e53559d8esm79055e9.9.2026.05.05.15.31.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2026 15:31:12 -0700 (PDT) From: feedable To: gcc-patches@gcc.gnu.org Cc: feedable Subject: [RFC PATCH 2/3] wasm: New backend Date: Wed, 6 May 2026 01:30:45 +0300 Message-ID: <20260505223045.347444-4-feedabl3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260505223045.347444-2-feedabl3@gmail.com> References: <20260505223045.347444-2-feedabl3@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, KAM_STOCKGEN, POISEN_SPAM_PILL, POISEN_SPAM_PILL_2, POISEN_SPAM_PILL_4, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org I don't really know what kind of a description is expected for a new backend implementation, so I'm leaving this empty for now as a placeholder. -- 8< -- gcc/ChangeLog: * config.gcc: Adjust for new backend. * config/wasm/attrs.md: New file. * config/wasm/t-wasm: New file. * config/wasm/wasm-asm.cc: New file. * config/wasm/wasm-cg.cc: New file. * config/wasm/wasm-modes.def: New file. * config/wasm/wasm-passes.cc: New file. * config/wasm/wasm-passes.def: New file. * config/wasm/wasm-protos.h: New file. * config/wasm/wasm.cc: New file. * config/wasm/wasm.h: New file. * config/wasm/wasm.md: New file. libgcc/ChangeLog: * config.host: Adjust for new backend. * config/wasm/t-wasm: New file. --- gcc/config.gcc | 7 + gcc/config/wasm/attrs.md | 84 +++ gcc/config/wasm/t-wasm | 13 + gcc/config/wasm/wasm-asm.cc | 945 ++++++++++++++++++++++++++++++++ gcc/config/wasm/wasm-cg.cc | 621 +++++++++++++++++++++ gcc/config/wasm/wasm-modes.def | 0 gcc/config/wasm/wasm-passes.cc | 153 ++++++ gcc/config/wasm/wasm-passes.def | 27 + gcc/config/wasm/wasm-protos.h | 12 + gcc/config/wasm/wasm.cc | 128 +++++ gcc/config/wasm/wasm.h | 307 +++++++++++ gcc/config/wasm/wasm.md | 495 +++++++++++++++++ libgcc/config.host | 5 + libgcc/config/wasm/t-wasm | 4 + 14 files changed, 2801 insertions(+) create mode 100644 gcc/config/wasm/attrs.md create mode 100644 gcc/config/wasm/t-wasm create mode 100644 gcc/config/wasm/wasm-asm.cc create mode 100644 gcc/config/wasm/wasm-cg.cc create mode 100644 gcc/config/wasm/wasm-modes.def create mode 100644 gcc/config/wasm/wasm-passes.cc create mode 100644 gcc/config/wasm/wasm-passes.def create mode 100644 gcc/config/wasm/wasm-protos.h create mode 100644 gcc/config/wasm/wasm.cc create mode 100644 gcc/config/wasm/wasm.h create mode 100644 gcc/config/wasm/wasm.md create mode 100644 libgcc/config/wasm/t-wasm diff --git a/gcc/config.gcc b/gcc/config.gcc index d1595a1d85e..822515c127e 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -614,6 +614,10 @@ tic6x-*-*) extra_headers="c6x_intrinsics.h" extra_options="${extra_options} c6x/c6x-tables.opt" ;; +wasm*-*-*) + cpu_type=wasm + extra_objs="wasm-cg.o wasm-asm.o wasm-passes.o" + ;; xtensa*-*-*) extra_options="${extra_options} fused-madd.opt" extra_objs="xtensa-dynconfig.o" @@ -3677,6 +3681,9 @@ visium-*-elf*) tm_file="elfos.h ${tm_file} visium/elf.h newlib-stdint.h" tmake_file="visium/t-visium visium/t-crtstuff" ;; +wasm*-*-*) + target_has_targetm_common=no + ;; xstormy16-*-elf) # For historical reasons, the target files omit the 'x'. tm_file="elfos.h newlib-stdint.h stormy16/stormy16.h" diff --git a/gcc/config/wasm/attrs.md b/gcc/config/wasm/attrs.md new file mode 100644 index 00000000000..6fbe39e5ce7 --- /dev/null +++ b/gcc/config/wasm/attrs.md @@ -0,0 +1,84 @@ +(define_c_enum "unspecv" [ + UNSPECV_CALL_ADDRESS_BARRIER +]) + +(define_mode_iterator QHSDI [QI HI SI DI]) +(define_mode_iterator QHSDI2 [QI HI SI DI]) +(define_mode_iterator QHSDISDF [QI HI SI DI SF DF]) +(define_mode_iterator F [SF DF]) +(define_mode_iterator REG [SI DI]) +(define_mode_iterator REGF [SI DI SF DF]) +(define_mode_attr REG2F [(SI "SF") (DI "DF") (SF "SI") (DF "DI")]) +(define_mode_attr reg2f [(SI "sf") (DI "df") (SF "si") (DF "di")]) +(define_mode_attr reg2f_types [ + (SF "i32") (DF "i64") + (SI "f32") (DI "f64")]) +(define_mode_attr types [ + (QI "i8") (HI "i16") + (SI "i32") (DI "i64") + (SF "f32") (DF "f64")]) +(define_mode_attr size [ + (QI "8") (HI "16") + (SI "32") (DI "64") + (SF "32") (DF "64")]) +(define_mode_attr promote_type [ + (QI "i32") (HI "i32") + (SI "i32") (DI "i64") + (SF "f32") (DF "f32")]) +(define_mode_attr promote_mode [ + (QI "SI") (HI "SI") + (SI "SI") (DI "DI") + (SF "SF") (DF "DF")]) +(define_mode_iterator SUBREGSI [QI HI]) +(define_mode_iterator SUBREGDI [QI HI SI]) +(define_mode_iterator P [(SI "Pmode == SImode") (DI "Pmode == DImode")]) + +(define_code_iterator irelop [le ge lt gt leu geu ltu gtu eq ne]) +(define_code_iterator frelop [le ge lt gt eq ne]) + +(define_code_iterator iunop [clz ctz popcount]) + +(define_code_iterator iconvop [sign_extend truncate]) + +(define_code_iterator ibinop [ + and ior xor + plus minus mult + div udiv mod umod + ashift ashiftrt lshiftrt + rotate rotatert]) + +(define_code_iterator fbinop [plus minus mult div smin smax copysign]) + +(define_code_iterator funop [abs neg sqrt]) + +(define_code_attr opname [ + (eq "eq") (ne "ne") + (le "le_s") (ge "ge_s") (lt "lt_s") (gt "gt_s") + (leu "le_u") (geu "ge_u") (ltu "lt_u") (gtu "gt_u") + (clz "clz") (ctz "ctz") (popcount "popcnt") + (and "and") (ior "or") (xor "xor") + (plus "add") (minus "sub") (mult "mul") + (div "div_s") (udiv "div_u") (mod "rem_s") (umod "rem_u") + (ashift "shl") (ashiftrt "shr_s") (lshiftrt "shr_u") + (rotate "rotl") (rotatert "rotr") + (sign_extend "extend") (zero_extend "zero_extend") + (truncate "trunc") + (abs "abs") (neg "neg") (sqrt "sqrt")]) +(define_code_attr opnamef [ + (eq "eq") (ne "ne") (le "le") (ge "ge") (lt "lt") (gt "gt") + (plus "add") (minus "sub") (mult "mul") (div "div") (smin "min") (smax "max") + (abs "abs") (neg "neg") (sqrt "sqrt") (copysign "copysign")]) +(define_code_attr opname_rt [ + (eq "eq") (ne "ne") + (le "le") (ge "ge") (lt "lt") (gt "gt") + (leu "leu") (geu "geu") (ltu "ltu") (gtu "gtu") + (clz "clz") (ctz "ctz") (popcount "popcount") + (and "and") (ior "ior") (xor "xor") + (plus "add") (minus "sub") (mult "mul") + (div "div") (udiv "udiv") (mod "mod") (umod "umod") + (ashift "ashl") (ashiftrt "ashr") (lshiftrt "lshr") + (rotate "rotl") (rotatert "rotr") + (sign_extend "extend") (zero_extend "zero_extend") + (truncate "ftrunc") + (smax "smax") (smin "smin") (copysign "copysign") + (abs "abs") (neg "neg") (sqrt "sqrt")]) diff --git a/gcc/config/wasm/t-wasm b/gcc/config/wasm/t-wasm new file mode 100644 index 00000000000..6d5846a8822 --- /dev/null +++ b/gcc/config/wasm/t-wasm @@ -0,0 +1,13 @@ +wasm-cg.o: $(srcdir)/config/wasm/wasm-cg.cc \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(INPUT_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +wasm-asm.o: $(srcdir)/config/wasm/wasm-asm.cc \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(INPUT_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +wasm-passes.o: $(srcdir)/config/wasm/wasm-passes.cc \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(INPUT_H) + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< + +PASSES_EXTRA += $(srcdir)/config/wasm/wasm-passes.def \ No newline at end of file diff --git a/gcc/config/wasm/wasm-asm.cc b/gcc/config/wasm/wasm-asm.cc new file mode 100644 index 00000000000..17306a82851 --- /dev/null +++ b/gcc/config/wasm/wasm-asm.cc @@ -0,0 +1,945 @@ +/* WebAssembly assembly output utilities. + Copyright (C) 2025-2025 Free Software Foundation, Inc. + Contributed by feedable. + + 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 +#include + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "stringpool.h" +#include "attribs.h" +#include "gimple.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "stringpool.h" +#include "optabs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "output.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "explow.h" +#include "expr.h" +#include "langhooks.h" +#include "cfgrtl.h" +#include "gimplify.h" +#include "reload.h" +#include "builtins.h" +#include "tree-pass.h" + +void wasm_print_operand (FILE *stream, rtx value, int mode); + +extern hash_map external_libcalls; + +namespace +{ + +int indent; +void output_indent(FILE *stream) +{ + fprintf (stream, "%*s", indent * 2, ""); +} +void output_indent() +{ + output_indent (asm_out_file); +} + +char *fake_out_file_data; +size_t fake_out_file_length; +FILE *saved_asm_out_file; + +hash_map> import_map; +hash_set external_libcall_set; + +bool +global_p (rtx reg) +{ + if (GET_CODE (reg) == SUBREG) + reg = SUBREG_REG (reg); + gcc_assert (GET_CODE (reg) == REG); + return REGNO (reg) == STACK_POINTER_REGNUM; +} + +bool +is_escape (char x) +{ + const char *escapes = "\"\'\\\t\n\r"; + while (*escapes and x != *escapes) + ++escapes; + return *escapes; +} + +void +assemble_string (FILE *stream, const char *string, int size, bool hex_only) +{ + fprintf (stream, " \""); + for (int i = 0; i != size; ++i) + { + char c[] = {'\\', string[i], '\0'}; + if (c[1] < 32 || c[1] > 127 || hex_only) + fprintf (stream, "\\%0.2hhx", c[1]); + else + fprintf (stream, "%s", c + !is_escape(c[1])); + } + fprintf (stream, "\""); +} + +void +assemble_zeroes (FILE *stream, unsigned HOST_WIDE_INT size) +{ + fputs (" \"", stream); + for (unsigned HOST_WIDE_INT i = 0; i != size; ++i) + fputs ("\\00", stream); + fputs ("\"", stream); +} + +void +assemble_const_int (FILE *stream, HOST_WIDE_INT value, unsigned size) +{ + char val[sizeof (HOST_WIDE_INT)]; + for (int i = 0; i != sizeof (HOST_WIDE_INT); ++i) + val[i] = value >> (i * 8); + assemble_string (stream, val, size, true); +} + +/* Can handle following shapes: + (const_int x) + (symbol_ref x) + (const (plus (symbol_ref x) (const_int y)) + (const (minus (symbol_ref x) (const_int y)) + (const (plus (const_int x) (const_int y)) + (const (minus (const_int x) (const_int y)) */ +bool +assemble_integer (FILE *stream, rtx x, unsigned size, int) +{ + HOST_WIDE_INT addend = 0; + + if (GET_CODE (x) == CONST) + { + x = XEXP (x, 0); + switch (GET_CODE (x)) + { + default: + return false; + case PLUS: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + return false; + addend = INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + break; + case MINUS: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + return false; + addend = -INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + break; + } + } + switch (GET_CODE (x)) + { + default: + return false; + case CONST_INT: + assemble_const_int (stream, INTVAL (x) + addend, size); + return true; + case SYMBOL_REF: + tree decl = SYMBOL_REF_DECL (x); + bool func_p = TREE_CODE (decl) == FUNCTION_DECL; + const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + assemble_zeroes (stream, size); + fprintf (stream, " (@reloc i%d %s ", size * 8, + func_p ? "functable" : "data"); + wasm_print_operand (stream, x, 'l'); + if (addend) + fprintf(stream, " %+ld", addend); + fprintf (stream, ")"); + return true; + } +} + + +void +print_type (FILE *stream, const_tree type, bool first = false) +{ + const char *delim = first ? "" : " "; + switch (TREE_CODE (type)) + { + default: + gcc_unreachable (); + case VOID_TYPE: + break; + case NULLPTR_TYPE: + case VECTOR_TYPE: + case COMPLEX_TYPE: + print_type (stream, ptr_type_node, first); + break; + case UNION_TYPE: + case RECORD_TYPE: + if (TYPE_EMPTY_P (type)) + break; + if (TYPE_TRANSPARENT_AGGR (type)) + print_type (stream, TREE_TYPE (first_field (type)), first); + else + print_type (stream, ptr_type_node, first); + break; + case POINTER_TYPE: + case REFERENCE_TYPE: + case OFFSET_TYPE: + case INTEGER_TYPE: + case BOOLEAN_TYPE: + case ENUMERAL_TYPE: + fprintf (stream, "%s%s", delim, + TYPE_PRECISION (type) > 32 ? "i64" : "i32"); + break; + case REAL_TYPE: + fprintf (stream, "%s%s", delim, + TYPE_PRECISION (type) > 32 ? "f64" : "f32"); + break; + case FUNCTION_TYPE: + case METHOD_TYPE: + function_args_iterator it; + tree *arg; + fprintf (stream, "%s(param", delim); + CUMULATIVE_ARGS args; + INIT_CUMULATIVE_ARGS(args, type, nullptr, false, 0); + FOREACH_FUNCTION_ARGS_PTR (type, arg, it) + { + function_arg_info info (*arg, stdarg_p (type)); + if (pass_by_reference(&args, info)) + print_type (stream, intSI_type_node); + else + print_type (stream, *arg); + } + fprintf (stream, ")"); + tree return_type = TREE_TYPE (type); + fprintf (stream, " (result"); + if (return_type != void_type_node) + { + print_type (stream, return_type); + } + fprintf (stream, ")"); + break; + } +} +void +print_local_decl (FILE *stream, rtx reg, bool param = false) +{ + output_indent (stream); + fprintf (stream, "(%s ", param ? "param" : "local"); + wasm_print_operand (stream, reg, 0); + tree param_type = lang_hooks.types.type_for_mode (GET_MODE (reg), 0); + print_type (stream, param_type); + fprintf (stream, ")\n"); +} + +void assemble_visibility (FILE *stream, const_tree decl) +{ + if (decl && DECL_VISIBILITY (decl) == VISIBILITY_HIDDEN) + fprintf (stream, " hidden"); +} + +void assemble_binding (FILE *stream, const_tree decl) +{ + if (!decl) + return; + if (DECL_WEAK (decl)) + fprintf (stream, " weak"); + if (!TREE_PUBLIC (decl)) + fprintf (stream, " local"); +} + +void assemble_init_prio (FILE *stream, const_tree decl) +{ + if (DECL_STATIC_CONSTRUCTOR (decl)) + { + int prio = decl_init_priority_lookup (const_cast (decl)); + fprintf (stream, " (init_prio %d)", prio); + } +} + +void assemble_sym_name (FILE *stream, const char *name) +{ + fprintf (stream, " (name \""); + assemble_name (stream, name); + fprintf (stream, "\")"); +} + + +void +assemble_data_import (FILE *stream, const_tree decl, const char *name) +{ + output_indent (stream); + fprintf (stream, "(@sym.import.data $"); + assemble_name (stream, name); + assemble_binding (stream, decl); + assemble_visibility (stream, decl); + assemble_sym_name (stream, name); + fprintf (stream, ")\n"); +} + +/* Given the type, guess where assign_params must've put it */ +void print_func_frame_related (FILE *stream, tree type) +{ + if (VOID_TYPE_P (type)) + return; + if (TREE_CODE (type) == COMPLEX_TYPE + && targetm.calls.split_complex_arg (type)) + { + print_func_frame_related (stream, TREE_TYPE (type)); + print_func_frame_related (stream, TREE_TYPE (type)); + return; + } + if (RECORD_OR_UNION_TYPE_P (type) + && TYPE_TRANSPARENT_AGGR (type)) + type = TREE_TYPE (first_field (type)); + + function_arg_info arg (type, true); + if (pass_by_reference (NULL, arg)) + print_type (stream, build_pointer_type (type)); + else + print_type (stream, type); +} + +void +print_func_type (FILE *stream, const_tree type, bool for_block = false) +{ + tree result = TREE_TYPE (type); + + /* Taken from assign_parms_augmented_arg_list, please keep in sync */ + bool return_by_ref = aggregate_value_p (result, type); + if (!for_block) + { + fprintf (stream, " (param"); + if (return_by_ref) + print_func_frame_related (stream, build_pointer_type (result)); + tree arg; + function_args_iterator it; + FOREACH_FUNCTION_ARGS (type, arg, it) + { + print_func_frame_related (stream, arg); + } + if (stdarg_p (type) || !TYPE_ARG_TYPES (type)) + print_func_frame_related (stream, ptr_type_node); + fprintf (stream, ")"); + } + + fprintf (stream, "%s(result", for_block ? "": " "); + if (!return_by_ref) + print_func_frame_related (stream, result); + fprintf (stream, ")"); +} + +void print_global_type (FILE *stream, const_tree type) +{ + if (!TYPE_READONLY (type)) + fprintf (stream, " (mut"); + print_type (stream, type); + if (!TYPE_READONLY (type)) + fprintf (stream, ")"); +} + +void +assemble_entity_import (FILE *stream, const char *name, const char *abi_name, + const_tree decl) +{ + const_tree type = DECL_P (decl) ? TREE_TYPE (decl) : decl; + + output_indent (stream); + fprintf (stream, "(import \"env\" \""); + assemble_name (stream, abi_name); + fprintf (stream, "\" " ); + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + case BOOLEAN_TYPE: + case ENUMERAL_TYPE: + case POINTER_TYPE: + case OFFSET_TYPE: + case RECORD_TYPE: + case UNION_TYPE: { + fprintf (stream, "(global $"); + assemble_name (stream, name); + print_global_type (stream, type); + break; + } + case FUNCTION_TYPE: + case METHOD_TYPE: { + fprintf (stream, "(func $"); + assemble_name (stream, name); + fprintf (stream, " (@sym"); + if (type != decl) { + assemble_init_prio (stream, decl); + assemble_binding (stream, decl); + assemble_visibility (stream, decl); + } + fprintf (stream, ")"); + + print_func_type (stream, type); + break; + } + default: + gcc_unreachable (); + } + fprintf (stream, "))\n"); +} + +void +decl_end (FILE *stream) +{ + fprintf (stream, ")\n"); +} + +void +assemble_import (FILE *stream, const char *name, const_tree decl) +{ + if (!FUNC_OR_METHOD_TYPE_P (TREE_TYPE (decl))) + return assemble_data_import (stream, decl, name); + assemble_entity_import (stream, name, name, decl); +} + +template +void +print_nan_payload (FILE *stream, const REAL_VALUE_TYPE *n) +{ + constexpr int ndata = sizeof (T) / 4; + long data[ndata]; + real_to_target (data, n, float_mode_for_size (sizeof (T) * 8).require ()); + int mantissa = std::numeric_limits::digits - 1; + int mantissa_words = ((mantissa - 1) / 32) + 1; + for (int i = mantissa_words; i != ndata; ++i) + data[i] = 0; + unsigned mask = (1 << (mantissa % 32)) - 1; + data[mantissa / 32] &= mask; + + bool start = true; + for (int i = ndata; i--;) + if (start && !data[i]) + ; + else if (start) + fprintf (stream, "%lx", data[i]), start = false; + else + fprintf (stream, "%0.8lx", data[i]); +} + +} + +// TARGET_ASM_INIT_SECTIONS +void +wasm_asm_init_sections () +{ + text_section = get_unnamed_section (SECTION_CODE, [](const char *){}, "T"); + data_section = get_unnamed_section (SECTION_WRITE, [](const char *){}, "D"); +} + +// TARGET_ASM_FILE_START +void +wasm_asm_file_start () +{ + output_indent(asm_out_file); + indent++; + fprintf (asm_out_file, "(module\n"); + saved_asm_out_file = asm_out_file; + asm_out_file = open_memstream (&fake_out_file_data, &fake_out_file_length); +} + +// TARGET_ASM_FILE_END +void +wasm_asm_file_end () +{ + /* ??? All wasm globals should become builtin decls */ + output_indent (saved_asm_out_file); + fprintf (saved_asm_out_file, "(import \"env\" \"__stack_pointer\"" + " (global $stack (mut i32)))\n"); + + output_indent (saved_asm_out_file); + fprintf (saved_asm_out_file, "(import \"env\" \"memory\" (memory 0))\n"); + output_indent (saved_asm_out_file); + fprintf (saved_asm_out_file, "(import \"env\" \"__indirect_function_table\" (table 0 funcref))\n"); + + for (auto [key, value]: import_map) + { + auto [name, decl] = value; + assemble_import (saved_asm_out_file, IDENTIFIER_POINTER (name), decl); + } + for (auto sym: external_libcall_set) + assemble_entity_import (saved_asm_out_file, XSTR (sym, 0), XSTR (sym, 0), + *external_libcalls.get (sym)); + + fflush (asm_out_file); + fwrite (fake_out_file_data, 1, fake_out_file_length, saved_asm_out_file); + + fprintf (saved_asm_out_file, ")\n"); +} + +// ASM_OUTPUT_FUNCTION_LABEL +void +wasm_asm_start_function (FILE *stream, tree decl, const char *name) +{ + output_indent(stream); + indent++; + fprintf (stream, "(func $"); + assemble_name (stream, name); + + bool override_args = false; + if (MAIN_NAME_P (DECL_NAME (decl))) + { + if (!cfun->machine->func_args) + name = "__main_void"; + else + name = "__main_argc_argv", override_args = true; + } + + fprintf (stream, " (@sym"); + if (TREE_CODE (decl) == FUNCTION_DECL) + { + assemble_init_prio (stream, decl); + assemble_binding (stream, decl); + assemble_visibility (stream, decl); + assemble_sym_name (stream, name); + } + fprintf (stream, ") "); + + tree type = TREE_TYPE (decl); + //print_func_type (stream, type); + fprintf (stream, "\n"); + int i; + rtx reg; + + if (override_args) + { + FOR_EACH_VEC_SAFE_ELT (cfun->machine->func_args, i, reg) + print_local_decl (stream, reg, i < 2); + if (i < 1) + { + output_indent (stream); + fprintf (stream, "(param i32)\n"); + } + if (i < 2) + { + output_indent (stream); + fprintf (stream, "(param i32)\n"); + } + } + else + { + FOR_EACH_VEC_SAFE_ELT (cfun->machine->func_args, i, reg) + print_local_decl (stream, reg, true); + if (stdarg_p (type)) + print_local_decl (stream, regno_reg_rtx[ARG_POINTER_REGNUM], true); + if (DECL_STATIC_CHAIN (decl)) + print_local_decl (stream, regno_reg_rtx[STATIC_CHAIN_REGNUM], true); + } + output_indent (stream); + print_func_type (stream, type, true); + fprintf (stream, "\n"); + + output_indent (stream); + fprintf (stream, ";; hard\n"); + if (cfun->machine->return_mode != VOIDmode) + print_local_decl (stream, gen_rtx_REG (cfun->machine->return_mode, + WASM_RETURN_REGNUM)); + + df_set_regs_ever_live(WASM_CONTROL_POINTER_REGNUM, true); + for (int r = 0; r < FIRST_PSEUDO_REGISTER; r++) + { + if (r == WASM_RETURN_REGNUM) + continue; + if (r == ARG_POINTER_REGNUM) + continue; + if (r == STATIC_CHAIN_REGNUM && DECL_STATIC_CHAIN (decl)) + continue; + reg = regno_reg_rtx[r]; + if (df_regs_ever_live_p (r)) + { + if (!global_p (reg)) + print_local_decl (stream, reg); + } + } + + output_indent (stream); + fprintf (stream, ";; locals\n"); + int max = max_reg_num(); + for (int r = FIRST_PSEUDO_REGISTER; r < max; r++) + { + reg = regno_reg_rtx[r]; + if (!reg) + continue; + if (reg == const0_rtx) + continue; + if (vec_safe_contains (cfun->machine->func_args, reg)) + continue; + if (!bitmap_bit_p (cfun->machine->regs_ever_live, r)) + continue; + print_local_decl (stream, reg); + } + output_indent (stream); + fprintf (stream, "(local.set $control (i32.const 0))\n"); + output_indent (stream); + fprintf (stream, "(loop $control "); + print_func_type (stream, type, true); + fprintf (stream, "\n"); + indent++; + int len = cfun->machine->labelno_to_labels->elements (); + for (i = len - 1; i >= 0; --i) + { + output_indent (stream); + indent++; + fprintf (stream, "(block $%d\n", i); + } + + output_indent (stream); + + fprintf (stream, "(br_table"); + for (i = 0; i < len; ++i) + fprintf (stream, " $%d", i); + fprintf (stream, " (local.get $control))\n"); +} + +// ASM_DECLARE_FUNCTION_SIZE +void +wasm_asm_end_function (FILE *stream, tree, const char *name) +{ + indent--; + output_indent (stream); + fprintf (stream, ") ;; loop $control\n"); + indent--; + output_indent (stream); + fprintf (stream, ") ;;%s\n", + IDENTIFIER_POINTER (targetm.asm_out.mangle_assembler_name (name))); +} + +// TARGET_ASM_ASSEMBLE_UNDEFINED_DECL +void +wasm_handle_import (FILE *, const char *name, const_tree decl) +{ + auto has_proto = [] (const_tree ty) + { + if (TREE_CODE (ty) != FUNCTION_DECL) + return true; + ty = TREE_TYPE (ty); + return TYPE_ARG_TYPES (ty) || TYPE_NO_NAMED_ARGS_STDARG_P (ty); + }; + bool existed = false; + tree key = targetm.asm_out.mangle_assembler_name (name); + auto &slot = import_map.get_or_insert (key, &existed); + if (!existed || (!has_proto (slot.second) && has_proto (decl))) + slot = {get_identifier (name), decl}; +} + +// TARGET_ASM_ASSEMBLE_EXTERNAL_LIBCALL +void +wasm_handle_libcall (rtx symbol) +{ + external_libcall_set.add (symbol); +} + +// TARGET_ASM_INTEGER +bool +wasm_assemble_integer (rtx x, unsigned size, int align) +{ + return assemble_integer (asm_out_file, x, size, align); +} + +// TARGET_ASM_END_DECL +void +wasm_assemble_decl_end () +{ + return decl_end(asm_out_file); +} + +// ASM_OUTPUT_ASCII +void +wasm_assemble_ascii (FILE *stream, const char *data, int len) +{ + return assemble_string (stream, data, len, false); +} + +// ASM_OUTPUT_SKIP +void +wasm_assemble_skip (FILE *stream, unsigned HOST_WIDE_INT len) +{ + return assemble_zeroes (stream, len); +} + +// ASM_DECLARE_OBJECT_NAME +// ASM_DECLARE_CONSTANT_NAME +void +wasm_assemble_data_begin (FILE *stream, tree decl, const char *name, + HOST_WIDE_INT size, HOST_WIDE_INT align, bool pub) +{ + if (!name) + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + if (!size) + size = tree_to_uhwi (DECL_SIZE_UNIT (decl)); + output_indent(stream); + /* ??? This is a crutch. We should know the alignment of the constant we're + declaring, but ASM_DECLARE_CONSTANT_NAME doesn't supply it. Assume it's + huge and move on for now. */ + align = align ? align : 1024; + fprintf (stream, "(data (@sym (align %ld)) (i32.const 0) (@sym $", align); + assemble_name (stream, name); + if (decl) + { + assemble_binding (stream, decl); + } + else + { + if (!pub) + fprintf (stream, " local"); + } + assemble_visibility (stream, decl); + assemble_sym_name (stream, name); + fprintf (stream, " (size %ld)", size); + fprintf (stream, ")"); +} + +// ASM_OUTPUT_ALIGNED_DECL_COMMON +// ASM_OUTPUT_ALIGNED_DECL_LOCAL +void +wasm_assemble_data_zeros (FILE *stream, tree decl, const char *name, + HOST_WIDE_INT size, HOST_WIDE_INT align, bool pub) +{ + wasm_assemble_data_begin (stream, decl, name, size, align, pub); + wasm_assemble_skip (stream, size); + decl_end(stream); +} + +// TARGET_PRINT_OPERAND_PUNCT_VALID_P +bool +wasm_valid_punct_p (unsigned char code) +{ + return code == '#'; +} + +// TARGET_PRINT_OPERAND +void +wasm_print_operand (FILE *stream, rtx value, int mode) +{ + if (value && GET_CODE (value) == UNSPEC_VOLATILE) + return wasm_print_operand (stream, XVECEXP (value, 0, 0), mode); + + if (mode == '#') /* print return */ + { + if (TREE_TYPE (TREE_TYPE (cfun->decl)) != void_type_node) + fprintf (stream, " (local.get $return)"); + } + else if (mode == 'M') /* print local/global */ + if (global_p(value)) + fprintf (stream, "global"); + else + fprintf (stream, "local"); + else if (mode == 'A') /* print an arglist */ + { + gcc_assert (GET_CODE (value) == PARALLEL); + int len = XVECLEN (value, 0); + for (int i = 1; i < len; ++i) + { + rtx arg = XVECEXP (value, 0, i); + gcc_assert (GET_CODE (arg) == USE); + rtx reg = XEXP (arg, 0); + gcc_assert (GET_CODE (reg) == REG); + fprintf (stream, " "); + wasm_print_operand (stream, reg, 'i'); + } + } + else if (mode == 'T') /* print a type */ + { + if (GET_CODE (value) == PARALLEL) + { + int len = XVECLEN (value, 0); + for (int i = 1; i < len; ++i) + { + rtx arg = XVECEXP (value, 0, i); + gcc_assert (GET_CODE (arg) == USE); + rtx reg = XEXP (arg, 0); + gcc_assert (GET_CODE (reg) == REG); + wasm_print_operand (stream, reg, mode); + } + } + else if (REG_P (value)) + print_type (stream, lang_hooks.types.type_for_mode (GET_MODE (value), 0)); + else + gcc_unreachable(); + } + else switch (GET_CODE (value)) + { + case MEM: + { + rtx addr = XEXP (value, 0); + if (GET_CODE (addr) == PLUS) + { + fprintf (stream, "offset=%lu ", UINTVAL (XEXP (addr, 1))); + addr = XEXP (addr, 0); + } + + wasm_print_operand (stream, addr, 'i'); + break; + } + case CONST: + case SYMBOL_REF: + { + rtx op = GET_CODE (value) == CONST ? XEXP (value, 0) : value; + rtx offset = NULL_RTX; + if (GET_CODE (op) == PLUS) + offset = XEXP (op, 1), op = XEXP (op, 0); + tree decl = SYMBOL_REF_P (op) ? SYMBOL_REF_DECL (op) : NULL_TREE; + bool fndecl_p = decl && TREE_CODE (decl) == FUNCTION_DECL; + if (mode == 'i') + fprintf (stream, "(i32.const 0 (@reloc %s ", + fndecl_p ? "functable" : "data"); + + fprintf (stream, "$"); + output_addr_const (stream, op); + if (offset) + { + fprintf (stream, " %+" HOST_WIDE_INT_PRINT "d", + INTVAL (offset)); + } + if (mode == 'i') + fprintf (stream, "))"); + break; + } + case SUBREG: + gcc_assert (SUBREG_BYTE (value) == 0); + wasm_print_operand (stream, SUBREG_REG (value), mode); + break; + case REG: + { + int reg = REGNO (value); + if (mode == 'i' || mode == 'o') + fprintf (stream, "%s%s.%s ", mode == 'o' ? "" : "(", + global_p (value) ? "global" : "local", + mode == 'i' ? "get" : "set"); + if (reg < FIRST_PSEUDO_REGISTER) + fprintf (stream, "%s", reg_names[reg]); + else + { + reg -= FIRST_PSEUDO_REGISTER; + fprintf (stream, "$local_%d", reg); + } + if (mode == 'i') + fprintf (stream, ")"); + break; + } + case CONST_INT: + if (mode == 'i') + fprintf (stream, "(i32.const "); + fprintf (stream, "%ld", INTVAL (value)); + if (mode == 'i') + fprintf (stream, ")"); + break; + case CONST_DOUBLE: + { + if (mode == 'i') + fprintf (stream, "(f%d.const ", GET_MODE_PRECISION (GET_MODE (value))); + const REAL_VALUE_TYPE *n = CONST_DOUBLE_REAL_VALUE (value); + if (REAL_VALUE_ISINF (*n)) + fprintf (stream, "%cinf", REAL_VALUE_NEGATIVE (*n) ? '-' : '+'); + else if (REAL_VALUE_ISNAN (*n)) + { + fprintf (stream, "%cnan", REAL_VALUE_NEGATIVE (*n) ? '-' : '+'); + if (!n->canonical) + { + fprintf (stream, ":0x"); + switch (GET_MODE (value)) + { + case DFmode: + print_nan_payload (stream, n); + break; + case SFmode: + print_nan_payload (stream, n); + break; + default: + gcc_unreachable(); + } + } + } + else + { + /* There are always 32 bits in each long, no matter the size of + the hosts long. */ + long tmp[2]; + REAL_VALUE_TO_TARGET_DOUBLE (*n, tmp); + int32_t tmp2[2] = {(int32_t)tmp[0], (int32_t)tmp[1]}; + double r; + std::memcpy (&r, tmp2, sizeof r); + fprintf (stream, "%a", r); + } + if (mode == 'i') + fprintf (stream, ")"); + break; + } + default: + gcc_unreachable (); + } +} + +static int get_cf_label (size_t no) +{ + rtx_insn **l = cfun->machine->labelno_to_labels->get (no); + if (!l) + return -1; + int cf = *cfun->machine->labels_to_cfno->get (*l); + gcc_assert (cf >= 0); + return cf; +} + +void +wasm_generate_internal_label (char *buf, const char *pfx, size_t no) +{ + if (!strcmp (pfx, "L")) + sprint_ul (buf, get_cf_label (no)); + else + sprintf (buf, "%s" HOST_WIDE_INT_PRINT_DEC, pfx, no); +} + +void +wasm_output_internal_label (FILE *stream, const char *pfx, size_t no) +{ + if (!strcmp (pfx, "L")) + { + int l = get_cf_label (no); + if (l >= 0) + { + indent--; + output_indent (stream); + fprintf (stream, ") ;; $%d\n", l); + } + } + else if (!strcmp (pfx, "LFB")) + /* Function begin */; + else if (!strcmp (pfx, "LFE")) + /* Function end */; + else + /* unknown */; +} diff --git a/gcc/config/wasm/wasm-cg.cc b/gcc/config/wasm/wasm-cg.cc new file mode 100644 index 00000000000..c8d7f6812d1 --- /dev/null +++ b/gcc/config/wasm/wasm-cg.cc @@ -0,0 +1,621 @@ +/* WebAssembly code generation utilities. + Copyright (C) 2025-2025 Free Software Foundation, Inc. + Contributed by feedable. + + 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 "regs.h" +#include "stringpool.h" +#include "attribs.h" +#include "gimple.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "stringpool.h" +#include "optabs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "output.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "explow.h" +#include "expr.h" +#include "langhooks.h" +#include "cfgrtl.h" +#include "gimplify.h" +#include "reload.h" +#include "builtins.h" +#include "tree-pass.h" +#include "cfghooks.h" + +hash_map external_libcalls; + +namespace +{ + +rtx +unify_mem (rtx mem) +{ + HOST_WIDE_INT offset = 0; + machine_mode mode = GET_MODE (mem); + if (SUBREG_P (mem) && MEM_P(SUBREG_REG (mem))) + { + offset += SUBREG_BYTE (mem); + mem = SUBREG_REG (mem); + } + if (!MEM_P (mem)) + return mem; + rtx addr = XEXP (mem, 0); + if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1))) + { + rtx offset_rtx = XEXP (addr, 1); + addr = XEXP (addr, 0); + offset += INTVAL (offset_rtx); + } + addr = force_reg (Pmode, addr); + addr = gen_rtx_PLUS (Pmode, addr, gen_rtx_CONST_INT (Pmode, offset)); + if (offset < 0) + addr = gen_rtx_PLUS (Pmode, force_reg (Pmode, addr), const0_rtx); + mem = copy_rtx (mem); + XEXP (mem, 0) = addr; + return mem; +} + +bool symref_p (rtx expr) +{ + if (GET_CODE(expr) == CONST) + { + expr = XEXP (expr, 0); + if (GET_CODE (expr) == PLUS) + expr = XEXP (expr, 0); + } + return GET_CODE (expr) == SYMBOL_REF; +} + +void +record_libcall (const_rtx sym, tree ret) +{ + auto_vec args; + vec *args_rtx = cfun->machine->call->args; + int cnt = vec_safe_length (args_rtx); + args.safe_grow (cnt); + for (int i = 0; i != cnt; ++i) + args[i] = lang_hooks.types.type_for_mode (GET_MODE ((*args_rtx)[i]), false); + tree ty = build_function_type_array (ret, cnt, args.address ()); + bool existed; + tree &slot = external_libcalls.get_or_insert (sym, &existed); + if (existed) + gcc_assert (slot == ty); + else + slot = ty; +} + +} + +machine_mode +wasm_real_register_mode (rtx reg) +{ + if (SUBREG_P (reg)) + reg = SUBREG_REG (reg); + if (!REG_P (reg)) + return VOIDmode; + machine_mode m = GET_MODE (reg); + PROMOTE_MODE (m, 0, NULL_TREE); + return m; +} + +static bool +expand_const (rtx dest, rtx src, machine_mode mode) +{ + if (!const_int_operand (src, mode) && !const_double_operand (src, mode) && + GET_CODE (src) != LABEL_REF && !symref_p (src)) + return false; + if (symref_p (src)) + + if (GET_CODE(src) == CONST) + if (GET_CODE (XEXP (src, 0)) == PLUS) + if (GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT) + { + tree symref_dcl = SYMBOL_REF_DECL (XEXP (XEXP (src, 0), 0)); + if (INTVAL (XEXP (XEXP (src, 0), 1)) <= 0) + src = force_reg (Pmode, XEXP (src, 0)); + else if (symref_dcl && TREE_CODE (symref_dcl) == FUNCTION_DECL) + src = force_reg (Pmode, XEXP (src, 0)); + } + + emit_insn (gen_rtx_SET (dest, src)); + return true; +} + +bool +wasm_expand_mov (rtx dest, rtx src, machine_mode mode) +{ + temporary_volatile_ok g {1}; + dest = unify_mem (dest); + src = unify_mem (src); + if (register_operand (dest, mode) && immediate_operand (src, mode)) + return expand_const (dest, src, mode); + gcc_assert (REG_P (dest) || MEM_P (dest) || SUBREG_P (dest)); + if (register_operand (dest, mode) && memory_operand (src, mode)) + { + emit_insn (gen_rtx_SET (dest, src)); + return true; + } + if (!SUBREG_P (src)) + src = force_reg (mode, src); + emit_insn (gen_rtx_SET (dest, src)); + return true; +} + +void +wasm_expand_conv (rtx dest, rtx src, rtx_code code, bool strict) +{ + if (code == TRUNCATE) + code = LOAD_EXTEND_OP (VOIDmode); + + auto real_reg = [](rtx reg) + { + if (!SUBREG_P (reg)) + return reg; + gcc_assert (SUBREG_BYTE (reg) == 0); + return SUBREG_REG (reg); + }; + rtx r_src = real_reg (src), r_dest = real_reg (dest); + + /* This is an intra-reg conversion */ + bool src_di_p = (GET_MODE (r_src) == DImode); + bool dest_di_p = (GET_MODE (r_dest) == DImode); + bool do_conv = src_di_p != dest_di_p; + bool do_intra = (GET_MODE (src) != SImode && GET_MODE (src) != DImode) || (GET_MODE (src) != DImode && (dest_di_p && src_di_p)); + machine_mode mid_mode = src_di_p ? DImode : SImode; + + rtx mid = do_conv + ? do_intra + ? gen_reg_rtx (mid_mode) + : src + : simplify_gen_subreg (mid_mode, dest, GET_MODE (r_dest), 0); + if (do_intra) + { + if (GET_MODE_PRECISION (GET_MODE (src)) >= GET_MODE_PRECISION (GET_MODE (mid))) + src = simplify_gen_subreg (mid_mode, src, GET_MODE (r_src), 0); + rtx op = gen_rtx_fmt_e_stat (code, mid_mode, src); + rtx_insn *i = emit_insn (gen_rtx_SET (mid, op)); + extract_insn (i); + } + + if (do_conv) + { + if (mid_mode == DImode) + code = TRUNCATE; + rtx op = gen_rtx_fmt_e_stat (code, GET_MODE (r_dest), mid); + rtx_insn *i = emit_insn (gen_rtx_SET (r_dest, op)); + extract_insn (i); + } +} + +void +wasm_expand_call(rtx retval, rtx fn, rtx aux) +{ + bool vararg_p = false; + bool has_proto = true; + bool is_fn = true; + bool need_chain = false; + + if (tree type = cfun->machine->call->type) + { + vararg_p = stdarg_p (type); + has_proto = TYPE_ARG_TYPES (type) || vararg_p; + } + + if (GET_CODE (XEXP (fn, 0)) == SYMBOL_REF) + if (tree decl = SYMBOL_REF_DECL (XEXP (fn, 0))) + { + if (TREE_CODE (decl) != FUNCTION_DECL) + is_fn = false; + else if (DECL_STATIC_CHAIN (decl)) + need_chain = true; + } + + /* Pretend unprototyped fn calls dynamic. That way we can assert the function + type ourselves, and if they don't agree with whatever is actually supplied + by the linker, ANSI C 3.3.2.2 says it's undefied anyway */ + if (!has_proto || !is_fn) + { + rtvec ops = gen_rtvec (1, force_reg (Pmode, XEXP (fn, 0))); + XEXP (fn, 0) = gen_rtx_UNSPEC_VOLATILE (SImode, ops, + UNSPECV_CALL_ADDRESS_BARRIER); + } + + rtx call = gen_rtx_CALL(VOIDmode, fn, aux); + if (retval) + call = gen_rtx_SET (retval, call); + + auto *args_rtx = cfun->machine->call->args; + + /* ??? This should be handled by the call machinery, not me! */ + if (vararg_p) + { + rtx stdarg_reg = gen_reg_rtx (SImode); + /* Rely on args being pushed in reverse order, and on only varargs being + pushed, so that the beginning of the vararg buffer is stack_pointer_rtx. + This is a hack. */ + emit_move_insn (stdarg_reg, stack_pointer_rtx); + vec_safe_insert (args_rtx, 0, stdarg_reg); + } + if (need_chain) + { + rtx chain_reg = gen_reg_rtx (SImode); + emit_move_insn (chain_reg, regno_reg_rtx[STATIC_CHAIN_REGNUM]); + vec_safe_insert (args_rtx, 0, chain_reg); + } + + int argc = vec_safe_length (args_rtx); + rtx res = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (argc + 1)); + XVECEXP (res, 0, 0) = call; + for (int i = 0; i < argc; ++i) + XVECEXP (res, 0, argc - i) = gen_rtx_USE (VOIDmode, (*args_rtx)[i]); + emit_call_insn (res); +} + +void +wasm_expand_prologue () +{ + emit_move_insn (regno_reg_rtx[WASM_BASE_POINTER_REGNUM], + stack_pointer_rtx); + if (crtl->stack_realign_needed) + { + int align = ~(crtl->max_used_stack_slot_alignment / BITS_PER_UNIT - 1); + rtx align_mask = gen_rtx_CONST_INT (SImode, align); + rtx reg = gen_reg_rtx (SImode); + emit_move_insn (reg, align_mask); + emit_insn (gen_andsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); + } + + if (flag_stack_usage_info) + current_function_static_stack_size = 0; + if (poly_int64 stack_space = get_frame_size ()) + { + HOST_WIDE_INT unit_boundary = STACK_BOUNDARY / BITS_PER_UNIT; + poly_int64 aligned = aligned_upper_bound (stack_space, unit_boundary); + rtx aligned_space_rtx = gen_int_mode (aligned, Pmode); + emit_insn (gen_sub2_insn (stack_pointer_rtx, aligned_space_rtx)); + emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + if (flag_stack_usage_info) + current_function_static_stack_size = aligned; + } + if (HOST_WIDE_INT stack_space = crtl->outgoing_args_size) + { + emit_insn (gen_sub2_insn (stack_pointer_rtx, + gen_int_mode (stack_space, Pmode))); + } +} + +void +wasm_expand_epilogue () +{ + emit_move_insn (stack_pointer_rtx, regno_reg_rtx[WASM_BASE_POINTER_REGNUM]); +} + +void +wasm_expand_compare (machine_mode m, rtx res, rtx op) +{ + rtx *left_p = &XEXP (op, 0), *right_p = &XEXP (op, 1); + + *left_p = force_reg (m, *left_p); + *right_p = force_reg (m, *right_p); + + rtx left = *left_p, right = *right_p; + + switch (GET_CODE (op)) + { + case ORDERED: + { + rtx eq = gen_reg_rtx (SImode), ne = gen_reg_rtx (SImode); + wasm_expand_compare (m, eq, gen_rtx_LE (SImode, left, right)); + wasm_expand_compare (m, ne, gen_rtx_GE (SImode, left, right)); + emit_insn (gen_iorsi3 (res, eq, ne)); + break; + } + case LTGT: + { + rtx eq = gen_reg_rtx (SImode), ne = gen_reg_rtx (SImode); + wasm_expand_compare (m, eq, gen_rtx_LT (SImode, left, right)); + wasm_expand_compare (m, ne, gen_rtx_GT (SImode, left, right)); + emit_insn (gen_iorsi3 (res, eq, ne)); + break; + } + case LTU: + case GTU: + case LEU: + case GEU: + case LT: + case GT: + case LE: + case GE: + case EQ: + case NE: + op = gen_rtx_fmt_ee (GET_CODE (op), SImode, left, right); + emit_insn (gen_rtx_SET (res, op)); + break; + case UNLT: + case UNGT: + case UNLE: + case UNGE: + case UNEQ: + case UNORDERED: + { + rtx_code cond_code = reverse_condition_maybe_unordered (GET_CODE (op)); + rtx cond = gen_rtx_fmt_ee (cond_code, SImode, left, right); + rtx mid = gen_reg_rtx (SImode); + wasm_expand_compare (m, mid, cond); + emit_insn (gen_xorsi3 (res, mid, gen_rtx_CONST_INT (SImode, STORE_FLAG_VALUE))); + break; + } + default: + gcc_unreachable (); + } +} + +bool +wasm_regno_mode_ok (unsigned regno, machine_mode mode) +{ + if (COMPLEX_MODE_P (mode)) + return false; + if (regno == WASM_CONTROL_POINTER_REGNUM) + return mode == SImode; + if (regno == ARG_POINTER_REGNUM + || regno == STACK_POINTER_REGNUM + || regno == FRAME_POINTER_REGNUM + || regno == STATIC_CHAIN_REGNUM + || regno == WASM_BASE_POINTER_REGNUM) + return mode == Pmode; + if (GET_MODE_CLASS (mode) == MODE_INT) + return mode < TImode; + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return mode == DFmode || mode == SFmode; + return false; +} + +bool +wasm_can_change_mode_class (machine_mode from, machine_mode to, reg_class_t) +{ + return QImode <= from && from <= SImode && QImode <= to && to <= SImode; +} + +// TARGET_LEGITIMATE_ADDRESS_P +bool +wasm_legitimate_address_p (machine_mode, rtx x, bool, code_helper) +{ + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)) && + INTVAL (XEXP (x, 1)) >= 0) + x = XEXP (x, 0); + switch (GET_CODE (x)) + { + case SYMBOL_REF: + case LABEL_REF: + case CONST_INT: + case REG: + return true; + default: + return false; + } +} + +// TARGET_MARK_ARG_REGNOS +void +wasm_mark_arg_regnos (bitmap regnos) +{ + for (rtx arg: cfun->machine->func_args) + bitmap_set_bit (regnos, REGNO(arg)); +} + +// TARGET_START_CALL_ARGS +void +wasm_start_call_args (cumulative_args_t args) +{ + cfun->machine->call = get_cumulative_args (args); +} + +// TARGET_CALL_ARGS +void +wasm_call_args (cumulative_args_t args, rtx arg, tree type) +{ + get_cumulative_args (args)->type = type; + if (arg == pc_rtx) + return; + +if (GET_CODE (arg) == PARALLEL) + { + rtvec elts = XVEC (arg, 0); + for (int i = 0; i != GET_NUM_ELEM (elts); ++i) + wasm_call_args (args, XEXP (RTVEC_ELT (elts, i), 0), type); + return; + } + gcc_assert (REG_P (arg)); + vec_safe_push (get_cumulative_args (args)->args, arg); +} + +// TARGET_END_CALL_ARGS +void +wasm_end_call_args (cumulative_args_t) +{ + cfun->machine->call = NULL; + cfun->machine->unproto_call_p = false; +} + +// TARGET_FUNCTION_ARG_ADVANCE +void +wasm_function_arg_advance (cumulative_args_t, const function_arg_info &) +{ +} + +// TARGET_FUNCTION_ARG_BOUNDARY +unsigned int wasm_vararg_align (machine_mode mode, const_tree type) +{ + unsigned align = 0; + + if (type) + align = TYPE_ALIGN (type); + if (align) + return MIN (MAX (align, BITS_PER_UNIT), BIGGEST_ALIGNMENT); + return get_mode_alignment (mode); +} + +// TARGET_FUNCTION_VALUE +rtx +wasm_function_value (const_tree type, const_tree ARG_UNUSED (func), + bool outgoing) +{ + machine_mode mode = TYPE_MODE (type); + if (!cfun) + /* fake return regnum since none is actually available */ + return gen_rtx_REG (mode, WASM_RETURN_REGNUM); + if (outgoing) + { + /* Where we actually put the actual return val */ + cfun->machine->return_mode = mode; + return gen_rtx_REG (mode, WASM_RETURN_REGNUM); + } + if (!cfun->machine->call) + /* fake return regnum again, not in a call */ + return gen_rtx_REG (mode, WASM_RETURN_REGNUM); + + /* Put the retval in a fresh reg, its mode may be different from actual + retval, and we can't have that */ + return gen_reg_rtx (mode); +} + + +// TARGET_FUNCTION_ARG +rtx +wasm_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + + if (arg.end_marker_p ()) + return NULL_RTX; + + if (!arg.named) + return NULL_RTX; + + if (arg.mode == TImode) + { + rtx const8_rtx = gen_rtx_CONST_INT (VOIDmode, 8); + rtvec para = gen_rtvec(2, + gen_rtx_EXPR_LIST (DImode, gen_reg_rtx (DImode), const0_rtx), + gen_rtx_EXPR_LIST (DImode, gen_reg_rtx (DImode), const8_rtx)); + return gen_rtx_PARALLEL (TImode, para); + } + return gen_reg_rtx (arg.mode); +} + +// TARGET_FUNCTION_INCOMING_ARG +rtx +wasm_function_incoming_arg (cumulative_args_t cum_v, + const function_arg_info &arg) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + + auto maybe_push_arg = [cum](rtx arg) + { + if (cum->incoming) + vec_safe_push (cum->args, arg); + }; + + if (arg.end_marker_p ()) + { + gcc_assert (!cfun->machine->func_args); + if (cum->incoming) + cfun->machine->func_args = cum->args; + return gen_rtx_CONST_INT (SImode, 80); + } + if (!arg.named) + return NULL_RTX; + + /* Int128 is passed as 2xint64_t, in LE order */ + if (arg.mode == TImode) + { + rtx const8_rtx = gen_rtx_CONST_INT (VOIDmode, 8); + + rtx lsb = gen_rtx_EXPR_LIST (DImode, gen_reg_rtx (DImode), const0_rtx); + rtx msb = gen_rtx_EXPR_LIST (DImode, gen_reg_rtx (DImode), const8_rtx); + maybe_push_arg (lsb); + maybe_push_arg (msb); + rtvec para = gen_rtvec(2, lsb, msb); + return gen_rtx_PARALLEL (TImode, para); + } + rtx reg = gen_reg_rtx (arg.mode); + maybe_push_arg (reg); + return reg; +} + +// TARGET_LIBCALL_VALUE +rtx wasm_libcall_value (machine_mode m, const_rtx sym) +{ + tree rtype = lang_hooks.types.type_for_mode (m, false); + if (cfun && cfun->machine->call) + record_libcall (sym, rtype); + return wasm_function_value (rtype, NULL_TREE, false); +} + +// TARGET_PASS_BY_REFERENCE +bool +wasm_pass_by_reference (cumulative_args_t, const function_arg_info &arg) +{ + if (arg.type) + { + if (TREE_CODE (arg.type) == COMPLEX_TYPE) + return true; + if (TREE_CODE (arg.type) == VECTOR_TYPE) + return true; + } + if (COMPLEX_MODE_P (arg.mode)) + return true; + if (VECTOR_MODE_P (arg.mode)) + return true; + if (arg.aggregate_type_p ()) + return true; + return false; +} +// TARGET_RETURN_IN_MEMORY +bool +wasm_return_in_memory (const_tree type, const_tree ARG_UNUSED (fntype)) +{ + if (TREE_CODE (type) == COMPLEX_TYPE) + return true; + if (TREE_CODE (type) == VECTOR_TYPE) + return true; + if (VECTOR_MODE_P (TYPE_MODE (type))) + return true; + if (COMPLEX_MODE_P (TYPE_MODE (type))) + return true; + return false; +} diff --git a/gcc/config/wasm/wasm-modes.def b/gcc/config/wasm/wasm-modes.def new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gcc/config/wasm/wasm-passes.cc b/gcc/config/wasm/wasm-passes.cc new file mode 100644 index 00000000000..fc28ef93030 --- /dev/null +++ b/gcc/config/wasm/wasm-passes.cc @@ -0,0 +1,153 @@ +/* WebAssembly passes for code generation. +Copyright (C) 2025-2025 Free Software Foundation, Inc. + Contributed by feedable. + + 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 "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "stringpool.h" +#include "attribs.h" +#include "gimple.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "stringpool.h" +#include "optabs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "output.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "explow.h" +#include "expr.h" +#include "langhooks.h" +#include "cfgrtl.h" +#include "gimplify.h" +#include "reload.h" +#include "builtins.h" +#include "tree-pass.h" +#include "cfghooks.h" + +namespace { + +const pass_data pass_data_count_labels = +{ + RTL_PASS, /* type */ + "count_labels", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_MACH_DEP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_count_labels : public rtl_opt_pass +{ + static void put_label (function *fun, rtx_insn *l, int &idx) + { + gcc_assert (!fun->machine->labels_to_cfno->put (l, idx++)); + int n = CODE_LABEL_NUMBER (l); + gcc_assert (!fun->machine->labelno_to_labels->put (n, l)); + } + static void mark_live (bitmap regs) + { + + df_clear_flags (DF_LR_RUN_DCE); + df_set_flags (DF_NO_INSN_RESCAN | DF_NO_HARD_REGS); + df_live_add_problem (); + df_live_set_all_dirty (); + df_analyze (); + //regstat_init_n_sets_and_refs (); + for (int i = FIRST_PSEUDO_REGISTER; i < max_reg_num (); ++i) + if (DF_REG_DEF_COUNT (i) || DF_REG_USE_COUNT (i)) + bitmap_set_bit (regs, i); + } + static void inject_trap (function *fun, basic_block bb) + { + rtx_insn *insn = BB_END (bb); + if (INSN_P (insn) + && (recog_memoized (insn) == CODE_FOR_trap)) + return; + if (!vec_safe_length (bb->succs)) + make_edge (bb, fun->cfg->x_exit_block_ptr, EDGE_FALLTHRU); + edge_iterator ei; + edge e; + FOR_EACH_EDGE (e, ei, bb->succs) + if (e->flags & EDGE_FALLTHRU && cfun->machine->return_mode != VOIDmode) + if (e->dest == fun->cfg->x_exit_block_ptr) + { + basic_block new_bb = split_edge (e); + emit_insn_after (gen_trap(), BB_END (new_bb)); + } + } + static rtx_insn *find_label (basic_block bb) + { + rtx_insn *insn; + FOR_BB_INSNS (bb, insn) + { + if (LABEL_P (insn)) + return insn; + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL) + return insn; + } + return NULL; + } +public: + pass_count_labels(gcc::context *ctxt) + : rtl_opt_pass(pass_data_count_labels, ctxt) + {} + + /* opt_pass methods: */ + unsigned int execute (function *fun) override + { + basic_block bb; + FOR_EACH_BB_FN (bb, fun) + inject_trap (fun, bb); + + bb = fun->cfg->x_entry_block_ptr->next_bb; + int i = 0; + put_label (fun, block_label (bb), i); + mark_live (fun->machine->regs_ever_live); + + FOR_BB_BETWEEN (bb, bb->next_bb, fun->cfg->x_exit_block_ptr, next_bb) + { + if (rtx_insn *x = find_label (bb)) + put_label (fun, x, i); + } + return 0; + } + +}; + +} // anon namespace + +rtl_opt_pass *make_pass_count_labels (gcc::context *ctx) +{ + return new pass_count_labels (ctx); +} diff --git a/gcc/config/wasm/wasm-passes.def b/gcc/config/wasm/wasm-passes.def new file mode 100644 index 00000000000..7c69cad12f5 --- /dev/null +++ b/gcc/config/wasm/wasm-passes.def @@ -0,0 +1,27 @@ +/* Description of target passes for WebAssembly + Copyright (C) 2025-2025 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 +. */ + +/* + Macros that can be used in this file: + INSERT_PASS_AFTER (PASS, INSTANCE, TGT_PASS) + INSERT_PASS_BEFORE (PASS, INSTANCE, TGT_PASS) + REPLACE_PASS (PASS, INSTANCE, TGT_PASS) + */ + + INSERT_PASS_BEFORE (pass_free_cfg, 1, pass_count_labels); \ No newline at end of file diff --git a/gcc/config/wasm/wasm-protos.h b/gcc/config/wasm/wasm-protos.h new file mode 100644 index 00000000000..9aa5f6c0fe5 --- /dev/null +++ b/gcc/config/wasm/wasm-protos.h @@ -0,0 +1,12 @@ + +#ifdef RTX_CODE +extern void wasm_expand_prologue (); +extern void wasm_expand_epilogue (); +extern void wasm_expand_call (rtx retval, rtx fn, rtx aux); +extern void wasm_emit_jump (rtx dest, rtx fn); +extern bool wasm_expand_mov (rtx dest, rtx src, machine_mode mode); +extern void wasm_expand_conv (rtx, rtx, rtx_code, bool = true); +extern void wasm_expand_compare (machine_mode m, rtx res, rtx cmp); +extern machine_mode wasm_real_register_mode (rtx reg); +rtl_opt_pass *make_pass_count_labels (gcc::context *ctx); +#endif diff --git a/gcc/config/wasm/wasm.cc b/gcc/config/wasm/wasm.cc new file mode 100644 index 00000000000..0a5e746d87c --- /dev/null +++ b/gcc/config/wasm/wasm.cc @@ -0,0 +1,128 @@ +#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 "memmodel.h" +#include "stringpool.h" +#include "optabs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "output.h" +#include "varasm.h" +#include "calls.h" +#include "explow.h" +#include "expr.h" +#include "builtins.h" + +#include "target.h" +#include "common/common-target.h" + +static machine_function * +wasm_init_machine_status (void) +{ + machine_function *p = ggc_cleared_alloc (); + p->labels_to_cfno = hash_map::create_ggc (31); + p->labelno_to_labels = hash_map::create_ggc (31); + p->regs_ever_live = BITMAP_GGC_ALLOC (); + return p; +} + +#define TARGET_OPTION_OVERRIDE wasm_option_override +static void +wasm_option_override (void) +{ + init_machine_status = wasm_init_machine_status; +} + +void wasm_asm_init_sections (); +#define TARGET_ASM_INIT_SECTIONS wasm_asm_init_sections +bool wasm_assemble_integer (rtx x, unsigned size, int align); +#define TARGET_ASM_INTEGER wasm_assemble_integer +void wasm_assemble_decl_end (); +#define TARGET_ASM_DECL_END wasm_assemble_decl_end +void wasm_handle_import (FILE *stream, const char *name, const_tree decl); +#define TARGET_ASM_ASSEMBLE_UNDEFINED_DECL wasm_handle_import +void wasm_handle_libcall (rtx symbol); +#define TARGET_ASM_EXTERNAL_LIBCALL wasm_handle_libcall +void wasm_assemble_data_begin (FILE *stream, tree decl, const char *name, + HOST_WIDE_INT size, HOST_WIDE_INT align, + bool pub); +#define TARGET_ASM_DECLARE_CONSTANT_NAME \ + [] (FILE *stream, const char *name, const_tree, HOST_WIDE_INT size) \ + { return wasm_assemble_data_begin (stream, 0, name, size, 0, false); } + +#define TARGET_ASM_GLOBALIZE_LABEL [](FILE *, const char *) {} +#define TARGET_ASM_ASSEMBLE_VISIBILITY [](tree, int) {} +#define TARGET_ASM_CONSTRUCTOR [] (rtx, int) {} +#define TARGET_ASM_DESTRUCTOR [] (rtx, int) {} +#define TARGET_USE_LATE_PROLOGUE_EPILOGUE [] { return true; } + +#define TARGET_ASM_FILE_START wasm_asm_file_start +void wasm_asm_file_start (); +#define TARGET_ASM_FILE_END wasm_asm_file_end +void wasm_asm_file_end (); + +bool wasm_valid_punct_p (unsigned char code); +#define TARGET_PRINT_OPERAND_PUNCT_VALID_P wasm_valid_punct_p +void wasm_print_operand (FILE *stream, rtx value, int mode); +#define TARGET_PRINT_OPERAND wasm_print_operand +void wasm_output_internal_label (FILE *stream, const char *pfx, size_t no); +#define TARGET_ASM_INTERNAL_LABEL wasm_output_internal_label +#define TARGET_HAVE_NAMED_SECTIONS false + +bool wasm_regno_mode_ok (unsigned regno, machine_mode mode); +#define TARGET_HARD_REGNO_MODE_OK wasm_regno_mode_ok +bool wasm_can_change_mode_class (machine_mode from, machine_mode to, reg_class_t); +#define TARGET_CAN_CHANGE_MODE_CLASS wasm_can_change_mode_class +bool wasm_legitimate_address_p (machine_mode, rtx x, bool, code_helper); +#define TARGET_LEGITIMATE_ADDRESS_P wasm_legitimate_address_p +void wasm_mark_arg_regnos (bitmap regnos); +#define TARGET_MARK_ARG_REGNOS wasm_mark_arg_regnos + +void wasm_start_call_args (cumulative_args_t args); +#define TARGET_START_CALL_ARGS wasm_start_call_args +void wasm_call_args (cumulative_args_t args, rtx arg, tree fntype); +#define TARGET_CALL_ARGS wasm_call_args +void wasm_end_call_args (cumulative_args_t); +#define TARGET_END_CALL_ARGS wasm_end_call_args +bool wasm_pass_by_reference(cumulative_args_t, const function_arg_info &arg); +#define TARGET_PASS_BY_REFERENCE wasm_pass_by_reference +bool wasm_return_in_memory (const_tree type, const_tree fntype); +#define TARGET_RETURN_IN_MEMORY wasm_return_in_memory +#define TARGET_SPLIT_COMPLEX_ARG [] (auto ...) { return false; } + + +#define TARGET_SCALAR_MODE_SUPPORTED_P [](scalar_mode m) \ + { return default_scalar_mode_supported_p(m) && m != TImode; } + +#define TARGET_EXCEPT_UNWIND_INFO [](auto...) { return UI_NONE; } + +#define TARGET_HAVE_STRUB_SUPPORT_FOR hook_bool_tree_false +#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS hook_bool_void_false +#define TARGET_STRICT_ARGUMENT_NAMING [] (auto ...) { return true; } +unsigned int wasm_vararg_align (machine_mode mode, const_tree type); +#define TARGET_FUNCTION_ARG_BOUNDARY wasm_vararg_align +void wasm_function_arg_advance (cumulative_args_t, const function_arg_info &); +#define TARGET_FUNCTION_ARG_ADVANCE wasm_function_arg_advance +rtx wasm_function_value (const_tree type, const_tree ARG_UNUSED (func), bool); +#define TARGET_FUNCTION_VALUE wasm_function_value +rtx wasm_function_arg (cumulative_args_t cum_v, const function_arg_info &arg); +#define TARGET_FUNCTION_ARG wasm_function_arg +rtx wasm_function_incoming_arg (cumulative_args_t cum_v, + const function_arg_info &arg); +#define TARGET_FUNCTION_INCOMING_ARG wasm_function_incoming_arg +rtx wasm_libcall_value (machine_mode, const_rtx); +#define TARGET_LIBCALL_VALUE wasm_libcall_value + +#include "target-def.h" +gcc_target targetm = TARGET_INITIALIZER; + +#include "common/common-target-def.h" +struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER; diff --git a/gcc/config/wasm/wasm.h b/gcc/config/wasm/wasm.h new file mode 100644 index 00000000000..8adfb8d3fe4 --- /dev/null +++ b/gcc/config/wasm/wasm.h @@ -0,0 +1,307 @@ +/* WebAssembly cpu description. + Copyright (C) 1997-2025 Free Software Foundation, Inc. + Contributed by feedable. + + 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_WASM_H +#define GCC_WASM_H + +#ifndef USED_FOR_TARGET +#include "statistics.h" +#include "vec.h" +#include "hash-table.h" +#include "hash-map.h" +#endif + +/* Debug */ +#define PREFERRED_DEBUGGING_TYPE NO_DEBUG +/* End debug */ + +#define CC1PLUS_SPEC "-stdlib=libc++" +#define ASM_SPEC "-r --enable-annotations %{v:-v} %{Wa,*:%*}" +#define STANDARD_STARTFILE_PREFIX_1 "/lib/wasm32-wasi/" +#define STARTFILE_SPEC "%R" STANDARD_STARTFILE_PREFIX_1 "crt1%O%s" +#define LIB_SPEC "%{!shared:%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}}" +#define LINK_SPEC "%{!o:-o a.out}" +//#define LIB_SPEC "%{!shared: -lm -lc}" + +/* Run-time target specifications. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("wasm32"); \ + builtin_assert ("machine=wasm32"); \ + builtin_assert ("cpu=wasm32"); \ + cpp_define (parse_in, "__wasm32__");\ + cpp_define (parse_in, "__wasi__"); \ + } \ + while (0) + +/* End run-time target specifications. */ + +/* Regs */ +#define WASM_RETURN_REGNUM 0 +#define STACK_POINTER_REGNUM 1 +#define FRAME_POINTER_REGNUM 2 +#define ARG_POINTER_REGNUM 3 +#define STATIC_CHAIN_REGNUM 4 +#define WASM_CONTROL_POINTER_REGNUM 5 +#define WASM_BASE_POINTER_REGNUM 6 + +/* If this isn't defined, loads form $args will be performed with nested + functions */ +#define FRAME_POINTER_CFA_OFFSET(FNDECL) ((void)(FNDECL), 0) + +/* ??? IRA is a bit silly with on targets that don't do register allocation, + in that it still needs free hard regs to play with, give it some for now */ +#define REGISTER_NAMES \ + { \ + "$return", \ + "$stack", \ + "$frame", \ + "$args", \ + "$chain", \ + "$control", \ + "$base", \ + "general0", \ + "general1", \ + "general2", \ + "general3", \ + } + +#define TARGET_NO_REGISTER_ALLOCATION true +#define FIRST_PSEUDO_REGISTER 11 +#define FIXED_REGISTERS { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 } +#define CALL_USED_REGISTERS { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 } + +enum reg_class { NO_REGS, ALL_REGS, LIM_REG_CLASSES }; +#define REG_CLASS_NAMES { "NO_REGS", "ALL_REGS" } +#define REG_CLASS_CONTENTS { { 0x0000 }, { 0xFFFF } } +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define GENERAL_REGS ALL_REGS +#define REGNO_REG_CLASS(R) ALL_REGS +#define BASE_REG_CLASS ALL_REGS +#define INDEX_REG_CLASS NO_REGS + +#define REGNO_OK_FOR_BASE_P(X) true +#define REGNO_OK_FOR_INDEX_P(X) false + +#define CLASS_MAX_NREGS(class, mode) 1 + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if ((MODE) == QImode || (MODE) == HImode) \ + (MODE) = SImode; \ +/* End regs */ + +/* Calling convention */ +#define PARM_BOUNDARY 8 +#define STACK_BOUNDARY 128 +#define FUNCTION_BOUNDARY 16 +#define BIGGEST_ALIGNMENT 128 +#define STRICT_ALIGNMENT 0 + +/* Copied from elf.h and other places. We'd otherwise use + BIGGEST_ALIGNMENT and fail a number of testcases. */ +#define MAX_OFILE_ALIGNMENT (32768 * 8) + +#define FRAME_GROWS_DOWNWARD 0 +#define STACK_GROWS_DOWNWARD 1 + +#ifndef USED_FOR_TARGET +#define CUMULATIVE_ARGS wasm_cumulative_args +struct wasm_cumulative_args { + bool incoming; + vec *args = NULL; + tree type = NULL; +}; +#endif + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ + CUM = {false} +#define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \ + CUM = {true} + +#define FUNCTION_ARG_REGNO_P(r) 0 +#define FUNCTION_VALUE_REGNO_P(r) 0 + +#define ELIMINABLE_REGS \ + { \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM} \ + } + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) (OFFSET) = 0 + +#define FIRST_PARM_OFFSET(FNDECL) 0 +/* We only use the stack for varargs, let's abuse that to achieve the proper + layout for the varargs buffer */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +#define TRAMPOLINE_SIZE 64 +#define TRAMPOLINE_ALIGNMENT 32 +/* End calling convention */ + +/* Costs */ +#define NO_FUNCTION_CSE 1 +#define SLOW_BYTE_ACCESS 0 +#define BRANCH_COST(speed_p, predictable_p) 6 +/* End costs */ + +/* Data layout */ +#define STORE_FLAG_VALUE 1 + +#define BITS_BIG_ENDIAN 1 +#define BYTES_BIG_ENDIAN 0 +#define WORDS_BIG_ENDIAN 0 +#define UNITS_PER_WORD 8 +#define MIN_UNITS_PER_WORD 4 + +#define Pmode SImode +#define FUNCTION_MODE SImode +#define CASE_VECTOR_MODE SImode + +#define POINTER_SIZE 32 +#define INT_TYPE_SIZE 32 +#define CHAR_TYPE_SIZE 8 +#define SHORT_TYPE_SIZE 16 +#define LONG_TYPE_SIZE 32 +#define LONG_LONG_TYPE_SIZE 64 +#define DEFAULT_SIGNED_CHAR 0 +#define WCHAR_TYPE_SIZE 32 +/* ??? Need to figure out why do we have to set this in order for TI to not + appear */ +#define MAX_FIXED_MODE_SIZE 64 + +#define SIZE_TYPE "long unsigned int" +#define WCHAR_TYPE "long int" +#define PTRDIFF_TYPE "long int" +#define INTPTR_TYPE "long int" +#define UINTPTR_TYPE "long unsigned int" + +/* From bpf */ +#define INT8_TYPE "signed char" +#define INT16_TYPE "short int" +#define INT32_TYPE "int" +#define INT64_TYPE "long long int" +#define UINT8_TYPE "unsigned char" +#define UINT16_TYPE "short unsigned int" +#define UINT32_TYPE "unsigned int" +#define UINT64_TYPE "long long unsigned int" + +#define INT_LEAST8_TYPE INT8_TYPE +#define INT_LEAST16_TYPE INT16_TYPE +#define INT_LEAST32_TYPE INT32_TYPE +#define INT_LEAST64_TYPE INT64_TYPE +#define UINT_LEAST8_TYPE UINT8_TYPE +#define UINT_LEAST16_TYPE UINT16_TYPE +#define UINT_LEAST32_TYPE UINT32_TYPE +#define UINT_LEAST64_TYPE UINT64_TYPE + +#define INT_FAST8_TYPE INT8_TYPE +#define INT_FAST16_TYPE INT16_TYPE +#define INT_FAST32_TYPE INT32_TYPE +#define INT_FAST64_TYPE INT64_TYPE +#define UINT_FAST8_TYPE UINT8_TYPE +#define UINT_FAST16_TYPE UINT16_TYPE +#define UINT_FAST32_TYPE UINT32_TYPE +#define UINT_FAST64_TYPE UINT64_TYPE + +#define MOVE_MAX 8 +#define MAX_REGS_PER_ADDRESS 1 +#define LEGITIMATE_PIC_OPERAND_P(X) 1 + + +#define WORD_REGISTER_OPERATIONS 1 +#define LOAD_EXTEND_OP(M) SIGN_EXTEND +/* End data layout */ + +/* Asm */ +#define ASM_COMMENT_START ";;" +#define ASM_APP_ON "" +#define ASM_APP_OFF "" + +#define GLOBAL_ASM_OP "" + +#ifndef USED_FOR_TARGET +void wasm_generate_internal_label (char *buf, const char *pfx, size_t no); +#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM)\ + wasm_generate_internal_label (LABEL, PREFIX, NUM) +#define ASM_OUTPUT_LABEL(stream, name) \ + gcc_unreachable() + //fprintf (stream, ") ;;%s\n", name) +#define ASM_OUTPUT_ALIGN(...) + +void wasm_assemble_data_begin (FILE *stream, tree decl, const char *name, + HOST_WIDE_INT size, HOST_WIDE_INT align, + bool pub); +#define ASM_DECLARE_OBJECT_NAME(FILE, DECL, NAME) \ + wasm_assemble_data_begin (FILE, NAME, DECL, 0, 0, true); +void wasm_assemble_data_zeros (FILE *stream, tree decl, const char *name, + HOST_WIDE_INT size, HOST_WIDE_INT align, + bool pub); +#define ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN) \ + wasm_assemble_data_zeros (FILE, DECL, NAME, SIZE, ALIGN, true); +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \ + wasm_assemble_data_zeros (FILE, DECL, NAME, SIZE, ALIGN, 0); + +void wasm_asm_start_function (FILE *stream, tree decl, const char *name); +#define ASM_OUTPUT_FUNCTION_LABEL(stream, name, decl) \ + wasm_asm_start_function(stream, decl, name) +void wasm_asm_end_function (FILE *stream, tree decl, const char *name); +#define ASM_DECLARE_FUNCTION_SIZE(stream, name, decl) \ + wasm_asm_end_function(stream, decl, name) + +void wasm_handle_import (FILE *stream, const char *name, const_tree decl); +#define ASM_OUTPUT_EXTERNAL(stream, decl, name) \ + wasm_handle_import (stream, name, decl) + +void wasm_assemble_skip (FILE *stream, unsigned HOST_WIDE_INT len); +#define ASM_OUTPUT_SKIP(...) wasm_assemble_skip (__VA_ARGS__) +void wasm_assemble_ascii (FILE *stream, const char *data, int len); +#define ASM_OUTPUT_ASCII(...) wasm_assemble_ascii (__VA_ARGS__) +#endif + +#define HAS_INIT_SECTION +#define SUPPORTS_INIT_PRIORITY 1 +#define SUPPORTS_WEAK 1 +/* End asm */ + + +#define FUNCTION_PROFILER(file, labelno) \ + sorry_at (input_location, \ + "profiling is not yet implemented for this architecture") + +#ifndef USED_FOR_TARGET +struct cfl_hash: int_hash {}; + +struct GTY(()) machine_function +{ + vec *func_args; /* Arg list for the current function. */ + machine_mode return_mode; + bool stdarg_p; + + wasm_cumulative_args *call; + bool unproto_call_p; + hash_map *labels_to_cfno; + hash_map *labelno_to_labels; + bitmap regs_ever_live; + hash_map *saved_libcalls; +}; +#endif + +#endif /* GCC_WASM_H */ diff --git a/gcc/config/wasm/wasm.md b/gcc/config/wasm/wasm.md new file mode 100644 index 00000000000..a5f32f4228d --- /dev/null +++ b/gcc/config/wasm/wasm.md @@ -0,0 +1,495 @@ +(include "attrs.md") + +(define_insn "nop" + [(const_int 0)] + "" + "(nop)" + []) + +(define_predicate "symbol_operand" + (match_code "symbol_ref")) +(define_predicate "funcref_operand" + (match_code "symbol_ref") + { + gcc_assert (GET_CODE (op) == SYMBOL_REF); + return !SYMBOL_REF_DECL (op) || TREE_CODE (SYMBOL_REF_DECL (op)) == FUNCTION_DECL; + }) +(define_predicate "varref_operand" + (match_code "symbol_ref") + { + gcc_assert (GET_CODE (op) == SYMBOL_REF); + return TREE_CODE (SYMBOL_REF_DECL (op)) == VAR_DECL; + }) +(define_predicate "subregister_for_si_operand" + (ior (match_code "reg") (match_code "subreg")) + { + rtx x = op; + if (SUBREG_P (x)) + x = SUBREG_REG (x); + if (!REG_P (x)) + return false; + machine_mode m = GET_MODE (x); + int sign = 0; + PROMOTE_MODE (m, sign, NULL_TREE); + return m == SImode; + }) + +(define_predicate "subregister_for_di_operand" + (ior (match_code "reg") (match_code "subreg")) + { + rtx x = op; + if (SUBREG_P (x)) + x = SUBREG_REG (x); + if (!REG_P (x)) + return false; + machine_mode m = GET_MODE (x); + int sign = 0; + PROMOTE_MODE (m, sign, NULL_TREE); + return m == DImode; + }) +(define_predicate "wasm_register_operand" + (match_operand 0 "register_operand") + { + machine_mode m = GET_MODE (op); + PROMOTE_MODE (m, 0, NULL_RTX); + if (mode == TImode) + return false; + return m == wasm_real_register_mode (op); + }) +(define_predicate "wasm_call_register_operand" + (ior (match_operand 0 "wasm_register_operand") + (match_test "GET_CODE (op) == UNSPEC_VOLATILE && wasm_register_operand (XVECEXP (op, 0, 0), mode)"))) +(define_predicate "wasm_subregister_operand" + (match_operand 0 "register_operand") + { + machine_mode m = wasm_real_register_mode (op); + return mode != TImode; + }) + +(define_predicate "immediate_or_register_operand" + (ior (match_operand 0 "immediate_operand") + (match_operand 0 "register_operand"))) + +(define_predicate "wasm_offset_operand" + (match_operand 0 "const_int_operand") + { + return INTVAL (op) >= 0; + }) + + (define_expand "mov" + [(set (match_operand:QHSDISDF 0 "nonimmediate_operand" "") + (match_operand:QHSDISDF 1 "general_operand" ""))] + "" + { + if (wasm_expand_mov (operands[0], operands[1], mode)) + DONE; + else FAIL; + }) + +(define_expand "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" +{ +}) + +(define_predicate "call_operation" + (match_code "parallel") +{ + int arg_end = XVECLEN (op, 0); + + for (int i = 1; i < arg_end; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != USE || !REG_P (XEXP (elt, 0))) + return false; + } + return true; +}) +(define_insn "speculation_barrier" [(const_int 0)] "" "") +(define_expand "prologue" + [(const_int 777)] + "" + { + wasm_expand_prologue (); + DONE; + }) +(define_expand "epilogue" + [(const_int 777)] + "" + { + wasm_expand_epilogue (); + emit_jump_insn (gen_return ()); + DONE; + }) + +;; Calls +(define_expand "call" + [(call (match_operand:SI 0 "memory_operand" "m") + (match_operand:SI 1 "" ""))] + "" + { + wasm_expand_call(NULL_RTX, operands[0], operands[1]); + DONE; + }) + +(define_expand "call_value" + [(set (match_operand 0 "register_operand" "=r") + (call (match_operand:SI 1 "memory_operand" "m") + (match_operand:SI 2 "" "")))] + "" + { + wasm_expand_call(operands[0], operands[1], operands[2]); + DONE; + }) + +(define_insn "*call_internal_indirect" + [(match_parallel 2 "call_operation" + [(call (mem:SI (match_operand:P 0 "wasm_call_register_operand")) + (match_operand 1))])] + "" + "(call_indirect (param%T2) (result)%A2 (%M0.get %0))") + +(define_insn "*call_value_internal_indirect" + [(match_parallel 3 "call_operation" + [(set (match_operand 0 "wasm_register_operand" "") + (call (mem:SI (match_operand:P 1 "wasm_call_register_operand")) + (match_operand 2)))])] + "" + "(%M0.set %0 (call_indirect (param%T3) (result%T0)%A3 (%M1.get %1)))") + +(define_insn "*call_internal" + [(match_parallel 2 "call_operation" + [(call (mem:SI (match_operand:P 0 "funcref_operand")) + (match_operand 1))])] + "" + "(call %0%A2)") + +(define_insn "*call_value_internal" + [(match_parallel 3 "call_operation" + [(set (match_operand 0 "wasm_register_operand" "") + (call (mem:SI (match_operand:P 1 "funcref_operand")) + (match_operand 2)))])] + "" + "(%M0.set %0 (call %1%A3))") + +;; Literals +(define_insn "*local_const" + [(set (match_operand:QHSDI 0 "register_operand") + (match_operand:QHSDI 1 "const_int_operand" ""))] + "" + "(%M0.set %0 (.const %1))") + +(define_insn "*local_const" + [(set (match_operand:F 0 "register_operand") + (match_operand:F 1 "immediate_operand" ""))] + "" + "(%M0.set %0 (.const %1))") + +;; Address taking +(define_insn "*local_const_addr" + [(set (match_operand:SI 0 "register_operand") + (match_operand:SI 1 "immediate_operand"))] + "" + "(%M0.set %0 %i1)") + +;; Moves +(define_insn "*local_move" + [(set (match_operand:QHSDISDF 0 "wasm_subregister_operand" "") + (match_operand:QHSDISDF 1 "wasm_subregister_operand" ""))] + "wasm_real_register_mode (operands[0]) == wasm_real_register_mode (operands[1])" + "(%M0.set %0 (%M1.get %1))") + +(define_insn "*local_move" + [(set (match_operand:QHSDI 0 "subregister_for_di_operand" "") + (match_operand:QHSDI 1 "subregister_for_si_operand" ""))] + "" + "(%M0.set %0 (i64.extend_i32_s (%M1.get %1)))") + +(define_insn "*local_move" + [(set (match_operand:QHSDI 0 "subregister_for_si_operand" "") + (match_operand:QHSDI 1 "subregister_for_di_operand" ""))] + "" + "(%M0.set %0 (i32.wrap_i64 (%M1.get %1)))") + +;; Comparisons +(define_expand "cstore4" + [(set (match_operand:SI 0 "register_operand" "") + (match_operator 1 "comparison_operator" + [(match_operand:REGF 2 "general_operand" "") + (match_operand:REGF 3 "general_operand" "")]))] + "" + { + wasm_expand_compare (mode, operands[0], operands[1]); + DONE; + }) + +(define_insn "*test_" + [(set (match_operand:SI 0 "wasm_register_operand") + (frelop:SI (match_operand:F 1 "wasm_register_operand") + (match_operand:F 2 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1) (%M2.get %2)))") + +(define_insn "*test_" + [(set (match_operand:SI 0 "wasm_register_operand") + (irelop:SI (match_operand:REG 1 "wasm_register_operand") + (match_operand:REG 2 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1) (%M2.get %2)))") + +;; Control flow +(define_insn "return" [(return)] "" "(return%#)") +(define_insn "trap" [(trap_if (const_int 1) (const_int 0))] "" "(unreachable)") + +(define_expand "cbranch4" + [(set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(match_operand:REGF 1 "general_operand" "") + (match_operand:REGF 2 "general_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + { + rtx cond = gen_reg_rtx (SImode); + emit_insn (gen_cstore4 (cond, operands[0], operands[1], operands[2])); + emit_jump_insn (gen_branch (operands[3], cond)); + DONE; + }) + +(define_insn "branch" + [(set (pc) + (if_then_else (ne (match_operand:SI 1 "wasm_register_operand" "") + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "(br_if $control (local.set $control (i32.const %l0)) (%M1.get %1))") + +(define_insn "*jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "(br $control (local.set $control (i32.const %l0)))") + +;; Int memops +(define_insn "*store" + [(set (match_operand:REGF 0 "memory_operand") + (match_operand:REGF 1 "wasm_register_operand"))] + "" + "(.store %m0 %i1)") + +(define_insn "*load" + [(set (match_operand:REGF 0 "wasm_register_operand") + (match_operand:REGF 1 "memory_operand"))] + "" + "(%o0 (.load %m1))") + +;; I64 subreg memops +(define_insn "*store" + [(set (match_operand:SUBREGDI 0 "memory_operand") + (match_operand:SUBREGDI 1 "subregister_for_di_operand"))] + "" + "(i64.store %m0 %i1)") + +(define_insn "*load" + [(set (match_operand:SUBREGDI 0 "subregister_for_di_operand") + (match_operand:SUBREGDI 1 "memory_operand"))] + "" + "(%o0 (i64.load_s %m1))") + +;; I32 subreg memops +(define_insn "*store" + [(set (match_operand:SUBREGSI 0 "memory_operand" "") + (match_operand:SUBREGSI 1 "subregister_for_si_operand" ""))] + "" + "(i32.store %m0 %i1)") + +(define_insn "*load" + [(set (match_operand:SUBREGSI 0 "subregister_for_si_operand") + (match_operand:SUBREGSI 1 "memory_operand"))] + "" + "(%o0 (i32.load_s %i1))") + +;; Binary integer +(define_expand "3" + [(set (match_operand:REG 0 "register_operand" "") + (ibinop:REG (match_operand:REG 1 "immediate_or_register_operand" "") + (match_operand:REG 2 "immediate_or_register_operand" "")))] + "" + { + if (!subregister_for__operand(operands[1], mode)) + operands[1] = force_reg (mode, operands[1]); + if (!subregister_for__operand(operands[2], mode)) + operands[2] = force_reg (mode, operands[2]); + }) + +(define_insn "*3" + [(set (match_operand:REG 0 "wasm_register_operand") + (ibinop:REG (match_operand:REG 1 "wasm_register_operand") + (match_operand:REG 2 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1) (%M2.get %2)))") + +;; Unary integer +(define_expand "2" + [(set (match_operand:REG 0 "register_operand") + (iunop:REG (match_operand:REG 1 "immediate_or_register_operand")))] + "" + { + if (!subregister_for__operand(operands[1], mode)) + operands[1] = force_reg (mode, operands[1]); + }) + +(define_insn "*2" + [(set (match_operand:REG 0 "wasm_register_operand") + (iunop:REG (match_operand:REG 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1)))") + +;; Binary float +(define_expand "3" + [(set (match_operand:F 0 "register_operand") + (fbinop:F (match_operand:F 1 "immediate_or_register_operand") + (match_operand:F 2 "immediate_or_register_operand")))] + "" + { + operands[1] = force_reg (mode, operands[1]); + operands[2] = force_reg (mode, operands[2]); + }) + +(define_insn "*3" + [(set (match_operand:F 0 "wasm_register_operand") + (fbinop:F (match_operand:F 1 "wasm_register_operand") + (match_operand:F 2 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1) (%M2.get %2)))") + +;; Unary float +(define_expand "2" + [(set (match_operand:F 0 "register_operand") + (funop:F (match_operand:F 1 "immediate_or_register_operand")))] + "" + { + operands[1] = force_reg (mode, operands[1]); + }) + +(define_insn "*2" + [(set (match_operand:F 0 "wasm_register_operand") + (funop:F (match_operand:F 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (. (%M1.get %1)))") + +;; Integer conv +(define_expand "2" + [(set (match_operand:QHSDI 0 "wasm_subregister_operand") + (iconvop:QHSDI (match_operand:QHSDI2 1 "wasm_subregister_operand")))] + "" + { + wasm_expand_conv (operands[0], operands[1], , false); + DONE; + }) + +(define_insn "*extend_si_" + [(set (match_operand:QHSDI 0 "register_operand") + (sign_extend:QHSDI (match_operand:SUBREGSI 1 "register_operand")))] + "wasm_real_register_mode (operands[0]) == SImode && wasm_real_register_mode (operands[1]) == SImode" + "(%M0.set %0 (i32.extend_s (%M1.get %1)))") + +(define_insn "*extend_di_" + [(set (match_operand:QHSDI 0 "register_operand") + (sign_extend:QHSDI (match_operand:SUBREGDI 1 "register_operand")))] + "wasm_real_register_mode (operands[0]) == DImode && wasm_real_register_mode (operands[1]) == DImode" + "(%M0.set %0 (i64.extend_s (%M1.get %1)))") +(define_insn "*extend_sidi" + [(set (match_operand:DI 0 "register_operand") + (sign_extend:DI (match_operand:SI 1 "register_operand")))] + "REG_P (operands[0]) && REG_P (operands[1])" + "(%M0.set %0 (i64.extend_i32_s (%M1.get %1)))") +(define_insn "*uextend_sidi" + [(set (match_operand:DI 0 "register_operand") + (zero_extend:DI (match_operand:SI 1 "register_operand")))] + "REG_P (operands[0]) && REG_P (operands[1])" + "(%M0.set %0 (i64.extend_i32_u (%M1.get %1)))") +(define_insn "*truncate_disi" + [(set (match_operand:SI 0 "register_operand") + (truncate:SI (match_operand:DI 1 "register_operand")))] + "REG_P (operands[0]) && REG_P (operands[1])" + "(%M0.set %0 (i32.wrap_i64 (%M1.get %1)))") + +;; Float conv +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "wasm_register_operand") + (float_extend:DF (match_operand:SF 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (f64.promote_f32 (%M1.get %1)))") + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "wasm_register_operand") + (float_truncate:SF (match_operand:DF 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (f32.demote_f64 (%M1.get %1)))") + +;; Float -> int conv +(define_insn "fix_trunc2" + [(set (match_operand:REG 0 "wasm_register_operand") + (fix:REG (match_operand:F 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (.trunc_sat__s (%M1.get %1)))") + +(define_insn "fixuns_trunc2" + [(set (match_operand:REG 0 "wasm_register_operand") + (unsigned_fix:REG (match_operand:F 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (.trunc_sat__u (%M1.get %1)))") + +;; Int -> float conv +(define_insn "float2" + [(set (match_operand:F 0 "wasm_register_operand") + (float:F (match_operand:REG 1 "wasm_register_operand")))] + "" + "(%M0.set %0 (.convert__s (%M1.get %1)))") + +;; Rectify int <-> float cast +;;(define_insn "floatuns2" +;; [(set (match_operand:F 0 "register_operand") +;; (unsigned_float:F (match_operand:REG 1 "register_operand")))] +;; "" +;; "(%M0.set %0 (.convert__u (%M1.get %1)))") +;; +;;(define_subst "swap_subreg_s_" +;; [(set (match_operand:REGF 0) +;; (subreg:REGF (match_operand: 1) 0))] +;; "" +;; [(set (subreg: (match_dup 0) 0) +;; (match_dup 1))]) +;;(define_subst_attr "swap_subregsi" "swap_subreg_s_si" "_from" "_to") +;;(define_subst_attr "swap_subregdi" "swap_subreg_s_di" "_from" "_to") +;;(define_subst_attr "swap_subregsf" "swap_subreg_s_sf" "_from" "_to") +;;(define_subst_attr "swap_subregdf" "swap_subreg_s_df" "_from" "_to") + +;; Float -> int cast +(define_insn "*cast" + [(set (match_operand:REG 0 "wasm_register_operand") + (subreg:REG (match_operand: 1 "wasm_register_operand") 0))] + "" + "(%M0.set %0 (.reinterpret_ (%M1.get %1)))") + +(define_insn "*cast" + [(set (subreg: (match_operand:REG 0 "wasm_register_operand") 0) + (match_operand: 1 "wasm_register_operand"))] + "" + "(%M0.set %0 (.reinterpret_ (%M1.get %1)))") + +;; Int -> float cast +(define_insn "*cast" + [(set (match_operand: 0 "wasm_register_operand") + (subreg: (match_operand:REG 1 "wasm_register_operand") 0))] + "" + "(%M0.set %0 (.reinterpret_ (%M1.get %1)))") + +(define_insn "*cast" + [(set (subreg:REG (match_operand: 0 "wasm_register_operand") 0) + (match_operand:REG 1 "wasm_register_operand"))] + "" + "(%M0.set %0 (.reinterpret_ (%M1.get %1)))") \ No newline at end of file diff --git a/libgcc/config.host b/libgcc/config.host index ac10ccc4340..61404f2a18d 100644 --- a/libgcc/config.host +++ b/libgcc/config.host @@ -1574,6 +1574,11 @@ nvptx-*) tmake_file="$tmake_file nvptx/t-nvptx" extra_parts="crt0.o" ;; +wasm*-*-*) + tmake_file="$tmake_file wasm/t-wasm" + # unwind_header=config/no-unwind.h + # extra_parts="crt0.o" + ;; *) echo "*** Configuration ${host} not supported" 1>&2 exit 1 diff --git a/libgcc/config/wasm/t-wasm b/libgcc/config/wasm/t-wasm new file mode 100644 index 00000000000..bd09d3c0dbf --- /dev/null +++ b/libgcc/config/wasm/t-wasm @@ -0,0 +1,4 @@ +LIB2ADDEH= + +# Debug information is not yet supported +LIBGCC2_DEBUG_CFLAGS = -g0 \ No newline at end of file