[llvm] [WebAssembly] Implement GlobalISel (PR #157161)

Demetrius Kanios via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 11 12:41:39 PDT 2025


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/157161

>From 829e084f79d55cd0a0221f8f0eb049c9c50e415d Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:40:31 -0700
Subject: [PATCH 01/17] Prepare basic GlobalISel setup and implement
 CallLowering::lowerFormalArguments and CallLowering::lowerReturn

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   4 +
 .../GISel/WebAssemblyCallLowering.cpp         | 687 ++++++++++++++++++
 .../GISel/WebAssemblyCallLowering.h           |  43 ++
 .../GISel/WebAssemblyInstructionSelector.cpp  |   0
 .../GISel/WebAssemblyInstructionSelector.h    |   0
 .../GISel/WebAssemblyLegalizerInfo.cpp        |  23 +
 .../GISel/WebAssemblyLegalizerInfo.h          |  29 +
 .../GISel/WebAssemblyRegisterBankInfo.cpp     |   0
 .../GISel/WebAssemblyRegisterBankInfo.h       |   0
 .../WebAssembly/WebAssemblySubtarget.cpp      |  30 +-
 .../Target/WebAssembly/WebAssemblySubtarget.h |  14 +
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |  30 +
 12 files changed, 859 insertions(+), 1 deletion(-)
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.h
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 1e83cbeac50d6..371d224efc1c5 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -15,6 +15,10 @@ tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
 add_public_tablegen_target(WebAssemblyCommonTableGen)
 
 add_llvm_target(WebAssemblyCodeGen
+  GISel/WebAssemblyCallLowering.cpp
+  GISel/WebAssemblyInstructionSelector.cpp
+  GISel/WebAssemblyRegisterBankInfo.cpp
+  GISel/WebAssemblyLegalizerInfo.cpp
   WebAssemblyAddMissingPrototypes.cpp
   WebAssemblyArgumentMove.cpp
   WebAssemblyAsmPrinter.cpp
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
new file mode 100644
index 0000000000000..5949d26a83840
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -0,0 +1,687 @@
+//===-- WebAssemblyCallLowering.cpp - Call lowering for GlobalISel -*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the lowering of LLVM calls to machine code calls for
+/// GlobalISel.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssemblyCallLowering.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyISelLowering.h"
+#include "WebAssemblyMachineFunctionInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "WebAssemblyUtilities.h"
+#include "llvm/CodeGen/Analysis.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/LowLevelTypeUtils.h"
+#include "llvm/CodeGenTypes/LowLevelType.h"
+#include "llvm/IR/Argument.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugLoc.h"
+
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#define DEBUG_TYPE "wasm-call-lowering"
+
+using namespace llvm;
+
+// Several of the following methods are internal utilities defined in
+// CodeGen/GlobalIsel/CallLowering.cpp
+// TODO: Find a better solution?
+
+// Internal utility from CallLowering.cpp
+static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) {
+  if (Flags.isSExt())
+    return TargetOpcode::G_SEXT;
+  if (Flags.isZExt())
+    return TargetOpcode::G_ZEXT;
+  return TargetOpcode::G_ANYEXT;
+}
+
+// Internal utility from CallLowering.cpp
+/// Pack values \p SrcRegs to cover the vector type result \p DstRegs.
+static MachineInstrBuilder
+mergeVectorRegsToResultRegs(MachineIRBuilder &B, ArrayRef<Register> DstRegs,
+                            ArrayRef<Register> SrcRegs) {
+  MachineRegisterInfo &MRI = *B.getMRI();
+  LLT LLTy = MRI.getType(DstRegs[0]);
+  LLT PartLLT = MRI.getType(SrcRegs[0]);
+
+  // Deal with v3s16 split into v2s16
+  LLT LCMTy = getCoverTy(LLTy, PartLLT);
+  if (LCMTy == LLTy) {
+    // Common case where no padding is needed.
+    assert(DstRegs.size() == 1);
+    return B.buildConcatVectors(DstRegs[0], SrcRegs);
+  }
+
+  // We need to create an unmerge to the result registers, which may require
+  // widening the original value.
+  Register UnmergeSrcReg;
+  if (LCMTy != PartLLT) {
+    assert(DstRegs.size() == 1);
+    return B.buildDeleteTrailingVectorElements(
+        DstRegs[0], B.buildMergeLikeInstr(LCMTy, SrcRegs));
+  } else {
+    // We don't need to widen anything if we're extracting a scalar which was
+    // promoted to a vector e.g. s8 -> v4s8 -> s8
+    assert(SrcRegs.size() == 1);
+    UnmergeSrcReg = SrcRegs[0];
+  }
+
+  int NumDst = LCMTy.getSizeInBits() / LLTy.getSizeInBits();
+
+  SmallVector<Register, 8> PadDstRegs(NumDst);
+  llvm::copy(DstRegs, PadDstRegs.begin());
+
+  // Create the excess dead defs for the unmerge.
+  for (int I = DstRegs.size(); I != NumDst; ++I)
+    PadDstRegs[I] = MRI.createGenericVirtualRegister(LLTy);
+
+  if (PadDstRegs.size() == 1)
+    return B.buildDeleteTrailingVectorElements(DstRegs[0], UnmergeSrcReg);
+  return B.buildUnmerge(PadDstRegs, UnmergeSrcReg);
+}
+
+// Internal utility from CallLowering.cpp
+/// Create a sequence of instructions to combine pieces split into register
+/// typed values to the original IR value. \p OrigRegs contains the destination
+/// value registers of type \p LLTy, and \p Regs contains the legalized pieces
+/// with type \p PartLLT. This is used for incoming values (physregs to vregs).
+static void buildCopyFromRegs(MachineIRBuilder &B, ArrayRef<Register> OrigRegs,
+                              ArrayRef<Register> Regs, LLT LLTy, LLT PartLLT,
+                              const ISD::ArgFlagsTy Flags) {
+  MachineRegisterInfo &MRI = *B.getMRI();
+
+  if (PartLLT == LLTy) {
+    // We should have avoided introducing a new virtual register, and just
+    // directly assigned here.
+    assert(OrigRegs[0] == Regs[0]);
+    return;
+  }
+
+  if (PartLLT.getSizeInBits() == LLTy.getSizeInBits() && OrigRegs.size() == 1 &&
+      Regs.size() == 1) {
+    B.buildBitcast(OrigRegs[0], Regs[0]);
+    return;
+  }
+
+  // A vector PartLLT needs extending to LLTy's element size.
+  // E.g. <2 x s64> = G_SEXT <2 x s32>.
+  if (PartLLT.isVector() == LLTy.isVector() &&
+      PartLLT.getScalarSizeInBits() > LLTy.getScalarSizeInBits() &&
+      (!PartLLT.isVector() ||
+       PartLLT.getElementCount() == LLTy.getElementCount()) &&
+      OrigRegs.size() == 1 && Regs.size() == 1) {
+    Register SrcReg = Regs[0];
+
+    LLT LocTy = MRI.getType(SrcReg);
+
+    if (Flags.isSExt()) {
+      SrcReg = B.buildAssertSExt(LocTy, SrcReg, LLTy.getScalarSizeInBits())
+                   .getReg(0);
+    } else if (Flags.isZExt()) {
+      SrcReg = B.buildAssertZExt(LocTy, SrcReg, LLTy.getScalarSizeInBits())
+                   .getReg(0);
+    }
+
+    // Sometimes pointers are passed zero extended.
+    LLT OrigTy = MRI.getType(OrigRegs[0]);
+    if (OrigTy.isPointer()) {
+      LLT IntPtrTy = LLT::scalar(OrigTy.getSizeInBits());
+      B.buildIntToPtr(OrigRegs[0], B.buildTrunc(IntPtrTy, SrcReg));
+      return;
+    }
+
+    B.buildTrunc(OrigRegs[0], SrcReg);
+    return;
+  }
+
+  if (!LLTy.isVector() && !PartLLT.isVector()) {
+    assert(OrigRegs.size() == 1);
+    LLT OrigTy = MRI.getType(OrigRegs[0]);
+
+    unsigned SrcSize = PartLLT.getSizeInBits().getFixedValue() * Regs.size();
+    if (SrcSize == OrigTy.getSizeInBits())
+      B.buildMergeValues(OrigRegs[0], Regs);
+    else {
+      auto Widened = B.buildMergeLikeInstr(LLT::scalar(SrcSize), Regs);
+      B.buildTrunc(OrigRegs[0], Widened);
+    }
+
+    return;
+  }
+
+  if (PartLLT.isVector()) {
+    assert(OrigRegs.size() == 1);
+    SmallVector<Register> CastRegs(Regs);
+
+    // If PartLLT is a mismatched vector in both number of elements and element
+    // size, e.g. PartLLT == v2s64 and LLTy is v3s32, then first coerce it to
+    // have the same elt type, i.e. v4s32.
+    // TODO: Extend this coersion to element multiples other than just 2.
+    if (TypeSize::isKnownGT(PartLLT.getSizeInBits(), LLTy.getSizeInBits()) &&
+        PartLLT.getScalarSizeInBits() == LLTy.getScalarSizeInBits() * 2 &&
+        Regs.size() == 1) {
+      LLT NewTy = PartLLT.changeElementType(LLTy.getElementType())
+                      .changeElementCount(PartLLT.getElementCount() * 2);
+      CastRegs[0] = B.buildBitcast(NewTy, Regs[0]).getReg(0);
+      PartLLT = NewTy;
+    }
+
+    if (LLTy.getScalarType() == PartLLT.getElementType()) {
+      mergeVectorRegsToResultRegs(B, OrigRegs, CastRegs);
+    } else {
+      unsigned I = 0;
+      LLT GCDTy = getGCDType(LLTy, PartLLT);
+
+      // We are both splitting a vector, and bitcasting its element types. Cast
+      // the source pieces into the appropriate number of pieces with the result
+      // element type.
+      for (Register SrcReg : CastRegs)
+        CastRegs[I++] = B.buildBitcast(GCDTy, SrcReg).getReg(0);
+      mergeVectorRegsToResultRegs(B, OrigRegs, CastRegs);
+    }
+
+    return;
+  }
+
+  assert(LLTy.isVector() && !PartLLT.isVector());
+
+  LLT DstEltTy = LLTy.getElementType();
+
+  // Pointer information was discarded. We'll need to coerce some register types
+  // to avoid violating type constraints.
+  LLT RealDstEltTy = MRI.getType(OrigRegs[0]).getElementType();
+
+  assert(DstEltTy.getSizeInBits() == RealDstEltTy.getSizeInBits());
+
+  if (DstEltTy == PartLLT) {
+    // Vector was trivially scalarized.
+
+    if (RealDstEltTy.isPointer()) {
+      for (Register Reg : Regs)
+        MRI.setType(Reg, RealDstEltTy);
+    }
+
+    B.buildBuildVector(OrigRegs[0], Regs);
+  } else if (DstEltTy.getSizeInBits() > PartLLT.getSizeInBits()) {
+    // Deal with vector with 64-bit elements decomposed to 32-bit
+    // registers. Need to create intermediate 64-bit elements.
+    SmallVector<Register, 8> EltMerges;
+    int PartsPerElt =
+        divideCeil(DstEltTy.getSizeInBits(), PartLLT.getSizeInBits());
+    LLT ExtendedPartTy = LLT::scalar(PartLLT.getSizeInBits() * PartsPerElt);
+
+    for (int I = 0, NumElts = LLTy.getNumElements(); I != NumElts; ++I) {
+      auto Merge =
+          B.buildMergeLikeInstr(ExtendedPartTy, Regs.take_front(PartsPerElt));
+      if (ExtendedPartTy.getSizeInBits() > RealDstEltTy.getSizeInBits())
+        Merge = B.buildTrunc(RealDstEltTy, Merge);
+      // Fix the type in case this is really a vector of pointers.
+      MRI.setType(Merge.getReg(0), RealDstEltTy);
+      EltMerges.push_back(Merge.getReg(0));
+      Regs = Regs.drop_front(PartsPerElt);
+    }
+
+    B.buildBuildVector(OrigRegs[0], EltMerges);
+  } else {
+    // Vector was split, and elements promoted to a wider type.
+    // FIXME: Should handle floating point promotions.
+    unsigned NumElts = LLTy.getNumElements();
+    LLT BVType = LLT::fixed_vector(NumElts, PartLLT);
+
+    Register BuildVec;
+    if (NumElts == Regs.size())
+      BuildVec = B.buildBuildVector(BVType, Regs).getReg(0);
+    else {
+      // Vector elements are packed in the inputs.
+      // e.g. we have a <4 x s16> but 2 x s32 in regs.
+      assert(NumElts > Regs.size());
+      LLT SrcEltTy = MRI.getType(Regs[0]);
+
+      LLT OriginalEltTy = MRI.getType(OrigRegs[0]).getElementType();
+
+      // Input registers contain packed elements.
+      // Determine how many elements per reg.
+      assert((SrcEltTy.getSizeInBits() % OriginalEltTy.getSizeInBits()) == 0);
+      unsigned EltPerReg =
+          (SrcEltTy.getSizeInBits() / OriginalEltTy.getSizeInBits());
+
+      SmallVector<Register, 0> BVRegs;
+      BVRegs.reserve(Regs.size() * EltPerReg);
+      for (Register R : Regs) {
+        auto Unmerge = B.buildUnmerge(OriginalEltTy, R);
+        for (unsigned K = 0; K < EltPerReg; ++K)
+          BVRegs.push_back(B.buildAnyExt(PartLLT, Unmerge.getReg(K)).getReg(0));
+      }
+
+      // We may have some more elements in BVRegs, e.g. if we have 2 s32 pieces
+      // for a <3 x s16> vector. We should have less than EltPerReg extra items.
+      if (BVRegs.size() > NumElts) {
+        assert((BVRegs.size() - NumElts) < EltPerReg);
+        BVRegs.truncate(NumElts);
+      }
+      BuildVec = B.buildBuildVector(BVType, BVRegs).getReg(0);
+    }
+    B.buildTrunc(OrigRegs[0], BuildVec);
+  }
+}
+
+// Internal utility from CallLowering.cpp
+/// Create a sequence of instructions to expand the value in \p SrcReg (of type
+/// \p SrcTy) to the types in \p DstRegs (of type \p PartTy). \p ExtendOp should
+/// contain the type of scalar value extension if necessary.
+///
+/// This is used for outgoing values (vregs to physregs)
+static void buildCopyToRegs(MachineIRBuilder &B, ArrayRef<Register> DstRegs,
+                            Register SrcReg, LLT SrcTy, LLT PartTy,
+                            unsigned ExtendOp = TargetOpcode::G_ANYEXT) {
+  // We could just insert a regular copy, but this is unreachable at the moment.
+  assert(SrcTy != PartTy && "identical part types shouldn't reach here");
+
+  const TypeSize PartSize = PartTy.getSizeInBits();
+
+  if (PartTy.isVector() == SrcTy.isVector() &&
+      PartTy.getScalarSizeInBits() > SrcTy.getScalarSizeInBits()) {
+    assert(DstRegs.size() == 1);
+    B.buildInstr(ExtendOp, {DstRegs[0]}, {SrcReg});
+    return;
+  }
+
+  if (SrcTy.isVector() && !PartTy.isVector() &&
+      TypeSize::isKnownGT(PartSize, SrcTy.getElementType().getSizeInBits())) {
+    // Vector was scalarized, and the elements extended.
+    auto UnmergeToEltTy = B.buildUnmerge(SrcTy.getElementType(), SrcReg);
+    for (int i = 0, e = DstRegs.size(); i != e; ++i)
+      B.buildAnyExt(DstRegs[i], UnmergeToEltTy.getReg(i));
+    return;
+  }
+
+  if (SrcTy.isVector() && PartTy.isVector() &&
+      PartTy.getSizeInBits() == SrcTy.getSizeInBits() &&
+      ElementCount::isKnownLT(SrcTy.getElementCount(),
+                              PartTy.getElementCount())) {
+    // A coercion like: v2f32 -> v4f32 or nxv2f32 -> nxv4f32
+    Register DstReg = DstRegs.front();
+    B.buildPadVectorWithUndefElements(DstReg, SrcReg);
+    return;
+  }
+
+  LLT GCDTy = getGCDType(SrcTy, PartTy);
+  if (GCDTy == PartTy) {
+    // If this already evenly divisible, we can create a simple unmerge.
+    B.buildUnmerge(DstRegs, SrcReg);
+    return;
+  }
+
+  if (SrcTy.isVector() && !PartTy.isVector() &&
+      SrcTy.getScalarSizeInBits() > PartTy.getSizeInBits()) {
+    LLT ExtTy =
+        LLT::vector(SrcTy.getElementCount(),
+                    LLT::scalar(PartTy.getScalarSizeInBits() * DstRegs.size() /
+                                SrcTy.getNumElements()));
+    auto Ext = B.buildAnyExt(ExtTy, SrcReg);
+    B.buildUnmerge(DstRegs, Ext);
+    return;
+  }
+
+  MachineRegisterInfo &MRI = *B.getMRI();
+  LLT DstTy = MRI.getType(DstRegs[0]);
+  LLT LCMTy = getCoverTy(SrcTy, PartTy);
+
+  if (PartTy.isVector() && LCMTy == PartTy) {
+    assert(DstRegs.size() == 1);
+    B.buildPadVectorWithUndefElements(DstRegs[0], SrcReg);
+    return;
+  }
+
+  const unsigned DstSize = DstTy.getSizeInBits();
+  const unsigned SrcSize = SrcTy.getSizeInBits();
+  unsigned CoveringSize = LCMTy.getSizeInBits();
+
+  Register UnmergeSrc = SrcReg;
+
+  if (!LCMTy.isVector() && CoveringSize != SrcSize) {
+    // For scalars, it's common to be able to use a simple extension.
+    if (SrcTy.isScalar() && DstTy.isScalar()) {
+      CoveringSize = alignTo(SrcSize, DstSize);
+      LLT CoverTy = LLT::scalar(CoveringSize);
+      UnmergeSrc = B.buildInstr(ExtendOp, {CoverTy}, {SrcReg}).getReg(0);
+    } else {
+      // Widen to the common type.
+      // FIXME: This should respect the extend type
+      Register Undef = B.buildUndef(SrcTy).getReg(0);
+      SmallVector<Register, 8> MergeParts(1, SrcReg);
+      for (unsigned Size = SrcSize; Size != CoveringSize; Size += SrcSize)
+        MergeParts.push_back(Undef);
+      UnmergeSrc = B.buildMergeLikeInstr(LCMTy, MergeParts).getReg(0);
+    }
+  }
+
+  if (LCMTy.isVector() && CoveringSize != SrcSize)
+    UnmergeSrc = B.buildPadVectorWithUndefElements(LCMTy, SrcReg).getReg(0);
+
+  B.buildUnmerge(DstRegs, UnmergeSrc);
+}
+
+// Test whether the given calling convention is supported.
+static bool callingConvSupported(CallingConv::ID CallConv) {
+  // We currently support the language-independent target-independent
+  // conventions. We don't yet have a way to annotate calls with properties like
+  // "cold", and we don't have any call-clobbered registers, so these are mostly
+  // all handled the same.
+  return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
+         CallConv == CallingConv::Cold ||
+         CallConv == CallingConv::PreserveMost ||
+         CallConv == CallingConv::PreserveAll ||
+         CallConv == CallingConv::CXX_FAST_TLS ||
+         CallConv == CallingConv::WASM_EmscriptenInvoke ||
+         CallConv == CallingConv::Swift;
+}
+
+static void fail(MachineIRBuilder &MIRBuilder, const char *Msg) {
+  MachineFunction &MF = MIRBuilder.getMF();
+  MIRBuilder.getContext().diagnose(
+      DiagnosticInfoUnsupported(MF.getFunction(), Msg, MIRBuilder.getDL()));
+}
+
+WebAssemblyCallLowering::WebAssemblyCallLowering(
+    const WebAssemblyTargetLowering &TLI)
+    : CallLowering(&TLI) {}
+
+bool WebAssemblyCallLowering::canLowerReturn(MachineFunction &MF,
+                                             CallingConv::ID CallConv,
+                                             SmallVectorImpl<BaseArgInfo> &Outs,
+                                             bool IsVarArg) const {
+  return WebAssembly::canLowerReturn(Outs.size(),
+                                     &MF.getSubtarget<WebAssemblySubtarget>());
+}
+
+bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
+                                          const Value *Val,
+                                          ArrayRef<Register> VRegs,
+                                          FunctionLoweringInfo &FLI,
+                                          Register SwiftErrorVReg) const {
+  auto MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
+
+  assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
+         "Return value without a vreg");
+
+  if (Val && !FLI.CanLowerReturn) {
+    insertSRetStores(MIRBuilder, Val->getType(), VRegs, FLI.DemoteRegister);
+  } else if (!VRegs.empty()) {
+    MachineFunction &MF = MIRBuilder.getMF();
+    const Function &F = MF.getFunction();
+    MachineRegisterInfo &MRI = MF.getRegInfo();
+    const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
+    auto &DL = F.getDataLayout();
+    LLVMContext &Ctx = Val->getType()->getContext();
+
+    SmallVector<EVT, 4> SplitEVTs;
+    ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
+    assert(VRegs.size() == SplitEVTs.size() &&
+           "For each split Type there should be exactly one VReg.");
+
+    SmallVector<ArgInfo, 8> SplitArgs;
+    CallingConv::ID CallConv = F.getCallingConv();
+
+    unsigned i = 0;
+    for (auto SplitEVT : SplitEVTs) {
+      Register CurVReg = VRegs[i];
+      ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
+      setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
+
+      splitToValueTypes(CurArgInfo, SplitArgs, DL, CallConv);
+      ++i;
+    }
+
+    for (auto &Arg : SplitArgs) {
+      EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+      MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+      LLT OrigLLT = getLLTForType(*Arg.Ty, DL);
+      LLT NewLLT = getLLTForMVT(NewVT);
+
+      // If we need to split the type over multiple regs, check it's a scenario
+      // we currently support.
+      unsigned NumParts =
+          TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+      ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
+      Arg.Flags.clear();
+
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        ISD::ArgFlagsTy Flags = OrigFlags;
+        if (Part == 0) {
+          Flags.setSplit();
+        } else {
+          Flags.setOrigAlign(Align(1));
+          if (Part == NumParts - 1)
+            Flags.setSplitEnd();
+        }
+
+        Arg.Flags.push_back(Flags);
+      }
+
+      Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end());
+      if (NumParts != 1 || OrigVT != NewVT) {
+        // If we can't directly assign the register, we need one or more
+        // intermediate values.
+        Arg.Regs.resize(NumParts);
+
+        // For each split register, create and assign a vreg that will store
+        // the incoming component of the larger value. These will later be
+        // merged to form the final vreg.
+        for (unsigned Part = 0; Part < NumParts; ++Part) {
+          Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+        }
+        buildCopyToRegs(MIRBuilder, Arg.Regs, Arg.OrigRegs[0], OrigLLT, NewLLT,
+                        extendOpFromFlags(Arg.Flags[0]));
+      }
+
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        MIB.addUse(Arg.Regs[Part]);
+      }
+    }
+  }
+
+  if (SwiftErrorVReg) {
+    llvm_unreachable("WASM does not `supportSwiftError`, yet SwiftErrorVReg is "
+                     "improperly valid.");
+  }
+
+  MIRBuilder.insertInstr(MIB);
+  return true;
+}
+
+static unsigned getWASMArgOpcode(MVT ArgType) {
+#define MVT_CASE(type)                                                         \
+  case MVT::type:                                                              \
+    return WebAssembly::ARGUMENT_##type;
+
+  switch (ArgType.SimpleTy) {
+    MVT_CASE(i32)
+    MVT_CASE(i64)
+    MVT_CASE(f32)
+    MVT_CASE(f64)
+    MVT_CASE(funcref)
+    MVT_CASE(externref)
+    MVT_CASE(exnref)
+    MVT_CASE(v16i8)
+    MVT_CASE(v8i16)
+    MVT_CASE(v4i32)
+    MVT_CASE(v2i64)
+    MVT_CASE(v4f32)
+    MVT_CASE(v2f64)
+    MVT_CASE(v8f16)
+  default:
+    break;
+  }
+  llvm_unreachable("Found unexpected type for WASM argument");
+
+#undef MVT_CASE
+}
+
+bool WebAssemblyCallLowering::lowerFormalArguments(
+    MachineIRBuilder &MIRBuilder, const Function &F,
+    ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const {
+
+  MachineFunction &MF = MIRBuilder.getMF();
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  WebAssemblyFunctionInfo *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
+  const DataLayout &DL = F.getDataLayout();
+  auto &TLI = *getTLI<WebAssemblyTargetLowering>();
+  LLVMContext &Ctx = MIRBuilder.getContext();
+  const CallingConv::ID CallConv = F.getCallingConv();
+
+  if (!callingConvSupported(F.getCallingConv())) {
+    fail(MIRBuilder, "WebAssembly doesn't support non-C calling conventions");
+    return false;
+  }
+
+  // Set up the live-in for the incoming ARGUMENTS.
+  MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
+
+  SmallVector<ArgInfo, 8> SplitArgs;
+
+  if (!FLI.CanLowerReturn) {
+    dbgs() << "grath\n";
+    insertSRetIncomingArgument(F, SplitArgs, FLI.DemoteRegister, MRI, DL);
+  }
+  unsigned i = 0;
+
+  bool HasSwiftErrorArg = false;
+  bool HasSwiftSelfArg = false;
+  for (const auto &Arg : F.args()) {
+    ArgInfo OrigArg{VRegs[i], Arg.getType(), i};
+    setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F);
+
+    HasSwiftSelfArg |= Arg.hasSwiftSelfAttr();
+    HasSwiftErrorArg |= Arg.hasSwiftErrorAttr();
+    if (Arg.hasInAllocaAttr()) {
+      fail(MIRBuilder, "WebAssembly hasn't implemented inalloca arguments");
+      return false;
+    }
+    if (Arg.hasNestAttr()) {
+      fail(MIRBuilder, "WebAssembly hasn't implemented nest arguments");
+      return false;
+    }
+    splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv());
+    ++i;
+  }
+
+  unsigned FinalArgIdx = 0;
+  for (auto &Arg : SplitArgs) {
+    EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+    MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+    LLT OrigLLT = getLLTForType(*Arg.Ty, DL);
+    LLT NewLLT = getLLTForMVT(NewVT);
+
+    // If we need to split the type over multiple regs, check it's a scenario
+    // we currently support.
+    unsigned NumParts =
+        TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+    ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
+    Arg.Flags.clear();
+
+    for (unsigned Part = 0; Part < NumParts; ++Part) {
+      ISD::ArgFlagsTy Flags = OrigFlags;
+      if (Part == 0) {
+        Flags.setSplit();
+      } else {
+        Flags.setOrigAlign(Align(1));
+        if (Part == NumParts - 1)
+          Flags.setSplitEnd();
+      }
+
+      Arg.Flags.push_back(Flags);
+    }
+
+    Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end());
+    if (NumParts != 1 || OrigVT != NewVT) {
+      // If we can't directly assign the register, we need one or more
+      // intermediate values.
+      Arg.Regs.resize(NumParts);
+
+      // For each split register, create and assign a vreg that will store
+      // the incoming component of the larger value. These will later be
+      // merged to form the final vreg.
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+      }
+      buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT,
+                        Arg.Flags[0]);
+    }
+
+    for (unsigned Part = 0; Part < NumParts; ++Part) {
+      MIRBuilder.buildInstr(getWASMArgOpcode(NewVT))
+          .addDef(Arg.Regs[Part])
+          .addImm(FinalArgIdx);
+      MFI->addParam(NewVT);
+      ++FinalArgIdx;
+    }
+  }
+
+  /**/
+
+  // For swiftcc, emit additional swiftself and swifterror arguments
+  // if there aren't. These additional arguments are also added for callee
+  // signature They are necessary to match callee and caller signature for
+  // indirect call.
+  auto PtrVT = TLI.getPointerTy(DL);
+  if (CallConv == CallingConv::Swift) {
+    if (!HasSwiftSelfArg) {
+      MFI->addParam(PtrVT);
+    }
+    if (!HasSwiftErrorArg) {
+      MFI->addParam(PtrVT);
+    }
+  }
+
+  // Varargs are copied into a buffer allocated by the caller, and a pointer to
+  // the buffer is passed as an argument.
+  if (F.isVarArg()) {
+    auto PtrVT = TLI.getPointerTy(DL);
+    Register VarargVreg = MF.getRegInfo().createGenericVirtualRegister(
+        getLLTForType(*PointerType::get(Ctx, 0), DL));
+    MFI->setVarargBufferVreg(VarargVreg);
+
+    MIRBuilder.buildInstr(getWASMArgOpcode(PtrVT))
+        .addDef(VarargVreg)
+        .addImm(FinalArgIdx);
+
+    MFI->addParam(PtrVT);
+    ++FinalArgIdx;
+  }
+
+  // Record the number and types of arguments and results.
+  SmallVector<MVT, 4> Params;
+  SmallVector<MVT, 4> Results;
+  computeSignatureVTs(MF.getFunction().getFunctionType(), &MF.getFunction(),
+                      MF.getFunction(), MF.getTarget(), Params, Results);
+  for (MVT VT : Results)
+    MFI->addResult(VT);
+
+  // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify
+  // the param logic here with ComputeSignatureVTs
+  assert(MFI->getParams().size() == Params.size() &&
+         std::equal(MFI->getParams().begin(), MFI->getParams().end(),
+                    Params.begin()));
+  return true;
+}
+
+bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
+                                        CallLoweringInfo &Info) const {
+  return false;
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.h
new file mode 100644
index 0000000000000..d22f7cbd17eb3
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.h
@@ -0,0 +1,43 @@
+//===-- WebAssemblyCallLowering.h - Call lowering for GlobalISel -*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file describes how to lower LLVM calls to machine code calls.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_GISEL_WEBASSEMBLYCALLLOWERING_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_GISEL_WEBASSEMBLYCALLLOWERING_H
+
+#include "WebAssemblyISelLowering.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/IR/CallingConv.h"
+
+namespace llvm {
+
+class WebAssemblyTargetLowering;
+
+class WebAssemblyCallLowering : public CallLowering {
+public:
+  WebAssemblyCallLowering(const WebAssemblyTargetLowering &TLI);
+
+  bool canLowerReturn(MachineFunction &MF, CallingConv::ID CallConv,
+                      SmallVectorImpl<BaseArgInfo> &Outs,
+                      bool IsVarArg) const override;
+  bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
+                   ArrayRef<Register> VRegs, FunctionLoweringInfo &FLI,
+                   Register SwiftErrorVReg) const override;
+  bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
+                            ArrayRef<ArrayRef<Register>> VRegs,
+                            FunctionLoweringInfo &FLI) const override;
+  bool lowerCall(MachineIRBuilder &MIRBuilder,
+                 CallLoweringInfo &Info) const override;
+};
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
new file mode 100644
index 0000000000000..3acdabb5612cc
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -0,0 +1,23 @@
+//===- WebAssemblyLegalizerInfo.h --------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the targeting of the Machinelegalizer class for
+/// WebAssembly
+//===----------------------------------------------------------------------===//
+
+#include "WebAssemblyLegalizerInfo.h"
+
+#define DEBUG_TYPE "wasm-legalinfo"
+
+using namespace llvm;
+using namespace LegalizeActions;
+
+WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
+    const WebAssemblySubtarget &ST) {
+  getLegacyLegalizerInfo().computeTables();
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
new file mode 100644
index 0000000000000..c02205fc7ae0d
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
@@ -0,0 +1,29 @@
+//===- WebAssemblyLegalizerInfo.h --------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file declares the targeting of the Machinelegalizer class for
+/// WebAssembly
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_GISEL_WEBASSEMBLYMACHINELEGALIZER_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_GISEL_WEBASSEMBLYMACHINELEGALIZER_H
+
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+
+namespace llvm {
+
+class WebAssemblySubtarget;
+
+/// This class provides the information for the BPF target legalizer for
+/// GlobalISel.
+class WebAssemblyLegalizerInfo : public LegalizerInfo {
+public:
+  WebAssemblyLegalizerInfo(const WebAssemblySubtarget &ST);
+};
+} // namespace llvm
+#endif
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index a3ce40f0297ec..3ea8b9f85819f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -13,8 +13,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblySubtarget.h"
+#include "GISel/WebAssemblyCallLowering.h"
+#include "GISel/WebAssemblyLegalizerInfo.h"
+#include "GISel/WebAssemblyRegisterBankInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
 #include "WebAssemblyInstrInfo.h"
+#include "WebAssemblyTargetMachine.h"
 #include "llvm/MC/TargetRegistry.h"
 using namespace llvm;
 
@@ -66,7 +70,15 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
                                            const TargetMachine &TM)
     : WebAssemblyGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS),
       TargetTriple(TT), InstrInfo(initializeSubtargetDependencies(CPU, FS)),
