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