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