-      TLInfo(TM, *this) {}
+      TLInfo(TM, *this) {
+  CallLoweringInfo.reset(new WebAssemblyCallLowering(*getTargetLowering()));
+  Legalizer.reset(new WebAssemblyLegalizerInfo(*this));
+  /*auto *RBI = new WebAssemblyRegisterBankInfo(*getRegisterInfo());
+  RegBankInfo.reset(RBI);
+
+  InstSelector.reset(createWebAssemblyInstructionSelector(
+  *static_cast<const WebAssemblyTargetMachine *>(&TM), *this, *RBI));*/
+}
 
 bool WebAssemblySubtarget::enableAtomicExpand() const {
   // If atomics are disabled, atomic ops are lowered instead of expanded
@@ -81,3 +93,19 @@ bool WebAssemblySubtarget::enableMachineScheduler() const {
 }
 
 bool WebAssemblySubtarget::useAA() const { return true; }
+
+const CallLowering *WebAssemblySubtarget::getCallLowering() const {
+  return CallLoweringInfo.get();
+}
+
+InstructionSelector *WebAssemblySubtarget::getInstructionSelector() const {
+  return InstSelector.get();
+}
+
+const LegalizerInfo *WebAssemblySubtarget::getLegalizerInfo() const {
+  return Legalizer.get();
+}
+
+const RegisterBankInfo *WebAssemblySubtarget::getRegBankInfo() const {
+  return RegBankInfo.get();
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 2f88bbba05d00..c195f995009b1 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -20,6 +20,10 @@
 #include "WebAssemblyISelLowering.h"
 #include "WebAssemblyInstrInfo.h"
 #include "WebAssemblySelectionDAGInfo.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+#include "llvm/CodeGen/RegisterBankInfo.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include <string>
 
@@ -64,6 +68,11 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
   WebAssemblySelectionDAGInfo TSInfo;
   WebAssemblyTargetLowering TLInfo;
 
+  std::unique_ptr<CallLowering> CallLoweringInfo;
+  std::unique_ptr<InstructionSelector> InstSelector;
+  std::unique_ptr<LegalizerInfo> Legalizer;
+  std::unique_ptr<RegisterBankInfo> RegBankInfo;
+
   WebAssemblySubtarget &initializeSubtargetDependencies(StringRef CPU,
                                                         StringRef FS);
 
@@ -118,6 +127,11 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
   /// Parses features string setting specified subtarget options. Definition of
   /// function is auto generated by tblgen.
   void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
+
+  const CallLowering *getCallLowering() const override;
+  InstructionSelector *getInstructionSelector() const override;
+  const LegalizerInfo *getLegalizerInfo() const override;
+  const RegisterBankInfo *getRegBankInfo() const override;
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index a9c638cde1259..192ad3d288cf1 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -20,6 +20,10 @@
 #include "WebAssemblyTargetObjectFile.h"
 #include "WebAssemblyTargetTransformInfo.h"
 #include "WebAssemblyUtilities.h"
+#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
+#include "llvm/CodeGen/GlobalISel/Legalizer.h"
+#include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
 #include "llvm/CodeGen/MIRParser/MIParser.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/RegAllocRegistry.h"
@@ -92,6 +96,7 @@ LLVMInitializeWebAssemblyTarget() {
 
   // Register backend passes
   auto &PR = *PassRegistry::getPassRegistry();
+  initializeGlobalISel(PR);
   initializeWebAssemblyAddMissingPrototypesPass(PR);
   initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
   initializeLowerGlobalDtorsLegacyPassPass(PR);
@@ -445,6 +450,11 @@ class WebAssemblyPassConfig final : public TargetPassConfig {
 
   // No reg alloc
   bool addRegAssignAndRewriteOptimized() override { return false; }
+
+  bool addIRTranslator() override;
+  bool addLegalizeMachineIR() override;
+  bool addRegBankSelect() override;
+  bool addGlobalInstructionSelect() override;
 };
 } // end anonymous namespace
 
@@ -665,6 +675,26 @@ bool WebAssemblyPassConfig::addPreISel() {
   return false;
 }
 
+bool WebAssemblyPassConfig::addIRTranslator() {
+  addPass(new IRTranslator());
+  return false;
+}
+
+bool WebAssemblyPassConfig::addLegalizeMachineIR() {
+  addPass(new Legalizer());
+  return false;
+}
+
+bool WebAssemblyPassConfig::addRegBankSelect() {
+  addPass(new RegBankSelect());
+  return false;
+}
+
+bool WebAssemblyPassConfig::addGlobalInstructionSelect() {
+  addPass(new InstructionSelect(getOptLevel()));
+  return false;
+}
+
 yaml::MachineFunctionInfo *
 WebAssemblyTargetMachine::createDefaultFuncInfoYAML() const {
   return new yaml::WebAssemblyFunctionInfo();

>From 4edcd29ff5dc53ab096fdd926b8b8ad8995810fa Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:41:02 -0700
Subject: [PATCH 02/17] Implement WebAssemblyCallLowering::lowerCall

---
 .../GISel/WebAssemblyCallLowering.cpp         | 453 +++++++++++++++++-
 1 file changed, 451 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 5949d26a83840..8956932b403ef 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -14,14 +14,21 @@
 
 #include "WebAssemblyCallLowering.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "Utils/WasmAddressSpaces.h"
 #include "WebAssemblyISelLowering.h"
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "WebAssemblySubtarget.h"
 #include "WebAssemblyUtilities.h"
+#include "llvm/Analysis/MemoryLocation.h"
 #include "llvm/CodeGen/Analysis.h"
+#include "llvm/CodeGen/CallingConvLower.h"
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 #include "llvm/CodeGen/LowLevelTypeUtils.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGenTypes/LowLevelType.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/DataLayout.h"
@@ -29,7 +36,10 @@
 
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/MC/MCSymbolWasm.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <cassert>
 
 #define DEBUG_TYPE "wasm-call-lowering"
 
@@ -555,7 +565,6 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
   SmallVector<ArgInfo, 8> SplitArgs;
 
   if (!FLI.CanLowerReturn) {
-    dbgs() << "grath\n";
     insertSRetIncomingArgument(F, SplitArgs, FLI.DemoteRegister, MRI, DL);
   }
   unsigned i = 0;
@@ -683,5 +692,445 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
 
 bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
                                         CallLoweringInfo &Info) const {
-  return false;
+  MachineFunction &MF = MIRBuilder.getMF();
+  auto DL = MIRBuilder.getDataLayout();
+  LLVMContext &Ctx = MIRBuilder.getContext();
+  const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
+  MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+  const WebAssemblySubtarget &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+
+  CallingConv::ID CallConv = Info.CallConv;
+  if (!callingConvSupported(CallConv)) {
+    fail(MIRBuilder,
+         "WebAssembly doesn't support language-specific or target-specific "
+         "calling conventions yet");
+    return false;
+  }
+
+  // TODO: investigate "PatchPoint"
+  /*
+  if (Info.IsPatchPoint) {
+    fail(MIRBuilder, "WebAssembly doesn't support patch point yet");
+    return false;
+    }
+    */
+
+  if (Info.IsTailCall) {
+      Info.LoweredTailCall = true;
+    auto NoTail = [&](const char *Msg) {
+      if (Info.CB && Info.CB->isMustTailCall())
+        fail(MIRBuilder, Msg);
+      Info.LoweredTailCall = false;
+    };
+
+    if (!Subtarget.hasTailCall())
+      NoTail("WebAssembly 'tail-call' feature not enabled");
+
+    // Varargs calls cannot be tail calls because the buffer is on the stack
+    if (Info.IsVarArg)
+      NoTail("WebAssembly does not support varargs tail calls");
+
+    // Do not tail call unless caller and callee return types match
+    const Function &F = MF.getFunction();
+    const TargetMachine &TM = TLI.getTargetMachine();
+    Type *RetTy = F.getReturnType();
+    SmallVector<MVT, 4> CallerRetTys;
+    SmallVector<MVT, 4> CalleeRetTys;
+    computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
+    computeLegalValueVTs(F, TM, Info.OrigRet.Ty, CalleeRetTys);
+    bool TypesMatch = CallerRetTys.size() == CalleeRetTys.size() &&
+                      std::equal(CallerRetTys.begin(), CallerRetTys.end(),
+                                 CalleeRetTys.begin());
+    if (!TypesMatch)
+      NoTail("WebAssembly tail call requires caller and callee return types to "
+             "match");
+
+    // If pointers to local stack values are passed, we cannot tail call
+    if (Info.CB) {
+      for (auto &Arg : Info.CB->args()) {
+        Value *Val = Arg.get();
+        // Trace the value back through pointer operations
+        while (true) {
+          Value *Src = Val->stripPointerCastsAndAliases();
+          if (auto *GEP = dyn_cast<GetElementPtrInst>(Src))
+            Src = GEP->getPointerOperand();
+          if (Val == Src)
+            break;
+          Val = Src;
+        }
+        if (isa<AllocaInst>(Val)) {
+          NoTail(
+              "WebAssembly does not support tail calling with stack arguments");
+          break;
+        }
+      }
+    }
+  }
+
+  MachineInstrBuilder CallInst;
+
+  bool IsIndirect = false;
+  Register IndirectIdx;
+
+  if (Info.Callee.isReg()) {
+      LLT CalleeType = MRI.getType(Info.Callee.getReg());
+      assert(CalleeType.isPointer() && "Trying to lower a call with a Callee other than a pointer???");
+
+      IsIndirect = true;
+      CallInst = MIRBuilder.buildInstrNoInsert(Info.LoweredTailCall ? WebAssembly::RET_CALL_INDIRECT : WebAssembly::CALL_INDIRECT);
+
+      // Placeholder for the type index.
+      // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp
+      CallInst.addImm(0);
+
+      MCSymbolWasm *Table;
+      if (CalleeType.getAddressSpace() == WebAssembly::WASM_ADDRESS_SPACE_DEFAULT) {
+          Table = WebAssembly::getOrCreateFunctionTableSymbol(
+                MF.getContext(), &Subtarget);
+          IndirectIdx = Info.Callee.getReg();
+
+          auto PtrSize = CalleeType.getSizeInBits();
+          auto PtrIntLLT = LLT::scalar(PtrSize);
+
+          IndirectIdx = MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
+          if (PtrSize > 32) {
+              IndirectIdx = MIRBuilder.buildTrunc(LLT::scalar(32), IndirectIdx).getReg(0);
+          }
+      } else if (CalleeType.getAddressSpace() == WebAssembly::WASM_ADDRESS_SPACE_FUNCREF) {
+          Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(
+                MF.getContext(), &Subtarget);
+
+          auto TableSetInstr = MIRBuilder.buildInstr(WebAssembly::TABLE_SET_FUNCREF);
+          TableSetInstr.addSym(Table);
+          TableSetInstr.addUse(Info.Callee.getReg());
+          IndirectIdx = MIRBuilder.buildConstant(LLT::scalar(32), 0).getReg(0);
+      } else {
+          fail(MIRBuilder, "Invalid address space for indirect call");
+          return false;
+      }
+
+      if (Subtarget.hasCallIndirectOverlong()) {
+        CallInst.addSym(Table);
+      } else {
+        // For the MVP there is at most one table whose number is 0, but we can't
+        // write a table symbol or issue relocations.  Instead we just ensure the
+        // table is live and write a zero.
+        Table->setNoStrip();
+        CallInst.addImm(0);
+      }
+  } else {
+      CallInst = MIRBuilder.buildInstrNoInsert(Info.LoweredTailCall ? WebAssembly::RET_CALL : WebAssembly::CALL);
+
+      if (Info.Callee.isGlobal()) {
+          CallInst.addGlobalAddress(Info.Callee.getGlobal());
+      } else if (Info.Callee.isSymbol()) {
+          // TODO: figure out how to trigger/test this
+          CallInst.addSym(Info.Callee.getMCSymbol());
+      } else {
+          llvm_unreachable("Trying to lower call with a callee other than reg, global, or a symbol.");
+      }
+  }
+
+
+  SmallVector<ArgInfo, 8> SplitArgs;
+
+  bool HasSwiftErrorArg = false;
+  bool HasSwiftSelfArg = false;
+
+  for (const auto &Arg : Info.OrigArgs) {
+    HasSwiftSelfArg |= Arg.Flags[0].isSwiftSelf();
+    HasSwiftErrorArg |= Arg.Flags[0].isSwiftError();
+    if (Arg.Flags[0].isNest()) {
+      fail(MIRBuilder, "WebAssembly hasn't implemented nest arguments");
+      return false;
+    }
+    if (Arg.Flags[0].isInAlloca()) {
+      fail(MIRBuilder, "WebAssembly hasn't implemented inalloca arguments");
+      return false;
+    }
+    if (Arg.Flags[0].isInConsecutiveRegs()) {
+      fail(MIRBuilder, "WebAssembly hasn't implemented cons regs arguments");
+      return false;
+    }
+    if (Arg.Flags[0].isInConsecutiveRegsLast()) {
+      fail(MIRBuilder,
+           "WebAssembly hasn't implemented cons regs last arguments");
+      return false;
+    }
+
+    if (Arg.Flags[0].isByVal() && Arg.Flags[0].getByValSize() != 0) {
+      MachineFrameInfo &MFI = MF.getFrameInfo();
+
+      unsigned MemSize = Arg.Flags[0].getByValSize();
+      Align MemAlign = Arg.Flags[0].getNonZeroByValAlign();
+      int FI = MFI.CreateStackObject(Arg.Flags[0].getByValSize(), MemAlign,
+                                     /*isSS=*/false);
+
+      auto StackAddrSpace = DL.getAllocaAddrSpace();
+      auto PtrLLT = getLLTForType(*PointerType::get(Ctx, StackAddrSpace), DL);
+      Register StackObjPtrVreg =
+          MF.getRegInfo().createGenericVirtualRegister(PtrLLT);
+
+      MIRBuilder.buildFrameIndex(StackObjPtrVreg, FI);
+
+      MachinePointerInfo DstPtrInfo = MachinePointerInfo::getFixedStack(MF, FI);
+
+      MachinePointerInfo SrcPtrInfo(Arg.OrigValue);
+      if (!Arg.OrigValue) {
+        // We still need to accurately track the stack address space if we
+        // don't know the underlying value.
+        SrcPtrInfo = MachinePointerInfo::getUnknownStack(MF);
+      }
+
+      Align DstAlign =
+          std::max(MemAlign, inferAlignFromPtrInfo(MF, DstPtrInfo));
+
+      Align SrcAlign =
+          std::max(MemAlign, inferAlignFromPtrInfo(MF, SrcPtrInfo));
+
+      MachineMemOperand *SrcMMO = MF.getMachineMemOperand(
+          SrcPtrInfo,
+          MachineMemOperand::MOLoad | MachineMemOperand::MODereferenceable,
+          MemSize, SrcAlign);
+
+      MachineMemOperand *DstMMO = MF.getMachineMemOperand(
+          DstPtrInfo,
+          MachineMemOperand::MOStore | MachineMemOperand::MODereferenceable,
+          MemSize, DstAlign);
+
+      const LLT SizeTy = LLT::scalar(PtrLLT.getSizeInBits());
+
+      auto SizeConst = MIRBuilder.buildConstant(SizeTy, MemSize);
+      MIRBuilder.buildMemCpy(StackObjPtrVreg, Arg.Regs[0], SizeConst, *DstMMO,
+                             *SrcMMO);
+    }
+
+    splitToValueTypes(Arg, SplitArgs, DL, CallConv);
+  }
+
+  unsigned NumFixedArgs = 0;
+
+  for (auto &Arg : SplitArgs) {
+    EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+    MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+    LLT OrigLLT = getLLTForType(*Arg.Ty, DL);
+    LLT NewLLT = getLLTForMVT(NewVT);
+
+    // If we need to split the type over multiple regs, check it's a scenario
+    // we currently support.
+    unsigned NumParts =
+        TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+    ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
+    Arg.Flags.clear();
+
+    for (unsigned Part = 0; Part < NumParts; ++Part) {
+      ISD::ArgFlagsTy Flags = OrigFlags;
+      if (Part == 0) {
+        Flags.setSplit();
+      } else {
+        Flags.setOrigAlign(Align(1));
+        if (Part == NumParts - 1)
+          Flags.setSplitEnd();
+      }
+
+      Arg.Flags.push_back(Flags);
+    }
+
+    Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end());
+    if (NumParts != 1 || OrigVT != NewVT) {
+      // If we can't directly assign the register, we need one or more
+      // intermediate values.
+      Arg.Regs.resize(NumParts);
+
+      // For each split register, create and assign a vreg that will store
+      // the incoming component of the larger value. These will later be
+      // merged to form the final vreg.
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+      }
+
+      buildCopyToRegs(MIRBuilder, Arg.Regs, Arg.OrigRegs[0], OrigLLT, NewLLT,
+                      extendOpFromFlags(Arg.Flags[0]));
+    }
+
+    if (!Arg.Flags[0].isVarArg()) {
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        CallInst.addUse(Arg.Regs[Part]);
+        ++NumFixedArgs;
+      }
+    }
+  }
+
+  if (CallConv == CallingConv::Swift) {
+    Type *PtrTy = PointerType::getUnqual(Ctx);
+    LLT PtrLLT = getLLTForType(*PtrTy, DL);
+
+    if (!HasSwiftSelfArg) {
+      CallInst.addUse(MIRBuilder.buildUndef(PtrLLT).getReg(0));
+    }
+    if (!HasSwiftErrorArg) {
+      CallInst.addUse(MIRBuilder.buildUndef(PtrLLT).getReg(0));
+    }
+  }
+
+  // Analyze operands of the call, assigning locations to each operand.
+  SmallVector<CCValAssign, 16> ArgLocs;
+  CCState CCInfo(CallConv, Info.IsVarArg, MF, ArgLocs, Ctx);
+
+  if (Info.IsVarArg) {
+    // Outgoing non-fixed arguments are placed in a buffer. First
+    // compute their offsets and the total amount of buffer space needed.
+    for (ArgInfo &Arg : drop_begin(SplitArgs, NumFixedArgs)) {
+      EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+      MVT PartVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+      Type *Ty = EVT(PartVT).getTypeForEVT(Ctx);
+
+      for (unsigned Part = 0; Part < Arg.Regs.size(); ++Part) {
+        Align Alignment = std::max(Arg.Flags[Part].getNonZeroOrigAlign(),
+                                   DL.getABITypeAlign(Ty));
+        unsigned Offset =
+            CCInfo.AllocateStack(DL.getTypeAllocSize(Ty), Alignment);
+        CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), PartVT, Offset,
+                                          PartVT, CCValAssign::Full));
+      }
+    }
+  }
+
+  unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
+
+  auto StackAddrSpace = DL.getAllocaAddrSpace();
+  auto PtrLLT = getLLTForType(*PointerType::get(Ctx, StackAddrSpace), DL);
+  auto SizeLLT = LLT::scalar(PtrLLT.getSizeInBits());
+
+  if (Info.IsVarArg && NumBytes) {
+    Register VarArgStackPtr =
+        MF.getRegInfo().createGenericVirtualRegister(PtrLLT);
+
+    MaybeAlign StackAlign = DL.getStackAlignment();
+    assert(StackAlign && "data layout string is missing stack alignment");
+    int FI = MF.getFrameInfo().CreateStackObject(NumBytes, *StackAlign,
+                                                 /*isSS=*/false);
+
+    MIRBuilder.buildFrameIndex(VarArgStackPtr, FI);
+
+    unsigned ValNo = 0;
+    for (ArgInfo &Arg : drop_begin(SplitArgs, NumFixedArgs)) {
+      EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+      MVT PartVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+      Type *Ty = EVT(PartVT).getTypeForEVT(Ctx);
+
+      for (unsigned Part = 0; Part < Arg.Regs.size(); ++Part) {
+        Align Alignment = std::max(Arg.Flags[Part].getNonZeroOrigAlign(),
+                                   DL.getABITypeAlign(Ty));
+
+        unsigned Offset = ArgLocs[ValNo++].getLocMemOffset();
+
+        Register DstPtr =
+            MIRBuilder
+                .buildPtrAdd(PtrLLT, VarArgStackPtr,
+                             MIRBuilder.buildConstant(SizeLLT, Offset).getReg(0))
+                .getReg(0);
+
+        MachineMemOperand *DstMMO = MF.getMachineMemOperand(
+            MachinePointerInfo::getFixedStack(MF, FI, Offset),
+            MachineMemOperand::MOStore | MachineMemOperand::MODereferenceable,
+            PartVT.getStoreSize(), Alignment);
+
+        MIRBuilder.buildStore(Arg.Regs[Part], DstPtr, *DstMMO);
+      }
+    }
+
+    CallInst.addUse(VarArgStackPtr);
+  } else if (Info.IsVarArg) {
+    CallInst.addUse(MIRBuilder.buildConstant(PtrLLT, 0).getReg(0));
+  }
+
+  if (IsIndirect) {
+    CallInst.addUse(IndirectIdx);
+  }
+
+  MIRBuilder.insertInstr(CallInst);
+
+  if (Info.LoweredTailCall) {
+      return true;
+  }
+
+  if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {
+    SmallVector<EVT, 4> SplitEVTs;
+    ComputeValueVTs(TLI, DL, Info.OrigRet.Ty, SplitEVTs);
+    assert(Info.OrigRet.Regs.size() == SplitEVTs.size() &&
+           "For each split Type there should be exactly one VReg.");
+
+    SmallVector<ArgInfo, 8> SplitReturns;
+
+    unsigned i = 0;
+    for (auto SplitEVT : SplitEVTs) {
+      Register CurVReg = Info.OrigRet.Regs[i];
+      ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
+      setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, *Info.CB);
+
+      splitToValueTypes(CurArgInfo, SplitReturns, DL, CallConv);
+      ++i;
+    }
+
+    for (auto &Ret : SplitReturns) {
+      EVT OrigVT = TLI.getValueType(DL, Ret.Ty);
+      MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+      LLT OrigLLT = getLLTForType(*Ret.Ty, DL);
+      LLT NewLLT = getLLTForMVT(NewVT);
+
+      // If we need to split the type over multiple regs, check it's a scenario
+      // we currently support.
+      unsigned NumParts =
+          TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+      ISD::ArgFlagsTy OrigFlags = Ret.Flags[0];
+      Ret.Flags.clear();
+
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        ISD::ArgFlagsTy Flags = OrigFlags;
+        if (Part == 0) {
+          Flags.setSplit();
+        } else {
+          Flags.setOrigAlign(Align(1));
+          if (Part == NumParts - 1)
+            Flags.setSplitEnd();
+        }
+
+        Ret.Flags.push_back(Flags);
+      }
+
+      Ret.OrigRegs.assign(Ret.Regs.begin(), Ret.Regs.end());
+      if (NumParts != 1 || OrigVT != NewVT) {
+        // If we can't directly assign the register, we need one or more
+        // intermediate values.
+        Ret.Regs.resize(NumParts);
+
+        // For each split register, create and assign a vreg that will store
+        // the incoming component of the larger value. These will later be
+        // merged to form the final vreg.
+        for (unsigned Part = 0; Part < NumParts; ++Part) {
+          Ret.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+        }
+        buildCopyFromRegs(MIRBuilder, Ret.OrigRegs, Ret.Regs, OrigLLT, NewLLT,
+                          Ret.Flags[0]);
+      }
+
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        CallInst.addDef(Ret.Regs[Part]);
+      }
+    }
+  }
+
+  if (!Info.CanLowerReturn) {
+    insertSRetLoads(MIRBuilder, Info.OrigRet.Ty, Info.OrigRet.Regs,
+                    Info.DemoteRegister, Info.DemoteStackIndex);
+
+    for (auto Reg : Info.OrigRet.Regs) {
+      CallInst.addDef(Reg);
+    }
+  }
+
+  return true;
 }

>From 4118dfe9d4eed05ad30af9b58ee9d3a8149af341 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:41:11 -0700
Subject: [PATCH 03/17] Fix formatting

---
 .../GISel/WebAssemblyCallLowering.cpp         | 128 ++++++++++--------
 1 file changed, 69 insertions(+), 59 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 8956932b403ef..23a6274e66661 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -697,7 +697,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   LLVMContext &Ctx = MIRBuilder.getContext();
   const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
   MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
-  const WebAssemblySubtarget &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+  const WebAssemblySubtarget &Subtarget =
+      MF.getSubtarget<WebAssemblySubtarget>();
 
   CallingConv::ID CallConv = Info.CallConv;
   if (!callingConvSupported(CallConv)) {
@@ -716,7 +717,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     */
 
   if (Info.IsTailCall) {
-      Info.LoweredTailCall = true;
+    Info.LoweredTailCall = true;
     auto NoTail = [&](const char *Msg) {
       if (Info.CB && Info.CB->isMustTailCall())
         fail(MIRBuilder, Msg);
@@ -773,65 +774,73 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   Register IndirectIdx;
 
   if (Info.Callee.isReg()) {
-      LLT CalleeType = MRI.getType(Info.Callee.getReg());
-      assert(CalleeType.isPointer() && "Trying to lower a call with a Callee other than a pointer???");
-
-      IsIndirect = true;
-      CallInst = MIRBuilder.buildInstrNoInsert(Info.LoweredTailCall ? WebAssembly::RET_CALL_INDIRECT : WebAssembly::CALL_INDIRECT);
-
-      // Placeholder for the type index.
-      // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp
-      CallInst.addImm(0);
-
-      MCSymbolWasm *Table;
-      if (CalleeType.getAddressSpace() == WebAssembly::WASM_ADDRESS_SPACE_DEFAULT) {
-          Table = WebAssembly::getOrCreateFunctionTableSymbol(
-                MF.getContext(), &Subtarget);
-          IndirectIdx = Info.Callee.getReg();
-
-          auto PtrSize = CalleeType.getSizeInBits();
-          auto PtrIntLLT = LLT::scalar(PtrSize);
-
-          IndirectIdx = MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
-          if (PtrSize > 32) {
-              IndirectIdx = MIRBuilder.buildTrunc(LLT::scalar(32), IndirectIdx).getReg(0);
-          }
-      } else if (CalleeType.getAddressSpace() == WebAssembly::WASM_ADDRESS_SPACE_FUNCREF) {
-          Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(
-                MF.getContext(), &Subtarget);
-
-          auto TableSetInstr = MIRBuilder.buildInstr(WebAssembly::TABLE_SET_FUNCREF);
-          TableSetInstr.addSym(Table);
-          TableSetInstr.addUse(Info.Callee.getReg());
-          IndirectIdx = MIRBuilder.buildConstant(LLT::scalar(32), 0).getReg(0);
-      } else {
-          fail(MIRBuilder, "Invalid address space for indirect call");
-          return false;
+    LLT CalleeType = MRI.getType(Info.Callee.getReg());
+    assert(CalleeType.isPointer() &&
+           "Trying to lower a call with a Callee other than a pointer???");
+
+    IsIndirect = true;
+    CallInst = MIRBuilder.buildInstrNoInsert(
+        Info.LoweredTailCall ? WebAssembly::RET_CALL_INDIRECT
+                             : WebAssembly::CALL_INDIRECT);
+
+    // Placeholder for the type index.
+    // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp
+    CallInst.addImm(0);
+
+    MCSymbolWasm *Table;
+    if (CalleeType.getAddressSpace() ==
+        WebAssembly::WASM_ADDRESS_SPACE_DEFAULT) {
+      Table = WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(),
+                                                          &Subtarget);
+      IndirectIdx = Info.Callee.getReg();
+
+      auto PtrSize = CalleeType.getSizeInBits();
+      auto PtrIntLLT = LLT::scalar(PtrSize);
+
+      IndirectIdx = MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
+      if (PtrSize > 32) {
+        IndirectIdx =
+            MIRBuilder.buildTrunc(LLT::scalar(32), IndirectIdx).getReg(0);
       }
+    } else if (CalleeType.getAddressSpace() ==
+               WebAssembly::WASM_ADDRESS_SPACE_FUNCREF) {
+      Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(MF.getContext(),
+                                                             &Subtarget);
+
+      auto TableSetInstr =
+          MIRBuilder.buildInstr(WebAssembly::TABLE_SET_FUNCREF);
+      TableSetInstr.addSym(Table);
+      TableSetInstr.addUse(Info.Callee.getReg());
+      IndirectIdx = MIRBuilder.buildConstant(LLT::scalar(32), 0).getReg(0);
+    } else {
+      fail(MIRBuilder, "Invalid address space for indirect call");
+      return false;
+    }
 
-      if (Subtarget.hasCallIndirectOverlong()) {
-        CallInst.addSym(Table);
-      } else {
-        // For the MVP there is at most one table whose number is 0, but we can't
-        // write a table symbol or issue relocations.  Instead we just ensure the
-        // table is live and write a zero.
-        Table->setNoStrip();
-        CallInst.addImm(0);
-      }
+    if (Subtarget.hasCallIndirectOverlong()) {
+      CallInst.addSym(Table);
+    } else {
+      // For the MVP there is at most one table whose number is 0, but we can't
+      // write a table symbol or issue relocations.  Instead we just ensure the
+      // table is live and write a zero.
+      Table->setNoStrip();
+      CallInst.addImm(0);
+    }
   } else {
-      CallInst = MIRBuilder.buildInstrNoInsert(Info.LoweredTailCall ? WebAssembly::RET_CALL : WebAssembly::CALL);
-
-      if (Info.Callee.isGlobal()) {
-          CallInst.addGlobalAddress(Info.Callee.getGlobal());
-      } else if (Info.Callee.isSymbol()) {
-          // TODO: figure out how to trigger/test this
-          CallInst.addSym(Info.Callee.getMCSymbol());
-      } else {
-          llvm_unreachable("Trying to lower call with a callee other than reg, global, or a symbol.");
-      }
+    CallInst = MIRBuilder.buildInstrNoInsert(
+        Info.LoweredTailCall ? WebAssembly::RET_CALL : WebAssembly::CALL);
+
+    if (Info.Callee.isGlobal()) {
+      CallInst.addGlobalAddress(Info.Callee.getGlobal());
+    } else if (Info.Callee.isSymbol()) {
+      // TODO: figure out how to trigger/test this
+      CallInst.addSym(Info.Callee.getMCSymbol());
+    } else {
+      llvm_unreachable("Trying to lower call with a callee other than reg, "
+                       "global, or a symbol.");
+    }
   }
 
-
   SmallVector<ArgInfo, 8> SplitArgs;
 
   bool HasSwiftErrorArg = false;
@@ -1028,8 +1037,9 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
 
         Register DstPtr =
             MIRBuilder
-                .buildPtrAdd(PtrLLT, VarArgStackPtr,
-                             MIRBuilder.buildConstant(SizeLLT, Offset).getReg(0))
+                .buildPtrAdd(
+                    PtrLLT, VarArgStackPtr,
+                    MIRBuilder.buildConstant(SizeLLT, Offset).getReg(0))
                 .getReg(0);
 
         MachineMemOperand *DstMMO = MF.getMachineMemOperand(
@@ -1053,7 +1063,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   MIRBuilder.insertInstr(CallInst);
 
   if (Info.LoweredTailCall) {
-      return true;
+    return true;
   }
 
   if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {

>From aadac679f7af01c31ec3e9159903abd070792489 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:41:18 -0700
Subject: [PATCH 04/17] Fix some issues with WebAssemblyCallLowering::lowerCall

---
 .../GISel/WebAssemblyCallLowering.cpp         | 27 +++++++++++++------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 23a6274e66661..23533c5ad1c75 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -798,10 +798,6 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
       auto PtrIntLLT = LLT::scalar(PtrSize);
 
       IndirectIdx = MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
-      if (PtrSize > 32) {
-        IndirectIdx =
-            MIRBuilder.buildTrunc(LLT::scalar(32), IndirectIdx).getReg(0);
-      }
     } else if (CalleeType.getAddressSpace() ==
                WebAssembly::WASM_ADDRESS_SPACE_FUNCREF) {
       Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(MF.getContext(),
@@ -833,8 +829,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     if (Info.Callee.isGlobal()) {
       CallInst.addGlobalAddress(Info.Callee.getGlobal());
     } else if (Info.Callee.isSymbol()) {
-      // TODO: figure out how to trigger/test this
-      CallInst.addSym(Info.Callee.getMCSymbol());
+      CallInst.addExternalSymbol(Info.Callee.getSymbolName());
     } else {
       llvm_unreachable("Trying to lower call with a callee other than reg, "
                        "global, or a symbol.");
@@ -1078,8 +1073,24 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     for (auto SplitEVT : SplitEVTs) {
       Register CurVReg = Info.OrigRet.Regs[i];
       ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
-      setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, *Info.CB);
-
+      if (Info.CB) {
+        setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, *Info.CB);
+      } else {
+        // we don't have a call base, so chances are we're looking at a libcall
+        // (external symbol).
+
+        // TODO: figure out how to get ALL the correct attributes
+        auto &Flags = CurArgInfo.Flags[0];
+        PointerType *PtrTy =
+            dyn_cast<PointerType>(CurArgInfo.Ty->getScalarType());
+        if (PtrTy) {
+          Flags.setPointer();
+          Flags.setPointerAddrSpace(PtrTy->getPointerAddressSpace());
+        }
+        Align MemAlign = DL.getABITypeAlign(CurArgInfo.Ty);
+        Flags.setMemAlign(MemAlign);
+        Flags.setOrigAlign(MemAlign);
+      }
       splitToValueTypes(CurArgInfo, SplitReturns, DL, CallConv);
       ++i;
     }

>From 1a8f429b5c9b265406a63b4a72a0d136a00876fd Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:41:44 -0700
Subject: [PATCH 05/17] Attempt to make CallLowering floating-point aware (use
 FPEXT and FPTRUNC instead of integer ANYEXT/TRUNC)

---
 .../GISel/WebAssemblyCallLowering.cpp         | 29 ++++++++++++++-----
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 23533c5ad1c75..3dd928a825995 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -29,6 +29,7 @@
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineMemOperand.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
 #include "llvm/CodeGenTypes/LowLevelType.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/DataLayout.h"
@@ -108,9 +109,12 @@ mergeVectorRegsToResultRegs(MachineIRBuilder &B, ArrayRef<Register> DstRegs,
 /// typed values to the original IR value. \p OrigRegs contains the destination
 /// value registers of type \p LLTy, and \p Regs contains the legalized pieces
 /// with type \p PartLLT. This is used for incoming values (physregs to vregs).
+
+// Modified to account for floating-point extends/truncations
 static void buildCopyFromRegs(MachineIRBuilder &B, ArrayRef<Register> OrigRegs,
                               ArrayRef<Register> Regs, LLT LLTy, LLT PartLLT,
-                              const ISD::ArgFlagsTy Flags) {
+                              const ISD::ArgFlagsTy Flags,
+                              bool IsFloatingPoint) {
   MachineRegisterInfo &MRI = *B.getMRI();
 
   if (PartLLT == LLTy) {
@@ -153,7 +157,10 @@ static void buildCopyFromRegs(MachineIRBuilder &B, ArrayRef<Register> OrigRegs,
       return;
     }
 
-    B.buildTrunc(OrigRegs[0], SrcReg);
+    if (IsFloatingPoint)
+      B.buildFPTrunc(OrigRegs[0], SrcReg);
+    else
+      B.buildTrunc(OrigRegs[0], SrcReg);
     return;
   }
 
@@ -166,7 +173,11 @@ static void buildCopyFromRegs(MachineIRBuilder &B, ArrayRef<Register> OrigRegs,
       B.buildMergeValues(OrigRegs[0], Regs);
     else {
       auto Widened = B.buildMergeLikeInstr(LLT::scalar(SrcSize), Regs);
-      B.buildTrunc(OrigRegs[0], Widened);
+
+      if (IsFloatingPoint)
+        B.buildFPTrunc(OrigRegs[0], Widened);
+      else
+        B.buildTrunc(OrigRegs[0], Widened);
     }
 
     return;
@@ -496,7 +507,9 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
           Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
         }
         buildCopyToRegs(MIRBuilder, Arg.Regs, Arg.OrigRegs[0], OrigLLT, NewLLT,
-                        extendOpFromFlags(Arg.Flags[0]));
+                        Arg.Ty->isFloatingPointTy()
+                            ? TargetOpcode::G_FPEXT
+                            : extendOpFromFlags(Arg.Flags[0]));
       }
 
       for (unsigned Part = 0; Part < NumParts; ++Part) {
@@ -630,7 +643,7 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
         Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
       }
       buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT,
-                        Arg.Flags[0]);
+                        Arg.Flags[0], Arg.Ty->isFloatingPointTy());
     }
 
     for (unsigned Part = 0; Part < NumParts; ++Part) {
@@ -955,7 +968,9 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
       }
 
       buildCopyToRegs(MIRBuilder, Arg.Regs, Arg.OrigRegs[0], OrigLLT, NewLLT,
-                      extendOpFromFlags(Arg.Flags[0]));
+                      Arg.Ty->isFloatingPointTy()
+                          ? TargetOpcode::G_FPEXT
+                          : extendOpFromFlags(Arg.Flags[0]));
     }
 
     if (!Arg.Flags[0].isVarArg()) {
@@ -1135,7 +1150,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
           Ret.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
         }
         buildCopyFromRegs(MIRBuilder, Ret.OrigRegs, Ret.Regs, OrigLLT, NewLLT,
-                          Ret.Flags[0]);
+                          Ret.Flags[0], Ret.Ty->isFloatingPointTy());
       }
 
       for (unsigned Part = 0; Part < NumParts; ++Part) {

>From 63fc5302c7de961be21cd003a267e1d14dc7392b Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:41:53 -0700
Subject: [PATCH 06/17] Fix lowerCall vararg crash.

---
 llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 3dd928a825995..7ee118387bfe4 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -976,8 +976,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     if (!Arg.Flags[0].isVarArg()) {
       for (unsigned Part = 0; Part < NumParts; ++Part) {
         CallInst.addUse(Arg.Regs[Part]);
-        ++NumFixedArgs;
       }
+      ++NumFixedArgs;
     }
   }
 

>From 1814899b8f0ef06d974dfca6e0d128458cf90975 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:42:03 -0700
Subject: [PATCH 07/17] Set up basic legalization (scalar only, limited support
 for FP, p0 only)

---
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 256 ++++++++++++++++++
 .../GISel/WebAssemblyLegalizerInfo.h          |   2 +
 2 files changed, 258 insertions(+)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 3acdabb5612cc..c6cd1c5b371e9 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -11,6 +11,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblyLegalizerInfo.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/IR/DerivedTypes.h"
 
 #define DEBUG_TYPE "wasm-legalinfo"
 
@@ -19,5 +25,255 @@ using namespace LegalizeActions;
 
 WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
     const WebAssemblySubtarget &ST) {
+  using namespace TargetOpcode;
+  const LLT s8 = LLT::scalar(8);
+  const LLT s16 = LLT::scalar(16);
+  const LLT s32 = LLT::scalar(32);
+  const LLT s64 = LLT::scalar(64);
+
+  const LLT p0 = LLT::pointer(0, ST.hasAddr64() ? 64 : 32);
+  const LLT p0s = LLT::scalar(ST.hasAddr64() ? 64 : 32);
+
+  getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
+
+  getActionDefinitionsBuilder(G_PHI)
+      .legalFor({p0, s32, s64})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+  getActionDefinitionsBuilder(G_BR).alwaysLegal();
+  getActionDefinitionsBuilder(G_BRCOND).legalFor({s32}).clampScalar(0, s32,
+                                                                    s32);
+  getActionDefinitionsBuilder(G_BRJT)
+      .legalFor({{p0, s32}})
+      .clampScalar(1, s32, s32);
+
+  getActionDefinitionsBuilder(G_SELECT)
+      .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64)
+      .clampScalar(1, s32, s32);
+
+  getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
+
+  getActionDefinitionsBuilder(G_ICMP)
+      .legalFor({{s32, s32}, {s32, s64}, {s32, p0}})
+      .widenScalarToNextPow2(1)
+      .clampScalar(1, s32, s64)
+      .clampScalar(0, s32, s32);
+
+  getActionDefinitionsBuilder(G_FCMP)
+      .legalFor({{s32, s32}, {s32, s64}})
+      .clampScalar(0, s32, s32)
+      .libcall();
+
+  getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
+
+  getActionDefinitionsBuilder(G_CONSTANT)
+      .legalFor({s32, s64, p0})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder(G_FCONSTANT)
+      .legalFor({s32, s64})
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder(G_IMPLICIT_DEF)
+      .legalFor({s32, s64, p0})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder(
+      {G_ADD, G_SUB, G_MUL, G_UDIV, G_SDIV, G_UREM, G_SREM})
+      .legalFor({s32, s64})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL, G_CTLZ, G_CTLZ_ZERO_UNDEF,
+                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_FSHL,
+                               G_FSHR})
+      .legalFor({{s32, s32}, {s64, s64}})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64)
+      .minScalarSameAs(1, 0)
+      .maxScalarSameAs(1, 0);
+
+  getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
+
+  getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
+      .legalFor({s32, s64})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_UMIN, G_UMAX, G_SMIN, G_SMAX}).lower();
+
+  getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FDIV, G_FMUL, G_FNEG, G_FABS,
+                               G_FCEIL, G_FFLOOR, G_FSQRT, G_INTRINSIC_TRUNC,
+                               G_FNEARBYINT, G_FRINT, G_INTRINSIC_ROUNDEVEN,
+                               G_FMINIMUM, G_FMAXIMUM})
+      .legalFor({s32, s64})
+      .minScalar(0, s32);
+
+  // TODO: _IEEE not lowering correctly?
+  getActionDefinitionsBuilder(
+      {G_FMINNUM, G_FMAXNUM, G_FMINNUM_IEEE, G_FMAXNUM_IEEE})
+      .lowerFor({s32, s64})
+      .minScalar(0, s32);
+
+  getActionDefinitionsBuilder({G_FMA, G_FREM})
+      .libcallFor({s32, s64})
+      .minScalar(0, s32);
+
+  getActionDefinitionsBuilder(G_FCOPYSIGN)
+      .legalFor({s32, s64})
+      .minScalar(0, s32)
+      .minScalarSameAs(1, 0)
+      .maxScalarSameAs(1, 0);
+
+  getActionDefinitionsBuilder({G_FPTOUI, G_FPTOUI_SAT, G_FPTOSI, G_FPTOSI_SAT})
+      .legalForCartesianProduct({s32, s64}, {s32, s64})
+      .minScalar(1, s32)
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_UITOFP, G_SITOFP})
+      .legalForCartesianProduct({s32, s64}, {s32, s64})
+      .minScalar(1, s32)
+      .widenScalarToNextPow2(1)
+      .clampScalar(1, s32, s64);
+
+  getActionDefinitionsBuilder(G_PTRTOINT).legalFor({{p0s, p0}});
+  getActionDefinitionsBuilder(G_INTTOPTR).legalFor({{p0, p0s}});
+  getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, p0s}});
+
+  getActionDefinitionsBuilder(G_LOAD)
+      .legalForTypesWithMemDesc(
+          {{s32, p0, s32, 1}, {s64, p0, s64, 1}, {p0, p0, p0, 1}})
+      .legalForTypesWithMemDesc({{s32, p0, s8, 1},
+                                 {s32, p0, s16, 1},
+
+                                 {s64, p0, s8, 1},
+                                 {s64, p0, s16, 1},
+                                 {s64, p0, s32, 1}})
+      .widenScalarToNextPow2(0)
+      .lowerIfMemSizeNotByteSizePow2()
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder(G_STORE)
+      .legalForTypesWithMemDesc(
+          {{s32, p0, s32, 1}, {s64, p0, s64, 1}, {p0, p0, p0, 1}})
+      .legalForTypesWithMemDesc({{s32, p0, s8, 1},
+                                 {s32, p0, s16, 1},
+
+                                 {s64, p0, s8, 1},
+                                 {s64, p0, s16, 1},
+                                 {s64, p0, s32, 1}})
+      .widenScalarToNextPow2(0)
+      .lowerIfMemSizeNotByteSizePow2()
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
+      .legalForTypesWithMemDesc({{s32, p0, s8, 1},
+                                 {s32, p0, s16, 1},
+
+                                 {s64, p0, s8, 1},
+                                 {s64, p0, s16, 1},
+                                 {s64, p0, s32, 1}})
+      .widenScalarToNextPow2(0)
+      .lowerIfMemSizeNotByteSizePow2()
+      .clampScalar(0, s32, s64)
+      .lower();
+
+  if (ST.hasBulkMemoryOpt()) {
+    getActionDefinitionsBuilder(G_BZERO).unsupported();
+
+    getActionDefinitionsBuilder(G_MEMSET)
+        .legalForCartesianProduct({p0}, {s32}, {p0s})
+        .customForCartesianProduct({p0}, {s8}, {p0s})
+        .immIdx(0);
+
+    getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
+        .legalForCartesianProduct({p0}, {p0}, {p0s})
+        .immIdx(0);
+
+    getActionDefinitionsBuilder(G_MEMCPY_INLINE)
+        .legalForCartesianProduct({p0}, {p0}, {p0s});
+  } else {
+    getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
+        .libcall();
+  }
+
+  // TODO: figure out how to combine G_ANYEXT of G_ASSERT_{S|Z}EXT (or
+  // appropriate G_AND and G_SEXT_IN_REG?) to a G_{S|Z}EXT + G_ASSERT_{S|Z}EXT
+  // for better optimization (since G_ANYEXT lowers to a ZEXT or SEXT
+  // instruction anyway).
+
+  getActionDefinitionsBuilder(G_ANYEXT)
+      .legalFor({{s64, s32}})
+      .clampScalar(0, s32, s64)
+      .clampScalar(1, s32, s64);
+
+  getActionDefinitionsBuilder({G_SEXT, G_ZEXT})
+      .legalFor({{s64, s32}})
+      .clampScalar(0, s32, s64)
+      .clampScalar(1, s32, s64)
+      .lower();
+
+  if (ST.hasSignExt()) {
+    getActionDefinitionsBuilder(G_SEXT_INREG)
+        .clampScalar(0, s32, s64)
+        .customFor({s32, s64})
+        .lower();
+  } else {
+    getActionDefinitionsBuilder(G_SEXT_INREG).lower();
+  }
+
+  getActionDefinitionsBuilder(G_TRUNC)
+      .legalFor({{s32, s64}})
+      .clampScalar(0, s32, s64)
+      .clampScalar(1, s32, s64)
+      .lower();
+
+  getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}});
+
+  getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}});
+
+  getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
+  getActionDefinitionsBuilder(G_VAARG)
+      .legalForCartesianProduct({s32, s64}, {p0})
+      .clampScalar(0, s32, s64);
+
   getLegacyLegalizerInfo().computeTables();
 }
+
+bool WebAssemblyLegalizerInfo::legalizeCustom(
+    LegalizerHelper &Helper, MachineInstr &MI,
+    LostDebugLocObserver &LocObserver) const {
+  switch (MI.getOpcode()) {
+  case TargetOpcode::G_SEXT_INREG: {
+    // Mark only 8/16/32-bit SEXT_INREG as legal
+    auto [DstType, SrcType] = MI.getFirst2LLTs();
+    auto ExtFromWidth = MI.getOperand(2).getImm();
+
+    if (ExtFromWidth == 8 || ExtFromWidth == 16 ||
+        (DstType.getScalarSizeInBits() == 64 && ExtFromWidth == 32)) {
+      return true;
+    }
+    return false;
+  }
+  case TargetOpcode::G_MEMSET: {
+    // Anyext the value being set to 32 bit (only the bottom 8 bits are read by
+    // the instruction).
+    Helper.Observer.changingInstr(MI);
+    auto &Value = MI.getOperand(1);
+
+    Register ExtValueReg =
+        Helper.MIRBuilder.buildAnyExt(LLT::scalar(32), Value).getReg(0);
+    Value.setReg(ExtValueReg);
+    Helper.Observer.changedInstr(MI);
+    return true;
+  }
+  default:
+    break;
+  }
+  return false;
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
index c02205fc7ae0d..5aca23c9514e1 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
@@ -24,6 +24,8 @@ class WebAssemblySubtarget;
 class WebAssemblyLegalizerInfo : public LegalizerInfo {
 public:
   WebAssemblyLegalizerInfo(const WebAssemblySubtarget &ST);
+
+  bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, LostDebugLocObserver &LocObserver) const override;
 };
 } // namespace llvm
 #endif

>From f70db5155e05557bdfd0c4e906bcdb1ee543ddd1 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sun, 28 Sep 2025 22:42:32 -0700
Subject: [PATCH 08/17] start on regbankselect

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   1 +
 .../GISel/WebAssemblyCallLowering.cpp         | 299 ++++++++++-------
 .../GISel/WebAssemblyRegisterBankInfo.cpp     | 302 ++++++++++++++++++
 .../GISel/WebAssemblyRegisterBankInfo.h       |  40 +++
 llvm/lib/Target/WebAssembly/WebAssembly.td    |   1 +
 .../WebAssembly/WebAssemblyRegisterBanks.td   |  20 ++
 .../WebAssembly/WebAssemblySubtarget.cpp      |   4 +-
 7 files changed, 549 insertions(+), 118 deletions(-)
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 371d224efc1c5..e573582509263 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -9,6 +9,7 @@ tablegen(LLVM WebAssemblyGenDisassemblerTables.inc -gen-disassembler)
 tablegen(LLVM WebAssemblyGenFastISel.inc -gen-fast-isel)
 tablegen(LLVM WebAssemblyGenInstrInfo.inc -gen-instr-info)
 tablegen(LLVM WebAssemblyGenMCCodeEmitter.inc -gen-emitter)
+tablegen(LLVM WebAssemblyGenRegisterBank.inc -gen-register-bank)
 tablegen(LLVM WebAssemblyGenRegisterInfo.inc -gen-register-info)
 tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 7ee118387bfe4..7f3c7e62ce02d 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -13,10 +13,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblyCallLowering.h"
+#include "GISel/WebAssemblyRegisterBankInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
 #include "Utils/WasmAddressSpaces.h"
 #include "WebAssemblyISelLowering.h"
 #include "WebAssemblyMachineFunctionInfo.h"
+#include "WebAssemblyRegisterInfo.h"
 #include "WebAssemblySubtarget.h"
 #include "WebAssemblyUtilities.h"
 #include "llvm/Analysis/MemoryLocation.h"
@@ -25,6 +27,7 @@
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
 #include "llvm/CodeGen/GlobalISel/CallLowering.h"
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
 #include "llvm/CodeGen/LowLevelTypeUtils.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -435,6 +438,12 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
                                           FunctionLoweringInfo &FLI,
                                           Register SwiftErrorVReg) const {
   auto MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
+  MachineFunction &MF = MIRBuilder.getMF();
+  auto &TLI = *getTLI<WebAssemblyTargetLowering>();
+  auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+  auto &TRI = *Subtarget.getRegisterInfo();
+  auto &TII = *Subtarget.getInstrInfo();
+  auto &RBI = *Subtarget.getRegBankInfo();
 
   assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
          "Return value without a vreg");
@@ -513,7 +522,11 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
       }
 
       for (unsigned Part = 0; Part < NumParts; ++Part) {
-        MIB.addUse(Arg.Regs[Part]);
+        auto NewOutReg = constrainRegToClass(MRI, TII, RBI, Arg.Regs[Part],
+                                             *TLI.getRegClassFor(NewVT));
+        if (NewOutReg != Arg.Regs[Part])
+          MIRBuilder.buildCopy(NewOutReg, Arg.Regs[Part]);
+        MIB.addUse(NewOutReg);
       }
     }
   }
@@ -564,6 +577,11 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
   WebAssemblyFunctionInfo *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
   const DataLayout &DL = F.getDataLayout();
   auto &TLI = *getTLI<WebAssemblyTargetLowering>();
+  auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+  auto &TRI = *Subtarget.getRegisterInfo();
+  auto &TII = *Subtarget.getInstrInfo();
+  auto &RBI = *Subtarget.getRegBankInfo();
+
   LLVMContext &Ctx = MIRBuilder.getContext();
   const CallingConv::ID CallConv = F.getCallingConv();
 
@@ -647,9 +665,12 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
     }
 
     for (unsigned Part = 0; Part < NumParts; ++Part) {
-      MIRBuilder.buildInstr(getWASMArgOpcode(NewVT))
-          .addDef(Arg.Regs[Part])
-          .addImm(FinalArgIdx);
+      auto ArgInst = MIRBuilder.buildInstr(getWASMArgOpcode(NewVT))
+                         .addDef(Arg.Regs[Part])
+                         .addImm(FinalArgIdx);
+
+      constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst,
+                               ArgInst->getDesc(), ArgInst->getOperand(0), 0);
       MFI->addParam(NewVT);
       ++FinalArgIdx;
     }
@@ -712,6 +733,9 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
   const WebAssemblySubtarget &Subtarget =
       MF.getSubtarget<WebAssemblySubtarget>();
+  auto &TRI = *Subtarget.getRegisterInfo();
+  auto &TII = *Subtarget.getInstrInfo();
+  auto &RBI = *Subtarget.getRegBankInfo();
 
   CallingConv::ID CallConv = Info.CallConv;
   if (!callingConvSupported(CallConv)) {
@@ -781,21 +805,128 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     }
   }
 
+  if (Info.LoweredTailCall) {
+    MF.getFrameInfo().setHasTailCall();
+  }
+
   MachineInstrBuilder CallInst;
 
   bool IsIndirect = false;
   Register IndirectIdx;
 
+  if (Info.Callee.isReg()) {
+    IsIndirect = true;
+    CallInst = MIRBuilder.buildInstr(Info.LoweredTailCall
+                                         ? WebAssembly::RET_CALL_INDIRECT
+                                         : WebAssembly::CALL_INDIRECT);
+  } else {
+    CallInst = MIRBuilder.buildInstr(
+        Info.LoweredTailCall ? WebAssembly::RET_CALL : WebAssembly::CALL);
+  }
+
+  if (!Info.LoweredTailCall) {
+    if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {
+      SmallVector<EVT, 4> SplitEVTs;
+      ComputeValueVTs(TLI, DL, Info.OrigRet.Ty, SplitEVTs);
+      assert(Info.OrigRet.Regs.size() == SplitEVTs.size() &&
+             "For each split Type there should be exactly one VReg.");
+
+      SmallVector<ArgInfo, 8> SplitReturns;
+
+      unsigned i = 0;
+      for (auto SplitEVT : SplitEVTs) {
+        Register CurVReg = Info.OrigRet.Regs[i];
+        ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
+        if (Info.CB) {
+          setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, *Info.CB);
+        } else {
+          // we don't have a call base, so chances are we're looking at a
+          // libcall (external symbol).
+
+          // TODO: figure out how to get ALL the correct attributes
+          auto &Flags = CurArgInfo.Flags[0];
+          PointerType *PtrTy =
+              dyn_cast<PointerType>(CurArgInfo.Ty->getScalarType());
+          if (PtrTy) {
+            Flags.setPointer();
+            Flags.setPointerAddrSpace(PtrTy->getPointerAddressSpace());
+          }
+          Align MemAlign = DL.getABITypeAlign(CurArgInfo.Ty);
+          Flags.setMemAlign(MemAlign);
+          Flags.setOrigAlign(MemAlign);
+        }
+        splitToValueTypes(CurArgInfo, SplitReturns, DL, CallConv);
+        ++i;
+      }
+
+      for (auto &Ret : SplitReturns) {
+        EVT OrigVT = TLI.getValueType(DL, Ret.Ty);
+        MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+        LLT OrigLLT = getLLTForType(*Ret.Ty, DL);
+        LLT NewLLT = getLLTForMVT(NewVT);
+
+        // If we need to split the type over multiple regs, check it's a
+        // scenario we currently support.
+        unsigned NumParts =
+            TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+        ISD::ArgFlagsTy OrigFlags = Ret.Flags[0];
+        Ret.Flags.clear();
+
+        for (unsigned Part = 0; Part < NumParts; ++Part) {
+          ISD::ArgFlagsTy Flags = OrigFlags;
+          if (Part == 0) {
+            Flags.setSplit();
+          } else {
+            Flags.setOrigAlign(Align(1));
+            if (Part == NumParts - 1)
+              Flags.setSplitEnd();
+          }
+
+          Ret.Flags.push_back(Flags);
+        }
+
+        Ret.OrigRegs.assign(Ret.Regs.begin(), Ret.Regs.end());
+        if (NumParts != 1 || OrigVT != NewVT) {
+          // If we can't directly assign the register, we need one or more
+          // intermediate values.
+          Ret.Regs.resize(NumParts);
+
+          // For each split register, create and assign a vreg that will store
+          // the incoming component of the larger value. These will later be
+          // merged to form the final vreg.
+          for (unsigned Part = 0; Part < NumParts; ++Part) {
+            Ret.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+          }
+          buildCopyFromRegs(MIRBuilder, Ret.OrigRegs, Ret.Regs, OrigLLT, NewLLT,
+                            Ret.Flags[0], Ret.Ty->isFloatingPointTy());
+        }
+
+        for (unsigned Part = 0; Part < NumParts; ++Part) {
+          // MRI.setRegClass(Ret.Regs[Part], TLI.getRegClassFor(NewVT));
+          auto NewRetReg = constrainRegToClass(MRI, TII, RBI, Ret.Regs[Part],
+                                               *TLI.getRegClassFor(NewVT));
+          if (Ret.Regs[Part] != NewRetReg)
+            MIRBuilder.buildCopy(NewRetReg, Ret.Regs[Part]);
+
+          CallInst.addDef(Ret.Regs[Part]);
+        }
+      }
+    }
+
+    if (!Info.CanLowerReturn) {
+      insertSRetLoads(MIRBuilder, Info.OrigRet.Ty, Info.OrigRet.Regs,
+                      Info.DemoteRegister, Info.DemoteStackIndex);
+    }
+  }
+  auto SavedInsertPt = MIRBuilder.getInsertPt();
+  MIRBuilder.setInstr(*CallInst);
+
   if (Info.Callee.isReg()) {
     LLT CalleeType = MRI.getType(Info.Callee.getReg());
     assert(CalleeType.isPointer() &&
            "Trying to lower a call with a Callee other than a pointer???");
 
-    IsIndirect = true;
-    CallInst = MIRBuilder.buildInstrNoInsert(
-        Info.LoweredTailCall ? WebAssembly::RET_CALL_INDIRECT
-                             : WebAssembly::CALL_INDIRECT);
-
     // Placeholder for the type index.
     // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp
     CallInst.addImm(0);
@@ -816,11 +947,25 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
       Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(MF.getContext(),
                                                              &Subtarget);
 
+      Type *PtrTy = PointerType::getUnqual(Ctx);
+      LLT PtrLLT = getLLTForType(*PtrTy, DL);
+      auto PtrIntLLT = LLT::scalar(PtrLLT.getSizeInBits());
+
+      IndirectIdx = MIRBuilder.buildConstant(PtrIntLLT, 0).getReg(0);
+
       auto TableSetInstr =
           MIRBuilder.buildInstr(WebAssembly::TABLE_SET_FUNCREF);
       TableSetInstr.addSym(Table);
+      TableSetInstr.addUse(IndirectIdx);
       TableSetInstr.addUse(Info.Callee.getReg());
-      IndirectIdx = MIRBuilder.buildConstant(LLT::scalar(32), 0).getReg(0);
+
+      constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *TableSetInstr,
+                               TableSetInstr->getDesc(),
+                               TableSetInstr->getOperand(1), 1);
+      constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *TableSetInstr,
+                               TableSetInstr->getDesc(),
+                               TableSetInstr->getOperand(2), 2);
+
     } else {
       fail(MIRBuilder, "Invalid address space for indirect call");
       return false;
@@ -836,9 +981,6 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
       CallInst.addImm(0);
     }
   } else {
-    CallInst = MIRBuilder.buildInstrNoInsert(
-        Info.LoweredTailCall ? WebAssembly::RET_CALL : WebAssembly::CALL);
-
     if (Info.Callee.isGlobal()) {
       CallInst.addGlobalAddress(Info.Callee.getGlobal());
     } else if (Info.Callee.isSymbol()) {
@@ -884,9 +1026,13 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
                                      /*isSS=*/false);
 
       auto StackAddrSpace = DL.getAllocaAddrSpace();
-      auto PtrLLT = getLLTForType(*PointerType::get(Ctx, StackAddrSpace), DL);
+      auto PtrLLT =
+          LLT::pointer(StackAddrSpace, DL.getPointerSizeInBits(StackAddrSpace));
+
       Register StackObjPtrVreg =
           MF.getRegInfo().createGenericVirtualRegister(PtrLLT);
+      MRI.setRegClass(StackObjPtrVreg, TLI.getRepRegClassFor(TLI.getPointerTy(
+                                           DL, StackAddrSpace)));
 
       MIRBuilder.buildFrameIndex(StackObjPtrVreg, FI);
 
@@ -975,6 +1121,10 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
 
     if (!Arg.Flags[0].isVarArg()) {
       for (unsigned Part = 0; Part < NumParts; ++Part) {
+        auto NewArgReg = constrainRegToClass(MRI, TII, RBI, Arg.Regs[Part],
+                                             *TLI.getRegClassFor(NewVT));
+        if (Arg.Regs[Part] != NewArgReg)
+          MIRBuilder.buildCopy(NewArgReg, Arg.Regs[Part]);
         CallInst.addUse(Arg.Regs[Part]);
       }
       ++NumFixedArgs;
@@ -984,12 +1134,17 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   if (CallConv == CallingConv::Swift) {
     Type *PtrTy = PointerType::getUnqual(Ctx);
     LLT PtrLLT = getLLTForType(*PtrTy, DL);
+    auto &PtrRegClass = *TLI.getRegClassFor(TLI.getSimpleValueType(DL, PtrTy));
 
     if (!HasSwiftSelfArg) {
-      CallInst.addUse(MIRBuilder.buildUndef(PtrLLT).getReg(0));
+      auto NewUndefReg = MIRBuilder.buildUndef(PtrLLT).getReg(0);
+      MRI.setRegClass(NewUndefReg, &PtrRegClass);
+      CallInst.addUse(NewUndefReg);
     }
     if (!HasSwiftErrorArg) {
-      CallInst.addUse(MIRBuilder.buildUndef(PtrLLT).getReg(0));
+      auto NewUndefReg = MIRBuilder.buildUndef(PtrLLT).getReg(0);
+      MRI.setRegClass(NewUndefReg, &PtrRegClass);
+      CallInst.addUse(NewUndefReg);
     }
   }
 
@@ -1019,12 +1174,14 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
 
   auto StackAddrSpace = DL.getAllocaAddrSpace();
-  auto PtrLLT = getLLTForType(*PointerType::get(Ctx, StackAddrSpace), DL);
-  auto SizeLLT = LLT::scalar(PtrLLT.getSizeInBits());
+  auto PtrLLT = LLT::pointer(StackAddrSpace, DL.getPointerSizeInBits(0));
+  auto SizeLLT = LLT::scalar(DL.getPointerSizeInBits(StackAddrSpace));
+  auto *PtrRegClass = TLI.getRegClassFor(TLI.getPointerTy(DL, StackAddrSpace));
 
   if (Info.IsVarArg && NumBytes) {
     Register VarArgStackPtr =
         MF.getRegInfo().createGenericVirtualRegister(PtrLLT);
+    MRI.setRegClass(VarArgStackPtr, PtrRegClass);
 
     MaybeAlign StackAlign = DL.getStackAlignment();
     assert(StackAlign && "data layout string is missing stack alignment");
@@ -1063,110 +1220,20 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
 
     CallInst.addUse(VarArgStackPtr);
   } else if (Info.IsVarArg) {
-    CallInst.addUse(MIRBuilder.buildConstant(PtrLLT, 0).getReg(0));
+    auto NewArgReg = MIRBuilder.buildConstant(PtrLLT, 0).getReg(0);
+    MRI.setRegClass(NewArgReg, PtrRegClass);
+    CallInst.addUse(NewArgReg);
   }
 
   if (IsIndirect) {
+    auto NewArgReg =
+        constrainRegToClass(MRI, TII, RBI, IndirectIdx, *PtrRegClass);
+    if (IndirectIdx != NewArgReg)
+      MIRBuilder.buildCopy(NewArgReg, IndirectIdx);
     CallInst.addUse(IndirectIdx);
   }
 
-  MIRBuilder.insertInstr(CallInst);
-
-  if (Info.LoweredTailCall) {
-    return true;
-  }
-
-  if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {
-    SmallVector<EVT, 4> SplitEVTs;
-    ComputeValueVTs(TLI, DL, Info.OrigRet.Ty, SplitEVTs);
-    assert(Info.OrigRet.Regs.size() == SplitEVTs.size() &&
-           "For each split Type there should be exactly one VReg.");
-
-    SmallVector<ArgInfo, 8> SplitReturns;
-
-    unsigned i = 0;
-    for (auto SplitEVT : SplitEVTs) {
-      Register CurVReg = Info.OrigRet.Regs[i];
-      ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
-      if (Info.CB) {
-        setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, *Info.CB);
-      } else {
-        // we don't have a call base, so chances are we're looking at a libcall
-        // (external symbol).
-
-        // TODO: figure out how to get ALL the correct attributes
-        auto &Flags = CurArgInfo.Flags[0];
-        PointerType *PtrTy =
-            dyn_cast<PointerType>(CurArgInfo.Ty->getScalarType());
-        if (PtrTy) {
-          Flags.setPointer();
-          Flags.setPointerAddrSpace(PtrTy->getPointerAddressSpace());
-        }
-        Align MemAlign = DL.getABITypeAlign(CurArgInfo.Ty);
-        Flags.setMemAlign(MemAlign);
-        Flags.setOrigAlign(MemAlign);
-      }
-      splitToValueTypes(CurArgInfo, SplitReturns, DL, CallConv);
-      ++i;
-    }
-
-    for (auto &Ret : SplitReturns) {
-      EVT OrigVT = TLI.getValueType(DL, Ret.Ty);
-      MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
-      LLT OrigLLT = getLLTForType(*Ret.Ty, DL);
-      LLT NewLLT = getLLTForMVT(NewVT);
-
-      // If we need to split the type over multiple regs, check it's a scenario
-      // we currently support.
-      unsigned NumParts =
-          TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
-
-      ISD::ArgFlagsTy OrigFlags = Ret.Flags[0];
-      Ret.Flags.clear();
-
-      for (unsigned Part = 0; Part < NumParts; ++Part) {
-        ISD::ArgFlagsTy Flags = OrigFlags;
-        if (Part == 0) {
-          Flags.setSplit();
-        } else {
-          Flags.setOrigAlign(Align(1));
-          if (Part == NumParts - 1)
-            Flags.setSplitEnd();
-        }
-
-        Ret.Flags.push_back(Flags);
-      }
-
-      Ret.OrigRegs.assign(Ret.Regs.begin(), Ret.Regs.end());
-      if (NumParts != 1 || OrigVT != NewVT) {
-        // If we can't directly assign the register, we need one or more
-        // intermediate values.
-        Ret.Regs.resize(NumParts);
-
-        // For each split register, create and assign a vreg that will store
-        // the incoming component of the larger value. These will later be
-        // merged to form the final vreg.
-        for (unsigned Part = 0; Part < NumParts; ++Part) {
-          Ret.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
-        }
-        buildCopyFromRegs(MIRBuilder, Ret.OrigRegs, Ret.Regs, OrigLLT, NewLLT,
-                          Ret.Flags[0], Ret.Ty->isFloatingPointTy());
-      }
-
-      for (unsigned Part = 0; Part < NumParts; ++Part) {
-        CallInst.addDef(Ret.Regs[Part]);
-      }
-    }
-  }
-
-  if (!Info.CanLowerReturn) {
-    insertSRetLoads(MIRBuilder, Info.OrigRet.Ty, Info.OrigRet.Regs,
-                    Info.DemoteRegister, Info.DemoteStackIndex);
-
-    for (auto Reg : Info.OrigRet.Regs) {
-      CallInst.addDef(Reg);
-    }
-  }
+  MIRBuilder.setInsertPt(MIRBuilder.getMBB(), SavedInsertPt);
 
   return true;
 }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index e69de29bb2d1d..e605c46aece85 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -0,0 +1,302 @@
+#include "WebAssemblyRegisterBankInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "WebAssemblyTargetMachine.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+
+#define GET_TARGET_REGBANK_IMPL
+
+#include "WebAssemblyGenRegisterBank.inc"
+
+namespace llvm {
+namespace WebAssembly {
+enum PartialMappingIdx {
+  PMI_None = -1,
+  PMI_I32 = 1,
+  PMI_I64,
+  PMI_F32,
+  PMI_F64,
+  PMI_Min = PMI_I32,
+};
+
+enum ValueMappingIdx {
+  InvalidIdx = 0,
+  I32Idx = 1,
+  I64Idx = 4,
+  F32Idx = 7,
+  F64Idx = 10
+};
+
+const RegisterBankInfo::PartialMapping PartMappings[]{{0, 32, I32RegBank},
+                                                      {0, 64, I64RegBank},
+                                                      {0, 32, F32RegBank},
+                                                      {0, 64, F64RegBank}};
+
+const RegisterBankInfo::ValueMapping ValueMappings[] = {
+    // invalid
+    {nullptr, 0},
+    // up to 3 operands as I32
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    // up to 3 operands as I64
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    // up to 3 operands as F32
+    {&PartMappings[PMI_F32 - PMI_Min], 1},
+    {&PartMappings[PMI_F32 - PMI_Min], 1},
+    {&PartMappings[PMI_F32 - PMI_Min], 1},
+    // up to 3 operands as F64
+    {&PartMappings[PMI_F64 - PMI_Min], 1},
+    {&PartMappings[PMI_F64 - PMI_Min], 1},
+    {&PartMappings[PMI_F64 - PMI_Min], 1}};
+
+} // namespace WebAssembly
+} // namespace llvm
+
+using namespace llvm;
+
+WebAssemblyRegisterBankInfo::WebAssemblyRegisterBankInfo(
+    const TargetRegisterInfo &TRI) {}
+
+// Instructions where use operands are floating point registers.
+// Def operands are general purpose.
+static bool isFloatingPointOpcodeUse(unsigned Opc) {
+  switch (Opc) {
+  case TargetOpcode::G_FPTOSI:
+  case TargetOpcode::G_FPTOUI:
+  case TargetOpcode::G_FCMP:
+    return true;
+  default:
+    return isPreISelGenericFloatingPointOpcode(Opc);
+  }
+}
+
+// Instructions where def operands are floating point registers.
+// Use operands are general purpose.
+static bool isFloatingPointOpcodeDef(unsigned Opc) {
+  switch (Opc) {
+  case TargetOpcode::G_SITOFP:
+  case TargetOpcode::G_UITOFP:
+    return true;
+  default:
+    return isPreISelGenericFloatingPointOpcode(Opc);
+  }
+}
+
+static bool isAmbiguous(unsigned Opc) {
+  switch (Opc) {
+  case TargetOpcode::G_LOAD:
+  case TargetOpcode::G_STORE:
+  case TargetOpcode::G_PHI:
+  case TargetOpcode::G_SELECT:
+  case TargetOpcode::G_IMPLICIT_DEF:
+  case TargetOpcode::G_UNMERGE_VALUES:
+  case TargetOpcode::G_MERGE_VALUES:
+    return true;
+  default:
+    return false;
+  }
+}
+
+const RegisterBankInfo::InstructionMapping &
+WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
+
+  unsigned Opc = MI.getOpcode();
+  const MachineFunction &MF = *MI.getParent()->getParent();
+  const MachineRegisterInfo &MRI = MF.getRegInfo();
+  const TargetSubtargetInfo &STI = MF.getSubtarget();
+  const TargetRegisterInfo &TRI = *STI.getRegisterInfo();
+
+  if ((Opc != TargetOpcode::COPY && !isPreISelGenericOpcode(Opc)) ||
+      Opc == TargetOpcode::G_PHI) {
+    const RegisterBankInfo::InstructionMapping &Mapping =
+        getInstrMappingImpl(MI);
+    if (Mapping.isValid())
+      return Mapping;
+  }
+
+  using namespace TargetOpcode;
+
+  unsigned NumOperands = MI.getNumOperands();
+  const ValueMapping *OperandsMapping = nullptr;
+  unsigned MappingID = DefaultMappingID;
+
+  // Check if LLT sizes match sizes of available register banks.
+  for (const MachineOperand &Op : MI.operands()) {
+    if (Op.isReg()) {
+      LLT RegTy = MRI.getType(Op.getReg());
+
+      if (RegTy.isScalar() &&
+          (RegTy.getSizeInBits() != 32 && RegTy.getSizeInBits() != 64))
+        return getInvalidInstructionMapping();
+
+      if (RegTy.isVector() && RegTy.getSizeInBits() != 128)
+        return getInvalidInstructionMapping();
+    }
+  }
+
+  switch (Opc) {
+  case G_BR:
+    return getInstructionMapping(MappingID, /*Cost=*/1,
+                                 getOperandsMapping({nullptr}), NumOperands);
+  case G_TRAP:
+    return getInstructionMapping(MappingID, /*Cost=*/1, nullptr, 0);
+  }
+
+  const LLT Op0Ty = MRI.getType(MI.getOperand(0).getReg());
+  unsigned Op0Size = Op0Ty.getSizeInBits();
+
+  auto &Op0IntValueMapping =
+      WebAssembly::ValueMappings[Op0Size == 64 ? WebAssembly::I64Idx
+                                               : WebAssembly::I32Idx];
+  auto &Op0FloatValueMapping =
+      WebAssembly::ValueMappings[Op0Size == 64 ? WebAssembly::F64Idx
+                                               : WebAssembly::F32Idx];
+  auto &Pointer0ValueMapping =
+      WebAssembly::ValueMappings[MI.getMF()->getDataLayout()
+                                             .getPointerSizeInBits(0) == 64
+                                     ? WebAssembly::I64Idx
+                                     : WebAssembly::I32Idx];
+
+  switch (Opc) {
+  case G_AND:
+  case G_OR:
+  case G_XOR:
+  case G_SHL:
+  case G_ASHR:
+  case G_LSHR:
+  case G_PTR_ADD:
+  case G_INTTOPTR:
+  case G_PTRTOINT:
+  case G_ADD:
+  case G_SUB:
+  case G_MUL:
+  case G_SDIV:
+  case G_SREM:
+  case G_UDIV:
+  case G_UREM:
+    OperandsMapping = &Op0IntValueMapping;
+    break;
+  case G_SEXT_INREG:
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, &Op0IntValueMapping, nullptr});
+    break;
+  case G_FRAME_INDEX:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  case G_ZEXT:
+  case G_ANYEXT:
+  case G_SEXT:
+  case G_TRUNC: {
+    const LLT Op1Ty = MRI.getType(MI.getOperand(1).getReg());
+    unsigned Op1Size = Op1Ty.getSizeInBits();
+
+    auto &Op1IntValueMapping =
+        WebAssembly::ValueMappings[Op1Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, &Op1IntValueMapping});
+    break;
+  }
+  case G_LOAD:
+  case G_STORE:
+    if (MRI.getType(MI.getOperand(1).getReg()).getAddressSpace() != 0)
+      break;
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, &Pointer0ValueMapping});
+    break;
+  case G_MEMCPY:
+  case G_MEMMOVE: {
+    if (MRI.getType(MI.getOperand(0).getReg()).getAddressSpace() != 0)
+      break;
+    if (MRI.getType(MI.getOperand(1).getReg()).getAddressSpace() != 0)
+      break;
+
+    const LLT Op2Ty = MRI.getType(MI.getOperand(2).getReg());
+    unsigned Op2Size = Op2Ty.getSizeInBits();
+    auto &Op2IntValueMapping =
+        WebAssembly::ValueMappings[Op2Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+    OperandsMapping =
+        getOperandsMapping({&Pointer0ValueMapping, &Pointer0ValueMapping,
+                            &Op2IntValueMapping, nullptr});
+    break;
+  }
+  case G_MEMSET: {
+    if (MRI.getType(MI.getOperand(0).getReg()).getAddressSpace() != 0)
+      break;
+    const LLT Op1Ty = MRI.getType(MI.getOperand(1).getReg());
+    unsigned Op1Size = Op1Ty.getSizeInBits();
+    auto &Op1IntValueMapping =
+        WebAssembly::ValueMappings[Op1Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+
+    const LLT Op2Ty = MRI.getType(MI.getOperand(2).getReg());
+    unsigned Op2Size = Op1Ty.getSizeInBits();
+    auto &Op2IntValueMapping =
+        WebAssembly::ValueMappings[Op2Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Pointer0ValueMapping, &Op1IntValueMapping,
+                            &Op2IntValueMapping, nullptr});
+    break;
+  }
+  case G_GLOBAL_VALUE:
+  case G_CONSTANT:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  case G_IMPLICIT_DEF:
+    OperandsMapping = &Op0IntValueMapping;
+    break;
+  case G_ICMP: {
+    const LLT Op2Ty = MRI.getType(MI.getOperand(2).getReg());
+    unsigned Op2Size = Op2Ty.getSizeInBits();
+
+    auto &Op2IntValueMapping =
+        WebAssembly::ValueMappings[Op2Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, nullptr, &Op2IntValueMapping,
+                            &Op2IntValueMapping});
+    break;
+  }
+  case G_BRCOND:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  case COPY: {
+    Register DstReg = MI.getOperand(0).getReg();
+    Register SrcReg = MI.getOperand(1).getReg();
+    // Check if one of the register is not a generic register.
+    if ((DstReg.isPhysical() || !MRI.getType(DstReg).isValid()) ||
+        (SrcReg.isPhysical() || !MRI.getType(SrcReg).isValid())) {
+      const RegisterBank *DstRB = getRegBank(DstReg, MRI, TRI);
+      const RegisterBank *SrcRB = getRegBank(SrcReg, MRI, TRI);
+      if (!DstRB)
+        DstRB = SrcRB;
+      else if (!SrcRB)
+        SrcRB = DstRB;
+      // If both RB are null that means both registers are generic.
+      // We shouldn't be here.
+      assert(DstRB && SrcRB && "Both RegBank were nullptr");
+      TypeSize DstSize = getSizeInBits(DstReg, MRI, TRI);
+      TypeSize SrcSize = getSizeInBits(SrcReg, MRI, TRI);
+      assert(DstSize == SrcSize &&
+             "Trying to copy between different sized regbanks? Why?");
+
+      return getInstructionMapping(
+          DefaultMappingID, copyCost(*DstRB, *SrcRB, DstSize),
+          getCopyMapping(DstRB->getID(), SrcRB->getID(), Size),
+          // We only care about the mapping of the destination.
+          /*NumOperands*/ 1);
+    }
+  }
+  }
+  if (!OperandsMapping)
+    return getInvalidInstructionMapping();
+
+  return getInstructionMapping(MappingID, /*Cost=*/1, OperandsMapping,
+                               NumOperands);
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
index e69de29bb2d1d..f0d95b56ef861 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
@@ -0,0 +1,40 @@
+//===- WebAssemblyRegisterBankInfo.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file declares the targeting of the RegisterBankInfo class for WASM.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERBANKINFO_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERBANKINFO_H
+
+#include "llvm/CodeGen/RegisterBankInfo.h"
+
+#define GET_REGBANK_DECLARATIONS
+#include "WebAssemblyGenRegisterBank.inc"
+
+namespace llvm {
+
+class TargetRegisterInfo;
+
+class WebAssemblyGenRegisterBankInfo : public RegisterBankInfo {
+#define GET_TARGET_REGBANK_CLASS
+#include "WebAssemblyGenRegisterBank.inc"
+};
+
+/// This class provides the information for the target register banks.
+class WebAssemblyRegisterBankInfo final
+    : public WebAssemblyGenRegisterBankInfo {
+public:
+  WebAssemblyRegisterBankInfo(const TargetRegisterInfo &TRI);
+
+  const InstructionMapping &
+  getInstrMapping(const MachineInstr &MI) const override;
+};
+} // end namespace llvm
+#endif
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td
index 089be5f1dc70e..3705a42fd21c9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.td
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.td
@@ -101,6 +101,7 @@ def FeatureWideArithmetic :
 //===----------------------------------------------------------------------===//
 
 include "WebAssemblyRegisterInfo.td"
+include "WebAssemblyRegisterBanks.td"
 
 //===----------------------------------------------------------------------===//
 // Instruction Descriptions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td
new file mode 100644
index 0000000000000..9ebece0e0bf09
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td
@@ -0,0 +1,20 @@
+//=- WebAssemblyRegisterBank.td - Describe the WASM Banks ----*- tablegen -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+
+def I32RegBank : RegisterBank<"I32RegBank", [I32]>;
+def I64RegBank : RegisterBank<"I64RegBank", [I64]>;
+def F32RegBank : RegisterBank<"F64RegBank", [F32]>;
+def F64RegBank : RegisterBank<"F64RegBank", [F64]>;
+
+def EXTERNREFRegBank : RegisterBank<"EXTERNREFRegBank", [EXTERNREF]>;
+def FUNCREFRegBank : RegisterBank<"FUNCREFRegBank", [FUNCREF]>;
+def EXNREFRegBank : RegisterBank<"EXNREFRegBank", [EXNREF]>;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index 3ea8b9f85819f..b99c35acabef6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -73,9 +73,9 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
       TLInfo(TM, *this) {
   CallLoweringInfo.reset(new WebAssemblyCallLowering(*getTargetLowering()));
   Legalizer.reset(new WebAssemblyLegalizerInfo(*this));
-  /*auto *RBI = new WebAssemblyRegisterBankInfo(*getRegisterInfo());
+  auto *RBI = new WebAssemblyRegisterBankInfo(*getRegisterInfo());
   RegBankInfo.reset(RBI);
-
+/*
   InstSelector.reset(createWebAssemblyInstructionSelector(
   *static_cast<const WebAssemblyTargetMachine *>(&TM), *this, *RBI));*/
 }

>From b02a647aabd2fbfdf61b5725743f5f6db418a00c Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 29 Sep 2025 11:23:22 -0700
Subject: [PATCH 09/17] Finish initial pass over regbankselect

---
 .../GISel/WebAssemblyCallLowering.cpp         |  21 +-
 .../GISel/WebAssemblyLegalizerInfo.cpp        |  55 ++--
 .../GISel/WebAssemblyRegisterBankInfo.cpp     | 254 +++++++++++++-----
 3 files changed, 241 insertions(+), 89 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 7f3c7e62ce02d..733d676ac988a 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -33,6 +33,7 @@
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
 #include "llvm/CodeGenTypes/LowLevelType.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/DataLayout.h"
@@ -481,6 +482,7 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
       MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
       LLT OrigLLT = getLLTForType(*Arg.Ty, DL);
       LLT NewLLT = getLLTForMVT(NewVT);
+      const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
 
       // If we need to split the type over multiple regs, check it's a scenario
       // we currently support.
@@ -522,10 +524,12 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
       }
 
       for (unsigned Part = 0; Part < NumParts; ++Part) {
-        auto NewOutReg = constrainRegToClass(MRI, TII, RBI, Arg.Regs[Part],
-                                             *TLI.getRegClassFor(NewVT));
-        if (NewOutReg != Arg.Regs[Part])
+        auto NewOutReg = Arg.Regs[Part];
+        if (!RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI)) {
+          NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
+          assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) && "Couldn't constrain brand-new register?");
           MIRBuilder.buildCopy(NewOutReg, Arg.Regs[Part]);
+        }
         MIB.addUse(NewOutReg);
       }
     }
@@ -864,6 +868,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
         MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
         LLT OrigLLT = getLLTForType(*Ret.Ty, DL);
         LLT NewLLT = getLLTForMVT(NewVT);
+        const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
 
         // If we need to split the type over multiple regs, check it's a
         // scenario we currently support.
@@ -903,12 +908,12 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
         }
 
         for (unsigned Part = 0; Part < NumParts; ++Part) {
-          // MRI.setRegClass(Ret.Regs[Part], TLI.getRegClassFor(NewVT));
-          auto NewRetReg = constrainRegToClass(MRI, TII, RBI, Ret.Regs[Part],
-                                               *TLI.getRegClassFor(NewVT));
-          if (Ret.Regs[Part] != NewRetReg)
+          auto NewRetReg = Ret.Regs[Part];
+          if (!RBI.constrainGenericRegister(NewRetReg, NewRegClass, MRI)) {
+            NewRetReg = MRI.createGenericVirtualRegister(NewLLT);
+            assert(RBI.constrainGenericRegister(NewRetReg, NewRegClass, MRI) && "Couldn't constrain brand-new register?");
             MIRBuilder.buildCopy(NewRetReg, Ret.Regs[Part]);
-
+          }
           CallInst.addDef(Ret.Regs[Part]);
         }
       }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index c6cd1c5b371e9..ae2ac0a512427 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -89,14 +89,17 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .clampScalar(0, s32, s64);
 
   getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL, G_CTLZ, G_CTLZ_ZERO_UNDEF,
-                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_FSHL,
-                               G_FSHR})
+                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP})
       .legalFor({{s32, s32}, {s64, s64}})
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
       .minScalarSameAs(1, 0)
       .maxScalarSameAs(1, 0);
 
+  getActionDefinitionsBuilder({G_FSHL, G_FSHR})
+      .legalFor({{s32, s32}, {s64, s64}})
+      .lower();
+
   getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
 
   getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
@@ -123,6 +126,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .libcallFor({s32, s64})
       .minScalar(0, s32);
 
+  getActionDefinitionsBuilder(G_LROUND).libcallForCartesianProduct({s32},
+                                                                   {s32, s64});
+
+  getActionDefinitionsBuilder(G_LLROUND).libcallForCartesianProduct({s64},
+                                                                    {s32, s64});
+
   getActionDefinitionsBuilder(G_FCOPYSIGN)
       .legalFor({s32, s64})
       .minScalar(0, s32)
@@ -154,9 +163,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                  {s64, p0, s8, 1},
                                  {s64, p0, s16, 1},
                                  {s64, p0, s32, 1}})
-      .widenScalarToNextPow2(0)
-      .lowerIfMemSizeNotByteSizePow2()
-      .clampScalar(0, s32, s64);
+      .clampScalar(0, s32, s64)
+      .lowerIfMemSizeNotByteSizePow2();
 
   getActionDefinitionsBuilder(G_STORE)
       .legalForTypesWithMemDesc(
@@ -167,9 +175,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                  {s64, p0, s8, 1},
                                  {s64, p0, s16, 1},
                                  {s64, p0, s32, 1}})
-      .widenScalarToNextPow2(0)
-      .lowerIfMemSizeNotByteSizePow2()
-      .clampScalar(0, s32, s64);
+      .clampScalar(0, s32, s64)
+      .lowerIfMemSizeNotByteSizePow2();
 
   getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
       .legalForTypesWithMemDesc({{s32, p0, s8, 1},
@@ -178,10 +185,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                  {s64, p0, s8, 1},
                                  {s64, p0, s16, 1},
                                  {s64, p0, s32, 1}})
-      .widenScalarToNextPow2(0)
-      .lowerIfMemSizeNotByteSizePow2()
       .clampScalar(0, s32, s64)
-      .lower();
+      .lowerIfMemSizeNotByteSizePow2();
 
   if (ST.hasBulkMemoryOpt()) {
     getActionDefinitionsBuilder(G_BZERO).unsupported();
@@ -204,7 +209,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   // TODO: figure out how to combine G_ANYEXT of G_ASSERT_{S|Z}EXT (or
   // appropriate G_AND and G_SEXT_IN_REG?) to a G_{S|Z}EXT + G_ASSERT_{S|Z}EXT
-  // for better optimization (since G_ANYEXT lowers to a ZEXT or SEXT
+  // for better optimization (since G_ANYEXT will lower to a ZEXT or SEXT
   // instruction anyway).
 
   getActionDefinitionsBuilder(G_ANYEXT)
@@ -221,8 +226,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
   if (ST.hasSignExt()) {
     getActionDefinitionsBuilder(G_SEXT_INREG)
         .clampScalar(0, s32, s64)
-        .customFor({s32, s64})
-        .lower();
+        .customFor({s32, s64});
   } else {
     getActionDefinitionsBuilder(G_SEXT_INREG).lower();
   }
@@ -242,23 +246,42 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .legalForCartesianProduct({s32, s64}, {p0})
       .clampScalar(0, s32, s64);
 
+  getActionDefinitionsBuilder(G_DYN_STACKALLOC).lowerFor({{p0, p0s}});
+
+  getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
+
   getLegacyLegalizerInfo().computeTables();
 }
 
 bool WebAssemblyLegalizerInfo::legalizeCustom(
     LegalizerHelper &Helper, MachineInstr &MI,
     LostDebugLocObserver &LocObserver) const {
+  auto &MRI = *Helper.MIRBuilder.getMRI();
+  auto &MIRBuilder = Helper.MIRBuilder;
+
   switch (MI.getOpcode()) {
   case TargetOpcode::G_SEXT_INREG: {
+    assert(MI.getOperand(2).isImm() && "Expected immediate");
+
     // Mark only 8/16/32-bit SEXT_INREG as legal
-    auto [DstType, SrcType] = MI.getFirst2LLTs();
+    auto [DstReg, SrcReg] = MI.getFirst2Regs();
+    auto DstType = MRI.getType(DstReg);
     auto ExtFromWidth = MI.getOperand(2).getImm();
 
     if (ExtFromWidth == 8 || ExtFromWidth == 16 ||
         (DstType.getScalarSizeInBits() == 64 && ExtFromWidth == 32)) {
       return true;
     }
-    return false;
+
+    Register TmpRes = MRI.createGenericVirtualRegister(DstType);
+
+    auto MIBSz = MIRBuilder.buildConstant(
+        DstType, DstType.getScalarSizeInBits() - ExtFromWidth);
+    MIRBuilder.buildShl(TmpRes, SrcReg, MIBSz->getOperand(0));
+    MIRBuilder.buildAShr(DstReg, TmpRes, MIBSz->getOperand(0));
+    MI.eraseFromParent();
+
+    return true;
   }
   case TargetOpcode::G_MEMSET: {
     // Anyext the value being set to 32 bit (only the bottom 8 bits are read by
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index e605c46aece85..fa4103a8b1b31 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -1,7 +1,9 @@
 #include "WebAssemblyRegisterBankInfo.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
 #include "WebAssemblySubtarget.h"
 #include "WebAssemblyTargetMachine.h"
 #include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/Support/ErrorHandling.h"
 
 #define GET_TARGET_REGBANK_IMPL
 
@@ -59,46 +61,6 @@ using namespace llvm;
 WebAssemblyRegisterBankInfo::WebAssemblyRegisterBankInfo(
     const TargetRegisterInfo &TRI) {}
 
-// Instructions where use operands are floating point registers.
-// Def operands are general purpose.
-static bool isFloatingPointOpcodeUse(unsigned Opc) {
-  switch (Opc) {
-  case TargetOpcode::G_FPTOSI:
-  case TargetOpcode::G_FPTOUI:
-  case TargetOpcode::G_FCMP:
-    return true;
-  default:
-    return isPreISelGenericFloatingPointOpcode(Opc);
-  }
-}
-
-// Instructions where def operands are floating point registers.
-// Use operands are general purpose.
-static bool isFloatingPointOpcodeDef(unsigned Opc) {
-  switch (Opc) {
-  case TargetOpcode::G_SITOFP:
-  case TargetOpcode::G_UITOFP:
-    return true;
-  default:
-    return isPreISelGenericFloatingPointOpcode(Opc);
-  }
-}
-
-static bool isAmbiguous(unsigned Opc) {
-  switch (Opc) {
-  case TargetOpcode::G_LOAD:
-  case TargetOpcode::G_STORE:
-  case TargetOpcode::G_PHI:
-  case TargetOpcode::G_SELECT:
-  case TargetOpcode::G_IMPLICIT_DEF:
-  case TargetOpcode::G_UNMERGE_VALUES:
-  case TargetOpcode::G_MERGE_VALUES:
-    return true;
-  default:
-    return false;
-  }
-}
-
 const RegisterBankInfo::InstructionMapping &
 WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
 
@@ -135,13 +97,35 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
         return getInvalidInstructionMapping();
     }
   }
-
   switch (Opc) {
   case G_BR:
     return getInstructionMapping(MappingID, /*Cost=*/1,
                                  getOperandsMapping({nullptr}), NumOperands);
   case G_TRAP:
-    return getInstructionMapping(MappingID, /*Cost=*/1, nullptr, 0);
+  case G_DEBUGTRAP:
+    return getInstructionMapping(MappingID, /*Cost=*/1, getOperandsMapping({}),
+                                 0);
+  case COPY:
+    Register DstReg = MI.getOperand(0).getReg();
+    if (DstReg.isPhysical()) {
+      if (DstReg.id() == WebAssembly::SP32) {
+        return getInstructionMapping(
+            MappingID, /*Cost=*/1,
+            getOperandsMapping(
+                {&WebAssembly::ValueMappings[WebAssembly::I32Idx]}),
+            1);
+      } else if (DstReg.id() == WebAssembly::SP64) {
+        return getInstructionMapping(
+            MappingID, /*Cost=*/1,
+            getOperandsMapping(
+                {&WebAssembly::ValueMappings[WebAssembly::I64Idx]}),
+            1);
+      } else {
+        llvm_unreachable("Trying to copy into WASM physical register other "
+                         "than sp32 or sp64?");
+      }
+    }
+    break;
   }
 
   const LLT Op0Ty = MRI.getType(MI.getOperand(0).getReg());
@@ -176,8 +160,39 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case G_SREM:
   case G_UDIV:
   case G_UREM:
+  case G_CTLZ:
+  case G_CTLZ_ZERO_UNDEF:
+  case G_CTTZ:
+  case G_CTTZ_ZERO_UNDEF:
+  case G_CTPOP:
+  case G_FSHL:
+  case G_FSHR:
     OperandsMapping = &Op0IntValueMapping;
     break;
+  case G_FADD:
+  case G_FSUB:
+  case G_FDIV:
+  case G_FMUL:
+  case G_FNEG:
+  case G_FABS:
+  case G_FCEIL:
+  case G_FFLOOR:
+  case G_FSQRT:
+  case G_INTRINSIC_TRUNC:
+  case G_FNEARBYINT:
+  case G_FRINT:
+  case G_INTRINSIC_ROUNDEVEN:
+  case G_FMINIMUM:
+  case G_FMAXIMUM:
+  case G_FMINNUM:
+  case G_FMAXNUM:
+  case G_FMINNUM_IEEE:
+  case G_FMAXNUM_IEEE:
+  case G_FMA:
+  case G_FREM:
+  case G_FCOPYSIGN:
+    OperandsMapping = &Op0FloatValueMapping;
+    break;
   case G_SEXT_INREG:
     OperandsMapping =
         getOperandsMapping({&Op0IntValueMapping, &Op0IntValueMapping, nullptr});
@@ -185,6 +200,9 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case G_FRAME_INDEX:
     OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
     break;
+  case G_VASTART:
+    OperandsMapping = &Op0IntValueMapping;
+    break;
   case G_ZEXT:
   case G_ANYEXT:
   case G_SEXT:
@@ -233,7 +251,7 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
                                                  : WebAssembly::I32Idx];
 
     const LLT Op2Ty = MRI.getType(MI.getOperand(2).getReg());
-    unsigned Op2Size = Op1Ty.getSizeInBits();
+    unsigned Op2Size = Op2Ty.getSizeInBits();
     auto &Op2IntValueMapping =
         WebAssembly::ValueMappings[Op2Size == 64 ? WebAssembly::I64Idx
                                                  : WebAssembly::I32Idx];
@@ -247,6 +265,9 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case G_CONSTANT:
     OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
     break;
+  case G_FCONSTANT:
+    OperandsMapping = getOperandsMapping({&Op0FloatValueMapping, nullptr});
+    break;
   case G_IMPLICIT_DEF:
     OperandsMapping = &Op0IntValueMapping;
     break;
@@ -263,37 +284,140 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
                             &Op2IntValueMapping});
     break;
   }
+  case G_FCMP: {
+    const LLT Op2Ty = MRI.getType(MI.getOperand(2).getReg());
+    unsigned Op2Size = Op2Ty.getSizeInBits();
+
+    auto &Op2FloatValueMapping =
+        WebAssembly::ValueMappings[Op2Size == 64 ? WebAssembly::F64Idx
+                                                 : WebAssembly::F32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, nullptr, &Op2FloatValueMapping,
+                            &Op2FloatValueMapping});
+    break;
+  }
   case G_BRCOND:
     OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
     break;
+  case G_JUMP_TABLE:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  case G_BRJT:
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, nullptr,
+                            &WebAssembly::ValueMappings[WebAssembly::I32Idx]});
+    break;
   case COPY: {
     Register DstReg = MI.getOperand(0).getReg();
     Register SrcReg = MI.getOperand(1).getReg();
-    // Check if one of the register is not a generic register.
-    if ((DstReg.isPhysical() || !MRI.getType(DstReg).isValid()) ||
-        (SrcReg.isPhysical() || !MRI.getType(SrcReg).isValid())) {
-      const RegisterBank *DstRB = getRegBank(DstReg, MRI, TRI);
-      const RegisterBank *SrcRB = getRegBank(SrcReg, MRI, TRI);
-      if (!DstRB)
-        DstRB = SrcRB;
-      else if (!SrcRB)
-        SrcRB = DstRB;
-      // If both RB are null that means both registers are generic.
-      // We shouldn't be here.
-      assert(DstRB && SrcRB && "Both RegBank were nullptr");
-      TypeSize DstSize = getSizeInBits(DstReg, MRI, TRI);
-      TypeSize SrcSize = getSizeInBits(SrcReg, MRI, TRI);
-      assert(DstSize == SrcSize &&
-             "Trying to copy between different sized regbanks? Why?");
-
-      return getInstructionMapping(
-          DefaultMappingID, copyCost(*DstRB, *SrcRB, DstSize),
-          getCopyMapping(DstRB->getID(), SrcRB->getID(), Size),
-          // We only care about the mapping of the destination.
-          /*NumOperands*/ 1);
+
+    const RegisterBank *DstRB = getRegBank(DstReg, MRI, TRI);
+    const RegisterBank *SrcRB = getRegBank(SrcReg, MRI, TRI);
+
+    if (!DstRB)
+      DstRB = SrcRB;
+    else if (!SrcRB)
+      SrcRB = DstRB;
+
+    assert(DstRB && SrcRB && "Both RegBank were nullptr");
+    TypeSize DstSize = getSizeInBits(DstReg, MRI, TRI);
+    TypeSize SrcSize = getSizeInBits(SrcReg, MRI, TRI);
+    assert(DstSize == SrcSize &&
+           "Trying to copy between different sized regbanks? Why?");
+
+    WebAssembly::ValueMappingIdx DstValMappingIdx = WebAssembly::InvalidIdx;
+    switch (DstRB->getID()) {
+    case WebAssembly::I32RegBankID:
+      DstValMappingIdx = WebAssembly::I32Idx;
+      break;
+    case WebAssembly::I64RegBankID:
+      DstValMappingIdx = WebAssembly::I64Idx;
+      break;
+    case WebAssembly::F32RegBankID:
+      DstValMappingIdx = WebAssembly::F32Idx;
+      break;
+    case WebAssembly::F64RegBankID:
+      DstValMappingIdx = WebAssembly::F64Idx;
+      break;
+    default:
+      break;
+    }
+
+    WebAssembly::ValueMappingIdx SrcValMappingIdx = WebAssembly::InvalidIdx;
+    switch (SrcRB->getID()) {
+    case WebAssembly::I32RegBankID:
+      SrcValMappingIdx = WebAssembly::I32Idx;
+      break;
+    case WebAssembly::I64RegBankID:
+      SrcValMappingIdx = WebAssembly::I64Idx;
+      break;
+    case WebAssembly::F32RegBankID:
+      SrcValMappingIdx = WebAssembly::F32Idx;
+      break;
+    case WebAssembly::F64RegBankID:
+      SrcValMappingIdx = WebAssembly::F64Idx;
+      break;
+    default:
+      break;
     }
+
+    OperandsMapping =
+        getOperandsMapping({&WebAssembly::ValueMappings[DstValMappingIdx],
+                            &WebAssembly::ValueMappings[SrcValMappingIdx]});
+    return getInstructionMapping(
+        MappingID, /*Cost=*/copyCost(*DstRB, *SrcRB, DstSize), OperandsMapping,
+        // We only care about the mapping of the destination for COPY.
+        1);
+  }
+  case G_SELECT:
+    OperandsMapping = getOperandsMapping(
+        {&Op0IntValueMapping, &WebAssembly::ValueMappings[WebAssembly::I32Idx],
+         &Op0IntValueMapping, &Op0IntValueMapping});
+    break;
+  case G_FPTOSI:
+  case G_FPTOSI_SAT:
+  case G_FPTOUI:
+  case G_FPTOUI_SAT: {
+    const LLT Op1Ty = MRI.getType(MI.getOperand(1).getReg());
+    unsigned Op1Size = Op1Ty.getSizeInBits();
+
+    auto &Op1FloatValueMapping =
+        WebAssembly::ValueMappings[Op1Size == 64 ? WebAssembly::F64Idx
+                                                 : WebAssembly::F32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Op0IntValueMapping, &Op1FloatValueMapping});
+    break;
   }
+  case G_SITOFP:
+  case G_UITOFP: {
+    const LLT Op1Ty = MRI.getType(MI.getOperand(1).getReg());
+    unsigned Op1Size = Op1Ty.getSizeInBits();
+
+    auto &Op1IntValueMapping =
+        WebAssembly::ValueMappings[Op1Size == 64 ? WebAssembly::I64Idx
+                                                 : WebAssembly::I32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Op0FloatValueMapping, &Op1IntValueMapping});
+    break;
   }
+  case G_FPEXT:
+  case G_FPTRUNC: {
+    const LLT Op1Ty = MRI.getType(MI.getOperand(1).getReg());
+    unsigned Op1Size = Op1Ty.getSizeInBits();
+
+    auto &Op1FloatValueMapping =
+        WebAssembly::ValueMappings[Op1Size == 64 ? WebAssembly::F64Idx
+                                                 : WebAssembly::F32Idx];
+
+    OperandsMapping =
+        getOperandsMapping({&Op0FloatValueMapping, &Op1FloatValueMapping});
+    break;
+  }
+  }
+
   if (!OperandsMapping)
     return getInvalidInstructionMapping();
 

>From dc9eeea9edc4e6a5b5bed22c46b398bdefc43769 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 2 Oct 2025 00:18:46 -0700
Subject: [PATCH 10/17] Begin work on instruction selection

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   1 +
 .../GISel/WebAssemblyInstructionSelector.cpp  | 526 ++++++++++++++++++
 .../GISel/WebAssemblyInstructionSelector.h    |   0
 llvm/lib/Target/WebAssembly/WebAssembly.h     |   9 +
 .../WebAssembly/WebAssemblyInstrMemory.td     |   6 +
 .../WebAssembly/WebAssemblySubtarget.cpp      |   5 +-
 6 files changed, 545 insertions(+), 2 deletions(-)
 delete mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index e573582509263..81d9ca88a466e 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -7,6 +7,7 @@ tablegen(LLVM WebAssemblyGenAsmWriter.inc -gen-asm-writer)
 tablegen(LLVM WebAssemblyGenDAGISel.inc -gen-dag-isel)
 tablegen(LLVM WebAssemblyGenDisassemblerTables.inc -gen-disassembler)
 tablegen(LLVM WebAssemblyGenFastISel.inc -gen-fast-isel)
+tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel)
 tablegen(LLVM WebAssemblyGenInstrInfo.inc -gen-instr-info)
 tablegen(LLVM WebAssemblyGenMCCodeEmitter.inc -gen-emitter)
 tablegen(LLVM WebAssemblyGenRegisterBank.inc -gen-register-bank)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index e69de29bb2d1d..aea7b9a424a62 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -0,0 +1,526 @@
+//===- WebAssemblyInstructionSelector.cpp ------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the targeting of the InstructionSelector class for
+/// WebAssembly.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#include "GISel/WebAssemblyRegisterBankInfo.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "Utils/WasmAddressSpaces.h"
+#include "Utils/WebAssemblyTypeUtilities.h"
+#include "WebAssemblyRegisterInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "WebAssemblyTargetMachine.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/CodeGen/RegisterBank.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#define DEBUG_TYPE "wasm-isel"
+
+using namespace llvm;
+
+namespace {
+
+#define GET_GLOBALISEL_PREDICATE_BITSET
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATE_BITSET
+
+class WebAssemblyInstructionSelector : public InstructionSelector {
+public:
+  WebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM,
+                                 const WebAssemblySubtarget &STI,
+                                 const WebAssemblyRegisterBankInfo &RBI);
+
+  bool select(MachineInstr &I) override;
+
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands32(MachineOperand &Root) const;
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands64(MachineOperand &Root) const;
+
+  static const char *getName() { return DEBUG_TYPE; }
+
+private:
+  bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
+  bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
+
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands(LLT AddrType, unsigned int ConstOpc,
+                     MachineOperand &Root) const;
+
+  const WebAssemblyTargetMachine &TM;
+  const WebAssemblySubtarget &STI;
+  const WebAssemblyInstrInfo &TII;
+  const WebAssemblyRegisterInfo &TRI;
+  const WebAssemblyRegisterBankInfo &RBI;
+
+#define GET_GLOBALISEL_PREDICATES_DECL
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATES_DECL
+
+#define GET_GLOBALISEL_TEMPORARIES_DECL
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_TEMPORARIES_DECL
+};
+
+} // end anonymous namespace
+
+#define GET_GLOBALISEL_IMPL
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_IMPL
+
+WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
+    const WebAssemblyTargetMachine &TM, const WebAssemblySubtarget &STI,
+    const WebAssemblyRegisterBankInfo &RBI)
+    : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()),
+      RBI(RBI),
+
+#define GET_GLOBALISEL_PREDICATES_INIT
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATES_INIT
+#define GET_GLOBALISEL_TEMPORARIES_INIT
+#include "WebAssemblyGenGlobalISel.inc"
+#undef GET_GLOBALISEL_TEMPORARIES_INIT
+{
+}
+
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
+                                                   unsigned int ConstOpc,
+                                                   MachineOperand &Root) const {
+
+  if (!Root.isReg())
+    return std::nullopt;
+
+  MachineRegisterInfo &MRI =
+      Root.getParent()->getParent()->getParent()->getRegInfo();
+  MachineInstr &RootDef = *MRI.getVRegDef(Root.getReg());
+
+  if (RootDef.getOpcode() == TargetOpcode::G_PTR_ADD) {
+    // RootDef will always be G_PTR_ADD
+    MachineOperand &LHS = RootDef.getOperand(1);
+
+    MachineOperand &RHS = RootDef.getOperand(2);
+    MachineInstr &LHSDef = *MRI.getVRegDef(LHS.getReg());
+    MachineInstr &RHSDef =
+        *MRI.getVRegDef(RHS.getReg()); // Will always be G_CONSTANT
+
+    // WebAssembly constant offsets are performed as unsigned with infinite
+    // precision, so we need to check for NoUnsignedWrap so that we don't fold
+    // and offset for an add that needs wrapping.
+    if (RootDef.getFlag(MachineInstr::MIFlag::NoUWrap)) {
+      for (size_t i = 0; i < 2; ++i) {
+        //MachineOperand &Op = i == 0 ? LHS : RHS;
+        MachineInstr &OpDef = i == 0 ? LHSDef : RHSDef;
+        MachineOperand &OtherOp = i == 0 ? RHS : LHS;
+        //MachineInstr &OtherOpDef = i == 0 ? RHSDef : LHSDef;
+
+        if (OpDef.getOpcode() == TargetOpcode::G_CONSTANT) {
+          auto Offset = OpDef.getOperand(1).getCImm()->getZExtValue();
+          auto Addr = OtherOp;
+
+          return {{
+              [=](MachineInstrBuilder &MIB) { MIB.addImm(Offset); },
+              [=](MachineInstrBuilder &MIB) { MIB.add(Addr); },
+          }};
+        }
+
+        if (!TM.isPositionIndependent()) {
+          if (OpDef.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
+            auto Offset = OpDef.getOperand(1).getGlobal();
+            auto Addr = OtherOp;
+
+            return {{
+                [=](MachineInstrBuilder &MIB) { MIB.addGlobalAddress(Offset); },
+                [=](MachineInstrBuilder &MIB) { MIB.add(Addr); },
+            }};
+          }
+        }
+      }
+    }
+  }
+
+  if (RootDef.getOpcode() == TargetOpcode::G_CONSTANT) {
+    auto Offset = RootDef.getOperand(1).getCImm()->getZExtValue();
+    auto Addr = MRI.createGenericVirtualRegister(AddrType);
+
+    MachineIRBuilder B(RootDef);
+
+    auto MIB = B.buildInstr(ConstOpc).addDef(Addr).addImm(0);
+    assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return {{
+        [=](MachineInstrBuilder &MIB) { MIB.addImm(Offset); },
+        [=](MachineInstrBuilder &MIB) { MIB.addReg(Addr); },
+    }};
+  }
+
+  return {{
+      [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
+      [=](MachineInstrBuilder &MIB) { MIB.add(Root); },
+  }};
+}
+
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands32(
+    MachineOperand &Root) const {
+  return selectAddrOperands(LLT::scalar(32), WebAssembly::CONST_I32, Root);
+}
+
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands64(
+    MachineOperand &Root) const {
+  return selectAddrOperands(LLT::scalar(64), WebAssembly::CONST_I64, Root);
+}
+
+bool WebAssemblyInstructionSelector::selectCopy(
+    MachineInstr &I, MachineRegisterInfo &MRI) const {
+  Register DstReg = I.getOperand(0).getReg();
+  Register SrcReg = I.getOperand(1).getReg();
+
+  if (DstReg.isPhysical()) {
+    if (DstReg.id() == WebAssembly::SP32) {
+      if (!RBI.constrainGenericRegister(DstReg, WebAssembly::I32RegClass,
+                                        MRI)) {
+        LLVM_DEBUG(dbgs() << "Failed to constrain "
+                          << TII.getName(I.getOpcode()) << " operand\n");
+        return false;
+      }
+      return true;
+    }
+    if (DstReg.id() == WebAssembly::SP64) {
+      if (!RBI.constrainGenericRegister(DstReg, WebAssembly::I64RegClass,
+                                        MRI)) {
+        LLVM_DEBUG(dbgs() << "Failed to constrain "
+                          << TII.getName(I.getOpcode()) << " operand\n");
+        return false;
+      }
+      return true;
+    }
+    llvm_unreachable("Copy to physical register other than SP32 or SP64?");
+  }
+
+  const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg);
+  if (!DstRC) {
+    const RegisterBank *DstBank = MRI.getRegBankOrNull(DstReg);
+    if (!DstBank) {
+      llvm_unreachable("Selecting copy with dst reg with no bank?");
+    }
+
+    switch (DstBank->getID()) {
+    case WebAssembly::I32RegBankID:
+      DstRC = &WebAssembly::I32RegClass;
+      break;
+    case WebAssembly::I64RegBankID:
+      DstRC = &WebAssembly::I64RegClass;
+      break;
+    case WebAssembly::F32RegBankID:
+      DstRC = &WebAssembly::F32RegClass;
+      break;
+    case WebAssembly::F64RegBankID:
+      DstRC = &WebAssembly::F64RegClass;
+      break;
+    default:
+      llvm_unreachable("Unknown reg bank to reg class mapping?");
+    }
+    if (!RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) {
+      LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
+                        << " operand\n");
+      return false;
+    }
+  }
+
+  const TargetRegisterClass *SrcRC = MRI.getRegClassOrNull(SrcReg);
+  if (!SrcRC) {
+    const RegisterBank *SrcBank = MRI.getRegBankOrNull(SrcReg);
+    if (!SrcBank) {
+      llvm_unreachable("Selecting copy with src reg with no bank?");
+    }
+
+    switch (SrcBank->getID()) {
+    case WebAssembly::I32RegBankID:
+      SrcRC = &WebAssembly::I32RegClass;
+      break;
+    case WebAssembly::I64RegBankID:
+      SrcRC = &WebAssembly::I64RegClass;
+      break;
+    case WebAssembly::F32RegBankID:
+      SrcRC = &WebAssembly::F32RegClass;
+      break;
+    case WebAssembly::F64RegBankID:
+      SrcRC = &WebAssembly::F64RegClass;
+      break;
+    default:
+      llvm_unreachable("Unknown reg bank to reg class mapping?");
+    }
+    if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI)) {
+      LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
+                        << " operand\n");
+      return false;
+    }
+  }
+
+  assert(TRI.getRegSizeInBits(*DstRC) == TRI.getRegSizeInBits(*SrcRC) &&
+         "Copy between mismatching register sizes?");
+
+  if (DstRC != SrcRC) {
+    if (DstRC == &WebAssembly::I32RegClass &&
+        SrcRC == &WebAssembly::F32RegClass) {
+      I.setDesc(TII.get(WebAssembly::I32_REINTERPRET_F32));
+      return true;
+    }
+
+    if (DstRC == &WebAssembly::F32RegClass &&
+        SrcRC == &WebAssembly::I32RegClass) {
+      I.setDesc(TII.get(WebAssembly::F32_REINTERPRET_I32));
+      return true;
+    }
+
+    if (DstRC == &WebAssembly::I64RegClass &&
+        SrcRC == &WebAssembly::F64RegClass) {
+      I.setDesc(TII.get(WebAssembly::I64_REINTERPRET_F64));
+      return true;
+    }
+
+    if (DstRC == &WebAssembly::F64RegClass &&
+        SrcRC == &WebAssembly::I64RegClass) {
+      I.setDesc(TII.get(WebAssembly::F64_REINTERPRET_I64));
+      return true;
+    }
+
+    llvm_unreachable("Found bitcast/copy edge case.");
+  }
+
+  return true;
+}
+
+bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
+  MachineBasicBlock &MBB = *I.getParent();
+  MachineFunction &MF = *MBB.getParent();
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  const TargetLowering &TLI = *STI.getTargetLowering();
+
+  if (!isPreISelGenericOpcode(I.getOpcode())) {
+    if (I.isCopy())
+      return selectCopy(I, MRI);
+
+    return true;
+  }
+
+  if (selectImpl(I, *CoverageInfo))
+    return true;
+
+  using namespace TargetOpcode;
+
+  auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
+  auto PtrIsI64 = PointerWidth == 64;
+
+  switch (I.getOpcode()) {
+  case G_CONSTANT: {
+    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+           "G_CONSTANT selection fell-through with non-pointer?");
+
+    auto OrigImm = I.getOperand(1).getCImm()->getValue();
+
+    auto MaskedVal = OrigImm.getLoBits(PointerWidth);
+    assert(MaskedVal.eq(OrigImm) &&
+           "Pointer immediate uses more bits than allowed");
+
+    I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::CONST_I64
+                                         : WebAssembly::CONST_I32));
+    I.removeOperand(1);
+    I.addOperand(MachineOperand::CreateImm(MaskedVal.getZExtValue()));
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+    return true;
+  }
+  case G_PTR_ADD: {
+    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+           "G_PTR_ADD selection fell-through with non-pointer?");
+
+    auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
+    I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::ADD_I64
+                                         : WebAssembly::ADD_I32));
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return true;
+  }
+  case G_PTRTOINT: {
+    assert(MRI.getType(I.getOperand(1).getReg()).isPointer() &&
+           "G_PTRTOINT selection fell-through with non-pointer?");
+
+    auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
+    I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::COPY_I64
+                                         : WebAssembly::COPY_I32));
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return true;
+  }
+  case G_ICMP: {
+    Register LHS = I.getOperand(2).getReg();
+    Register RHS = I.getOperand(3).getReg();
+    CmpInst::Predicate Cond =
+        static_cast<CmpInst::Predicate>(I.getOperand(1).getPredicate());
+
+    auto CmpWidth = MRI.getType(LHS).getSizeInBits();
+    assert(CmpWidth == MRI.getType(RHS).getSizeInBits() &&
+           "LHS and RHS for ICMP are diffrent lengths???");
+
+    auto IsI64 = CmpWidth == 64;
+
+    unsigned int CmpOpcode;
+    switch (Cond) {
+    case CmpInst::ICMP_EQ:
+      CmpOpcode = IsI64 ? WebAssembly::EQ_I64 : WebAssembly::EQ_I32;
+      break;
+    case CmpInst::ICMP_NE:
+      CmpOpcode = IsI64 ? WebAssembly::NE_I64 : WebAssembly::NE_I32;
+      break;
+    case CmpInst::ICMP_UGT:
+      CmpOpcode = IsI64 ? WebAssembly::GT_U_I64 : WebAssembly::GT_U_I32;
+      break;
+    case CmpInst::ICMP_UGE:
+      CmpOpcode = IsI64 ? WebAssembly::GE_U_I64 : WebAssembly::GE_U_I32;
+      break;
+    case CmpInst::ICMP_ULT:
+      CmpOpcode = IsI64 ? WebAssembly::LT_U_I64 : WebAssembly::LT_U_I32;
+      break;
+    case CmpInst::ICMP_ULE:
+      CmpOpcode = IsI64 ? WebAssembly::LE_U_I64 : WebAssembly::LE_U_I32;
+      break;
+    case CmpInst::ICMP_SGT:
+      CmpOpcode = IsI64 ? WebAssembly::GT_S_I64 : WebAssembly::GT_S_I32;
+      break;
+    case CmpInst::ICMP_SGE:
+      CmpOpcode = IsI64 ? WebAssembly::GE_S_I64 : WebAssembly::GE_S_I32;
+      break;
+    case CmpInst::ICMP_SLT:
+      CmpOpcode = IsI64 ? WebAssembly::LT_S_I64 : WebAssembly::LT_S_I32;
+      break;
+    case CmpInst::ICMP_SLE:
+      CmpOpcode = IsI64 ? WebAssembly::LE_S_I64 : WebAssembly::LE_S_I32;
+      break;
+    default:
+      llvm_unreachable("Unknown ICMP predicate");
+    }
+
+    I.setDesc(TII.get(CmpOpcode));
+    I.removeOperand(1);
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return true;
+  }
+  case G_FRAME_INDEX: {
+    MachineIRBuilder B(I);
+
+    auto MIB = B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
+                   .addDef(I.getOperand(0).getReg())
+                   .addReg(PtrIsI64 ? WebAssembly::SP64 : WebAssembly::SP32)
+                   .addFrameIndex(I.getOperand(1).getIndex());
+    assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    I.eraseFromParent();
+    return true;
+  }
+  case G_GLOBAL_VALUE:
+    assert(I.getOperand(1).getTargetFlags() == 0 &&
+           "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
+    assert(WebAssembly::isValidAddressSpace(
+               MRI.getType(I.getOperand(0).getReg()).getAddressSpace()) &&
+           "Invalid address space for WebAssembly target");
+
+    unsigned OperandFlags = 0;
+    const llvm::GlobalValue *GV = I.getOperand(1).getGlobal();
+    // Since WebAssembly tables cannot yet be shared accross modules, we don't
+    // need special treatment for tables in PIC mode.
+    if (TLI.isPositionIndependent() &&
+        !WebAssembly::isWebAssemblyTableType(GV->getValueType())) {
+      if (TM.shouldAssumeDSOLocal(GV)) {
+        const char *BaseName;
+        if (GV->getValueType()->isFunctionTy()) {
+          BaseName = MF.createExternalSymbolName("__table_base");
+          OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL;
+        } else {
+          BaseName = MF.createExternalSymbolName("__memory_base");
+          OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL;
+        }
+        MachineIRBuilder B(I);
+
+        auto MemBase = MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(MemBase, PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
+        auto Offset = MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(Offset, PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
+
+        B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
+                           : WebAssembly::GLOBAL_GET_I32)
+            .addDef(MemBase)
+            .addExternalSymbol(BaseName);
+
+        B.buildInstr(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32)
+            .addDef(Offset)
+            .addGlobalAddress(GV, I.getOperand(1).getOffset(), OperandFlags);
+
+        auto MIB =
+            B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
+                .addDef(I.getOperand(0).getReg())
+                .addReg(MemBase)
+                .addReg(Offset);
+        assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+               "Couldn't constrain registers for instruction");
+
+        I.eraseFromParent();
+        return true;
+      }
+      OperandFlags = WebAssemblyII::MO_GOT;
+    }
+
+    auto NewOpc = MF.getDataLayout().getPointerSizeInBits() == 64
+                      ? WebAssembly::CONST_I64
+                      : WebAssembly::CONST_I32;
+
+    if (OperandFlags & WebAssemblyII::MO_GOT) {
+      NewOpc = MF.getDataLayout().getPointerSizeInBits() == 64
+                   ? WebAssembly::GLOBAL_GET_I64
+                   : WebAssembly::GLOBAL_GET_I32;
+    }
+
+    I.setDesc(TII.get(NewOpc));
+    I.getOperand(1).setTargetFlags(OperandFlags);
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return true;
+  }
+
+  return false;
+}
+
+namespace llvm {
+InstructionSelector *
+createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM,
+                                     const WebAssemblySubtarget &Subtarget,
+                                     const WebAssemblyRegisterBankInfo &RBI) {
+  return new WebAssemblyInstructionSelector(TM, Subtarget, RBI);
+}
+} // namespace llvm
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.h
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 2dbd597f01cc9..0c56c5077c563 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -15,6 +15,9 @@
 #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H
 #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H
 
+#include "GISel/WebAssemblyRegisterBankInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
 #include "llvm/PassRegistry.h"
 #include "llvm/Support/CodeGen.h"
 
@@ -32,6 +35,12 @@ FunctionPass *createWebAssemblyOptimizeReturned();
 FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv();
 FunctionPass *createWebAssemblyRefTypeMem2Local();
 
+// GlobalISel
+InstructionSelector *
+createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &,
+                                     const WebAssemblySubtarget &,
+                                     const WebAssemblyRegisterBankInfo &);
+
 // ISel and immediate followup passes.
 FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
                                        CodeGenOptLevel OptLevel);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
index 0cbe9d0c6a6a4..5d5af1b7a61bd 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
@@ -33,6 +33,12 @@
 def AddrOps32 : ComplexPattern<i32, 2, "SelectAddrOperands32">;
 def AddrOps64 : ComplexPattern<i64, 2, "SelectAddrOperands64">;
 
+def gi_AddrOps32 : GIComplexOperandMatcher<s32, "selectAddrOperands32">,
+                   GIComplexPatternEquiv<AddrOps32>;
+
+def gi_AddrOps64 : GIComplexOperandMatcher<s64, "selectAddrOperands32">,
+                   GIComplexPatternEquiv<AddrOps64>;
+
 // Defines atomic and non-atomic loads, regular and extending.
 multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode,
                            list<Predicate> reqs = []> {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index b99c35acabef6..315cbb65371a0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -12,6 +12,7 @@
 ///
 //===----------------------------------------------------------------------===//
 
+#include "WebAssembly.h"
 #include "WebAssemblySubtarget.h"
 #include "GISel/WebAssemblyCallLowering.h"
 #include "GISel/WebAssemblyLegalizerInfo.h"
@@ -75,9 +76,9 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
   Legalizer.reset(new WebAssemblyLegalizerInfo(*this));
   auto *RBI = new WebAssemblyRegisterBankInfo(*getRegisterInfo());
   RegBankInfo.reset(RBI);
-/*
+
   InstSelector.reset(createWebAssemblyInstructionSelector(
-  *static_cast<const WebAssemblyTargetMachine *>(&TM), *this, *RBI));*/
+  *static_cast<const WebAssemblyTargetMachine *>(&TM), *this, *RBI));
 }
 
 bool WebAssemblySubtarget::enableAtomicExpand() const {

>From cb51d03d787d78d86fc6009bd877813ea29517b3 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Fri, 3 Oct 2025 10:41:03 -0700
Subject: [PATCH 11/17] Instruction selection wave 2

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   4 +-
 .../GISel/WebAssemblyCallLowering.cpp         |  15 +-
 .../GISel/WebAssemblyInstructionSelector.cpp  | 273 +++++++++++-------
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 147 +++++++++-
 .../GISel/WebAssemblyRegisterBankInfo.cpp     |   5 +-
 .../Target/WebAssembly/WebAssemblyGISel.td    | 133 +++++++++
 .../WebAssembly/WebAssemblyInstrMemory.td     |   6 -
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |   6 +
 8 files changed, 470 insertions(+), 119 deletions(-)
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyGISel.td

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 81d9ca88a466e..aa604ee8cb2c9 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -7,13 +7,15 @@ tablegen(LLVM WebAssemblyGenAsmWriter.inc -gen-asm-writer)
 tablegen(LLVM WebAssemblyGenDAGISel.inc -gen-dag-isel)
 tablegen(LLVM WebAssemblyGenDisassemblerTables.inc -gen-disassembler)
 tablegen(LLVM WebAssemblyGenFastISel.inc -gen-fast-isel)
-tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel)
 tablegen(LLVM WebAssemblyGenInstrInfo.inc -gen-instr-info)
 tablegen(LLVM WebAssemblyGenMCCodeEmitter.inc -gen-emitter)
 tablegen(LLVM WebAssemblyGenRegisterBank.inc -gen-register-bank)
 tablegen(LLVM WebAssemblyGenRegisterInfo.inc -gen-register-info)
 tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
 
+set(LLVM_TARGET_DEFINITIONS WebAssemblyGISel.td)
+tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel)
+
 add_public_tablegen_target(WebAssemblyCommonTableGen)
 
 add_llvm_target(WebAssemblyCodeGen
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 733d676ac988a..1990d3e4e3adb 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -527,7 +527,8 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
         auto NewOutReg = Arg.Regs[Part];
         if (!RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI)) {
           NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
-          assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) && "Couldn't constrain brand-new register?");
+          assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) &&
+                 "Couldn't constrain brand-new register?");
           MIRBuilder.buildCopy(NewOutReg, Arg.Regs[Part]);
         }
         MIB.addUse(NewOutReg);
@@ -704,9 +705,12 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
         getLLTForType(*PointerType::get(Ctx, 0), DL));
     MFI->setVarargBufferVreg(VarargVreg);
 
-    MIRBuilder.buildInstr(getWASMArgOpcode(PtrVT))
-        .addDef(VarargVreg)
-        .addImm(FinalArgIdx);
+    auto ArgInst = MIRBuilder.buildInstr(getWASMArgOpcode(PtrVT))
+                       .addDef(VarargVreg)
+                       .addImm(FinalArgIdx);
+
+    constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst,
+                             ArgInst->getDesc(), ArgInst->getOperand(0), 0);
 
     MFI->addParam(PtrVT);
     ++FinalArgIdx;
@@ -911,7 +915,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
           auto NewRetReg = Ret.Regs[Part];
           if (!RBI.constrainGenericRegister(NewRetReg, NewRegClass, MRI)) {
             NewRetReg = MRI.createGenericVirtualRegister(NewLLT);
-            assert(RBI.constrainGenericRegister(NewRetReg, NewRegClass, MRI) && "Couldn't constrain brand-new register?");
+            assert(RBI.constrainGenericRegister(NewRetReg, NewRegClass, MRI) &&
+                   "Couldn't constrain brand-new register?");
             MIRBuilder.buildCopy(NewRetReg, Ret.Regs[Part]);
           }
           CallInst.addDef(Ret.Regs[Part]);
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index aea7b9a424a62..0ef5f357718ac 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -23,10 +23,12 @@
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 #include "llvm/CodeGen/GlobalISel/Utils.h"
 #include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineJumpTableInfo.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/RegisterBank.h"
 #include "llvm/CodeGen/TargetLowering.h"
 #include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -126,10 +128,10 @@ WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
     // and offset for an add that needs wrapping.
     if (RootDef.getFlag(MachineInstr::MIFlag::NoUWrap)) {
       for (size_t i = 0; i < 2; ++i) {
-        //MachineOperand &Op = i == 0 ? LHS : RHS;
+        // MachineOperand &Op = i == 0 ? LHS : RHS;
         MachineInstr &OpDef = i == 0 ? LHSDef : RHSDef;
         MachineOperand &OtherOp = i == 0 ? RHS : LHS;
-        //MachineInstr &OtherOpDef = i == 0 ? RHSDef : LHSDef;
+        // MachineInstr &OtherOpDef = i == 0 ? RHSDef : LHSDef;
 
         if (OpDef.getOpcode() == TargetOpcode::G_CONSTANT) {
           auto Offset = OpDef.getOperand(1).getCImm()->getZExtValue();
@@ -172,6 +174,23 @@ WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
     }};
   }
 
+  if (!TM.isPositionIndependent() &&
+      RootDef.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
+    auto *Offset = RootDef.getOperand(1).getGlobal();
+    auto Addr = MRI.createGenericVirtualRegister(AddrType);
+
+    MachineIRBuilder B(RootDef);
+
+    auto MIB = B.buildInstr(ConstOpc).addDef(Addr).addImm(0);
+    assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+           "Couldn't constrain registers for instruction");
+
+    return {{
+        [=](MachineInstrBuilder &MIB) { MIB.addGlobalAddress(Offset); },
+        [=](MachineInstrBuilder &MIB) { MIB.addReg(Addr); },
+    }};
+  }
+
   return {{
       [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
       [=](MachineInstrBuilder &MIB) { MIB.add(Root); },
@@ -195,29 +214,22 @@ bool WebAssemblyInstructionSelector::selectCopy(
   Register DstReg = I.getOperand(0).getReg();
   Register SrcReg = I.getOperand(1).getReg();
 
+  const TargetRegisterClass *DstRC;
   if (DstReg.isPhysical()) {
-    if (DstReg.id() == WebAssembly::SP32) {
-      if (!RBI.constrainGenericRegister(DstReg, WebAssembly::I32RegClass,
-                                        MRI)) {
-        LLVM_DEBUG(dbgs() << "Failed to constrain "
-                          << TII.getName(I.getOpcode()) << " operand\n");
-        return false;
-      }
-      return true;
-    }
-    if (DstReg.id() == WebAssembly::SP64) {
-      if (!RBI.constrainGenericRegister(DstReg, WebAssembly::I64RegClass,
-                                        MRI)) {
-        LLVM_DEBUG(dbgs() << "Failed to constrain "
-                          << TII.getName(I.getOpcode()) << " operand\n");
-        return false;
-      }
-      return true;
+    switch (DstReg.id()) {
+    case WebAssembly::SP32:
+      DstRC = &WebAssembly::I32RegClass;
+      break;
+    case WebAssembly::SP64:
+      DstRC = &WebAssembly::I64RegClass;
+      break;
+    default:
+      llvm_unreachable("Copy to physical register other than SP32 or SP64?");
     }
-    llvm_unreachable("Copy to physical register other than SP32 or SP64?");
+  } else {
+    DstRC = MRI.getRegClassOrNull(DstReg);
   }
 
-  const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg);
   if (!DstRC) {
     const RegisterBank *DstBank = MRI.getRegBankOrNull(DstReg);
     if (!DstBank) {
@@ -240,14 +252,29 @@ bool WebAssemblyInstructionSelector::selectCopy(
     default:
       llvm_unreachable("Unknown reg bank to reg class mapping?");
     }
-    if (!RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) {
+    if (!constrainOperandRegClass(*MF, TRI, MRI, TII, RBI, I, *DstRC,
+                                  I.getOperand(0))) {
       LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
                         << " operand\n");
       return false;
     }
   }
 
-  const TargetRegisterClass *SrcRC = MRI.getRegClassOrNull(SrcReg);
+  const TargetRegisterClass *SrcRC;
+  if (SrcReg.isPhysical()) {
+    switch (SrcReg.id()) {
+    case WebAssembly::SP32:
+      SrcRC = &WebAssembly::I32RegClass;
+      break;
+    case WebAssembly::SP64:
+      SrcRC = &WebAssembly::I64RegClass;
+      break;
+    default:
+      llvm_unreachable("Copy to physical register other than SP32 or SP64?");
+    }
+  } else {
+    SrcRC = MRI.getRegClassOrNull(SrcReg);
+  }
   if (!SrcRC) {
     const RegisterBank *SrcBank = MRI.getRegBankOrNull(SrcReg);
     if (!SrcBank) {
@@ -270,7 +297,8 @@ bool WebAssemblyInstructionSelector::selectCopy(
     default:
       llvm_unreachable("Unknown reg bank to reg class mapping?");
     }
-    if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI)) {
+    if (!constrainOperandRegClass(*MF, TRI, MRI, TII, RBI, I, *SrcRC,
+                                  I.getOperand(1))) {
       LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
                         << " operand\n");
       return false;
@@ -311,12 +339,67 @@ bool WebAssemblyInstructionSelector::selectCopy(
   return true;
 }
 
+static const TargetRegisterClass *
+getRegClassForTypeOnBank(const RegisterBank &RB) {
+  switch (RB.getID()) {
+  case WebAssembly::I32RegBankID:
+    return &WebAssembly::I32RegClass;
+  case WebAssembly::I64RegBankID:
+    return &WebAssembly::I64RegClass;
+  case WebAssembly::F32RegBankID:
+    return &WebAssembly::F32RegClass;
+  case WebAssembly::F64RegBankID:
+    return &WebAssembly::F64RegClass;
+  case WebAssembly::EXNREFRegBankID:
+    return &WebAssembly::EXNREFRegClass;
+  case WebAssembly::EXTERNREFRegBankID:
+    return &WebAssembly::EXTERNREFRegClass;
+  case WebAssembly::FUNCREFRegBankID:
+    return &WebAssembly::FUNCREFRegClass;
+    // case WebAssembly::V128RegBankID:
+    //   return &WebAssembly::V128RegClass;
+  }
+
+  return nullptr;
+}
+
 bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
   MachineBasicBlock &MBB = *I.getParent();
   MachineFunction &MF = *MBB.getParent();
   MachineRegisterInfo &MRI = MF.getRegInfo();
   const TargetLowering &TLI = *STI.getTargetLowering();
 
+  if (!I.isPreISelOpcode() || I.getOpcode() == TargetOpcode::G_PHI) {
+    if (I.getOpcode() == TargetOpcode::PHI ||
+        I.getOpcode() == TargetOpcode::G_PHI) {
+      const Register DefReg = I.getOperand(0).getReg();
+      const LLT DefTy = MRI.getType(DefReg);
+
+      const RegClassOrRegBank &RegClassOrBank =
+          MRI.getRegClassOrRegBank(DefReg);
+
+      const TargetRegisterClass *DefRC =
+          dyn_cast<const TargetRegisterClass *>(RegClassOrBank);
+
+      if (!DefRC) {
+        if (!DefTy.isValid()) {
+          LLVM_DEBUG(dbgs() << "PHI operand has no type, not a gvreg?\n");
+          return false;
+        }
+        const RegisterBank &RB = *cast<const RegisterBank *>(RegClassOrBank);
+        DefRC = getRegClassForTypeOnBank(RB);
+        if (!DefRC) {
+          LLVM_DEBUG(dbgs() << "PHI operand has unexpected size/bank\n");
+          return false;
+        }
+      }
+
+      I.setDesc(TII.get(TargetOpcode::PHI));
+
+      return RBI.constrainGenericRegister(DefReg, *DefRC, MRI) != nullptr;
+    }
+  }
+
   if (!isPreISelGenericOpcode(I.getOpcode())) {
     if (I.isCopy())
       return selectCopy(I, MRI);
@@ -333,22 +416,61 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
   auto PtrIsI64 = PointerWidth == 64;
 
   switch (I.getOpcode()) {
-  case G_CONSTANT: {
-    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
-           "G_CONSTANT selection fell-through with non-pointer?");
+  case G_IMPLICIT_DEF: {
+    const Register DefReg = I.getOperand(0).getReg();
+    const LLT DefTy = MRI.getType(DefReg);
 
-    auto OrigImm = I.getOperand(1).getCImm()->getValue();
+    const RegClassOrRegBank &RegClassOrBank = MRI.getRegClassOrRegBank(DefReg);
 
-    auto MaskedVal = OrigImm.getLoBits(PointerWidth);
-    assert(MaskedVal.eq(OrigImm) &&
-           "Pointer immediate uses more bits than allowed");
+    const TargetRegisterClass *DefRC =
+        dyn_cast<const TargetRegisterClass *>(RegClassOrBank);
 
-    I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::CONST_I64
-                                         : WebAssembly::CONST_I32));
-    I.removeOperand(1);
-    I.addOperand(MachineOperand::CreateImm(MaskedVal.getZExtValue()));
-    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+    if (!DefRC) {
+      if (!DefTy.isValid()) {
+        LLVM_DEBUG(
+            dbgs() << "IMPLICIT_DEF operand has no type, not a gvreg?\n");
+        return false;
+      }
+      const RegisterBank &RB = *cast<const RegisterBank *>(RegClassOrBank);
+      DefRC = getRegClassForTypeOnBank(RB);
+      if (!DefRC) {
+        LLVM_DEBUG(dbgs() << "IMPLICIT_DEF operand has unexpected size/bank\n");
+        return false;
+      }
+    }
+
+    I.setDesc(TII.get(TargetOpcode::IMPLICIT_DEF));
+
+    return RBI.constrainGenericRegister(DefReg, *DefRC, MRI) != nullptr;
+    return true;
+  }
+  case G_BRJT: {
+    auto JT = I.getOperand(1);
+    auto Index = I.getOperand(2);
+
+    assert(JT.getTargetFlags() == 0 && "WebAssembly doesn't set target flags");
+
+    MachineIRBuilder B(I);
+
+    MachineJumpTableInfo *MJTI = MF.getJumpTableInfo();
+    const auto &MBBs = MJTI->getJumpTables()[JT.getIndex()].MBBs;
+
+    auto MIB = B.buildInstr(PtrIsI64 ? WebAssembly::BR_TABLE_I64
+                                     : WebAssembly::BR_TABLE_I32)
+                   .add(Index);
+
+    for (auto *MBB : MBBs)
+      MIB.addMBB(MBB);
+
+    // Add the first MBB as a dummy default target for now. This will be
+    // replaced with the proper default target (and the preceding range check
+    // eliminated) if possible by WebAssemblyFixBrTableDefaults.
+    MIB.addMBB(*MBBs.begin());
+
+    assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
            "Couldn't constrain registers for instruction");
+
+    I.eraseFromParent();
     return true;
   }
   case G_PTR_ADD: {
@@ -367,7 +489,6 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
     assert(MRI.getType(I.getOperand(1).getReg()).isPointer() &&
            "G_PTRTOINT selection fell-through with non-pointer?");
 
-    auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
     I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::COPY_I64
                                          : WebAssembly::COPY_I32));
     assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
@@ -375,56 +496,12 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
 
     return true;
   }
-  case G_ICMP: {
-    Register LHS = I.getOperand(2).getReg();
-    Register RHS = I.getOperand(3).getReg();
-    CmpInst::Predicate Cond =
-        static_cast<CmpInst::Predicate>(I.getOperand(1).getPredicate());
-
-    auto CmpWidth = MRI.getType(LHS).getSizeInBits();
-    assert(CmpWidth == MRI.getType(RHS).getSizeInBits() &&
-           "LHS and RHS for ICMP are diffrent lengths???");
-
-    auto IsI64 = CmpWidth == 64;
-
-    unsigned int CmpOpcode;
-    switch (Cond) {
-    case CmpInst::ICMP_EQ:
-      CmpOpcode = IsI64 ? WebAssembly::EQ_I64 : WebAssembly::EQ_I32;
-      break;
-    case CmpInst::ICMP_NE:
-      CmpOpcode = IsI64 ? WebAssembly::NE_I64 : WebAssembly::NE_I32;
-      break;
-    case CmpInst::ICMP_UGT:
-      CmpOpcode = IsI64 ? WebAssembly::GT_U_I64 : WebAssembly::GT_U_I32;
-      break;
-    case CmpInst::ICMP_UGE:
-      CmpOpcode = IsI64 ? WebAssembly::GE_U_I64 : WebAssembly::GE_U_I32;
-      break;
-    case CmpInst::ICMP_ULT:
-      CmpOpcode = IsI64 ? WebAssembly::LT_U_I64 : WebAssembly::LT_U_I32;
-      break;
-    case CmpInst::ICMP_ULE:
-      CmpOpcode = IsI64 ? WebAssembly::LE_U_I64 : WebAssembly::LE_U_I32;
-      break;
-    case CmpInst::ICMP_SGT:
-      CmpOpcode = IsI64 ? WebAssembly::GT_S_I64 : WebAssembly::GT_S_I32;
-      break;
-    case CmpInst::ICMP_SGE:
-      CmpOpcode = IsI64 ? WebAssembly::GE_S_I64 : WebAssembly::GE_S_I32;
-      break;
-    case CmpInst::ICMP_SLT:
-      CmpOpcode = IsI64 ? WebAssembly::LT_S_I64 : WebAssembly::LT_S_I32;
-      break;
-    case CmpInst::ICMP_SLE:
-      CmpOpcode = IsI64 ? WebAssembly::LE_S_I64 : WebAssembly::LE_S_I32;
-      break;
-    default:
-      llvm_unreachable("Unknown ICMP predicate");
-    }
+  case G_INTTOPTR: {
+    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+           "G_INTTOPTR selection fell-through with non-pointer?");
 
-    I.setDesc(TII.get(CmpOpcode));
-    I.removeOperand(1);
+    I.setDesc(TII.get(PointerWidth == 64 ? WebAssembly::COPY_I64
+                                         : WebAssembly::COPY_I32));
     assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
            "Couldn't constrain registers for instruction");
 
@@ -433,14 +510,10 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
   case G_FRAME_INDEX: {
     MachineIRBuilder B(I);
 
-    auto MIB = B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
-                   .addDef(I.getOperand(0).getReg())
-                   .addReg(PtrIsI64 ? WebAssembly::SP64 : WebAssembly::SP32)
-                   .addFrameIndex(I.getOperand(1).getIndex());
-    assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+    I.setDesc(
+        TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
+    assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
            "Couldn't constrain registers for instruction");
-
-    I.eraseFromParent();
     return true;
   }
   case G_GLOBAL_VALUE:
@@ -467,13 +540,17 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
         }
         MachineIRBuilder B(I);
 
-        auto MemBase = MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
-        MRI.setRegClass(MemBase, PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
-        auto Offset = MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
-        MRI.setRegClass(Offset, PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
+        auto MemBase =
+            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(MemBase, PtrIsI64 ? &WebAssembly::I64RegClass
+                                          : &WebAssembly::I32RegClass);
+        auto Offset =
+            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(Offset, PtrIsI64 ? &WebAssembly::I64RegClass
+                                         : &WebAssembly::I32RegClass);
 
         B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
-                           : WebAssembly::GLOBAL_GET_I32)
+                              : WebAssembly::GLOBAL_GET_I32)
             .addDef(MemBase)
             .addExternalSymbol(BaseName);
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index ae2ac0a512427..633dd48cb3ac6 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -43,9 +43,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
   getActionDefinitionsBuilder(G_BR).alwaysLegal();
   getActionDefinitionsBuilder(G_BRCOND).legalFor({s32}).clampScalar(0, s32,
                                                                     s32);
-  getActionDefinitionsBuilder(G_BRJT)
-      .legalFor({{p0, s32}})
-      .clampScalar(1, s32, s32);
+  getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, p0s}});
 
   getActionDefinitionsBuilder(G_SELECT)
       .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
@@ -62,7 +60,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .clampScalar(0, s32, s32);
 
   getActionDefinitionsBuilder(G_FCMP)
-      .legalFor({{s32, s32}, {s32, s64}})
+      .customFor({{s32, s32}, {s32, s64}})
       .clampScalar(0, s32, s32)
       .libcall();
 
@@ -150,8 +148,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .widenScalarToNextPow2(1)
       .clampScalar(1, s32, s64);
 
-  getActionDefinitionsBuilder(G_PTRTOINT).legalFor({{p0s, p0}});
-  getActionDefinitionsBuilder(G_INTTOPTR).legalFor({{p0, p0s}});
+  getActionDefinitionsBuilder(G_PTRTOINT)
+      .legalFor({p0s, p0})
+      .customForCartesianProduct({s32, s64}, {p0});
+  getActionDefinitionsBuilder(G_INTTOPTR)
+      .legalFor({p0, p0s})
+      .customForCartesianProduct({p0}, {s32, s64});
   getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, p0s}});
 
   getActionDefinitionsBuilder(G_LOAD)
@@ -260,6 +262,139 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
   auto &MIRBuilder = Helper.MIRBuilder;
 
   switch (MI.getOpcode()) {
+  case WebAssembly::G_PTRTOINT: {
+    auto TmpReg = MRI.createGenericVirtualRegister(
+        LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
+
+    MIRBuilder.buildPtrToInt(TmpReg, MI.getOperand(1));
+    MIRBuilder.buildAnyExtOrTrunc(MI.getOperand(0), TmpReg);
+    MI.eraseFromParent();
+    return true;
+  }
+  case WebAssembly::G_INTTOPTR: {
+    auto TmpReg = MRI.createGenericVirtualRegister(
+        LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
+
+    MIRBuilder.buildAnyExtOrTrunc(TmpReg, MI.getOperand(1));
+    MIRBuilder.buildIntToPtr(MI.getOperand(0), TmpReg);
+    MI.eraseFromParent();
+    return true;
+  }
+  case TargetOpcode::G_FCMP: {
+    Register LHS = MI.getOperand(2).getReg();
+    Register RHS = MI.getOperand(3).getReg();
+    CmpInst::Predicate Cond =
+        static_cast<CmpInst::Predicate>(MI.getOperand(1).getPredicate());
+
+    auto CmpWidth = MRI.getType(LHS).getSizeInBits();
+    assert(CmpWidth == MRI.getType(RHS).getSizeInBits() &&
+           "LHS and RHS for FCMP are diffrent lengths???");
+
+    auto IsI64 = CmpWidth == 64;
+
+    switch (Cond) {
+    case CmpInst::FCMP_FALSE:
+      return false;
+    case CmpInst::FCMP_OEQ:
+      return true;
+    case CmpInst::FCMP_OGT:
+      return true;
+    case CmpInst::FCMP_OGE:
+      return true;
+    case CmpInst::FCMP_OLT:
+      return true;
+    case CmpInst::FCMP_OLE:
+      return true;
+    case CmpInst::FCMP_ONE: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegC = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, TmpRegA, LHS, RHS);
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, TmpRegB, LHS, RHS);
+      MIRBuilder.buildOr(TmpRegC, TmpRegA, TmpRegB);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegC);
+      break;
+    }
+    case CmpInst::FCMP_ORD: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegC = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, TmpRegA, LHS, LHS);
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, TmpRegB, RHS, RHS);
+      MIRBuilder.buildAnd(TmpRegC, TmpRegA, TmpRegB);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegC);
+      break;
+    }
+    case CmpInst::FCMP_UNO: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegC = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, TmpRegA, LHS, LHS);
+      MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, TmpRegB, RHS, RHS);
+      MIRBuilder.buildOr(TmpRegC, TmpRegA, TmpRegB);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegC);
+      break;
+    }
+    case CmpInst::FCMP_UEQ: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_ONE, TmpRegA, LHS, RHS);
+      MIRBuilder.buildNot(TmpRegB, TmpRegA);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegB);
+      break;
+    }
+    case CmpInst::FCMP_UGT: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OLE, TmpRegA, LHS, RHS);
+      MIRBuilder.buildNot(TmpRegB, TmpRegA);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegB);
+      break;
+    }
+    case CmpInst::FCMP_UGE: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, TmpRegA, LHS, RHS);
+      MIRBuilder.buildNot(TmpRegB, TmpRegA);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegB);
+      break;
+    }
+    case CmpInst::FCMP_ULT: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OGE, TmpRegA, LHS, RHS);
+      MIRBuilder.buildNot(TmpRegB, TmpRegA);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegB);
+      break;
+    }
+    case CmpInst::FCMP_ULE: {
+      auto TmpRegA = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto TmpRegB = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, TmpRegA, LHS, RHS);
+      MIRBuilder.buildNot(TmpRegB, TmpRegA);
+      MIRBuilder.buildAnyExt(MI.getOperand(0).getReg(), TmpRegB);
+      break;
+    }
+    case CmpInst::FCMP_UNE:
+      return true;
+    case CmpInst::FCMP_TRUE:
+      return false;
+    default:
+      llvm_unreachable("Unknown FCMP predicate");
+    }
+
+    MI.eraseFromParent();
+
+    return true;
+  }
   case TargetOpcode::G_SEXT_INREG: {
     assert(MI.getOperand(2).isImm() && "Expected immediate");
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index fa4103a8b1b31..096cd2125ec22 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -304,9 +304,8 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
     break;
   case G_BRJT:
-    OperandsMapping =
-        getOperandsMapping({&Op0IntValueMapping, nullptr,
-                            &WebAssembly::ValueMappings[WebAssembly::I32Idx]});
+    OperandsMapping = getOperandsMapping(
+        {&Op0IntValueMapping, nullptr, &Pointer0ValueMapping});
     break;
   case COPY: {
     Register DstReg = MI.getOperand(0).getReg();
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
new file mode 100644
index 0000000000000..331424759f0df
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
@@ -0,0 +1,133 @@
+//===-- WebAssemblyGIsel.td - WASM GlobalISel Patterns -----*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file contains patterns that are relevant to GlobalISel, including
+/// GIComplexOperandMatcher definitions for equivalent SelectionDAG
+/// ComplexPatterns.
+//
+//===----------------------------------------------------------------------===//
+
+include "WebAssembly.td"
+
+
+//===----------------------------------------------------------------------===//
+// Pointer types and related patterns
+//===----------------------------------------------------------------------===//
+
+defvar ModeAddr32 = DefaultMode;
+def ModeAddr64 : HwMode<"", [HasAddr64]>;
+
+def Addr0VT : ValueTypeByHwMode<[ModeAddr32, ModeAddr64],
+                               [i32,  i64]>;
+
+def p0 : PtrValueTypeByHwMode<Addr0VT, 0>;
+
+// G_CONSTANT with p0
+def : Pat<(p0 (imm:$addr)),
+          (CONST_I32 imm:$addr)>, Requires<[HasAddr32]>;
+def : Pat<(p0 (imm:$addr)),
+          (CONST_I64 imm:$addr)>, Requires<[HasAddr64]>;
+
+// G_LOAD of p0
+def : Pat<(p0 (load (AddrOps32 offset32_op:$offset, I32:$addr))),
+          (LOAD_I32_A32 0,
+                        offset32_op:$offset,
+                        I32:$addr)>,
+          Requires<[HasAddr32]>;
+
+def : Pat<(p0 (load (AddrOps64 offset64_op:$offset, I64:$addr))),
+          (LOAD_I64_A64 0,
+                        offset64_op:$offset,
+                        I64:$addr)>,
+          Requires<[HasAddr64]>;
+
+// G_STORE of p0
+def : Pat<(store p0:$val, (AddrOps32 offset32_op:$offset, I32:$addr)),
+        (STORE_I32_A32 0,
+                       offset32_op:$offset,
+                       I32:$addr,
+                       p0:$val)>,
+        Requires<[HasAddr32]>;
+
+def : Pat<(store p0:$val, (AddrOps64 offset64_op:$offset, I64:$addr)),
+        (STORE_I64_A64 0,
+                       offset64_op:$offset,
+                       I64:$addr,
+                       p0:$val)>,
+        Requires<[HasAddr64]>;
+
+// G_SELECT of p0
+def : Pat<(select I32:$cond, p0:$lhs, p0:$rhs),
+          (SELECT_I32 I32:$lhs, I32:$rhs, I32:$cond)>, Requires<[HasAddr32]>;
+def : Pat<(select I32:$cond, p0:$lhs, p0:$rhs),
+          (SELECT_I64 I64:$lhs, I64:$rhs, I32:$cond)>, Requires<[HasAddr64]>;
+
+// ISD::SELECT requires its operand to conform to getBooleanContents, but
+// WebAssembly's select interprets any non-zero value as true, so we can fold
+// a setne with 0 into a select.
+def : Pat<(select (i32 (setne I32:$cond, 0)), p0:$lhs, p0:$rhs),
+          (SELECT_I32 I32:$lhs, I32:$rhs, I32:$cond)>, Requires<[HasAddr32]>;
+def : Pat<(select (i32 (setne I32:$cond, 0)), p0:$lhs, p0:$rhs),
+          (SELECT_I64 I64:$lhs, I64:$rhs, I32:$cond)>, Requires<[HasAddr64]>;
+
+// And again, this time with seteq instead of setne and the arms reversed.
+def : Pat<(select (i32 (seteq I32:$cond, 0)), p0:$lhs, p0:$rhs),
+          (SELECT_I32 I32:$rhs, I32:$lhs, I32:$cond)>, Requires<[HasAddr32]>;
+def : Pat<(select (i32 (seteq I32:$cond, 0)), p0:$lhs, p0:$rhs),
+          (SELECT_I64 I64:$rhs, I64:$lhs, I32:$cond)>, Requires<[HasAddr64]>;
+
+
+// G_ICMP between p0
+multiclass ComparisonP0<CondCode cond, string Name> {
+  def : Pat<(setcc p0:$lhs, p0:$rhs, cond),
+            (!cast<NI>(Name # "_I32") I32:$lhs, I32:$rhs)>, Requires<[HasAddr32]>;
+  def : Pat<(setcc p0:$lhs, p0:$rhs, cond),
+            (!cast<NI>(Name # "_I64") I64:$lhs, I64:$rhs)>, Requires<[HasAddr64]>;
+}
+
+defm : ComparisonP0<SETEQ, "EQ">;
+defm : ComparisonP0<SETNE, "NE">;
+defm : ComparisonP0<SETLT,  "LT_S">;
+defm : ComparisonP0<SETULT, "LT_U">;
+defm : ComparisonP0<SETGT,  "GT_S">;
+defm : ComparisonP0<SETUGT, "GT_U">;
+defm : ComparisonP0<SETLE,  "LE_S">;
+defm : ComparisonP0<SETULE, "LE_U">;
+defm : ComparisonP0<SETGE,  "GE_S">;
+defm : ComparisonP0<SETUGE, "GE_U">;
+
+//===----------------------------------------------------------------------===//
+// Miscallenous patterns
+//===----------------------------------------------------------------------===//
+
+def : Pat<(i32 (fp_to_sint_sat_gi F32:$src)), (I32_TRUNC_S_SAT_F32 F32:$src)>;
+def : Pat<(i32 (fp_to_uint_sat_gi F32:$src)), (I32_TRUNC_U_SAT_F32 F32:$src)>;
+def : Pat<(i32 (fp_to_sint_sat_gi F64:$src)), (I32_TRUNC_S_SAT_F64 F64:$src)>;
+def : Pat<(i32 (fp_to_uint_sat_gi F64:$src)), (I32_TRUNC_U_SAT_F64 F64:$src)>;
+def : Pat<(i64 (fp_to_sint_sat_gi F32:$src)), (I64_TRUNC_S_SAT_F32 F32:$src)>;
+def : Pat<(i64 (fp_to_uint_sat_gi F32:$src)), (I64_TRUNC_U_SAT_F32 F32:$src)>;
+def : Pat<(i64 (fp_to_sint_sat_gi F64:$src)), (I64_TRUNC_S_SAT_F64 F64:$src)>;
+def : Pat<(i64 (fp_to_uint_sat_gi F64:$src)), (I64_TRUNC_U_SAT_F64 F64:$src)>;
+
+def : GINodeEquiv<G_DEBUGTRAP, debugtrap>;
+
+def : Pat<(i32 (ctlz_zero_undef I32:$src)), (CLZ_I32 I32:$src)>;
+def : Pat<(i64 (ctlz_zero_undef I64:$src)), (CLZ_I64 I64:$src)>;
+def : Pat<(i32 (cttz_zero_undef I32:$src)), (CTZ_I32 I32:$src)>;
+def : Pat<(i64 (cttz_zero_undef I64:$src)), (CTZ_I64 I64:$src)>;
+
+//===----------------------------------------------------------------------===//
+// Complex pattern equivalents
+//===----------------------------------------------------------------------===//
+
+def gi_AddrOps32 : GIComplexOperandMatcher<s32, "selectAddrOperands32">,
+                   GIComplexPatternEquiv<AddrOps32>;
+
+def gi_AddrOps64 : GIComplexOperandMatcher<s64, "selectAddrOperands64">,
+                   GIComplexPatternEquiv<AddrOps64>;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
index 5d5af1b7a61bd..0cbe9d0c6a6a4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
@@ -33,12 +33,6 @@
 def AddrOps32 : ComplexPattern<i32, 2, "SelectAddrOperands32">;
 def AddrOps64 : ComplexPattern<i64, 2, "SelectAddrOperands64">;
 
-def gi_AddrOps32 : GIComplexOperandMatcher<s32, "selectAddrOperands32">,
-                   GIComplexPatternEquiv<AddrOps32>;
-
-def gi_AddrOps64 : GIComplexOperandMatcher<s64, "selectAddrOperands32">,
-                   GIComplexPatternEquiv<AddrOps64>;
-
 // Defines atomic and non-atomic loads, regular and extending.
 multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode,
                            list<Predicate> reqs = []> {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 192ad3d288cf1..6a3e8148837fa 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -692,6 +692,12 @@ bool WebAssemblyPassConfig::addRegBankSelect() {
 
 bool WebAssemblyPassConfig::addGlobalInstructionSelect() {
   addPass(new InstructionSelect(getOptLevel()));
+
+  addPass(createWebAssemblyArgumentMove());
+  addPass(createWebAssemblySetP2AlignOperands());
+  addPass(createWebAssemblyFixBrTableDefaults());
+  addPass(createWebAssemblyCleanCodeAfterTrap());
+
   return false;
 }
 

>From 8b18b1c8bdfda0674fa34f3183f90aa62434d0b4 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 6 Oct 2025 13:43:16 -0700
Subject: [PATCH 12/17] Fix error due to difference HwMode signature

---
 llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp  | 3 ---
 llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp | 2 --
 llvm/lib/Target/WebAssembly/WebAssemblyGISel.td                | 2 +-
 3 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 1990d3e4e3adb..43ba7b1a983aa 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -440,10 +440,7 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
                                           Register SwiftErrorVReg) const {
   auto MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
   MachineFunction &MF = MIRBuilder.getMF();
-  auto &TLI = *getTLI<WebAssemblyTargetLowering>();
   auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
-  auto &TRI = *Subtarget.getRegisterInfo();
-  auto &TII = *Subtarget.getInstrInfo();
   auto &RBI = *Subtarget.getRegBankInfo();
 
   assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 633dd48cb3ac6..3e9d5957a22bc 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -290,8 +290,6 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
     assert(CmpWidth == MRI.getType(RHS).getSizeInBits() &&
            "LHS and RHS for FCMP are diffrent lengths???");
 
-    auto IsI64 = CmpWidth == 64;
-
     switch (Cond) {
     case CmpInst::FCMP_FALSE:
       return false;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
index 331424759f0df..5ed2dede7a080 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
@@ -21,7 +21,7 @@ include "WebAssembly.td"
 //===----------------------------------------------------------------------===//
 
 defvar ModeAddr32 = DefaultMode;
-def ModeAddr64 : HwMode<"", [HasAddr64]>;
+def ModeAddr64 : HwMode<[HasAddr64]>;
 
 def Addr0VT : ValueTypeByHwMode<[ModeAddr32, ModeAddr64],
                                [i32,  i64]>;

>From fb879924863426996bb7e9a6d4f0e35c95168d88 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 9 Oct 2025 12:15:09 -0700
Subject: [PATCH 13/17] Setup combiners

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |  11 +-
 .../GISel/WebAssemblyCallLowering.cpp         |  11 +-
 .../GISel/WebAssemblyLegalizerInfo.cpp        |   2 +-
 .../WebAssemblyO0PreLegalizerCombiner.cpp     | 154 ++++++++++++++++
 .../WebAssemblyPostLegalizerCombiner.cpp      | 166 +++++++++++++++++
 .../GISel/WebAssemblyPreLegalizerCombiner.cpp | 172 ++++++++++++++++++
 .../GISel/WebAssemblyRegisterBankInfo.cpp     |   4 +
 llvm/lib/Target/WebAssembly/WebAssembly.h     |   9 +
 .../Target/WebAssembly/WebAssemblyCombine.td  |  26 +++
 .../Target/WebAssembly/WebAssemblyGISel.td    |   1 +
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |  17 ++
 11 files changed, 564 insertions(+), 9 deletions(-)
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyCombine.td

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index aa604ee8cb2c9..fa79d35c8872f 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -15,14 +15,23 @@ tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
 
 set(LLVM_TARGET_DEFINITIONS WebAssemblyGISel.td)
 tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel)
+tablegen(LLVM WebAssemblyGenO0PreLegalizeGICombiner.inc -gen-global-isel-combiner
+              -combiners="WebAssemblyO0PreLegalizerCombiner")
+tablegen(LLVM WebAssemblyGenPreLegalizeGICombiner.inc -gen-global-isel-combiner
+              -combiners="WebAssemblyPreLegalizerCombiner")
+tablegen(LLVM WebAssemblyGenPostLegalizeGICombiner.inc -gen-global-isel-combiner
+              -combiners="WebAssemblyPostLegalizerCombiner")
 
 add_public_tablegen_target(WebAssemblyCommonTableGen)
 
 add_llvm_target(WebAssemblyCodeGen
   GISel/WebAssemblyCallLowering.cpp
   GISel/WebAssemblyInstructionSelector.cpp
-  GISel/WebAssemblyRegisterBankInfo.cpp
+  GISel/WebAssemblyO0PreLegalizerCombiner.cpp
+  GISel/WebAssemblyPostLegalizerCombiner.cpp
+  GISel/WebAssemblyPreLegalizerCombiner.cpp
   GISel/WebAssemblyLegalizerInfo.cpp
+  GISel/WebAssemblyRegisterBankInfo.cpp
   WebAssemblyAddMissingPrototypes.cpp
   WebAssemblyArgumentMove.cpp
   WebAssemblyAsmPrinter.cpp
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 43ba7b1a983aa..f852716f86268 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -521,13 +521,10 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
       }
 
       for (unsigned Part = 0; Part < NumParts; ++Part) {
-        auto NewOutReg = Arg.Regs[Part];
-        if (!RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI)) {
-          NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
-          assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) &&
-                 "Couldn't constrain brand-new register?");
-          MIRBuilder.buildCopy(NewOutReg, Arg.Regs[Part]);
-        }
+        auto NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
+        assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) &&
+                "Couldn't constrain brand-new register?");
+        MIRBuilder.buildCopy(NewOutReg, Arg.Regs[Part]);
         MIB.addUse(NewOutReg);
       }
     }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 3e9d5957a22bc..3f4f318961dbf 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -87,7 +87,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .clampScalar(0, s32, s64);
 
   getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL, G_CTLZ, G_CTLZ_ZERO_UNDEF,
-                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP})
+                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_ROTL, G_ROTR})
       .legalFor({{s32, s32}, {s64, s64}})
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
new file mode 100644
index 0000000000000..521aa2535e362
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
@@ -0,0 +1,154 @@
+//=== WebAssemblyVO0PreLegalizerCombiner.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass does combining of machine instructions at the generic MI level,
+// before the legalizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/Combiner.h"
+#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
+#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+
+#define GET_GICOMBINER_DEPS
+#include "WebAssemblyGenO0PreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "wasm-O0-prelegalizer-combiner"
+
+using namespace llvm;
+
+namespace {
+#define GET_GICOMBINER_TYPES
+#include "WebAssemblyGenO0PreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+class WebAssemblyO0PreLegalizerCombinerImpl : public Combiner {
+protected:
+  const CombinerHelper Helper;
+  const WebAssemblyO0PreLegalizerCombinerImplRuleConfig &RuleConfig;
+  const WebAssemblySubtarget &STI;
+
+public:
+  WebAssemblyO0PreLegalizerCombinerImpl(
+      MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+      GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+      const WebAssemblyO0PreLegalizerCombinerImplRuleConfig &RuleConfig,
+      const WebAssemblySubtarget &STI);
+
+  static const char *getName() { return "WebAssemblyO0PreLegalizerCombiner"; }
+
+  bool tryCombineAll(MachineInstr &I) const override;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "WebAssemblyGenO0PreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "WebAssemblyGenO0PreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+WebAssemblyO0PreLegalizerCombinerImpl::WebAssemblyO0PreLegalizerCombinerImpl(
+    MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+    GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+    const WebAssemblyO0PreLegalizerCombinerImplRuleConfig &RuleConfig,
+    const WebAssemblySubtarget &STI)
+    : Combiner(MF, CInfo, TPC, &VT, CSEInfo),
+      Helper(Observer, B, /*IsPreLegalize*/ true, &VT), RuleConfig(RuleConfig),
+      STI(STI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "WebAssemblyGenO0PreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+// Pass boilerplate
+// ================
+
+class WebAssemblyO0PreLegalizerCombiner : public MachineFunctionPass {
+public:
+  static char ID;
+
+  WebAssemblyO0PreLegalizerCombiner();
+
+  StringRef getPassName() const override {
+    return "WebAssemblyO0PreLegalizerCombiner";
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+  WebAssemblyO0PreLegalizerCombinerImplRuleConfig RuleConfig;
+};
+} // end anonymous namespace
+
+void WebAssemblyO0PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<TargetPassConfig>();
+  AU.setPreservesCFG();
+  getSelectionDAGFallbackAnalysisUsage(AU);
+  AU.addRequired<GISelValueTrackingAnalysisLegacy>();
+  AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
+  MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+WebAssemblyO0PreLegalizerCombiner::WebAssemblyO0PreLegalizerCombiner()
+    : MachineFunctionPass(ID) {
+  if (!RuleConfig.parseCommandLineOption())
+    report_fatal_error("Invalid rule identifier");
+}
+
+bool WebAssemblyO0PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+  if (MF.getProperties().hasFailedISel())
+    return false;
+  auto &TPC = getAnalysis<TargetPassConfig>();
+
+  const Function &F = MF.getFunction();
+  GISelValueTracking *VT =
+      &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+
+  const WebAssemblySubtarget &ST = MF.getSubtarget<WebAssemblySubtarget>();
+
+  CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
+                     /*LegalizerInfo*/ nullptr, /*EnableOpt*/ false,
+                     F.hasOptSize(), F.hasMinSize());
+  // Disable fixed-point iteration in the Combiner. This improves compile-time
+  // at the cost of possibly missing optimizations. See PR#94291 for details.
+  CInfo.MaxIterations = 1;
+
+  WebAssemblyO0PreLegalizerCombinerImpl Impl(MF, CInfo, &TPC, *VT,
+                                       /*CSEInfo*/ nullptr, RuleConfig, ST);
+  return Impl.combineMachineInstrs();
+}
+
+char WebAssemblyO0PreLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(WebAssemblyO0PreLegalizerCombiner, DEBUG_TYPE,
+                      "Combine WebAssembly machine instrs before legalization",
+                      false, false)
+INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
+INITIALIZE_PASS_END(WebAssemblyO0PreLegalizerCombiner, DEBUG_TYPE,
+                    "Combine WebAssembly machine instrs before legalization", false,
+                    false)
+
+FunctionPass *llvm::createWebAssemblyO0PreLegalizerCombiner() {
+  return new WebAssemblyO0PreLegalizerCombiner();
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
new file mode 100644
index 0000000000000..4bf687fc4785c
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
@@ -0,0 +1,166 @@
+//=== WebAssemblyPostLegalizerCombiner.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Post-legalization combines on generic MachineInstrs.
+///
+/// The combines here must preserve instruction legality.
+///
+/// Combines which don't rely on instruction legality should go in the
+/// WebAssemblyPreLegalizerCombiner.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "WebAssemblyTargetMachine.h"
+#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
+#include "llvm/CodeGen/GlobalISel/Combiner.h"
+#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
+#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+
+#define GET_GICOMBINER_DEPS
+#include "WebAssemblyGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "wasm-postlegalizer-combiner"
+
+using namespace llvm;
+
+namespace {
+
+#define GET_GICOMBINER_TYPES
+#include "WebAssemblyGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+class WebAssemblyPostLegalizerCombinerImpl : public Combiner {
+protected:
+  const CombinerHelper Helper;
+  const WebAssemblyPostLegalizerCombinerImplRuleConfig &RuleConfig;
+  const WebAssemblySubtarget &STI;
+
+public:
+  WebAssemblyPostLegalizerCombinerImpl(
+      MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+      GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+      const WebAssemblyPostLegalizerCombinerImplRuleConfig &RuleConfig,
+      const WebAssemblySubtarget &STI, MachineDominatorTree *MDT,
+      const LegalizerInfo *LI);
+
+  static const char *getName() { return "WebAssemblyPostLegalizerCombiner"; }
+
+  bool tryCombineAll(MachineInstr &I) const override;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "WebAssemblyGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "WebAssemblyGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+WebAssemblyPostLegalizerCombinerImpl::WebAssemblyPostLegalizerCombinerImpl(
+    MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+    GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+    const WebAssemblyPostLegalizerCombinerImplRuleConfig &RuleConfig,
+    const WebAssemblySubtarget &STI, MachineDominatorTree *MDT,
+    const LegalizerInfo *LI)
+    : Combiner(MF, CInfo, TPC, &VT, CSEInfo),
+      Helper(Observer, B, /*IsPreLegalize*/ false, &VT, MDT, LI),
+      RuleConfig(RuleConfig), STI(STI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "WebAssemblyGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+class WebAssemblyPostLegalizerCombiner : public MachineFunctionPass {
+public:
+  static char ID;
+
+  WebAssemblyPostLegalizerCombiner();
+
+  StringRef getPassName() const override {
+    return "WebAssemblyPostLegalizerCombiner";
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+  WebAssemblyPostLegalizerCombinerImplRuleConfig RuleConfig;
+};
+} // end anonymous namespace
+
+void WebAssemblyPostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<TargetPassConfig>();
+  AU.setPreservesCFG();
+  getSelectionDAGFallbackAnalysisUsage(AU);
+  AU.addRequired<GISelValueTrackingAnalysisLegacy>();
+  AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
+  AU.addRequired<MachineDominatorTreeWrapperPass>();
+  AU.addPreserved<MachineDominatorTreeWrapperPass>();
+  AU.addRequired<GISelCSEAnalysisWrapperPass>();
+  AU.addPreserved<GISelCSEAnalysisWrapperPass>();
+  MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+WebAssemblyPostLegalizerCombiner::WebAssemblyPostLegalizerCombiner()
+    : MachineFunctionPass(ID) {
+  if (!RuleConfig.parseCommandLineOption())
+    report_fatal_error("Invalid rule identifier");
+}
+
+bool WebAssemblyPostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+  if (MF.getProperties().hasFailedISel())
+    return false;
+  assert(MF.getProperties().hasLegalized() && "Expected a legalized function?");
+  auto *TPC = &getAnalysis<TargetPassConfig>();
+  const Function &F = MF.getFunction();
+  bool EnableOpt =
+      MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
+
+  const WebAssemblySubtarget &ST = MF.getSubtarget<WebAssemblySubtarget>();
+  const auto *LI = ST.getLegalizerInfo();
+
+  GISelValueTracking *VT =
+      &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+  MachineDominatorTree *MDT =
+      &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+  GISelCSEAnalysisWrapper &Wrapper =
+      getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
+  auto *CSEInfo = &Wrapper.get(TPC->getCSEConfig());
+
+  CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
+                     /*LegalizerInfo*/ nullptr, EnableOpt, F.hasOptSize(),
+                     F.hasMinSize());
+  WebAssemblyPostLegalizerCombinerImpl Impl(MF, CInfo, TPC, *VT, CSEInfo, RuleConfig,
+                                      ST, MDT, LI);
+  return Impl.combineMachineInstrs();
+}
+
+char WebAssemblyPostLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(WebAssemblyPostLegalizerCombiner, DEBUG_TYPE,
+                      "Combine WebAssembly MachineInstrs after legalization", false,
+                      false)
+INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_END(WebAssemblyPostLegalizerCombiner, DEBUG_TYPE,
+                    "Combine WebAssembly MachineInstrs after legalization", false,
+                    false)
+
+FunctionPass *llvm::createWebAssemblyPostLegalizerCombiner() {
+  return new WebAssemblyPostLegalizerCombiner();
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp
new file mode 100644
index 0000000000000..a939eafd77392
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp
@@ -0,0 +1,172 @@
+//=== WebAssemblyPreLegalizerCombiner.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass does combining of machine instructions at the generic MI level,
+// before the legalizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
+#include "llvm/CodeGen/GlobalISel/Combiner.h"
+#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
+#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/Target/TargetMachine.h"
+
+#define GET_GICOMBINER_DEPS
+#include "WebAssemblyGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "wasm-prelegalizer-combiner"
+
+using namespace llvm;
+
+namespace {
+
+#define GET_GICOMBINER_TYPES
+#include "WebAssemblyGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+class WebAssemblyPreLegalizerCombinerImpl : public Combiner {
+protected:
+  const CombinerHelper Helper;
+  const WebAssemblyPreLegalizerCombinerImplRuleConfig &RuleConfig;
+  const WebAssemblySubtarget &STI;
+
+public:
+  WebAssemblyPreLegalizerCombinerImpl(
+      MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+      GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+      const WebAssemblyPreLegalizerCombinerImplRuleConfig &RuleConfig,
+      const WebAssemblySubtarget &STI, MachineDominatorTree *MDT,
+      const LegalizerInfo *LI);
+
+  static const char *getName() { return "WebAssembly00PreLegalizerCombiner"; }
+
+  bool tryCombineAll(MachineInstr &I) const override;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "WebAssemblyGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "WebAssemblyGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+WebAssemblyPreLegalizerCombinerImpl::WebAssemblyPreLegalizerCombinerImpl(
+    MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+    GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+    const WebAssemblyPreLegalizerCombinerImplRuleConfig &RuleConfig,
+    const WebAssemblySubtarget &STI, MachineDominatorTree *MDT,
+    const LegalizerInfo *LI)
+    : Combiner(MF, CInfo, TPC, &VT, CSEInfo),
+      Helper(Observer, B, /*IsPreLegalize*/ true, &VT, MDT, LI),
+      RuleConfig(RuleConfig), STI(STI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "WebAssemblyGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+// Pass boilerplate
+// ================
+
+class WebAssemblyPreLegalizerCombiner : public MachineFunctionPass {
+public:
+  static char ID;
+
+  WebAssemblyPreLegalizerCombiner();
+
+  StringRef getPassName() const override { return "WebAssemblyPreLegalizerCombiner"; }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+  WebAssemblyPreLegalizerCombinerImplRuleConfig RuleConfig;
+};
+} // end anonymous namespace
+
+void WebAssemblyPreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<TargetPassConfig>();
+  AU.setPreservesCFG();
+  getSelectionDAGFallbackAnalysisUsage(AU);
+  AU.addRequired<GISelValueTrackingAnalysisLegacy>();
+  AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
+  AU.addRequired<MachineDominatorTreeWrapperPass>();
+  AU.addPreserved<MachineDominatorTreeWrapperPass>();
+  AU.addRequired<GISelCSEAnalysisWrapperPass>();
+  AU.addPreserved<GISelCSEAnalysisWrapperPass>();
+  MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+WebAssemblyPreLegalizerCombiner::WebAssemblyPreLegalizerCombiner()
+    : MachineFunctionPass(ID) {
+  if (!RuleConfig.parseCommandLineOption())
+    report_fatal_error("Invalid rule identifier");
+}
+
+bool WebAssemblyPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+  if (MF.getProperties().hasFailedISel())
+    return false;
+  auto &TPC = getAnalysis<TargetPassConfig>();
+
+  // Enable CSE.
+  GISelCSEAnalysisWrapper &Wrapper =
+      getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
+  auto *CSEInfo = &Wrapper.get(TPC.getCSEConfig());
+
+  const WebAssemblySubtarget &ST = MF.getSubtarget<WebAssemblySubtarget>();
+  const auto *LI = ST.getLegalizerInfo();
+
+  const Function &F = MF.getFunction();
+  bool EnableOpt =
+      MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
+  GISelValueTracking *VT =
+      &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+  MachineDominatorTree *MDT =
+      &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+  CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
+                     /*LegalizerInfo*/ nullptr, EnableOpt, F.hasOptSize(),
+                     F.hasMinSize());
+  // Disable fixed-point iteration to reduce compile-time
+  CInfo.MaxIterations = 1;
+  CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
+  // This is the first Combiner, so the input IR might contain dead
+  // instructions.
+  CInfo.EnableFullDCE = true;
+  WebAssemblyPreLegalizerCombinerImpl Impl(MF, CInfo, &TPC, *VT, CSEInfo, RuleConfig,
+                                     ST, MDT, LI);
+  return Impl.combineMachineInstrs();
+}
+
+char WebAssemblyPreLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(WebAssemblyPreLegalizerCombiner, DEBUG_TYPE,
+                      "Combine WebAssembly machine instrs before legalization", false,
+                      false)
+INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
+INITIALIZE_PASS_END(WebAssemblyPreLegalizerCombiner, DEBUG_TYPE,
+                    "Combine WebAssembly machine instrs before legalization", false,
+                    false)
+
+FunctionPass *llvm::createWebAssemblyPreLegalizerCombiner() {
+  return new WebAssemblyPreLegalizerCombiner();
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index 096cd2125ec22..edb217a0c71d6 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -167,6 +167,8 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case G_CTPOP:
   case G_FSHL:
   case G_FSHR:
+  case G_ROTR:
+  case G_ROTL:
     OperandsMapping = &Op0IntValueMapping;
     break;
   case G_FADD:
@@ -218,6 +220,8 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     break;
   }
   case G_LOAD:
+  case G_ZEXTLOAD:
+  case G_SEXTLOAD:
   case G_STORE:
     if (MRI.getType(MI.getOperand(1).getReg()).getAddressSpace() != 0)
       break;
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 0c56c5077c563..5901e4b1a47aa 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -41,6 +41,15 @@ createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &,
                                      const WebAssemblySubtarget &,
                                      const WebAssemblyRegisterBankInfo &);
 
+FunctionPass *createWebAssemblyPostLegalizerCombiner();
+void initializeWebAssemblyPostLegalizerCombinerPass(PassRegistry &);
+
+FunctionPass *createWebAssemblyO0PreLegalizerCombiner();
+void initializeWebAssemblyO0PreLegalizerCombinerPass(PassRegistry &);
+
+FunctionPass *createWebAssemblyPreLegalizerCombiner();
+void initializeWebAssemblyPreLegalizerCombinerPass(PassRegistry &);
+
 // ISel and immediate followup passes.
 FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
                                        CodeGenOptLevel OptLevel);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td b/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td
new file mode 100644
index 0000000000000..d70fd27fa2ad3
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td
@@ -0,0 +1,26 @@
+//=- WebAssemblyCombine.td - Define WASM Combine Rules -------*- tablegen -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Target/GlobalISel/Combine.td"
+
+def WebAssemblyPreLegalizerCombiner: GICombiner<
+  "WebAssemblyPreLegalizerCombinerImpl", [all_combines]> {
+}
+
+def WebAssemblyO0PreLegalizerCombiner: GICombiner<
+  "WebAssemblyO0PreLegalizerCombinerImpl", [optnone_combines]> {
+}
+
+// Post-legalization combines which are primarily optimizations.
+def WebAssemblyPostLegalizerCombiner
+    : GICombiner<"WebAssemblyPostLegalizerCombinerImpl",
+                 []> {
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
index 5ed2dede7a080..55656731eaf3e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 include "WebAssembly.td"
+include "WebAssemblyCombine.td"
 
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 6a3e8148837fa..bf452cb9e520f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -97,6 +97,9 @@ LLVMInitializeWebAssemblyTarget() {
   // Register backend passes
   auto &PR = *PassRegistry::getPassRegistry();
   initializeGlobalISel(PR);
+  initializeWebAssemblyO0PreLegalizerCombinerPass(PR);
+  initializeWebAssemblyPreLegalizerCombinerPass(PR);
+  initializeWebAssemblyPostLegalizerCombinerPass(PR);
   initializeWebAssemblyAddMissingPrototypesPass(PR);
   initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
   initializeLowerGlobalDtorsLegacyPassPass(PR);
@@ -452,7 +455,9 @@ class WebAssemblyPassConfig final : public TargetPassConfig {
   bool addRegAssignAndRewriteOptimized() override { return false; }
 
   bool addIRTranslator() override;
+  void addPreLegalizeMachineIR() override;
   bool addLegalizeMachineIR() override;
+  void addPreRegBankSelect() override;
   bool addRegBankSelect() override;
   bool addGlobalInstructionSelect() override;
 };
@@ -680,11 +685,23 @@ bool WebAssemblyPassConfig::addIRTranslator() {
   return false;
 }
 
+void WebAssemblyPassConfig::addPreLegalizeMachineIR() {
+  if (getOptLevel() == CodeGenOptLevel::None) {
+    addPass(createWebAssemblyO0PreLegalizerCombiner());
+  } else {
+    addPass(createWebAssemblyPreLegalizerCombiner());
+  }
+}
 bool WebAssemblyPassConfig::addLegalizeMachineIR() {
   addPass(new Legalizer());
   return false;
 }
 
+void WebAssemblyPassConfig::addPreRegBankSelect() {
+  if (getOptLevel() != CodeGenOptLevel::None)
+    addPass(createWebAssemblyPostLegalizerCombiner());
+}
+
 bool WebAssemblyPassConfig::addRegBankSelect() {
   addPass(new RegBankSelect());
   return false;

>From 60f4aff1f3694d3491fc9b62dffc19c62ced9d00 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Fri, 10 Oct 2025 00:29:08 -0700
Subject: [PATCH 14/17] Implement more floating-point ops

---
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 75 +++++++++++++++++--
 .../GISel/WebAssemblyRegisterBankInfo.cpp     |  2 +
 .../Target/WebAssembly/WebAssemblyGISel.td    |  4 +
 .../WebAssembly/WebAssemblyRegisterBanks.td   |  2 +-
 4 files changed, 74 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 3f4f318961dbf..563570a2096fd 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -87,7 +87,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .clampScalar(0, s32, s64);
 
   getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL, G_CTLZ, G_CTLZ_ZERO_UNDEF,
-                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_ROTL, G_ROTR})
+                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_ROTL,
+                               G_ROTR})
       .legalFor({{s32, s32}, {s64, s64}})
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
@@ -110,14 +111,16 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
   getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FDIV, G_FMUL, G_FNEG, G_FABS,
                                G_FCEIL, G_FFLOOR, G_FSQRT, G_INTRINSIC_TRUNC,
                                G_FNEARBYINT, G_FRINT, G_INTRINSIC_ROUNDEVEN,
-                               G_FMINIMUM, G_FMAXIMUM})
+                               G_FMINIMUM, G_FMAXIMUM, G_STRICT_FMUL})
       .legalFor({s32, s64})
       .minScalar(0, s32);
 
-  // TODO: _IEEE not lowering correctly?
-  getActionDefinitionsBuilder(
-      {G_FMINNUM, G_FMAXNUM, G_FMINNUM_IEEE, G_FMAXNUM_IEEE})
-      .lowerFor({s32, s64})
+  getActionDefinitionsBuilder({G_FMINNUM, G_FMAXNUM})
+      .customFor({s32, s64})
+      .minScalar(0, s32);
+
+  getActionDefinitionsBuilder(G_FCANONICALIZE)
+      .customFor({s32, s64})
       .minScalar(0, s32);
 
   getActionDefinitionsBuilder({G_FMA, G_FREM})
@@ -262,7 +265,63 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
   auto &MIRBuilder = Helper.MIRBuilder;
 
   switch (MI.getOpcode()) {
-  case WebAssembly::G_PTRTOINT: {
+  case TargetOpcode::G_FCANONICALIZE: {
+    auto One = MRI.createGenericVirtualRegister(
+        MRI.getType(MI.getOperand(0).getReg()));
+    MIRBuilder.buildFConstant(One, 1.0);
+
+    MIRBuilder.buildInstr(TargetOpcode::G_STRICT_FMUL)
+        .addDef(MI.getOperand(0).getReg())
+        .addUse(MI.getOperand(1).getReg())
+        .addUse(One)
+        .setMIFlags(MI.getFlags())
+        .setMIFlag(MachineInstr::MIFlag::NoFPExcept);
+
+    MI.eraseFromParent();
+    return true;
+  }
+  case TargetOpcode::G_FMINNUM: {
+    if (!MI.getFlag(MachineInstr::MIFlag::FmNoNans))
+      return false;
+
+    if (MI.getFlag(MachineInstr::MIFlag::FmNsz)) {
+      MIRBuilder.buildInstr(TargetOpcode::G_FMINIMUM)
+          .addDef(MI.getOperand(0).getReg())
+          .addUse(MI.getOperand(1).getReg())
+          .addUse(MI.getOperand(2).getReg())
+          .setMIFlags(MI.getFlags());
+    } else {
+      auto TmpReg = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OLT, TmpReg,
+                           MI.getOperand(1), MI.getOperand(2));
+      MIRBuilder.buildSelect(MI.getOperand(0), TmpReg, MI.getOperand(1),
+                             MI.getOperand(2));
+    }
+    MI.eraseFromParent();
+    return true;
+  }
+  case TargetOpcode::G_FMAXNUM: {
+    if (!MI.getFlag(MachineInstr::MIFlag::FmNoNans))
+      return false;
+    if (MI.getFlag(MachineInstr::MIFlag::FmNsz)) {
+      MIRBuilder.buildInstr(TargetOpcode::G_FMAXIMUM)
+          .addDef(MI.getOperand(0).getReg())
+          .addUse(MI.getOperand(1).getReg())
+          .addUse(MI.getOperand(2).getReg())
+          .setMIFlags(MI.getFlags());
+    } else {
+      auto TmpReg = MRI.createGenericVirtualRegister(LLT::scalar(1));
+
+      MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OGT, TmpReg,
+                           MI.getOperand(1), MI.getOperand(2));
+      MIRBuilder.buildSelect(MI.getOperand(0), TmpReg, MI.getOperand(1),
+                             MI.getOperand(2));
+    }
+    MI.eraseFromParent();
+    return true;
+  }
+  case TargetOpcode::G_PTRTOINT: {
     auto TmpReg = MRI.createGenericVirtualRegister(
         LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
 
@@ -271,7 +330,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
     MI.eraseFromParent();
     return true;
   }
-  case WebAssembly::G_INTTOPTR: {
+  case TargetOpcode::G_INTTOPTR: {
     auto TmpReg = MRI.createGenericVirtualRegister(
         LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index edb217a0c71d6..b09b0f0f4b0ff 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -193,6 +193,8 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   case G_FMA:
   case G_FREM:
   case G_FCOPYSIGN:
+  case G_FCANONICALIZE:
+  case G_STRICT_FMUL:
     OperandsMapping = &Op0FloatValueMapping;
     break;
   case G_SEXT_INREG:
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
index 55656731eaf3e..3f70e3d0fc125 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
@@ -123,6 +123,10 @@ def : Pat<(i64 (ctlz_zero_undef I64:$src)), (CLZ_I64 I64:$src)>;
 def : Pat<(i32 (cttz_zero_undef I32:$src)), (CTZ_I32 I32:$src)>;
 def : Pat<(i64 (cttz_zero_undef I64:$src)), (CTZ_I64 I64:$src)>;
 
+
+def : Pat<(f32 (strict_fmul F32:$lhs, F32:$rhs)), (MUL_F32 F32:$lhs, F32:$rhs)>;
+def : Pat<(f64 (strict_fmul F64:$lhs, F64:$rhs)), (MUL_F64 F64:$lhs, F64:$rhs)>;
+
 //===----------------------------------------------------------------------===//
 // Complex pattern equivalents
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td
index 9ebece0e0bf09..7a527a321e2b7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterBanks.td
@@ -12,7 +12,7 @@
 
 def I32RegBank : RegisterBank<"I32RegBank", [I32]>;
 def I64RegBank : RegisterBank<"I64RegBank", [I64]>;
-def F32RegBank : RegisterBank<"F64RegBank", [F32]>;
+def F32RegBank : RegisterBank<"F32RegBank", [F32]>;
 def F64RegBank : RegisterBank<"F64RegBank", [F64]>;
 
 def EXTERNREFRegBank : RegisterBank<"EXTERNREFRegBank", [EXTERNREF]>;

>From 178c794fcb5f461138e16514198916a146f1c1f5 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Fri, 10 Oct 2025 13:36:11 -0700
Subject: [PATCH 15/17] Add scalarization for vector ops (fallback when SIMD
 isn't available)

---
 .../GISel/WebAssemblyCallLowering.cpp         |   7 +-
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 101 ++++++++++++++++--
 2 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index f852716f86268..5800d4b7fc399 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -659,8 +659,6 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
       for (unsigned Part = 0; Part < NumParts; ++Part) {
         Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
       }
-      buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT,
-                        Arg.Flags[0], Arg.Ty->isFloatingPointTy());
     }
 
     for (unsigned Part = 0; Part < NumParts; ++Part) {
@@ -673,6 +671,11 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
       MFI->addParam(NewVT);
       ++FinalArgIdx;
     }
+
+    if (NumParts != 1 || OrigVT != NewVT) {
+      buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT,
+                        Arg.Flags[0], Arg.Ty->isFloatingPointTy());
+    }
   }
 
   /**/
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 563570a2096fd..ce92105f5bbd5 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -47,6 +47,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_SELECT)
       .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
       .clampScalar(1, s32, s32);
@@ -55,12 +56,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_ICMP)
       .legalFor({{s32, s32}, {s32, s64}, {s32, p0}})
+      .scalarize(0)
       .widenScalarToNextPow2(1)
       .clampScalar(1, s32, s64)
       .clampScalar(0, s32, s32);
 
   getActionDefinitionsBuilder(G_FCMP)
       .customFor({{s32, s32}, {s32, s64}})
+      .scalarize(0)
       .clampScalar(0, s32, s32)
       .libcall();
 
@@ -77,32 +80,36 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_IMPLICIT_DEF)
       .legalFor({s32, s64, p0})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
 
   getActionDefinitionsBuilder(
       {G_ADD, G_SUB, G_MUL, G_UDIV, G_SDIV, G_UREM, G_SREM})
       .legalFor({s32, s64})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
 
   getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL, G_CTLZ, G_CTLZ_ZERO_UNDEF,
-                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP, G_ROTL,
-                               G_ROTR})
+                               G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP})
       .legalFor({{s32, s32}, {s64, s64}})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
       .minScalarSameAs(1, 0)
       .maxScalarSameAs(1, 0);
 
-  getActionDefinitionsBuilder({G_FSHL, G_FSHR})
+  getActionDefinitionsBuilder({G_FSHL, G_FSHR, G_ROTL, G_ROTR})
       .legalFor({{s32, s32}, {s64, s64}})
+      .scalarize(0)
       .lower();
 
   getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
 
   getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
       .legalFor({s32, s64})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
 
@@ -113,14 +120,35 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                G_FNEARBYINT, G_FRINT, G_INTRINSIC_ROUNDEVEN,
                                G_FMINIMUM, G_FMAXIMUM, G_STRICT_FMUL})
       .legalFor({s32, s64})
+      .scalarize(0)
       .minScalar(0, s32);
 
   getActionDefinitionsBuilder({G_FMINNUM, G_FMAXNUM})
       .customFor({s32, s64})
+      .scalarize(0)
       .minScalar(0, s32);
 
+  getActionDefinitionsBuilder({G_VECREDUCE_OR, G_VECREDUCE_AND}).scalarize(1);
+
+  getActionDefinitionsBuilder(G_BITCAST)
+      .customIf([=](const LegalityQuery &Query) {
+        // Handle casts from i1 vectors to scalars.
+        LLT DstTy = Query.Types[0];
+        LLT SrcTy = Query.Types[1];
+        return DstTy.isScalar() && SrcTy.isVector() &&
+               SrcTy.getScalarSizeInBits() == 1;
+      })
+      .lowerIf([=](const LegalityQuery &Query) {
+        return Query.Types[0].isVector() != Query.Types[1].isVector();
+      })
+      .scalarize(0);
+
+  getActionDefinitionsBuilder(G_MERGE_VALUES)
+      .lowerFor({{s64, s32}, {s64, s16}, {s64, s8}, {s32, s16}, {s32, s8}});
+
   getActionDefinitionsBuilder(G_FCANONICALIZE)
       .customFor({s32, s64})
+      .scalarize(0)
       .minScalar(0, s32);
 
   getActionDefinitionsBuilder({G_FMA, G_FREM})
@@ -135,6 +163,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_FCOPYSIGN)
       .legalFor({s32, s64})
+      .scalarize(0)
       .minScalar(0, s32)
       .minScalarSameAs(1, 0)
       .maxScalarSameAs(1, 0);
@@ -147,6 +176,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder({G_UITOFP, G_SITOFP})
       .legalForCartesianProduct({s32, s64}, {s32, s64})
+      .scalarize(0)
       .minScalar(1, s32)
       .widenScalarToNextPow2(1)
       .clampScalar(1, s32, s64);
@@ -169,7 +199,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                  {s64, p0, s16, 1},
                                  {s64, p0, s32, 1}})
       .clampScalar(0, s32, s64)
-      .lowerIfMemSizeNotByteSizePow2();
+      .lowerIfMemSizeNotByteSizePow2()
+      .scalarize(0);
 
   getActionDefinitionsBuilder(G_STORE)
       .legalForTypesWithMemDesc(
@@ -181,7 +212,25 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
                                  {s64, p0, s16, 1},
                                  {s64, p0, s32, 1}})
       .clampScalar(0, s32, s64)
-      .lowerIfMemSizeNotByteSizePow2();
+      .lowerIf([=](const LegalityQuery &Query) {
+        return Query.Types[0].isScalar() &&
+               Query.Types[0] != Query.MMODescrs[0].MemoryTy;
+      })
+      .bitcastIf(
+          [=](const LegalityQuery &Query) {
+            // Handle stores of i1 vectors.
+            LLT Ty = Query.Types[0];
+            return Ty.isVector() && Ty.getScalarSizeInBits() == 1;
+          },
+          [=](const LegalityQuery &Query) {
+            const LLT VecTy = Query.Types[0];
+            return std::pair(0, LLT::scalar(VecTy.getSizeInBits()));
+          })
+      .scalarize(0);
+
+  getActionDefinitionsBuilder(
+      {G_SHUFFLE_VECTOR, G_EXTRACT_VECTOR_ELT, G_INSERT_VECTOR_ELT})
+      .lower();
 
   getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
       .legalForTypesWithMemDesc({{s32, p0, s8, 1},
@@ -219,11 +268,13 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_ANYEXT)
       .legalFor({{s64, s32}})
+      .scalarize(0)
       .clampScalar(0, s32, s64)
       .clampScalar(1, s32, s64);
 
   getActionDefinitionsBuilder({G_SEXT, G_ZEXT})
       .legalFor({{s64, s32}})
+      .scalarize(0)
       .clampScalar(0, s32, s64)
       .clampScalar(1, s32, s64)
       .lower();
@@ -238,13 +289,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_TRUNC)
       .legalFor({{s32, s64}})
+      .scalarize(0)
       .clampScalar(0, s32, s64)
       .clampScalar(1, s32, s64)
       .lower();
 
-  getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}});
+  getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}}).scalarize(0);
 
-  getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}});
+  getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}}).scalarize(0);
 
   getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
   getActionDefinitionsBuilder(G_VAARG)
@@ -339,6 +391,41 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
     MI.eraseFromParent();
     return true;
   }
+  case TargetOpcode::G_BITCAST: {
+    if (MIRBuilder.getMF().getSubtarget<WebAssemblySubtarget>().hasSIMD128()) {
+      return false;
+    }
+
+    auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+
+    if (!DstTy.isScalar() || !SrcTy.isVector() ||
+        SrcTy.getElementType() != LLT::scalar(1))
+      return false;
+
+    Register ResultReg = MRI.createGenericVirtualRegister(DstTy);
+    MIRBuilder.buildConstant(ResultReg, 0);
+
+    for (unsigned i = 0; i < SrcTy.getNumElements(); i++) {
+      auto Elm = MRI.createGenericVirtualRegister(LLT::scalar(1));
+      auto ExtElm = MRI.createGenericVirtualRegister(DstTy);
+      auto ShiftedElm = MRI.createGenericVirtualRegister(DstTy);
+      auto Idx = MRI.createGenericVirtualRegister(LLT::scalar(8));
+      auto NewResultReg = MRI.createGenericVirtualRegister(DstTy);
+
+      MIRBuilder.buildConstant(Idx, i);
+      MIRBuilder.buildExtractVectorElement(Elm, SrcReg, Idx);
+      MIRBuilder.buildZExt(ExtElm, Elm, false);
+      MIRBuilder.buildShl(ShiftedElm, ExtElm, Idx);
+      MIRBuilder.buildOr(NewResultReg, ResultReg, ShiftedElm);
+
+      ResultReg = NewResultReg;
+    }
+
+    MIRBuilder.buildCopy(DstReg, ResultReg);
+
+    MI.eraseFromParent();
+    return true;
+  }
   case TargetOpcode::G_FCMP: {
     Register LHS = MI.getOperand(2).getReg();
     Register RHS = MI.getOperand(3).getReg();

>From 04fffd68830a53387096e71e62abab9ea7c270c5 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sat, 11 Oct 2025 12:16:13 -0700
Subject: [PATCH 16/17] Enable regbankselect to choose f32/f64 for ambiguous
 instructions (e.g. load)

---
 .../GISel/WebAssemblyLegalizerInfo.cpp        |   1 +
 .../GISel/WebAssemblyRegisterBankInfo.cpp     | 203 +++++++++++++++++-
 .../GISel/WebAssemblyRegisterBankInfo.h       |  28 +++
 .../WebAssembly/WebAssemblyInstrMemory.td     |  24 +--
 .../WebAssembly/WebAssemblyInstrSIMD.td       |   2 +-
 5 files changed, 236 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index ce92105f5bbd5..35bc8af7b189a 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -38,6 +38,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 
   getActionDefinitionsBuilder(G_PHI)
       .legalFor({p0, s32, s64})
+      .scalarize(0)
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
   getActionDefinitionsBuilder(G_BR).alwaysLegal();
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index b09b0f0f4b0ff..b57c79721c403 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -61,14 +61,113 @@ using namespace llvm;
 WebAssemblyRegisterBankInfo::WebAssemblyRegisterBankInfo(
     const TargetRegisterInfo &TRI) {}
 
+bool WebAssemblyRegisterBankInfo::isPHIWithFPConstraints(
+    const MachineInstr &MI, const MachineRegisterInfo &MRI,
+    const WebAssemblyRegisterInfo &TRI, const unsigned Depth) const {
+  if (!MI.isPHI() || Depth > MaxFPRSearchDepth)
+    return false;
+
+  return any_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+                [&](const MachineInstr &UseMI) {
+                  if (onlyUsesFP(UseMI, MRI, TRI, Depth + 1))
+                    return true;
+                  return isPHIWithFPConstraints(UseMI, MRI, TRI, Depth + 1);
+                });
+}
+
+bool WebAssemblyRegisterBankInfo::hasFPConstraints(
+    const MachineInstr &MI, const MachineRegisterInfo &MRI,
+    const WebAssemblyRegisterInfo &TRI, unsigned Depth) const {
+  unsigned Op = MI.getOpcode();
+  // if (Op == TargetOpcode::G_INTRINSIC && isFPIntrinsic(MRI, MI))
+  //   return true;
+
+  // Do we have an explicit floating point instruction?
+  if (isPreISelGenericFloatingPointOpcode(Op))
+    return true;
+
+  // No. Check if we have a copy-like instruction. If we do, then we could
+  // still be fed by floating point instructions.
+  if (Op != TargetOpcode::COPY && !MI.isPHI() &&
+      !isPreISelGenericOptimizationHint(Op))
+    return false;
+
+  // Check if we already know the register bank.
+  auto *RB = getRegBank(MI.getOperand(0).getReg(), MRI, TRI);
+  if (RB == &WebAssembly::F32RegBank || RB == &WebAssembly::F64RegBank)
+    return true;
+  if (RB == &WebAssembly::I32RegBank || RB == &WebAssembly::I64RegBank)
+    return false;
+
+  // We don't know anything.
+  //
+  // If we have a phi, we may be able to infer that it will be assigned a FPR
+  // based off of its inputs.
+  if (!MI.isPHI() || Depth > MaxFPRSearchDepth)
+    return false;
+
+  return any_of(MI.explicit_uses(), [&](const MachineOperand &Op) {
+    return Op.isReg() &&
+           onlyDefinesFP(*MRI.getVRegDef(Op.getReg()), MRI, TRI, Depth + 1);
+  });
+}
+
+bool WebAssemblyRegisterBankInfo::onlyUsesFP(const MachineInstr &MI,
+                                             const MachineRegisterInfo &MRI,
+                                             const WebAssemblyRegisterInfo &TRI,
+                                             unsigned Depth) const {
+  switch (MI.getOpcode()) {
+  case TargetOpcode::G_FPTOSI:
+  case TargetOpcode::G_FPTOUI:
+  case TargetOpcode::G_FPTOSI_SAT:
+  case TargetOpcode::G_FPTOUI_SAT:
+  case TargetOpcode::G_FCMP:
+  case TargetOpcode::G_LROUND:
+  case TargetOpcode::G_LLROUND:
+    return true;
+  default:
+    break;
+  }
+  return hasFPConstraints(MI, MRI, TRI, Depth);
+}
+
+bool WebAssemblyRegisterBankInfo::onlyDefinesFP(
+    const MachineInstr &MI, const MachineRegisterInfo &MRI,
+    const WebAssemblyRegisterInfo &TRI, unsigned Depth) const {
+  switch (MI.getOpcode()) {
+  case TargetOpcode::G_SITOFP:
+  case TargetOpcode::G_UITOFP:
+  case TargetOpcode::G_EXTRACT_VECTOR_ELT:
+  case TargetOpcode::G_INSERT_VECTOR_ELT:
+  case TargetOpcode::G_BUILD_VECTOR:
+  case TargetOpcode::G_BUILD_VECTOR_TRUNC:
+    return true;
+  default:
+    break;
+  }
+  return hasFPConstraints(MI, MRI, TRI, Depth);
+}
+
+bool WebAssemblyRegisterBankInfo::prefersFPUse(
+    const MachineInstr &MI, const MachineRegisterInfo &MRI,
+    const WebAssemblyRegisterInfo &TRI, unsigned Depth) const {
+  switch (MI.getOpcode()) {
+  case TargetOpcode::G_SITOFP:
+  case TargetOpcode::G_UITOFP:
+    return MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() ==
+           MRI.getType(MI.getOperand(1).getReg()).getSizeInBits();
+  }
+  return onlyDefinesFP(MI, MRI, TRI, Depth);
+}
+
 const RegisterBankInfo::InstructionMapping &
 WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
 
   unsigned Opc = MI.getOpcode();
   const MachineFunction &MF = *MI.getParent()->getParent();
   const MachineRegisterInfo &MRI = MF.getRegInfo();
-  const TargetSubtargetInfo &STI = MF.getSubtarget();
-  const TargetRegisterInfo &TRI = *STI.getRegisterInfo();
+  const WebAssemblySubtarget &STI = MF.getSubtarget<WebAssemblySubtarget>();
+  const WebAssemblyRegisterInfo &TRI = *STI.getRegisterInfo();
 
   if ((Opc != TargetOpcode::COPY && !isPreISelGenericOpcode(Opc)) ||
       Opc == TargetOpcode::G_PHI) {
@@ -223,13 +322,50 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
   }
   case G_LOAD:
   case G_ZEXTLOAD:
-  case G_SEXTLOAD:
-  case G_STORE:
+  case G_SEXTLOAD: {
     if (MRI.getType(MI.getOperand(1).getReg()).getAddressSpace() != 0)
       break;
+
+    auto *LoadValueMapping = &Op0IntValueMapping;
+    if (any_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+               [&](const MachineInstr &UseMI) {
+                 // If we have at least one direct or indirect use
+                 // in a FP instruction,
+                 // assume this was a floating point load in the IR. If it was
+                 // not, we would have had a bitcast before reaching that
+                 // instruction.
+                 //
+                 // Int->FP conversion operations are also captured in
+                 // prefersFPUse().
+
+                 if (isPHIWithFPConstraints(UseMI, MRI, TRI))
+                   return true;
+
+                 return onlyUsesFP(UseMI, MRI, TRI) ||
+                        prefersFPUse(UseMI, MRI, TRI);
+               }))
+      LoadValueMapping = &Op0FloatValueMapping;
     OperandsMapping =
-        getOperandsMapping({&Op0IntValueMapping, &Pointer0ValueMapping});
+        getOperandsMapping({LoadValueMapping, &Pointer0ValueMapping});
+    break;
+  }
+  case G_STORE: {
+    if (MRI.getType(MI.getOperand(1).getReg()).getAddressSpace() != 0)
+      break;
+
+    Register VReg = MI.getOperand(0).getReg();
+    if (!VReg)
+      break;
+    MachineInstr *DefMI = MRI.getVRegDef(VReg);
+    if (onlyDefinesFP(*DefMI, MRI, TRI)) {
+      OperandsMapping =
+          getOperandsMapping({&Op0FloatValueMapping, &Pointer0ValueMapping});
+    } else {
+      OperandsMapping =
+          getOperandsMapping({&Op0IntValueMapping, &Pointer0ValueMapping});
+    }
     break;
+  }
   case G_MEMCPY:
   case G_MEMMOVE: {
     if (MRI.getType(MI.getOperand(0).getReg()).getAddressSpace() != 0)
@@ -375,11 +511,60 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
         // We only care about the mapping of the destination for COPY.
         1);
   }
-  case G_SELECT:
-    OperandsMapping = getOperandsMapping(
-        {&Op0IntValueMapping, &WebAssembly::ValueMappings[WebAssembly::I32Idx],
-         &Op0IntValueMapping, &Op0IntValueMapping});
+  case G_SELECT: {
+    // Try to minimize the number of copies. If we have more floating point
+    // constrained values than not, then we'll put everything on FPR. Otherwise,
+    // everything has to be on GPR.
+    unsigned NumFP = 0;
+
+    // Check if the uses of the result always produce floating point values.
+    //
+    // For example:
+    //
+    // %z = G_SELECT %cond %x %y
+    // fpr = G_FOO %z ...
+    if (any_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+               [&](MachineInstr &MI) { return onlyUsesFP(MI, MRI, TRI); }))
+      ++NumFP;
+
+    // Check if the defs of the source values always produce floating point
+    // values.
+    //
+    // For example:
+    //
+    // %x = G_SOMETHING_ALWAYS_FLOAT %a ...
+    // %z = G_SELECT %cond %x %y
+    //
+    // Also check whether or not the sources have already been decided to be
+    // FPR. Keep track of this.
+    //
+    // This doesn't check the condition, since it's just whatever is in NZCV.
+    // This isn't passed explicitly in a register to fcsel/csel.
+    for (unsigned Idx = 2; Idx < 4; ++Idx) {
+      Register VReg = MI.getOperand(Idx).getReg();
+      MachineInstr *DefMI = MRI.getVRegDef(VReg);
+      if (getRegBank(VReg, MRI, TRI) == &WebAssembly::F32RegBank ||
+          getRegBank(VReg, MRI, TRI) == &WebAssembly::F64RegBank ||
+          onlyDefinesFP(*DefMI, MRI, TRI))
+        ++NumFP;
+    }
+
+    // If we have more FP constraints than not, then move everything over to
+    // FPR.
+    if (NumFP >= 2) {
+      OperandsMapping =
+          getOperandsMapping({&Op0FloatValueMapping,
+                              &WebAssembly::ValueMappings[WebAssembly::I32Idx],
+                              &Op0FloatValueMapping, &Op0FloatValueMapping});
+
+    } else {
+      OperandsMapping =
+          getOperandsMapping({&Op0IntValueMapping,
+                              &WebAssembly::ValueMappings[WebAssembly::I32Idx],
+                              &Op0IntValueMapping, &Op0IntValueMapping});
+    }
     break;
+  }
   case G_FPTOSI:
   case G_FPTOSI_SAT:
   case G_FPTOUI:
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
index f0d95b56ef861..d2cde32cff45e 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERBANKINFO_H
 #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERBANKINFO_H
 
+#include "WebAssemblyRegisterInfo.h"
 #include "llvm/CodeGen/RegisterBankInfo.h"
 
 #define GET_REGBANK_DECLARATIONS
@@ -35,6 +36,33 @@ class WebAssemblyRegisterBankInfo final
 
   const InstructionMapping &
   getInstrMapping(const MachineInstr &MI) const override;
+
+  /// Maximum recursion depth for hasFPConstraints.
+  const unsigned MaxFPRSearchDepth = 2;
+
+  /// \returns true if \p MI is a PHI that its def is used by
+  /// any instruction that onlyUsesFP.
+  bool isPHIWithFPConstraints(const MachineInstr &MI,
+                              const MachineRegisterInfo &MRI,
+                              const WebAssemblyRegisterInfo &TRI,
+                              unsigned Depth = 0) const;
+
+  /// \returns true if \p MI only uses and defines FPRs.
+  bool hasFPConstraints(const MachineInstr &MI, const MachineRegisterInfo &MRI,
+                        const WebAssemblyRegisterInfo &TRI,
+                        unsigned Depth = 0) const;
+
+  /// \returns true if \p MI only uses FPRs.
+  bool onlyUsesFP(const MachineInstr &MI, const MachineRegisterInfo &MRI,
+                  const WebAssemblyRegisterInfo &TRI, unsigned Depth = 0) const;
+
+  /// \returns true if \p MI only defines FPRs.
+  bool onlyDefinesFP(const MachineInstr &MI, const MachineRegisterInfo &MRI,
+                     const WebAssemblyRegisterInfo &TRI, unsigned Depth = 0) const;
+
+  /// \returns true if \p MI can take both fpr and gpr uses, but prefers fp.
+  bool prefersFPUse(const MachineInstr &MI, const MachineRegisterInfo &MRI,
+                    const WebAssemblyRegisterInfo &TRI, unsigned Depth = 0) const;
 };
 } // end namespace llvm
 #endif
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
index 0cbe9d0c6a6a4..85fb6812769f4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
@@ -145,8 +145,8 @@ defm STORE_I64  : WebAssemblyStore<I64, "i64.store", 0x37>;
 defm STORE_F32  : WebAssemblyStore<F32, "f32.store", 0x38>;
 defm STORE_F64  : WebAssemblyStore<F64, "f64.store", 0x39>;
 
-multiclass StorePat<ValueType ty, SDPatternOperator kind, string Name> {
-  def : Pat<(kind ty:$val, (AddrOps32 offset32_op:$offset, I32:$addr)),
+multiclass StorePat<ValueType ty, WebAssemblyRegClass rc, SDPatternOperator kind, string Name> {
+  def : Pat<(kind (ty rc:$val), (AddrOps32 offset32_op:$offset, I32:$addr)),
             (!cast<NI>(Name # "_A32") 0,
                                       offset32_op:$offset,
                                       I32:$addr,
@@ -160,10 +160,10 @@ multiclass StorePat<ValueType ty, SDPatternOperator kind, string Name> {
             Requires<[HasAddr64]>;
 }
 
-defm : StorePat<i32, store, "STORE_I32">;
-defm : StorePat<i64, store, "STORE_I64">;
-defm : StorePat<f32, store, "STORE_F32">;
-defm : StorePat<f64, store, "STORE_F64">;
+defm : StorePat<i32, I32, store, "STORE_I32">;
+defm : StorePat<i64, I64, store, "STORE_I64">;
+defm : StorePat<f32, F32, store, "STORE_F32">;
+defm : StorePat<f64, F64, store, "STORE_F64">;
 
 // Truncating store.
 defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>;
@@ -176,13 +176,13 @@ defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>;
 defm STORE_F16_F32 :
   WebAssemblyStore<F32, "f32.store_f16", 0xfc31, [HasFP16]>;
 
-defm : StorePat<i32, truncstorei8, "STORE8_I32">;
-defm : StorePat<i32, truncstorei16, "STORE16_I32">;
-defm : StorePat<i64, truncstorei8, "STORE8_I64">;
-defm : StorePat<i64, truncstorei16, "STORE16_I64">;
-defm : StorePat<i64, truncstorei32, "STORE32_I64">;
+defm : StorePat<i32, I32, truncstorei8, "STORE8_I32">;
+defm : StorePat<i32, I32, truncstorei16, "STORE16_I32">;
+defm : StorePat<i64, I64, truncstorei8, "STORE8_I64">;
+defm : StorePat<i64, I64, truncstorei16, "STORE16_I64">;
+defm : StorePat<i64, I64, truncstorei32, "STORE32_I64">;
 
-defm : StorePat<f32, int_wasm_storef16_f32, "STORE_F16_F32">;
+defm : StorePat<f32, F32, int_wasm_storef16_f32, "STORE_F16_F32">;
 
 multiclass MemoryOps<WebAssemblyRegClass rc, string B> {
 // Current memory size.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
index 130602650d34e..58307bc1d9779 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
@@ -391,7 +391,7 @@ defm STORE_V128_A64 :
 
 // Def store patterns from WebAssemblyInstrMemory.td for vector types
 foreach vec = AllVecs in {
-defm : StorePat<vec.vt, store, "STORE_V128">;
+defm : StorePat<vec.vt, V128, store, "STORE_V128">;
 }
 
 // Store lane

>From 43cbdf2ce22e951d496a4fd31e3de401a31965c2 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Sat, 11 Oct 2025 12:41:21 -0700
Subject: [PATCH 17/17] Ensure GlobalISel is actually linked in

---
 llvm/lib/Target/WebAssembly/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index fa79d35c8872f..dcadafb5a0a91 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -88,6 +88,7 @@ add_llvm_target(WebAssemblyCodeGen
   CodeGen
   CodeGenTypes
   Core
+  GlobalISel
   MC
   Scalar
   SelectionDAG



More information about the llvm-commits mailing list