[llvm] [WebAssembly] Implement GlobalISel (PR #157161)
Demetrius Kanios via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 18:08:42 PDT 2026
https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/157161
>From 4240fd51f7ba1a310b865d4bf5cd39de4ab390d1 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/41] 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 17df119d62709..ffb4ad182c81b 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -16,6 +16,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 a57ebfad03b1c..f2175fcc9171c 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);
@@ -442,6 +447,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
@@ -662,6 +672,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 527abebabb8c8f4267085818effda4e6b0649d07 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/41] 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 17e2e8d020d487e9e47f9368f400c4140ff9d464 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/41] 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 4ce15bb3956a37df8a749841a6cf4b41940af8b4 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/41] 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 744cf5a02e52476967b623c8aac816799f14d767 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/41] 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 af74b742793a76b90f7aed35c07d845f27b16fe9 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/41] 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 b11c19256344af7c285700cfa082a9eb10934e9b 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/41] 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 9b06b7ee66796150c7c5cf9c053d78b9c54580cb 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/41] 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 ffb4ad182c81b..e0be3d6af2f0c 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 WebAssemblyGenSDNodeInfo.inc -gen-sd-node-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 67015ffcfc760..67a2331b26b6e 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 177340bea3cef51d63e76aa271d02a8c0819708b 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/41] 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 5d8f652d42368536ffa3fb966b2edcdc7a876cdd 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/41] 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 e0be3d6af2f0c..e80850eb073fb 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 4ffd964550ef812c353f292dc76edcb0ba9ba4ad 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/41] 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 e80850eb073fb..a5a23d39cf700 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -7,7 +7,6 @@ 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)
@@ -15,6 +14,9 @@ tablegen(LLVM WebAssemblyGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM WebAssemblyGenSDNodeInfo.inc -gen-sd-node-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 f2175fcc9171c..bfeddf161bd58 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -689,6 +689,12 @@ bool WebAssemblyPassConfig::addRegBankSelect() {
bool WebAssemblyPassConfig::addGlobalInstructionSelect() {
addPass(new InstructionSelect(getOptLevel()));
+
+ addPass(createWebAssemblyArgumentMove());
+ addPass(createWebAssemblySetP2AlignOperands());
+ addPass(createWebAssemblyFixBrTableDefaults());
+ addPass(createWebAssemblyCleanCodeAfterTrap());
+
return false;
}
>From e5708dd36ffeb78a0d26d4ec442a1cda6c5915b1 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/41] 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 193052360dc37018883fa82b8c686c643b97356a 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/41] 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 a5a23d39cf700..a295e2daac20f 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -16,14 +16,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 bfeddf161bd58..13dbc0578f91f 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);
@@ -449,7 +452,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;
};
@@ -677,11 +682,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 67733b3c6302350fd29feca79ad2c1df61465ef8 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/41] 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 0fedde9c436040bbfc38dd615d9211bea0ef2b4f 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/41] 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 e8281c9bfec7ca8bb533ac031f801c6107ed1bd4 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/41] 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 fff9049ed39ba..7476e3892e24f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
@@ -396,7 +396,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 b742791e58ce744e525b68860618dfd74509a1c5 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/41] 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 a295e2daac20f..217df5f987e85 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -89,6 +89,7 @@ add_llvm_target(WebAssemblyCodeGen
CodeGen
CodeGenTypes
Core
+ GlobalISel
MC
Scalar
SelectionDAG
>From c4803196632159eb522e9729082b32af729c60a8 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 4 Dec 2025 15:03:59 -0800
Subject: [PATCH 18/41] Lower GISel bulk memory ops
---
.../GISel/WebAssemblyInstructionSelector.cpp | 5 +-
.../GISel/WebAssemblyLegalizerInfo.cpp | 67 ++++++++++++++++---
2 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 0ef5f357718ac..de3c6d0d8b6e6 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -145,7 +145,7 @@ WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
if (!TM.isPositionIndependent()) {
if (OpDef.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
- auto Offset = OpDef.getOperand(1).getGlobal();
+ auto *Offset = OpDef.getOperand(1).getGlobal();
auto Addr = OtherOp;
return {{
@@ -516,7 +516,7 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
"Couldn't constrain registers for instruction");
return true;
}
- case G_GLOBAL_VALUE:
+ case G_GLOBAL_VALUE: {
assert(I.getOperand(1).getTargetFlags() == 0 &&
"Unexpected target flags on generic G_GLOBAL_VALUE instruction");
assert(WebAssembly::isValidAddressSpace(
@@ -589,6 +589,7 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
return true;
}
+ }
return false;
}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 35bc8af7b189a..a159719049d67 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -247,16 +247,15 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_BZERO).unsupported();
getActionDefinitionsBuilder(G_MEMSET)
- .legalForCartesianProduct({p0}, {s32}, {p0s})
- .customForCartesianProduct({p0}, {s8}, {p0s})
+ .customForCartesianProduct({p0}, {s8, s32}, {p0s})
.immIdx(0);
getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
- .legalForCartesianProduct({p0}, {p0}, {p0s})
+ .customForCartesianProduct({p0}, {p0}, {p0s})
.immIdx(0);
getActionDefinitionsBuilder(G_MEMCPY_INLINE)
- .legalForCartesianProduct({p0}, {p0}, {p0s});
+ .customForCartesianProduct({p0}, {p0}, {p0s});
} else {
getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
.libcall();
@@ -566,13 +565,63 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
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);
+ if (MRI.getType(Value.getReg()).getSizeInBits() == 8) {
+ Register ExtValueReg =
+ Helper.MIRBuilder.buildAnyExt(LLT::scalar(32), Value).getReg(0);
+ Value.setReg(ExtValueReg);
+ return true;
+ }
+
+
+ MachineIRBuilder B(MI);
+ assert(MI.getNumMemOperands() == 1);
+
+ auto *DstMem = MI.memoperands()[0];
+ auto &Dst = MI.getOperand(0);
+ auto &Len = MI.getOperand(1);
+ // auto TailCall = I.getOperand(3);
+
+ auto PointerWidth = MI.getMF()->getDataLayout().getPointerSizeInBits();
+ auto PtrIsI64 = PointerWidth == 64;
+
+ B.buildInstr(PtrIsI64 ? WebAssembly::MEMSET_A64 : WebAssembly::MEMSET_A32)
+ .addImm(0) // DstMem idx
+ .add(Dst)
+ .add(Value)
+ .add(Len)
+ .addMemOperand(DstMem);
+
+ MI.eraseFromParent();
+
+ return true;
+ }
+ case TargetOpcode::G_MEMCPY:
+ case TargetOpcode::G_MEMMOVE: {
+ MachineIRBuilder B(MI);
+ assert(MI.getNumMemOperands() == 2);
+
+ auto *DstMem = MI.memoperands()[0];
+ auto *SrcMem = MI.memoperands()[1];
+ auto &Dst = MI.getOperand(0);
+ auto &Src = MI.getOperand(1);
+ auto &Len = MI.getOperand(2);
+ // auto TailCall = I.getOperand(3);
+
+ auto PointerWidth = MI.getMF()->getDataLayout().getPointerSizeInBits();
+ auto PtrIsI64 = PointerWidth == 64;
+
+ B.buildInstr(PtrIsI64 ? WebAssembly::MEMCPY_A64 : WebAssembly::MEMCPY_A32)
+ .addImm(0) // DstMem idx
+ .addImm(0) // SrcMem idx
+ .add(Dst)
+ .add(Src)
+ .add(Len)
+ .addMemOperand(DstMem)
+ .addMemOperand(SrcMem);
+
+ MI.eraseFromParent();
return true;
}
default:
>From 044f6672bd05aecc5d32cf8bdc12146752ebfdda Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Wed, 14 Jan 2026 11:29:35 -0800
Subject: [PATCH 19/41] Reformat
---
.../GISel/WebAssemblyCallLowering.cpp | 5 +---
.../GISel/WebAssemblyLegalizerInfo.cpp | 2 --
.../GISel/WebAssemblyLegalizerInfo.h | 3 ++-
.../WebAssemblyO0PreLegalizerCombiner.cpp | 13 ++++++----
.../WebAssemblyPostLegalizerCombiner.cpp | 20 +++++++++-------
.../GISel/WebAssemblyPreLegalizerCombiner.cpp | 24 +++++++++++--------
.../GISel/WebAssemblyRegisterBankInfo.h | 6 +++--
.../WebAssembly/WebAssemblySubtarget.cpp | 4 ++--
8 files changed, 42 insertions(+), 35 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 5800d4b7fc399..ab44263ff3a89 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -13,7 +13,6 @@
//===----------------------------------------------------------------------===//
#include "WebAssemblyCallLowering.h"
-#include "GISel/WebAssemblyRegisterBankInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "Utils/WasmAddressSpaces.h"
#include "WebAssemblyISelLowering.h"
@@ -42,9 +41,7 @@
#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"
@@ -523,7 +520,7 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
for (unsigned Part = 0; Part < NumParts; ++Part) {
auto NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
assert(RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI) &&
- "Couldn't constrain brand-new register?");
+ "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 a159719049d67..1a4d063b816c7 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -16,7 +16,6 @@
#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"
@@ -574,7 +573,6 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
return true;
}
-
MachineIRBuilder B(MI);
assert(MI.getNumMemOperands() == 1);
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
index 5aca23c9514e1..5e9c079f63da3 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
@@ -25,7 +25,8 @@ class WebAssemblyLegalizerInfo : public LegalizerInfo {
public:
WebAssemblyLegalizerInfo(const WebAssemblySubtarget &ST);
- bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, LostDebugLocObserver &LocObserver) const override;
+ bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI,
+ LostDebugLocObserver &LocObserver) const override;
};
} // namespace llvm
#endif
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
index 521aa2535e362..745b3d3de9403 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyO0PreLegalizerCombiner.cpp
@@ -100,7 +100,8 @@ class WebAssemblyO0PreLegalizerCombiner : public MachineFunctionPass {
};
} // end anonymous namespace
-void WebAssemblyO0PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+void WebAssemblyO0PreLegalizerCombiner::getAnalysisUsage(
+ AnalysisUsage &AU) const {
AU.addRequired<TargetPassConfig>();
AU.setPreservesCFG();
getSelectionDAGFallbackAnalysisUsage(AU);
@@ -115,7 +116,8 @@ WebAssemblyO0PreLegalizerCombiner::WebAssemblyO0PreLegalizerCombiner()
report_fatal_error("Invalid rule identifier");
}
-bool WebAssemblyO0PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyO0PreLegalizerCombiner::runOnMachineFunction(
+ MachineFunction &MF) {
if (MF.getProperties().hasFailedISel())
return false;
auto &TPC = getAnalysis<TargetPassConfig>();
@@ -134,7 +136,8 @@ bool WebAssemblyO0PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF
CInfo.MaxIterations = 1;
WebAssemblyO0PreLegalizerCombinerImpl Impl(MF, CInfo, &TPC, *VT,
- /*CSEInfo*/ nullptr, RuleConfig, ST);
+ /*CSEInfo*/ nullptr, RuleConfig,
+ ST);
return Impl.combineMachineInstrs();
}
@@ -146,8 +149,8 @@ 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)
+ "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
index 4bf687fc4785c..49416298ff1b4 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
@@ -1,4 +1,4 @@
-//=== WebAssemblyPostLegalizerCombiner.cpp --------------------------*- C++ -*-===//
+//=== 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.
@@ -104,7 +104,8 @@ class WebAssemblyPostLegalizerCombiner : public MachineFunctionPass {
};
} // end anonymous namespace
-void WebAssemblyPostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+void WebAssemblyPostLegalizerCombiner::getAnalysisUsage(
+ AnalysisUsage &AU) const {
AU.addRequired<TargetPassConfig>();
AU.setPreservesCFG();
getSelectionDAGFallbackAnalysisUsage(AU);
@@ -123,7 +124,8 @@ WebAssemblyPostLegalizerCombiner::WebAssemblyPostLegalizerCombiner()
report_fatal_error("Invalid rule identifier");
}
-bool WebAssemblyPostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyPostLegalizerCombiner::runOnMachineFunction(
+ MachineFunction &MF) {
if (MF.getProperties().hasFailedISel())
return false;
assert(MF.getProperties().hasLegalized() && "Expected a legalized function?");
@@ -146,20 +148,20 @@ bool WebAssemblyPostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF)
CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
/*LegalizerInfo*/ nullptr, EnableOpt, F.hasOptSize(),
F.hasMinSize());
- WebAssemblyPostLegalizerCombinerImpl Impl(MF, CInfo, TPC, *VT, CSEInfo, RuleConfig,
- ST, MDT, LI);
+ 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)
+ "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)
+ "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
index a939eafd77392..81f424db67227 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPreLegalizerCombiner.cpp
@@ -1,4 +1,4 @@
-//=== WebAssemblyPreLegalizerCombiner.cpp ---------------------------------------===//
+//=== 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.
@@ -92,7 +92,9 @@ class WebAssemblyPreLegalizerCombiner : public MachineFunctionPass {
WebAssemblyPreLegalizerCombiner();
- StringRef getPassName() const override { return "WebAssemblyPreLegalizerCombiner"; }
+ StringRef getPassName() const override {
+ return "WebAssemblyPreLegalizerCombiner";
+ }
bool runOnMachineFunction(MachineFunction &MF) override;
@@ -103,7 +105,8 @@ class WebAssemblyPreLegalizerCombiner : public MachineFunctionPass {
};
} // end anonymous namespace
-void WebAssemblyPreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+void WebAssemblyPreLegalizerCombiner::getAnalysisUsage(
+ AnalysisUsage &AU) const {
AU.addRequired<TargetPassConfig>();
AU.setPreservesCFG();
getSelectionDAGFallbackAnalysisUsage(AU);
@@ -122,7 +125,8 @@ WebAssemblyPreLegalizerCombiner::WebAssemblyPreLegalizerCombiner()
report_fatal_error("Invalid rule identifier");
}
-bool WebAssemblyPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyPreLegalizerCombiner::runOnMachineFunction(
+ MachineFunction &MF) {
if (MF.getProperties().hasFailedISel())
return false;
auto &TPC = getAnalysis<TargetPassConfig>();
@@ -151,21 +155,21 @@ bool WebAssemblyPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF)
// 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);
+ 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)
+ "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)
+ "Combine WebAssembly machine instrs before legalization",
+ false, false)
FunctionPass *llvm::createWebAssemblyPreLegalizerCombiner() {
return new WebAssemblyPreLegalizerCombiner();
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
index d2cde32cff45e..a48762e415eee 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.h
@@ -58,11 +58,13 @@ class WebAssemblyRegisterBankInfo final
/// \returns true if \p MI only defines FPRs.
bool onlyDefinesFP(const MachineInstr &MI, const MachineRegisterInfo &MRI,
- const WebAssemblyRegisterInfo &TRI, unsigned Depth = 0) const;
+ 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;
+ const WebAssemblyRegisterInfo &TRI,
+ unsigned Depth = 0) const;
};
} // end namespace llvm
#endif
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index 315cbb65371a0..641eef73044cd 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -12,12 +12,12 @@
///
//===----------------------------------------------------------------------===//
-#include "WebAssembly.h"
#include "WebAssemblySubtarget.h"
#include "GISel/WebAssemblyCallLowering.h"
#include "GISel/WebAssemblyLegalizerInfo.h"
#include "GISel/WebAssemblyRegisterBankInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssembly.h"
#include "WebAssemblyInstrInfo.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/MC/TargetRegistry.h"
@@ -78,7 +78,7 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
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 300571b502e93fceabd029a4cfb82e3a2cf9ded4 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Wed, 14 Jan 2026 14:33:47 -0800
Subject: [PATCH 20/41] Fix lowering of calls into weak aliases to become
`call_indirect` instead of `call`
---
.../GISel/WebAssemblyCallLowering.cpp | 35 +++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index ab44263ff3a89..81ca7ab5b3889 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -816,7 +816,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
bool IsIndirect = false;
Register IndirectIdx;
- if (Info.Callee.isReg()) {
+ if (Info.Callee.isReg() ||
+ (Info.Callee.isGlobal() && Info.Callee.getGlobal()->isInterposable())) {
IsIndirect = true;
CallInst = MIRBuilder.buildInstr(Info.LoweredTailCall
? WebAssembly::RET_CALL_INDIRECT
@@ -986,7 +987,37 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
} else {
if (Info.Callee.isGlobal()) {
- CallInst.addGlobalAddress(Info.Callee.getGlobal());
+ if (Info.Callee.getGlobal()->isInterposable()) {
+ // Placeholder for the type index.
+ // This gets replaced with the correct value in
+ // WebAssemblyMCInstLower.cpp
+ CallInst.addImm(0);
+
+ Type *PtrTy = PointerType::getUnqual(Ctx);
+ LLT PtrLLT = getLLTForType(*PtrTy, DL);
+ auto PtrSize = PtrLLT.getSizeInBits();
+ auto PtrIntLLT = LLT::scalar(PtrSize);
+
+ IndirectIdx =
+ MIRBuilder.buildGlobalValue(PtrLLT, Info.Callee.getGlobal())
+ .getReg(0);
+ IndirectIdx =
+ MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
+
+ auto *Table = WebAssembly::getOrCreateFunctionTableSymbol(
+ MF.getContext(), &Subtarget);
+ 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.addGlobalAddress(Info.Callee.getGlobal());
+ }
} else if (Info.Callee.isSymbol()) {
CallInst.addExternalSymbol(Info.Callee.getSymbolName());
} else {
>From b96eae5485a82d61254e58bb6ca4be332f522efe Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 15 Jan 2026 15:11:37 -0800
Subject: [PATCH 21/41] Add `G_USUBSAT` and `G_USUBO` lowering (legalization)
---
llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 1a4d063b816c7..2cfb7c229ba70 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -114,6 +114,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.clampScalar(0, s32, s64);
getActionDefinitionsBuilder({G_UMIN, G_UMAX, G_SMIN, G_SMAX}).lower();
+ getActionDefinitionsBuilder({G_USUBSAT, G_USUBO}).lower();
getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FDIV, G_FMUL, G_FNEG, G_FABS,
G_FCEIL, G_FFLOOR, G_FSQRT, G_INTRINSIC_TRUNC,
>From c30bfe4fbe85a4449f53f8f537e470b90f118733 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 15 Jan 2026 15:15:50 -0800
Subject: [PATCH 22/41] More fixes for calls into aliases
---
.../Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 81ca7ab5b3889..deed8ebd833e2 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -816,8 +816,13 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
bool IsIndirect = false;
Register IndirectIdx;
+ // Use indirect calls when callee is pointer in a register
+ // or it's a weak (interposable) or non-function global.
+ // Makes sure aliases behave.
if (Info.Callee.isReg() ||
- (Info.Callee.isGlobal() && Info.Callee.getGlobal()->isInterposable())) {
+ (Info.Callee.isGlobal() &&
+ (Info.Callee.getGlobal()->isInterposable() ||
+ !Info.Callee.getGlobal()->getValueType()->isFunctionTy()))) {
IsIndirect = true;
CallInst = MIRBuilder.buildInstr(Info.LoweredTailCall
? WebAssembly::RET_CALL_INDIRECT
@@ -987,7 +992,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
} else {
if (Info.Callee.isGlobal()) {
- if (Info.Callee.getGlobal()->isInterposable()) {
+ if (IsIndirect) {
// Placeholder for the type index.
// This gets replaced with the correct value in
// WebAssemblyMCInstLower.cpp
>From 13edde2fce4abff451e19219112ab8822dbd613e Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 20 Jan 2026 10:50:51 -0800
Subject: [PATCH 23/41] Add RegBankSelect support for `DBG_VALUE`
---
.../GISel/WebAssemblyRegisterBankInfo.cpp | 48 ++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index b57c79721c403..62f5bb051a144 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -1,8 +1,8 @@
#include "WebAssemblyRegisterBankInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblySubtarget.h"
-#include "WebAssemblyTargetMachine.h"
#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/Support/ErrorHandling.h"
#define GET_TARGET_REGBANK_IMPL
@@ -196,6 +196,52 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
return getInvalidInstructionMapping();
}
}
+
+ if (MI.isDebugValue()) {
+ const MachineOperand &MO = MI.getOperand(0);
+
+ if (!MO.isReg())
+ return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
+
+ auto &CurBankOrClass = MRI.getRegClassOrRegBank(MO.getReg());
+
+ const RegisterBank *NewBank;
+
+ if (auto *CurBank = CurBankOrClass.dyn_cast<const RegisterBank *>()) {
+ NewBank = CurBank;
+ } else if (auto *CurClass =
+ CurBankOrClass.dyn_cast<const TargetRegisterClass *>()) {
+ getRegBankFromRegClass(*CurClass, MRI.getType(MO.getReg())).dump();
+ NewBank = &getRegBankFromRegClass(*CurClass, MRI.getType(MO.getReg()));
+ } else {
+ llvm_unreachable("Encountered DBG_VALUE with an unmapped register.");
+ }
+
+ WebAssembly::ValueMappingIdx ValueMappingIdx;
+
+ switch (NewBank->getID()) {
+ case WebAssembly::I32RegBankID:
+ ValueMappingIdx = WebAssembly::I32Idx;
+ break;
+ case WebAssembly::I64RegBankID:
+ ValueMappingIdx = WebAssembly::I64Idx;
+ break;
+ case WebAssembly::F32RegBankID:
+ ValueMappingIdx = WebAssembly::F32Idx;
+ break;
+ case WebAssembly::F64RegBankID:
+ ValueMappingIdx = WebAssembly::F64Idx;
+ break;
+ default:
+ llvm_unreachable("Encountered unexpected register bank.");
+ }
+
+ return getInstructionMapping(
+ MappingID, /*Cost=*/1,
+ getOperandsMapping({&WebAssembly::ValueMappings[ValueMappingIdx]}),
+ NumOperands);
+ }
+
switch (Opc) {
case G_BR:
return getInstructionMapping(MappingID, /*Cost=*/1,
>From b04c7d5967bf11d2da818d9291e4c591fb25ebd9 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 20 Jan 2026 11:07:46 -0800
Subject: [PATCH 24/41] Prepare RegBankSelect for intrinsics, and add error for
`llvm.clear_cache`
---
.../GISel/WebAssemblyRegisterBankInfo.cpp | 24 +++++++++++++++----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index 62f5bb051a144..cd6ca9b728518 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -1,8 +1,10 @@
#include "WebAssemblyRegisterBankInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
+#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/ErrorHandling.h"
#define GET_TARGET_REGBANK_IMPL
@@ -250,7 +252,7 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case G_DEBUGTRAP:
return getInstructionMapping(MappingID, /*Cost=*/1, getOperandsMapping({}),
0);
- case COPY:
+ case COPY: {
Register DstReg = MI.getOperand(0).getReg();
if (DstReg.isPhysical()) {
if (DstReg.id() == WebAssembly::SP32) {
@@ -259,19 +261,31 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
getOperandsMapping(
{&WebAssembly::ValueMappings[WebAssembly::I32Idx]}),
1);
- } else if (DstReg.id() == WebAssembly::SP64) {
+ }
+
+ 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?");
}
+
+ llvm_unreachable("Trying to copy into WASM physical register other "
+ "than sp32 or sp64?");
}
break;
}
+ case G_INTRINSIC:
+ case G_INTRINSIC_W_SIDE_EFFECTS: {
+ switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
+ case Intrinsic::clear_cache:
+ reportFatalUsageError("llvm.clear_cache is not supported on wasm");
+ break;
+ }
+ return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
+ }
+ }
const LLT Op0Ty = MRI.getType(MI.getOperand(0).getReg());
unsigned Op0Size = Op0Ty.getSizeInBits();
>From da2b07d333c302741268247e1d524579cc1221fb Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 20 Jan 2026 12:36:49 -0800
Subject: [PATCH 25/41] Cleanup and adjust FCMP legalization
---
.../GISel/WebAssemblyLegalizerInfo.cpp | 116 +++++++++---------
1 file changed, 57 insertions(+), 59 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 2cfb7c229ba70..aff77de115adf 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -14,8 +14,10 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblySubtarget.h"
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/IR/InstrTypes.h"
#define DEBUG_TYPE "wasm-legalinfo"
@@ -310,12 +312,20 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getLegacyLegalizerInfo().computeTables();
}
+static MachineInstrBuilder buildEqz(MachineIRBuilder &MIBuilder,
+ const DstOp &Dst, const SrcOp &Src0) {
+ auto Zero = MIBuilder.buildConstant(Dst.getLLTTy(*MIBuilder.getMRI()), 0);
+ return MIBuilder.buildICmp(llvm::CmpInst::ICMP_EQ, Dst, Src0, Zero);
+}
+
bool WebAssemblyLegalizerInfo::legalizeCustom(
LegalizerHelper &Helper, MachineInstr &MI,
LostDebugLocObserver &LocObserver) const {
auto &MRI = *Helper.MIRBuilder.getMRI();
auto &MIRBuilder = Helper.MIRBuilder;
+ const LLT s1 = LLT::scalar(1);
+
switch (MI.getOpcode()) {
case TargetOpcode::G_FCANONICALIZE: {
auto One = MRI.createGenericVirtualRegister(
@@ -343,7 +353,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.addUse(MI.getOperand(2).getReg())
.setMIFlags(MI.getFlags());
} else {
- auto TmpReg = MRI.createGenericVirtualRegister(LLT::scalar(1));
+ auto TmpReg = MRI.createGenericVirtualRegister(s1);
MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OLT, TmpReg,
MI.getOperand(1), MI.getOperand(2));
@@ -363,7 +373,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.addUse(MI.getOperand(2).getReg())
.setMIFlags(MI.getFlags());
} else {
- auto TmpReg = MRI.createGenericVirtualRegister(LLT::scalar(1));
+ auto TmpReg = MRI.createGenericVirtualRegister(s1);
MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OGT, TmpReg,
MI.getOperand(1), MI.getOperand(2));
@@ -398,15 +408,14 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
- if (!DstTy.isScalar() || !SrcTy.isVector() ||
- SrcTy.getElementType() != LLT::scalar(1))
+ if (!DstTy.isScalar() || !SrcTy.isVector() || SrcTy.getElementType() != s1)
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 Elm = MRI.createGenericVirtualRegister(s1);
auto ExtElm = MRI.createGenericVirtualRegister(DstTy);
auto ShiftedElm = MRI.createGenericVirtualRegister(DstTy);
auto Idx = MRI.createGenericVirtualRegister(LLT::scalar(8));
@@ -450,81 +459,70 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ MIRBuilder.buildOr(
+ s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, s1, LHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, s1, LHS, RHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ MIRBuilder.buildAnd(
+ s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, s1, RHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, s1, LHS, LHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ MIRBuilder.buildOr(
+ s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, s1, RHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, s1, LHS, LHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ buildEqz(
+ MIRBuilder, s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_ONE, s1, LHS, RHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ buildEqz(
+ MIRBuilder, s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLE, s1, LHS, RHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ buildEqz(
+ MIRBuilder, s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, s1, LHS, RHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ buildEqz(
+ MIRBuilder, s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGE, s1, LHS, RHS).getReg(0)));
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);
+ MIRBuilder.buildAnyExt(
+ MI.getOperand(0).getReg(),
+ buildEqz(
+ MIRBuilder, s1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, s1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_UNE:
>From 75f2abedb976f37289cabee1c137275046c4b2bc Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 20 Jan 2026 13:54:31 -0800
Subject: [PATCH 26/41] Gate floating-point -> integer conversions behind
`nontrapping-fptoint`. Disable scalarization of vector ops (prefer fallback
to ISelDAG).
[skip ci]
---
.../GISel/WebAssemblyLegalizerInfo.cpp | 65 ++++++++++---------
.../GISel/WebAssemblyRegisterBankInfo.cpp | 6 +-
2 files changed, 37 insertions(+), 34 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index aff77de115adf..aa7c3d7569168 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -39,7 +39,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_PHI)
.legalFor({p0, s32, s64})
- .scalarize(0)
+ //.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
getActionDefinitionsBuilder(G_BR).alwaysLegal();
@@ -49,7 +49,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_SELECT)
.legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
- .scalarize(0)
+ //.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s32);
@@ -58,14 +58,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_ICMP)
.legalFor({{s32, s32}, {s32, s64}, {s32, p0}})
- .scalarize(0)
+ //.scalarize(0)
.widenScalarToNextPow2(1)
.clampScalar(1, s32, s64)
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder(G_FCMP)
.customFor({{s32, s32}, {s32, s64}})
- .scalarize(0)
+ //.scalarize(0)
.clampScalar(0, s32, s32)
.libcall();
@@ -82,21 +82,21 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_IMPLICIT_DEF)
.legalFor({s32, s64, p0})
- .scalarize(0)
+ //.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)
+ //.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})
.legalFor({{s32, s32}, {s64, s64}})
- .scalarize(0)
+ //.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64)
.minScalarSameAs(1, 0)
@@ -104,14 +104,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder({G_FSHL, G_FSHR, G_ROTL, G_ROTR})
.legalFor({{s32, s32}, {s64, s64}})
- .scalarize(0)
+ //.scalarize(0)
.lower();
getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
.legalFor({s32, s64})
- .scalarize(0)
+ //.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
@@ -123,15 +123,15 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
G_FNEARBYINT, G_FRINT, G_INTRINSIC_ROUNDEVEN,
G_FMINIMUM, G_FMAXIMUM, G_STRICT_FMUL})
.legalFor({s32, s64})
- .scalarize(0)
+ //.scalarize(0)
.minScalar(0, s32);
getActionDefinitionsBuilder({G_FMINNUM, G_FMAXNUM})
.customFor({s32, s64})
- .scalarize(0)
+ //.scalarize(0)
.minScalar(0, s32);
- getActionDefinitionsBuilder({G_VECREDUCE_OR, G_VECREDUCE_AND}).scalarize(1);
+ getActionDefinitionsBuilder({G_VECREDUCE_OR, G_VECREDUCE_AND});//.scalarize(1);
getActionDefinitionsBuilder(G_BITCAST)
.customIf([=](const LegalityQuery &Query) {
@@ -143,15 +143,15 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
})
.lowerIf([=](const LegalityQuery &Query) {
return Query.Types[0].isVector() != Query.Types[1].isVector();
- })
- .scalarize(0);
+ });
+ //.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)
+ //.scalarize(0)
.minScalar(0, s32);
getActionDefinitionsBuilder({G_FMA, G_FREM})
@@ -166,20 +166,23 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_FCOPYSIGN)
.legalFor({s32, s64})
- .scalarize(0)
+ //.scalarize(0)
.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);
+ if (ST.hasNontrappingFPToInt()) {
+ 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})
- .scalarize(0)
+ //.scalarize(0)
.minScalar(1, s32)
.widenScalarToNextPow2(1)
.clampScalar(1, s32, s64);
@@ -202,8 +205,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
{s64, p0, s16, 1},
{s64, p0, s32, 1}})
.clampScalar(0, s32, s64)
- .lowerIfMemSizeNotByteSizePow2()
- .scalarize(0);
+ .lowerIfMemSizeNotByteSizePow2();
+ //.scalarize(0);
getActionDefinitionsBuilder(G_STORE)
.legalForTypesWithMemDesc(
@@ -228,8 +231,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
[=](const LegalityQuery &Query) {
const LLT VecTy = Query.Types[0];
return std::pair(0, LLT::scalar(VecTy.getSizeInBits()));
- })
- .scalarize(0);
+ });
+ //.scalarize(0);
getActionDefinitionsBuilder(
{G_SHUFFLE_VECTOR, G_EXTRACT_VECTOR_ELT, G_INSERT_VECTOR_ELT})
@@ -270,13 +273,13 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_ANYEXT)
.legalFor({{s64, s32}})
- .scalarize(0)
+ //.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64);
getActionDefinitionsBuilder({G_SEXT, G_ZEXT})
.legalFor({{s64, s32}})
- .scalarize(0)
+ //.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64)
.lower();
@@ -291,14 +294,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_TRUNC)
.legalFor({{s32, s64}})
- .scalarize(0)
+ //.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64)
.lower();
- getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}}).scalarize(0);
+ getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}});//.scalarize(0);
- getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}}).scalarize(0);
+ getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}});//.scalarize(0);
getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
getActionDefinitionsBuilder(G_VAARG)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index cd6ca9b728518..7f9fbe3f5686c 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -279,9 +279,9 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case G_INTRINSIC:
case G_INTRINSIC_W_SIDE_EFFECTS: {
switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
- case Intrinsic::clear_cache:
- reportFatalUsageError("llvm.clear_cache is not supported on wasm");
- break;
+ case Intrinsic::clear_cache:
+ reportFatalUsageError("llvm.clear_cache is not supported on wasm");
+ break;
}
return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
}
>From 5a48d56dfba24e77dea9f632e0fbb84488f79383 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 20 Jan 2026 15:37:14 -0800
Subject: [PATCH 27/41] Fix duplicate post-isel pass issue when disabling
GlobalISel abort.
---
.../Target/WebAssembly/WebAssemblyTargetMachine.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 13dbc0578f91f..ee57941114801 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -707,10 +707,13 @@ bool WebAssemblyPassConfig::addRegBankSelect() {
bool WebAssemblyPassConfig::addGlobalInstructionSelect() {
addPass(new InstructionSelect(getOptLevel()));
- addPass(createWebAssemblyArgumentMove());
- addPass(createWebAssemblySetP2AlignOperands());
- addPass(createWebAssemblyFixBrTableDefaults());
- addPass(createWebAssemblyCleanCodeAfterTrap());
+ // We insert only if ISelDAG won't insert these at a later point.
+ if (isGlobalISelAbortEnabled()) {
+ addPass(createWebAssemblyArgumentMove());
+ addPass(createWebAssemblySetP2AlignOperands());
+ addPass(createWebAssemblyFixBrTableDefaults());
+ addPass(createWebAssemblyCleanCodeAfterTrap());
+ }
return false;
}
>From a2a1c28fe948af90710abc40863102d74ce617ce Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 13:12:53 -0800
Subject: [PATCH 28/41] Clean up and adjust FCMP lowerings.
---
.../GISel/WebAssemblyLegalizerInfo.cpp | 32 +++++++++++--------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index aa7c3d7569168..cda5da8d44c73 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -131,7 +131,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
//.scalarize(0)
.minScalar(0, s32);
- getActionDefinitionsBuilder({G_VECREDUCE_OR, G_VECREDUCE_AND});//.scalarize(1);
+ getActionDefinitionsBuilder(
+ {G_VECREDUCE_OR, G_VECREDUCE_AND}); //.scalarize(1);
getActionDefinitionsBuilder(G_BITCAST)
.customIf([=](const LegalityQuery &Query) {
@@ -299,9 +300,10 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.clampScalar(1, s32, s64)
.lower();
- getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}});//.scalarize(0);
+ getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}}); //.scalarize(0);
- getActionDefinitionsBuilder(G_FPTRUNC).legalFor({{s32, s64}});//.scalarize(0);
+ getActionDefinitionsBuilder(G_FPTRUNC).legalFor(
+ {{s32, s64}}); //.scalarize(0);
getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
getActionDefinitionsBuilder(G_VAARG)
@@ -331,9 +333,9 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
switch (MI.getOpcode()) {
case TargetOpcode::G_FCANONICALIZE: {
- auto One = MRI.createGenericVirtualRegister(
- MRI.getType(MI.getOperand(0).getReg()));
- MIRBuilder.buildFConstant(One, 1.0);
+ auto One =
+ MIRBuilder.buildFConstant(MRI.getType(MI.getOperand(0).getReg()), 1.0)
+ .getReg(0);
MIRBuilder.buildInstr(TargetOpcode::G_STRICT_FMUL)
.addDef(MI.getOperand(0).getReg())
@@ -356,11 +358,12 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.addUse(MI.getOperand(2).getReg())
.setMIFlags(MI.getFlags());
} else {
- auto TmpReg = MRI.createGenericVirtualRegister(s1);
+ auto Cond = MIRBuilder
+ .buildFCmp(CmpInst::Predicate::FCMP_OLT, s1,
+ MI.getOperand(1), MI.getOperand(2))
+ .getReg(0);
- MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OLT, TmpReg,
- MI.getOperand(1), MI.getOperand(2));
- MIRBuilder.buildSelect(MI.getOperand(0), TmpReg, MI.getOperand(1),
+ MIRBuilder.buildSelect(MI.getOperand(0), Cond, MI.getOperand(1),
MI.getOperand(2));
}
MI.eraseFromParent();
@@ -376,11 +379,12 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.addUse(MI.getOperand(2).getReg())
.setMIFlags(MI.getFlags());
} else {
- auto TmpReg = MRI.createGenericVirtualRegister(s1);
+ auto Cond = MIRBuilder
+ .buildFCmp(CmpInst::Predicate::FCMP_OGT, s1,
+ MI.getOperand(1), MI.getOperand(2))
+ .getReg(0);
- MIRBuilder.buildFCmp(CmpInst::Predicate::FCMP_OGT, TmpReg,
- MI.getOperand(1), MI.getOperand(2));
- MIRBuilder.buildSelect(MI.getOperand(0), TmpReg, MI.getOperand(1),
+ MIRBuilder.buildSelect(MI.getOperand(0), Cond, MI.getOperand(1),
MI.getOperand(2));
}
MI.eraseFromParent();
>From 28b44a2452e1881ad7727af97c20e77faaa8a1fa Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:44:44 -0800
Subject: [PATCH 29/41] Implement vararg related instructions.
---
.../GISel/WebAssemblyInstructionSelector.cpp | 25 ++++++
.../GISel/WebAssemblyLegalizerInfo.cpp | 90 ++++++++++++++++++-
.../GISel/WebAssemblyLegalizerInfo.h | 3 +
.../GISel/WebAssemblyRegisterBankInfo.cpp | 4 +-
4 files changed, 117 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index de3c6d0d8b6e6..dbf477952c9a7 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -589,6 +589,31 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
return true;
}
+ case G_VASTART: {
+ MachineIRBuilder B(I);
+
+ auto *OldMMO = I.memoperands()[0];
+
+ auto *NewMMO = MF.getMachineMemOperand(
+ OldMMO->getPointerInfo(), MachineMemOperand::Flags::MOStore,
+ OldMMO->getSize(), MF.getDataLayout().getPointerABIAlignment(0));
+
+ auto MIB = B.buildInstr(PtrIsI64 ? WebAssembly::STORE_I64_A64
+ : WebAssembly::STORE_I32_A32)
+ .addImm(0) // alignment; rewritten in later pass
+ .addImm(0) // offset
+ .addReg(I.getOperand(0).getReg()) // base
+ .addReg(MF.getInfo<WebAssemblyFunctionInfo>()
+ ->getVarargBufferVreg()) // value
+ .addMemOperand(NewMMO);
+
+ assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+ "Couldn't constrain registers for instruction");
+
+ I.eraseFromParent();
+
+ return true;
+ }
}
return false;
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index cda5da8d44c73..08a5d6f61504c 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -13,11 +13,13 @@
#include "WebAssemblyLegalizerInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Intrinsics.h"
#define DEBUG_TYPE "wasm-legalinfo"
@@ -307,8 +309,15 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
getActionDefinitionsBuilder(G_VAARG)
- .legalForCartesianProduct({s32, s64}, {p0})
- .clampScalar(0, s32, s64);
+ .customIf(([=](const LegalityQuery &Query) {
+ return Query.Types[1] == p0 && (Query.Types[0].isScalar());
+ })) // TODO: replace this with typical lowerForCartesianProduct once widen
+ // and narrow scalar works
+ .clampScalar(
+ 0, s32,
+ s64) // Note: currently not functional (not implemented in core)
+ .widenScalarToNextPow2(0, /*Min*/ 8); // Note: currently not functional
+ // (not implemented in core)
getActionDefinitionsBuilder(G_DYN_STACKALLOC).lowerFor({{p0, p0s}});
@@ -628,8 +637,85 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
MI.eraseFromParent();
return true;
}
+ case TargetOpcode::G_VAARG: {
+ MachineFunction &MF = MIRBuilder.getMF();
+ Align Alignment(MI.getOperand(2).getImm());
+ Register Dst = MI.getOperand(0).getReg();
+ Register ListPtr = MI.getOperand(1).getReg();
+
+ LLT PtrTy = MRI.getType(ListPtr);
+ LLT IntPtrTy = LLT::scalar(PtrTy.getSizeInBits());
+
+ const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
+ const Align PtrAlign = Align(PtrSize);
+ auto List = MIRBuilder.buildLoad(
+ PtrTy, ListPtr,
+ *MF.getMachineMemOperand(MachinePointerInfo(),
+ MachineMemOperand::MOLoad, PtrTy, PtrAlign));
+
+ MachineInstrBuilder DstPtr;
+ if (Alignment > PtrAlign) {
+ // Realign the list to the actual required alignment.
+ auto AlignMinus1 =
+ MIRBuilder.buildConstant(IntPtrTy, Alignment.value() - 1);
+ auto ListTmp = MIRBuilder.buildPtrAdd(PtrTy, List, AlignMinus1.getReg(0));
+ DstPtr = MIRBuilder.buildMaskLowPtrBits(PtrTy, ListTmp, Log2(Alignment));
+ } else
+ DstPtr = List;
+
+ LLT ValTy = MRI.getType(Dst);
+ uint64_t ValSize = ValTy.getSizeInBits() / 8;
+ MIRBuilder.buildLoad(Dst, DstPtr,
+ *MF.getMachineMemOperand(
+ MachinePointerInfo(), MachineMemOperand::MOLoad,
+ ValTy, std::max(Alignment, PtrAlign)));
+
+ auto Size = MIRBuilder.buildConstant(IntPtrTy, alignTo(ValSize, PtrAlign));
+
+ auto NewList = MIRBuilder.buildPtrAdd(PtrTy, DstPtr, Size.getReg(0));
+
+ MIRBuilder.buildStore(NewList, ListPtr,
+ *MF.getMachineMemOperand(MachinePointerInfo(),
+ MachineMemOperand::MOStore,
+ PtrTy, PtrAlign));
+
+ MI.eraseFromParent();
+
+ return true;
+ }
default:
break;
}
return false;
}
+
+bool WebAssemblyLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &MI) const {
+ auto &MRI = *Helper.MIRBuilder.getMRI();
+ auto &MIRBuilder = Helper.MIRBuilder;
+
+ switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
+ default:
+ return true;
+ case Intrinsic::vacopy: {
+ auto PointerWidth = MI.getMF()->getDataLayout().getPointerSize();
+
+ MachineFunction &MF = *MI.getMF();
+ auto Val = MF.getRegInfo().createGenericVirtualRegister(
+ LLT::scalar(PointerWidth * 8));
+
+ MIRBuilder.buildLoad(Val, MI.getOperand(2),
+ *MF.getMachineMemOperand(
+ MachinePointerInfo(), MachineMemOperand::MOLoad,
+ PointerWidth, Align(PointerWidth)));
+ MIRBuilder.buildStore(Val, MI.getOperand(1),
+ *MF.getMachineMemOperand(
+ MachinePointerInfo(), MachineMemOperand::MOStore,
+ PointerWidth, Align(PointerWidth)));
+ MI.eraseFromParent();
+ return true;
+ }
+ case Intrinsic::clear_cache:
+ reportFatalUsageError("llvm.clear_cache is not supported on wasm");
+ }
+}
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
index 5e9c079f63da3..e2216fca3ab88 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.h
@@ -27,6 +27,9 @@ class WebAssemblyLegalizerInfo : public LegalizerInfo {
bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI,
LostDebugLocObserver &LocObserver) const override;
+
+ bool legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &MI) const override;
};
} // namespace llvm
#endif
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index 7f9fbe3f5686c..aa880ffa0f151 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -279,9 +279,7 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case G_INTRINSIC:
case G_INTRINSIC_W_SIDE_EFFECTS: {
switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
- case Intrinsic::clear_cache:
- reportFatalUsageError("llvm.clear_cache is not supported on wasm");
- break;
+ default: break;
}
return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
}
>From d9b5d36dbca072b6abd436ad7fa76a068a01d292 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:45:27 -0800
Subject: [PATCH 30/41] Fix crash trying to reconstrain registers.
---
.../Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index deed8ebd833e2..9769c03f6dca2 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -1161,10 +1161,8 @@ 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]);
+ auto NewArgReg = MRI.createGenericVirtualRegister(LLT(NewVT));
+ MRI.setRegClass(NewArgReg, TLI.getRegClassFor(NewVT)); MIRBuilder.buildCopy(NewArgReg, Arg.Regs[Part]);
CallInst.addUse(Arg.Regs[Part]);
}
++NumFixedArgs;
>From d0a60798f92d45b656674a9b478b4335f132a971 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:50:07 -0800
Subject: [PATCH 31/41] Fix legalization and regbankselect of G_FSHL and G_FSHR
---
.../GISel/WebAssemblyLegalizerInfo.cpp | 4 +++-
.../GISel/WebAssemblyRegisterBankInfo.cpp | 21 ++++++++++++-------
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 08a5d6f61504c..5b9fc1d2242e5 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -104,7 +104,9 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.minScalarSameAs(1, 0)
.maxScalarSameAs(1, 0);
- getActionDefinitionsBuilder({G_FSHL, G_FSHR, G_ROTL, G_ROTR})
+ getActionDefinitionsBuilder({G_FSHL, G_FSHR}).lower();
+
+ getActionDefinitionsBuilder({G_ROTL, G_ROTR})
.legalFor({{s32, s32}, {s64, s64}})
//.scalarize(0)
.lower();
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index aa880ffa0f151..b85badd9ecdec 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -25,9 +25,9 @@ enum PartialMappingIdx {
enum ValueMappingIdx {
InvalidIdx = 0,
I32Idx = 1,
- I64Idx = 4,
- F32Idx = 7,
- F64Idx = 10
+ I64Idx = 5,
+ F32Idx = 9,
+ F64Idx = 13
};
const RegisterBankInfo::PartialMapping PartMappings[]{{0, 32, I32RegBank},
@@ -38,19 +38,23 @@ const RegisterBankInfo::PartialMapping PartMappings[]{{0, 32, I32RegBank},
const RegisterBankInfo::ValueMapping ValueMappings[] = {
// invalid
{nullptr, 0},
- // up to 3 operands as I32
+ // up to 4 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_I32 - PMI_Min], 1},
+ // up to 4 operands as I64
+ {&PartMappings[PMI_I64 - PMI_Min], 1},
{&PartMappings[PMI_I64 - PMI_Min], 1},
{&PartMappings[PMI_I64 - PMI_Min], 1},
{&PartMappings[PMI_I64 - PMI_Min], 1},
- // up to 3 operands as F32
+ // up to 4 operands as F32
+ {&PartMappings[PMI_F32 - PMI_Min], 1},
{&PartMappings[PMI_F32 - PMI_Min], 1},
{&PartMappings[PMI_F32 - PMI_Min], 1},
{&PartMappings[PMI_F32 - PMI_Min], 1},
- // up to 3 operands as F64
+ // up to 4 operands as F64
+ {&PartMappings[PMI_F64 - PMI_Min], 1},
{&PartMappings[PMI_F64 - PMI_Min], 1},
{&PartMappings[PMI_F64 - PMI_Min], 1},
{&PartMappings[PMI_F64 - PMI_Min], 1}};
@@ -279,7 +283,8 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case G_INTRINSIC:
case G_INTRINSIC_W_SIDE_EFFECTS: {
switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
- default: break;
+ default:
+ break;
}
return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
}
>From 237b169d2f8ba3c83d79fb556db735f51615fdc1 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:51:08 -0800
Subject: [PATCH 32/41] Implement G_PTR_MASK
---
.../GISel/WebAssemblyInstructionSelector.cpp | 10 ++++++++++
.../WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp | 2 ++
.../WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp | 1 +
3 files changed, 13 insertions(+)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index dbf477952c9a7..1d74e6f4cfc19 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -507,6 +507,16 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
return true;
}
+ case G_PTRMASK: {
+ assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+ "G_PTRMASK selection fell-through with non-pointer?");
+
+ I.setDesc(TII.get(PtrIsI64 ? WebAssembly::AND_I64 : WebAssembly::AND_I32));
+ assert(constrainSelectedInstRegOperands(I, TII, TRI, RBI) &&
+ "Couldn't constrain registers for instruction");
+
+ return true;
+ }
case G_FRAME_INDEX: {
MachineIRBuilder B(I);
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 5b9fc1d2242e5..f89ae8bcefcbc 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -198,7 +198,9 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_INTTOPTR)
.legalFor({p0, p0s})
.customForCartesianProduct({p0}, {s32, s64});
+
getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, p0s}});
+ getActionDefinitionsBuilder(G_PTRMASK).legalFor({{p0, p0s}});
getActionDefinitionsBuilder(G_LOAD)
.legalForTypesWithMemDesc(
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index b85badd9ecdec..25b4783339f0e 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -313,6 +313,7 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case G_ASHR:
case G_LSHR:
case G_PTR_ADD:
+ case G_PTRMASK:
case G_INTTOPTR:
case G_PTRTOINT:
case G_ADD:
>From f530d692c91dad95d6e9ca9f639aa721f33de0cc Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:52:13 -0800
Subject: [PATCH 33/41] Fix flag propogation for G_PTR_ADD from getelementptr
in IRTranslator
---
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index a0fe900778cca..ffda7123b5576 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
@@ -64,6 +65,7 @@
#include "llvm/IR/IntrinsicsAMDGPU.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Operator.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Statepoint.h"
#include "llvm/IR/Type.h"
@@ -1600,8 +1602,16 @@ bool IRTranslator::translateGetElementPtr(const User &U,
uint32_t PtrAddFlags = 0;
// Each PtrAdd generated to implement the GEP inherits its nuw, nusw, inbounds
// flags.
- if (const Instruction *I = dyn_cast<Instruction>(&U))
- PtrAddFlags = MachineInstr::copyFlagsFromInstruction(*I);
+ if (const GEPOperator *GEP = dyn_cast<GEPOperator>(&U)) {
+ if (GEP->hasNoUnsignedSignedWrap())
+ PtrAddFlags |= MachineInstr::MIFlag::NoUSWrap;
+
+ if (GEP->hasNoUnsignedWrap())
+ PtrAddFlags |= MachineInstr::MIFlag::NoUWrap;
+
+ if (GEP->isInBounds())
+ PtrAddFlags |= MachineInstr::MIFlag::InBounds;
+ }
auto PtrAddFlagsWithConst = [&](int64_t Offset) {
// For nusw/inbounds GEP with an offset that is nonnegative when interpreted
@@ -1706,6 +1716,7 @@ bool IRTranslator::translateGetElementPtr(const User &U,
}
if (Offset != 0) {
+ Offset = APInt(OffsetTy.getSizeInBits(), Offset, true, true).getSExtValue();
auto OffsetMIB =
MIRBuilder.buildConstant(OffsetTy, Offset);
>From d22f0f094ec7a95a9051a9b49d4022071774b260 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:55:49 -0800
Subject: [PATCH 34/41] Modify `selectAddrOperands` to use an adaption of
FastISel `computeAddress` instead.
---
.../GISel/WebAssemblyInstructionSelector.cpp | 247 ++++++++++++------
1 file changed, 168 insertions(+), 79 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 1d74e6f4cfc19..8c7e11fa9bf7b 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -15,6 +15,7 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "Utils/WasmAddressSpaces.h"
#include "Utils/WebAssemblyTypeUtilities.h"
+#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblyRegisterInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyTargetMachine.h"
@@ -24,11 +25,13 @@
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
+#include "llvm/CodeGen/MachineMemOperand.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/GlobalValue.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/ErrorHandling.h"
@@ -45,6 +48,67 @@ namespace {
class WebAssemblyInstructionSelector : public InstructionSelector {
public:
+ // From FastISel
+ // All possible address modes.
+ class Address {
+ public:
+ enum BaseKind { RegBase, FrameIndexBase };
+
+ private:
+ BaseKind Kind = RegBase;
+ union {
+ unsigned Reg;
+ int FI;
+ } Base;
+
+ // Whether the base has been determined yet
+ bool IsBaseSet = false;
+
+ int64_t Offset = 0;
+
+ const GlobalValue *GV = nullptr;
+
+ public:
+ // Innocuous defaults for our address.
+ Address() { Base.Reg = 0; }
+ void setKind(BaseKind K) {
+ assert(!isSet() && "Can't change kind with non-zero base");
+ Kind = K;
+ }
+ BaseKind getKind() const { return Kind; }
+ bool isRegBase() const { return Kind == RegBase; }
+ bool isFIBase() const { return Kind == FrameIndexBase; }
+ void setReg(unsigned Reg) {
+ assert(isRegBase() && "Invalid base register access!");
+ assert(!IsBaseSet && "Base cannot be reset");
+ Base.Reg = Reg;
+ IsBaseSet = true;
+ }
+ unsigned getReg() const {
+ assert(isRegBase() && "Invalid base register access!");
+ return Base.Reg;
+ }
+ void setFI(unsigned FI) {
+ assert(isFIBase() && "Invalid base frame index access!");
+ assert(!IsBaseSet && "Base cannot be reset");
+ Base.FI = FI;
+ IsBaseSet = true;
+ }
+ unsigned getFI() const {
+ assert(isFIBase() && "Invalid base frame index access!");
+ return Base.FI;
+ }
+
+ void setOffset(int64_t NewOffset) {
+ assert(NewOffset >= 0 && "Offsets must be non-negative");
+ Offset = NewOffset;
+ }
+ int64_t getOffset() const { return Offset; }
+ void setGlobalValue(const GlobalValue *G) { GV = G; }
+ const GlobalValue *getGlobalValue() const { return GV; }
+ bool isSet() const { return IsBaseSet; }
+ };
+
WebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM,
const WebAssemblySubtarget &STI,
const WebAssemblyRegisterBankInfo &RBI);
@@ -62,6 +126,8 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
+ bool computeAddress(const MachineInstr &MI, Address &Addr) const;
+
InstructionSelector::ComplexRendererFns
selectAddrOperands(LLT AddrType, unsigned int ConstOpc,
MachineOperand &Root) const;
@@ -102,98 +168,124 @@ WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
{
}
-InstructionSelector::ComplexRendererFns
-WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
- unsigned int ConstOpc,
- MachineOperand &Root) const {
+bool WebAssemblyInstructionSelector::computeAddress(const MachineInstr &MI,
+ Address &Addr) const {
+ unsigned Opcode = MI.getOpcode();
+ auto &MRI = MI.getMF()->getRegInfo();
- if (!Root.isReg())
- return std::nullopt;
+ if (MI.getNumDefs() == 0 || !MI.getOperand(0).isReg())
+ return false;
- MachineRegisterInfo &MRI =
- Root.getParent()->getParent()->getParent()->getRegInfo();
- MachineInstr &RootDef = *MRI.getVRegDef(Root.getReg());
+ Register Def = MI.getOperand(0).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 (!Def.isValid() || !MRI.getType(Def).isPointer() ||
+ !(MRI.getType(Def).getAddressSpace() == 0))
+ return false;
+
+ switch (Opcode) {
+ default:
+ break;
+ case TargetOpcode::G_GLOBAL_VALUE: {
+ if (TM.isPositionIndependent())
+ return false;
+ if (Addr.getGlobalValue())
+ return false;
+ if (MI.getOperand(1).getGlobal()->isThreadLocal())
+ return false;
- if (!TM.isPositionIndependent()) {
- if (OpDef.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
- auto *Offset = OpDef.getOperand(1).getGlobal();
- auto Addr = OtherOp;
+ uint64_t TmpOffset = Addr.getOffset() + MI.getOperand(1).getOffset();
+ if (int64_t(TmpOffset) < 0)
+ return false;
- return {{
- [=](MachineInstrBuilder &MIB) { MIB.addGlobalAddress(Offset); },
- [=](MachineInstrBuilder &MIB) { MIB.add(Addr); },
- }};
- }
- }
- }
+ Addr.setGlobalValue(MI.getOperand(1).getGlobal());
+ Addr.setOffset(TmpOffset);
+ return true;
+ }
+ case TargetOpcode::G_FRAME_INDEX: {
+ if (Addr.isSet()) {
+ return false;
}
+ Addr.setKind(Address::FrameIndexBase);
+ Addr.setFI(MI.getOperand(1).getIndex());
+ return true;
}
+ case TargetOpcode::G_PTR_ADD: {
+ // We should not fold operands into an offset when 'nuw' (no unsigned wrap)
+ // is not present, because the address calculation does not wrap.
+ if (!MI.getFlag(MachineInstr::MIFlag::NoUWrap))
+ break;
- if (RootDef.getOpcode() == TargetOpcode::G_CONSTANT) {
- auto Offset = RootDef.getOperand(1).getCImm()->getZExtValue();
- auto Addr = MRI.createGenericVirtualRegister(AddrType);
+ const MachineInstr *LHS = MRI.getVRegDef(MI.getOperand(1).getReg());
+ const MachineInstr *RHS = MRI.getVRegDef(MI.getOperand(2).getReg());
- MachineIRBuilder B(RootDef);
+ if (LHS->getOpcode() == TargetOpcode::G_CONSTANT)
+ std::swap(LHS, RHS);
- auto MIB = B.buildInstr(ConstOpc).addDef(Addr).addImm(0);
- assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
- "Couldn't constrain registers for instruction");
+ if (RHS->getOpcode() == TargetOpcode::G_CONSTANT) {
+ uint64_t TmpOffset =
+ Addr.getOffset() + RHS->getOperand(1).getCImm()->getSExtValue();
+ if (int64_t(TmpOffset) >= 0) {
+ Addr.setOffset(TmpOffset);
+ return computeAddress(*LHS, Addr);
+ }
+ }
+
+ Address Backup = Addr;
+ if (computeAddress(*LHS, Addr) && computeAddress(*RHS, Addr))
+ return true;
+ Addr = Backup;
- return {{
- [=](MachineInstrBuilder &MIB) { MIB.addImm(Offset); },
- [=](MachineInstrBuilder &MIB) { MIB.addReg(Addr); },
- }};
+ break;
+ }
}
- if (!TM.isPositionIndependent() &&
- RootDef.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
- auto *Offset = RootDef.getOperand(1).getGlobal();
- auto Addr = MRI.createGenericVirtualRegister(AddrType);
+ if (Addr.isSet())
+ return false;
+ Addr.setReg(Def);
+ return Addr.getReg() != 0;
+}
+
+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());
+
+ Address Addr = Address();
- MachineIRBuilder B(RootDef);
+ if (!computeAddress(RootDef, Addr))
+ return std::nullopt;
- auto MIB = B.buildInstr(ConstOpc).addDef(Addr).addImm(0);
- assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
- "Couldn't constrain registers for instruction");
+ if (Addr.isRegBase()) {
+ unsigned Reg = Addr.getReg();
+ if (Reg == 0) {
+ Reg = MRI.createGenericVirtualRegister(AddrType);
+
+ MachineIRBuilder B(RootDef);
- return {{
- [=](MachineInstrBuilder &MIB) { MIB.addGlobalAddress(Offset); },
- [=](MachineInstrBuilder &MIB) { MIB.addReg(Addr); },
- }};
+ auto MIB = B.buildInstr(ConstOpc).addDef(Reg).addImm(0);
+ assert(constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI) &&
+ "Couldn't constrain registers for instruction");
+ Addr.setReg(Reg);
+ }
}
return {{
- [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
- [=](MachineInstrBuilder &MIB) { MIB.add(Root); },
+ [=](MachineInstrBuilder &MIB) {
+ Addr.getGlobalValue()
+ ? MIB.addGlobalAddress(Addr.getGlobalValue(), Addr.getOffset())
+ : MIB.addImm(Addr.getOffset());
+ },
+ [=](MachineInstrBuilder &MIB) {
+ Addr.isFIBase() ? MIB.addFrameIndex(Addr.getFI())
+ : MIB.addReg(Addr.getReg());
+ },
}};
}
@@ -582,14 +674,11 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
OperandFlags = WebAssemblyII::MO_GOT;
}
- auto NewOpc = MF.getDataLayout().getPointerSizeInBits() == 64
- ? WebAssembly::CONST_I64
- : WebAssembly::CONST_I32;
+ auto NewOpc = PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
if (OperandFlags & WebAssemblyII::MO_GOT) {
- NewOpc = MF.getDataLayout().getPointerSizeInBits() == 64
- ? WebAssembly::GLOBAL_GET_I64
- : WebAssembly::GLOBAL_GET_I32;
+ NewOpc =
+ PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 : WebAssembly::GLOBAL_GET_I32;
}
I.setDesc(TII.get(NewOpc));
>From f70f2e3c87500a2814cf3537b0641ad1798d7876 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 14:58:11 -0800
Subject: [PATCH 35/41] Incorporate `Localizer` pass.
---
.../WebAssembly/WebAssemblyISelLowering.cpp | 29 +++++++++++++++++++
.../WebAssembly/WebAssemblyISelLowering.h | 6 ++++
.../WebAssembly/WebAssemblyTargetMachine.cpp | 2 ++
3 files changed, 37 insertions(+)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 5abf0e8f59d2a..c5050886f2d74 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -27,6 +27,8 @@
#include "llvm/CodeGen/SDPatternMatch.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/Function.h"
@@ -1037,6 +1039,33 @@ bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT,
return true;
}
+bool WebAssemblyTargetLowering::shouldLocalize(
+ const MachineInstr &MI, const TargetTransformInfo *TTI) const {
+
+ // Rebalancing to prioritize code-size
+ // We only sink constants if they have a single-user
+
+ auto &MF = *MI.getMF();
+ auto &MRI = MF.getRegInfo();
+
+ switch (MI.getOpcode()) {
+ default:
+ break;
+ case TargetOpcode::G_CONSTANT:
+ case TargetOpcode::G_FCONSTANT:
+ case TargetOpcode::G_FRAME_INDEX:
+ case TargetOpcode::G_INTTOPTR:
+ case TargetOpcode::G_PTR_ADD:
+ case TargetOpcode::G_GLOBAL_VALUE: {
+ // We only sink single-uses, so no need to be PIC aware for globals
+
+ Register Reg = MI.getOperand(0).getReg();
+ return MRI.hasAtMostUserInstrs(Reg, 1);
+ }
+ }
+ return TargetLoweringBase::shouldLocalize(MI, TTI);
+}
+
bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const {
EVT ExtT = ExtVal.getValueType();
EVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getValueType(0);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 204384f06ab25..21c9d1fb99877 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -15,6 +15,8 @@
#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H
#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/TargetLowering.h"
namespace llvm {
@@ -59,6 +61,10 @@ class WebAssemblyTargetLowering final : public TargetLowering {
bool isIntDivCheap(EVT VT, AttributeList Attr) const override;
bool isVectorLoadExtDesirable(SDValue ExtVal) const override;
bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
+
+ bool shouldLocalize(const MachineInstr &MI,
+ const TargetTransformInfo *TTI) const override;
+
EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
EVT VT) const override;
bool getTgtMemIntrinsic(IntrinsicInfo &Info, const CallBase &I,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index ee57941114801..f7d50b0de49fd 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -23,6 +23,7 @@
#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
#include "llvm/CodeGen/GlobalISel/Legalizer.h"
+#include "llvm/CodeGen/GlobalISel/Localizer.h"
#include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
#include "llvm/CodeGen/MIRParser/MIParser.h"
#include "llvm/CodeGen/Passes.h"
@@ -688,6 +689,7 @@ void WebAssemblyPassConfig::addPreLegalizeMachineIR() {
} else {
addPass(createWebAssemblyPreLegalizerCombiner());
}
+ addPass(new Localizer());
}
bool WebAssemblyPassConfig::addLegalizeMachineIR() {
addPass(new Legalizer());
>From 6e4e76039787bf590add3893404a6e42a9c7c7c5 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 26 Jan 2026 15:04:01 -0800
Subject: [PATCH 36/41] Add some post-legalize combiners (truncstore,
fold_global_offset)
[skip ci]
---
.../WebAssemblyPostLegalizerCombiner.cpp | 99 ++++++++++++++++---
llvm/lib/Target/WebAssembly/WebAssembly.h | 2 +-
.../Target/WebAssembly/WebAssemblyCombine.td | 18 +++-
.../WebAssembly/WebAssemblyTargetMachine.cpp | 4 +-
4 files changed, 105 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
index 49416298ff1b4..4c681567d8fe0 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyPostLegalizerCombiner.cpp
@@ -24,10 +24,13 @@
#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/GlobalValue.h"
#define GET_GICOMBINER_DEPS
#include "WebAssemblyGenPostLegalizeGICombiner.inc"
@@ -43,6 +46,60 @@ namespace {
#include "WebAssemblyGenPostLegalizeGICombiner.inc"
#undef GET_GICOMBINER_TYPES
+bool matchFoldGlobalOffset(MachineOperand &Dst, MachineOperand &Global,
+ MachineOperand &Offset, BuildFnTy &MatchInfo) {
+ if (Dst.getParent()->getMF()->getTarget().isPositionIndependent())
+ return false;
+
+ // We assume at this point that we are looking at the Global as the operand of
+ // a G_GLOBAL_VALUE, which is on the left-hand side of a NUW G_PTRADD, with
+ // Offset being the immediate from a G_CONSTANT on the right-hand side.
+ if (!Dst.isReg() || !Global.isGlobal() || !Offset.isCImm())
+ return false;
+
+ Register DstReg = Dst.getReg();
+ auto *GV = Global.getGlobal();
+
+ if (GV->isThreadLocal())
+ return false;
+
+ uint64_t NewOffset = Global.getOffset() + Offset.getCImm()->getSExtValue();
+
+ if (int64_t(NewOffset) < 0) {
+ return false;
+ }
+
+ MatchInfo = [=](MachineIRBuilder &B) {
+ B.buildInstr(TargetOpcode::G_GLOBAL_VALUE)
+ .addDef(DstReg)
+ .addGlobalAddress(GV, NewOffset);
+ };
+
+ return true;
+}
+
+bool matchFormTruncstore(MachineInstr &MI, MachineRegisterInfo &MRI,
+ Register &SrcReg) {
+ assert(MI.getOpcode() == TargetOpcode::G_STORE);
+ Register DstReg = MI.getOperand(0).getReg();
+ if (MRI.getType(DstReg).isVector())
+ return false;
+ // Match a store of a truncate.
+ if (!mi_match(DstReg, MRI, m_GTrunc(MIPatternMatch::m_Reg(SrcReg))))
+ return false;
+ // Only form truncstores for value types of max 64b.
+ return MRI.getType(SrcReg).getSizeInBits() <= 64;
+}
+
+void applyFormTruncstore(MachineInstr &MI, MachineRegisterInfo &MRI,
+ MachineIRBuilder &B, GISelChangeObserver &Observer,
+ Register &SrcReg) {
+ assert(MI.getOpcode() == TargetOpcode::G_STORE);
+ Observer.changingInstr(MI);
+ MI.getOperand(0).setReg(SrcReg);
+ Observer.changedInstr(MI);
+}
+
class WebAssemblyPostLegalizerCombinerImpl : public Combiner {
protected:
const CombinerHelper Helper;
@@ -90,7 +147,7 @@ class WebAssemblyPostLegalizerCombiner : public MachineFunctionPass {
public:
static char ID;
- WebAssemblyPostLegalizerCombiner();
+ WebAssemblyPostLegalizerCombiner(bool IsOptNone = false);
StringRef getPassName() const override {
return "WebAssemblyPostLegalizerCombiner";
@@ -100,6 +157,7 @@ class WebAssemblyPostLegalizerCombiner : public MachineFunctionPass {
void getAnalysisUsage(AnalysisUsage &AU) const override;
private:
+ bool IsOptNone;
WebAssemblyPostLegalizerCombinerImplRuleConfig RuleConfig;
};
} // end anonymous namespace
@@ -111,15 +169,18 @@ void WebAssemblyPostLegalizerCombiner::getAnalysisUsage(
getSelectionDAGFallbackAnalysisUsage(AU);
AU.addRequired<GISelValueTrackingAnalysisLegacy>();
AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
- AU.addRequired<MachineDominatorTreeWrapperPass>();
- AU.addPreserved<MachineDominatorTreeWrapperPass>();
- AU.addRequired<GISelCSEAnalysisWrapperPass>();
- AU.addPreserved<GISelCSEAnalysisWrapperPass>();
+ if (!IsOptNone) {
+ AU.addRequired<MachineDominatorTreeWrapperPass>();
+ AU.addPreserved<MachineDominatorTreeWrapperPass>();
+ AU.addRequired<GISelCSEAnalysisWrapperPass>();
+ AU.addPreserved<GISelCSEAnalysisWrapperPass>();
+ }
MachineFunctionPass::getAnalysisUsage(AU);
}
-WebAssemblyPostLegalizerCombiner::WebAssemblyPostLegalizerCombiner()
- : MachineFunctionPass(ID) {
+WebAssemblyPostLegalizerCombiner::WebAssemblyPostLegalizerCombiner(
+ bool IsOptNone)
+ : MachineFunctionPass(ID), IsOptNone(IsOptNone) {
if (!RuleConfig.parseCommandLineOption())
report_fatal_error("Invalid rule identifier");
}
@@ -139,15 +200,25 @@ bool WebAssemblyPostLegalizerCombiner::runOnMachineFunction(
GISelValueTracking *VT =
&getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
- MachineDominatorTree *MDT =
- &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
- GISelCSEAnalysisWrapper &Wrapper =
- getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
- auto *CSEInfo = &Wrapper.get(TPC->getCSEConfig());
+ MachineDominatorTree *MDT = nullptr;
+ GISelCSEInfo *CSEInfo = nullptr;
+
+ if (!IsOptNone) {
+ MDT = &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+
+ GISelCSEAnalysisWrapper &Wrapper =
+ getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
+ CSEInfo = &Wrapper.get(TPC->getCSEConfig());
+ }
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;
+ // Legalizer performs DCE, so a full DCE pass is unnecessary.
+ CInfo.EnableFullDCE = false;
WebAssemblyPostLegalizerCombinerImpl Impl(MF, CInfo, TPC, *VT, CSEInfo,
RuleConfig, ST, MDT, LI);
return Impl.combineMachineInstrs();
@@ -163,6 +234,6 @@ INITIALIZE_PASS_END(WebAssemblyPostLegalizerCombiner, DEBUG_TYPE,
"Combine WebAssembly MachineInstrs after legalization",
false, false)
-FunctionPass *llvm::createWebAssemblyPostLegalizerCombiner() {
- return new WebAssemblyPostLegalizerCombiner();
+FunctionPass *llvm::createWebAssemblyPostLegalizerCombiner(bool IsOptNone) {
+ return new WebAssemblyPostLegalizerCombiner(IsOptNone);
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 5901e4b1a47aa..7fbeffedc284b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -41,7 +41,7 @@ createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &,
const WebAssemblySubtarget &,
const WebAssemblyRegisterBankInfo &);
-FunctionPass *createWebAssemblyPostLegalizerCombiner();
+FunctionPass *createWebAssemblyPostLegalizerCombiner(bool IsOptNone);
void initializeWebAssemblyPostLegalizerCombinerPass(PassRegistry &);
FunctionPass *createWebAssemblyO0PreLegalizerCombiner();
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td b/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td
index d70fd27fa2ad3..a3c8377c12118 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCombine.td
@@ -11,6 +11,22 @@
include "llvm/Target/GlobalISel/Combine.td"
+def fold_global_offset : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (G_GLOBAL_VALUE $base, $g),
+ (G_CONSTANT $offset, $imm),
+ (G_PTR_ADD $root, $base, $offset, (MIFlags NoUWrap)),
+ [{ return matchFoldGlobalOffset(${root}, ${g}, ${imm}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFnMO(${root}, ${matchinfo}); }])>;
+
+def form_truncstore_matchdata : GIDefMatchData<"Register">;
+def form_truncstore : GICombineRule<
+ (defs root:$root, form_truncstore_matchdata:$matchinfo),
+ (match (G_STORE $src, $addr):$root,
+ [{ return matchFormTruncstore(*${root}, MRI, ${matchinfo}); }]),
+ (apply [{ applyFormTruncstore(*${root}, MRI, B, Observer, ${matchinfo}); }])
+>;
+
def WebAssemblyPreLegalizerCombiner: GICombiner<
"WebAssemblyPreLegalizerCombinerImpl", [all_combines]> {
}
@@ -22,5 +38,5 @@ def WebAssemblyO0PreLegalizerCombiner: GICombiner<
// Post-legalization combines which are primarily optimizations.
def WebAssemblyPostLegalizerCombiner
: GICombiner<"WebAssemblyPostLegalizerCombinerImpl",
- []> {
+ [fold_global_offset, form_truncstore]> {
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index f7d50b0de49fd..155508681b75f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -697,8 +697,8 @@ bool WebAssemblyPassConfig::addLegalizeMachineIR() {
}
void WebAssemblyPassConfig::addPreRegBankSelect() {
- if (getOptLevel() != CodeGenOptLevel::None)
- addPass(createWebAssemblyPostLegalizerCombiner());
+ addPass(createWebAssemblyPostLegalizerCombiner(getOptLevel() ==
+ CodeGenOptLevel::None));
}
bool WebAssemblyPassConfig::addRegBankSelect() {
>From d878ac7899de3cdd4520a664a9ba46b71bf33efd Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 27 Jan 2026 00:16:30 -0800
Subject: [PATCH 37/41] Fix `selectAddrOperands` for PIC
---
.../GISel/WebAssemblyInstructionSelector.cpp | 21 +++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 8c7e11fa9bf7b..73e9fc39e6cd4 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -186,12 +186,17 @@ bool WebAssemblyInstructionSelector::computeAddress(const MachineInstr &MI,
default:
break;
case TargetOpcode::G_GLOBAL_VALUE: {
- if (TM.isPositionIndependent())
- return false;
if (Addr.getGlobalValue())
return false;
- if (MI.getOperand(1).getGlobal()->isThreadLocal())
- return false;
+
+ if (TM.isPositionIndependent() ||
+ MI.getOperand(1).getGlobal()->isThreadLocal()) {
+ if (Addr.isSet())
+ return false;
+
+ Addr.setReg(Def);
+ return true;
+ }
uint64_t TmpOffset = Addr.getOffset() + MI.getOperand(1).getOffset();
if (int64_t(TmpOffset) < 0)
@@ -259,8 +264,12 @@ WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
Address Addr = Address();
- if (!computeAddress(RootDef, Addr))
- return std::nullopt;
+ if (!computeAddress(RootDef, Addr)) {
+ // something inhibited the complex matching logic
+ // just fall back to using the register as base, and 0 offset
+ Addr = Address();
+ Addr.setReg(Root.getReg());
+ }
if (Addr.isRegBase()) {
unsigned Reg = Addr.getReg();
>From 0aa0de8c3b5177b7fb3268cac0125797e52e3500 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Wed, 28 Jan 2026 21:27:42 -0800
Subject: [PATCH 38/41] Mark the `G_PTR_ADD`s used to store varargs onto the
stack during `lowerCall` as NUW
---
.../Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 9769c03f6dca2..270a87fa52527 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -1162,7 +1162,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
if (!Arg.Flags[0].isVarArg()) {
for (unsigned Part = 0; Part < NumParts; ++Part) {
auto NewArgReg = MRI.createGenericVirtualRegister(LLT(NewVT));
- MRI.setRegClass(NewArgReg, TLI.getRegClassFor(NewVT)); MIRBuilder.buildCopy(NewArgReg, Arg.Regs[Part]);
+ MRI.setRegClass(NewArgReg, TLI.getRegClassFor(NewVT));
+ MIRBuilder.buildCopy(NewArgReg, Arg.Regs[Part]);
CallInst.addUse(Arg.Regs[Part]);
}
++NumFixedArgs;
@@ -1244,7 +1245,8 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
MIRBuilder
.buildPtrAdd(
PtrLLT, VarArgStackPtr,
- MIRBuilder.buildConstant(SizeLLT, Offset).getReg(0))
+ MIRBuilder.buildConstant(SizeLLT, Offset).getReg(0),
+ MachineInstr::MIFlag::NoUWrap)
.getReg(0);
MachineMemOperand *DstMMO = MF.getMachineMemOperand(
>From 3d73f82a053afc00d1ea2bd39dd96f976bec37b6 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 29 Jan 2026 12:57:44 -0800
Subject: [PATCH 39/41] Legalize extending `G_LOAD` into `G_ZEXTLOAD` instead.
[skip ci]
---
.../GISel/WebAssemblyLegalizerInfo.cpp | 21 +++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index f89ae8bcefcbc..66bff8dc4d54c 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -205,12 +205,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
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}})
+ .customIf(
+ LegalityPredicates::typePairAndMemDescInSet(0, 1, /*MMOIdx*/ 0,
+ {{s32, p0, s8, 1},
+ {s32, p0, s16, 1},
+
+ {s64, p0, s8, 1},
+ {s64, p0, s16, 1},
+ {s64, p0, s32, 1}}))
.clampScalar(0, s32, s64)
.lowerIfMemSizeNotByteSizePow2();
//.scalarize(0);
@@ -345,6 +347,13 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
const LLT s1 = LLT::scalar(1);
switch (MI.getOpcode()) {
+ case TargetOpcode::G_LOAD: {
+ // If we are here we are looking at a valid load with a MemTy smaller than the ValueTy
+ // just turn it into a G_ZEXTLOAD to aid optimizations
+
+ MI.setDesc(MIRBuilder.getTII().get(TargetOpcode::G_ZEXTLOAD));
+ return true;
+ }
case TargetOpcode::G_FCANONICALIZE: {
auto One =
MIRBuilder.buildFConstant(MRI.getType(MI.getOperand(0).getReg()), 1.0)
>From b05802eccabbc94eff07525aeb0b6ef957f761ea Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 7 Apr 2026 12:47:11 -0700
Subject: [PATCH 40/41] Transfer rewritten `lowerReturn`
[skip ci]
---
.../GISel/WebAssemblyCallLowering.cpp | 211 +++++++++---------
1 file changed, 111 insertions(+), 100 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 3864747997c68..395ed0151bc90 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -47,14 +47,6 @@
using namespace llvm;
-static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) {
- if (Flags.isSExt())
- return TargetOpcode::G_SEXT;
- if (Flags.isZExt())
- return TargetOpcode::G_ZEXT;
- return TargetOpcode::G_ANYEXT;
-}
-
// Test whether the given calling convention is supported.
static bool callingConvSupported(CallingConv::ID CallConv) {
// We currently support the language-independent target-independent
@@ -70,6 +62,32 @@ static bool callingConvSupported(CallingConv::ID CallConv) {
CallConv == CallingConv::Swift;
}
+static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) {
+ if (Flags.isSExt())
+ return TargetOpcode::G_SEXT;
+ if (Flags.isZExt())
+ return TargetOpcode::G_ZEXT;
+ return TargetOpcode::G_ANYEXT;
+}
+
+static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) {
+ if (Ty == MVT::externref) {
+ return LLT::pointer(
+ WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF,
+ DL.getPointerSizeInBits(
+ WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF));
+ }
+
+ if (Ty == MVT::funcref) {
+ return LLT::pointer(
+ WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF,
+ DL.getPointerSizeInBits(
+ WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF));
+ }
+
+ return llvm::getLLTForMVT(Ty);
+}
+
WebAssemblyCallLowering::WebAssemblyCallLowering(
const WebAssemblyTargetLowering &TLI)
: CallLowering(&TLI) {}
@@ -87,106 +105,117 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
ArrayRef<Register> VRegs,
FunctionLoweringInfo &FLI,
Register SwiftErrorVReg) const {
- auto MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
MachineFunction &MF = MIRBuilder.getMF();
- auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
- auto &RBI = *Subtarget.getRegBankInfo();
+ const WebAssemblySubtarget &Subtarget =
+ MF.getSubtarget<WebAssemblySubtarget>();
+ const RegisterBankInfo &RBI = *Subtarget.getRegBankInfo();
+ const Function &F = MF.getFunction();
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
+ const DataLayout &DL = F.getDataLayout();
+
+ MachineInstrBuilder 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();
+ "Return value without a vreg or vice versa");
+
+ if (Val) {
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.");
+ if (!FLI.CanLowerReturn) {
+ insertSRetStores(MIRBuilder, Val->getType(), VRegs, FLI.DemoteRegister);
+ } else {
+ 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();
+ SmallVector<ArgInfo, 8> SplitRets;
+ 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);
+ unsigned RetIdx = 0;
+ for (EVT SplitEVT : SplitEVTs) {
+ Register CurVReg = VRegs[RetIdx];
+ ArgInfo CurRetInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
+ setArgFlags(CurRetInfo, AttributeList::ReturnIndex, DL, F);
- splitToValueTypes(CurArgInfo, SplitArgs, DL, CallConv);
- ++i;
- }
+ splitToValueTypes(CurRetInfo, SplitRets, DL, CallConv);
+ ++RetIdx;
+ }
- 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);
- const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
+ for (ArgInfo &Ret : SplitRets) {
+ const EVT OrigVT = TLI.getValueType(DL, Ret.Ty);
+ const MVT NewVT =
+ TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+ const LLT OrigLLT =
+ getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
+ const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
- // 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);
+ const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
- ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
- Arg.Flags.clear();
+ // If we need to split the type over multiple regs, check it's a
+ // scenario we currently support.
+ const unsigned NumParts =
+ TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
- 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();
- }
+ const ISD::ArgFlagsTy OrigFlags = Ret.Flags[0];
+ Ret.Flags.clear();
- Arg.Flags.push_back(Flags);
- }
+ for (unsigned Part = 0; Part < NumParts; ++Part) {
+ ISD::ArgFlagsTy Flags = OrigFlags;
- 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);
+ if (Part == 0) {
+ Flags.setSplit();
+ } else {
+ Flags.setOrigAlign(Align(1));
+ if (Part == NumParts - 1)
+ Flags.setSplitEnd();
+ }
- // 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);
+ Ret.Flags.push_back(Flags);
}
- buildCopyToRegs(MIRBuilder, Arg.Regs, Arg.OrigRegs[0], OrigLLT, NewLLT,
- extendOpFromFlags(Arg.Flags[0]));
- }
- for (unsigned Part = 0; Part < NumParts; ++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);
+ Ret.OrigRegs.assign(Ret.Regs.begin(), Ret.Regs.end());
+ if (NumParts != 1 || OrigLLT != NewLLT) {
+ // 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) {
+ Register NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
+
+ if (!RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI))
+ reportFatalInternalError(
+ "Couldn't constrain brand-new register?");
+
+ MIB.addUse(NewOutReg);
+
+ Ret.Regs[Part] = NewOutReg;
+ }
+
+ buildCopyToRegs(MIRBuilder, Ret.Regs, Ret.OrigRegs[0], OrigLLT,
+ NewLLT, extendOpFromFlags(Ret.Flags[0]));
+ } else {
+ MIB.addUse(Ret.Regs[0]);
+ }
}
}
}
if (SwiftErrorVReg) {
- llvm_unreachable("WASM does not `supportSwiftError`, yet SwiftErrorVReg is "
- "improperly valid.");
+ reportFatalInternalError(
+ "Wasm does not `supportSwiftError`, yet SwiftErrorVReg is "
+ "improperly valid.");
}
MIRBuilder.insertInstr(MIB);
return true;
}
-static unsigned getWASMArgumentOpcode(MVT ArgType) {
+static unsigned getWasmArgumentOpcode(MVT ArgType) {
switch (ArgType.SimpleTy) {
case MVT::i32:
return WebAssembly::ARGUMENT_i32;
@@ -224,24 +253,6 @@ static unsigned getWASMArgumentOpcode(MVT ArgType) {
llvm_unreachable("Found unexpected type for WASM argument");
}
-static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) {
- if (Ty == MVT::externref) {
- return LLT::pointer(
- WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF,
- DL.getPointerSizeInBits(
- WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF));
- }
-
- if (Ty == MVT::funcref) {
- return LLT::pointer(
- WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF,
- DL.getPointerSizeInBits(
- WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF));
- }
-
- return llvm::getLLTForMVT(Ty);
-}
-
bool WebAssemblyCallLowering::lowerFormalArguments(
MachineIRBuilder &MIRBuilder, const Function &F,
ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const {
@@ -336,7 +347,7 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
for (unsigned Part = 0; Part < NumParts; ++Part) {
MachineInstrBuilder ArgInst =
- MIRBuilder.buildInstr(getWASMArgumentOpcode(NewVT))
+ MIRBuilder.buildInstr(getWasmArgumentOpcode(NewVT))
.addDef(Arg.Regs[Part])
.addImm(FinalArgIdx);
@@ -377,7 +388,7 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
MFI->setVarargBufferVreg(VarargVreg);
MachineInstrBuilder ArgInst =
- MIRBuilder.buildInstr(getWASMArgumentOpcode(PtrVT))
+ MIRBuilder.buildInstr(getWasmArgumentOpcode(PtrVT))
.addDef(VarargVreg)
.addImm(FinalArgIdx);
>From 58218fe804dd034ebde6592c139f1945a88e3524 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 7 Apr 2026 16:59:38 -0700
Subject: [PATCH 41/41] Enable extended LLTs
[skip ci]
---
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 10 +-
llvm/lib/Target/WebAssembly/CMakeLists.txt | 2 +-
.../GISel/WebAssemblyCallLowering.cpp | 10 +-
.../GISel/WebAssemblyInstructionSelector.cpp | 4 +-
.../GISel/WebAssemblyLegalizerInfo.cpp | 163 ++---
.../GISel/WebAssemblyRegisterBankInfo.cpp | 647 +-----------------
.../WebAssembly/WebAssemblyTargetMachine.cpp | 3 +
7 files changed, 132 insertions(+), 707 deletions(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 6f214f7207825..2b3a48481c766 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -889,8 +889,8 @@ bool IRTranslator::emitJumpTableHeader(SwitchCG::JumpTable &JT,
// This value may be smaller or larger than the target's pointer type, and
// therefore require extension or truncating.
auto *PtrIRTy = PointerType::getUnqual(SValue.getContext());
- const LLT PtrScalarTy = LLT::scalar(DL->getTypeSizeInBits(PtrIRTy));
- Sub = MIB.buildZExtOrTrunc(PtrScalarTy, Sub);
+ const LLT PtrIntTy = LLT::integer(DL->getTypeSizeInBits(PtrIRTy));
+ Sub = MIB.buildZExtOrTrunc(PtrIntTy, Sub);
JT.Reg = Sub.getReg(0);
@@ -905,7 +905,7 @@ bool IRTranslator::emitJumpTableHeader(SwitchCG::JumpTable &JT,
// largest case in the switch.
auto Cst = getOrCreateVReg(
*ConstantInt::get(SValue.getType(), JTH.Last - JTH.First));
- Cst = MIB.buildZExtOrTrunc(PtrScalarTy, Cst).getReg(0);
+ Cst = MIB.buildZExtOrTrunc(PtrIntTy, Cst).getReg(0);
auto Cmp = MIB.buildICmp(CmpInst::ICMP_UGT, LLT::integer(1), Sub, Cst);
auto BrCond = MIB.buildBrCond(Cmp.getReg(0), *JT.Default);
@@ -1119,14 +1119,14 @@ void IRTranslator::emitBitTestHeader(SwitchCG::BitTestBlock &B,
LLT MaskTy = SwitchOpTy;
if (MaskTy.getSizeInBits() > PtrTy.getSizeInBits() ||
!llvm::has_single_bit<uint32_t>(MaskTy.getSizeInBits()))
- MaskTy = LLT::scalar(PtrTy.getSizeInBits());
+ MaskTy = LLT::integer(PtrTy.getSizeInBits());
else {
// Ensure that the type will fit the mask value.
for (const SwitchCG::BitTestCase &Case : B.Cases) {
if (!isUIntN(SwitchOpTy.getSizeInBits(), Case.Mask)) {
// Switch table case range are encoded into series of masks.
// Just use pointer type, it's guaranteed to fit.
- MaskTy = LLT::scalar(PtrTy.getSizeInBits());
+ MaskTy = LLT::integer(PtrTy.getSizeInBits());
break;
}
}
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index d82cc8525d50c..918ab2446e392 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -15,7 +15,7 @@ tablegen(LLVM WebAssemblyGenSDNodeInfo.inc -gen-sd-node-info)
tablegen(LLVM WebAssemblyGenSubtargetInfo.inc -gen-subtarget)
set(LLVM_TARGET_DEFINITIONS WebAssemblyGISel.td)
-tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel)
+tablegen(LLVM WebAssemblyGenGlobalISel.inc -gen-global-isel -optimize-match-table=false -gisel-extended-llt)
tablegen(LLVM WebAssemblyGenO0PreLegalizeGICombiner.inc -gen-global-isel-combiner
-combiners="WebAssemblyO0PreLegalizerCombiner")
tablegen(LLVM WebAssemblyGenPreLegalizeGICombiner.inc -gen-global-isel-combiner
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 395ed0151bc90..17b709a9c94c2 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -640,7 +640,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
IndirectIdx = Info.Callee.getReg();
auto PtrSize = CalleeType.getSizeInBits();
- auto PtrIntLLT = LLT::scalar(PtrSize);
+ auto PtrIntLLT = LLT::integer(PtrSize);
IndirectIdx = MIRBuilder.buildPtrToInt(PtrIntLLT, IndirectIdx).getReg(0);
} else if (CalleeType.getAddressSpace() ==
@@ -650,7 +650,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
Type *PtrTy = PointerType::getUnqual(Ctx);
LLT PtrLLT = getLLTForType(*PtrTy, DL);
- auto PtrIntLLT = LLT::scalar(PtrLLT.getSizeInBits());
+ auto PtrIntLLT = LLT::integer(PtrLLT.getSizeInBits());
IndirectIdx = MIRBuilder.buildConstant(PtrIntLLT, 0).getReg(0);
@@ -691,7 +691,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
Type *PtrTy = PointerType::getUnqual(Ctx);
LLT PtrLLT = getLLTForType(*PtrTy, DL);
auto PtrSize = PtrLLT.getSizeInBits();
- auto PtrIntLLT = LLT::scalar(PtrSize);
+ auto PtrIntLLT = LLT::integer(PtrSize);
IndirectIdx =
MIRBuilder.buildGlobalValue(PtrLLT, Info.Callee.getGlobal())
@@ -786,7 +786,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
MachineMemOperand::MOStore | MachineMemOperand::MODereferenceable,
MemSize, DstAlign);
- const LLT SizeTy = LLT::scalar(PtrLLT.getSizeInBits());
+ const LLT SizeTy = LLT::integer(PtrLLT.getSizeInBits());
auto SizeConst = MIRBuilder.buildConstant(SizeTy, MemSize);
MIRBuilder.buildMemCpy(StackObjPtrVreg, Arg.Regs[0], SizeConst, *DstMMO,
@@ -898,7 +898,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
auto StackAddrSpace = DL.getAllocaAddrSpace();
auto PtrLLT = LLT::pointer(StackAddrSpace, DL.getPointerSizeInBits(0));
- auto SizeLLT = LLT::scalar(DL.getPointerSizeInBits(StackAddrSpace));
+ auto SizeLLT = LLT::integer(DL.getPointerSizeInBits(StackAddrSpace));
auto *PtrRegClass = TLI.getRegClassFor(TLI.getPointerTy(DL, StackAddrSpace));
if (Info.IsVarArg && NumBytes) {
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index d190870cbd7e7..20a968b91f146 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -300,13 +300,13 @@ WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
InstructionSelector::ComplexRendererFns
WebAssemblyInstructionSelector::selectAddrOperands32(
MachineOperand &Root) const {
- return selectAddrOperands(LLT::scalar(32), WebAssembly::CONST_I32, Root);
+ return selectAddrOperands(LLT::integer(32), WebAssembly::CONST_I32, Root);
}
InstructionSelector::ComplexRendererFns
WebAssemblyInstructionSelector::selectAddrOperands64(
MachineOperand &Root) const {
- return selectAddrOperands(LLT::scalar(64), WebAssembly::CONST_I64, Root);
+ return selectAddrOperands(LLT::integer(64), WebAssembly::CONST_I64, Root);
}
bool WebAssemblyInstructionSelector::selectCopy(
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index a784d83854b05..b757d5993c699 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -34,8 +34,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
const LLT s32 = LLT::scalar(32);
const LLT s64 = LLT::scalar(64);
+ const LLT i32 = LLT::integer(32);
+ const LLT i64 = LLT::integer(64);
+ const LLT f32 = LLT::floatIEEE(32);
+ const LLT f64 = LLT::floatIEEE(64);
+
+
const LLT p0 = LLT::pointer(0, ST.hasAddr64() ? 64 : 32);
- const LLT p0s = LLT::scalar(ST.hasAddr64() ? 64 : 32);
+ const LLT p0i = LLT::integer(ST.hasAddr64() ? 64 : 32);
getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
@@ -45,12 +51,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
getActionDefinitionsBuilder(G_BR).alwaysLegal();
- getActionDefinitionsBuilder(G_BRCOND).legalFor({s32}).clampScalar(0, s32,
+ getActionDefinitionsBuilder(G_BRCOND).legalFor({i32}).clampScalar(0, s32,
s32);
- getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, p0s}});
+ getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, p0i}});
getActionDefinitionsBuilder(G_SELECT)
- .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
+ .legalFor({{s32, i32}, {s64, i32}, {p0, i32}})
//.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64)
@@ -59,14 +65,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
getActionDefinitionsBuilder(G_ICMP)
- .legalFor({{s32, s32}, {s32, s64}, {s32, p0}})
+ .legalFor({{i32, i32}, {i32, i64}, {i32, p0}})
//.scalarize(0)
.widenScalarToNextPow2(1)
.clampScalar(1, s32, s64)
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder(G_FCMP)
- .customFor({{s32, s32}, {s32, s64}})
+ .customFor({{i32, f32}, {i32, f64}})
//.scalarize(0)
.clampScalar(0, s32, s32)
.libcall();
@@ -74,12 +80,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
getActionDefinitionsBuilder(G_CONSTANT)
- .legalFor({s32, s64, p0})
+ .legalFor({i32, i64, p0})
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
getActionDefinitionsBuilder(G_FCONSTANT)
- .legalFor({s32, s64})
+ .legalFor({f32, f64})
.clampScalar(0, s32, s64);
getActionDefinitionsBuilder(G_IMPLICIT_DEF)
@@ -90,14 +96,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(
{G_ADD, G_SUB, G_MUL, G_UDIV, G_SDIV, G_UREM, G_SREM})
- .legalFor({s32, s64})
+ .legalFor({i32, i64})
//.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})
- .legalFor({{s32, s32}, {s64, s64}})
+ .legalFor({{i32, i32}, {i64, i64}})
//.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64)
@@ -107,14 +113,14 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder({G_FSHL, G_FSHR}).lower();
getActionDefinitionsBuilder({G_ROTL, G_ROTR})
- .legalFor({{s32, s32}, {s64, s64}})
+ .legalFor({{i32, i32}, {i64, i64}})
//.scalarize(0)
.lower();
getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
- .legalFor({s32, s64})
+ .legalFor({i32, i64})
//.scalarize(0)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
@@ -126,12 +132,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
G_FCEIL, G_FFLOOR, G_FSQRT, G_INTRINSIC_TRUNC,
G_FNEARBYINT, G_FRINT, G_INTRINSIC_ROUNDEVEN,
G_FMINIMUM, G_FMAXIMUM, G_STRICT_FMUL})
- .legalFor({s32, s64})
+ .legalFor({f32, f64})
//.scalarize(0)
.minScalar(0, s32);
getActionDefinitionsBuilder({G_FMINNUM, G_FMAXNUM})
- .customFor({s32, s64})
+ .customFor({f32, f64})
//.scalarize(0)
.minScalar(0, s32);
@@ -139,6 +145,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
{G_VECREDUCE_OR, G_VECREDUCE_AND}); //.scalarize(1);
getActionDefinitionsBuilder(G_BITCAST)
+ .legalFor({{i32, f32}, {f32, i32}, {i64, f64}, {f64, i64}})
.customIf([=](const LegalityQuery &Query) {
// Handle casts from i1 vectors to scalars.
LLT DstTy = Query.Types[0];
@@ -155,22 +162,22 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.lowerFor({{s64, s32}, {s64, s16}, {s64, s8}, {s32, s16}, {s32, s8}});
getActionDefinitionsBuilder(G_FCANONICALIZE)
- .customFor({s32, s64})
+ .customFor({f32, f64})
//.scalarize(0)
.minScalar(0, s32);
getActionDefinitionsBuilder({G_FMA, G_FREM})
- .libcallFor({s32, s64})
+ .libcallFor({f32, f64})
.minScalar(0, s32);
- getActionDefinitionsBuilder(G_LROUND).libcallForCartesianProduct({s32},
- {s32, s64});
+ getActionDefinitionsBuilder(G_LROUND).libcallForCartesianProduct({i32},
+ {f32, f64});
- getActionDefinitionsBuilder(G_LLROUND).libcallForCartesianProduct({s64},
- {s32, s64});
+ getActionDefinitionsBuilder(G_LLROUND).libcallForCartesianProduct({i64},
+ {f32, f64});
getActionDefinitionsBuilder(G_FCOPYSIGN)
- .legalFor({s32, s64})
+ .legalFor({f32, f64})
//.scalarize(0)
.minScalar(0, s32)
.minScalarSameAs(1, 0)
@@ -179,28 +186,28 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
if (ST.hasNontrappingFPToInt()) {
getActionDefinitionsBuilder(
{G_FPTOUI, G_FPTOUI_SAT, G_FPTOSI, G_FPTOSI_SAT})
- .legalForCartesianProduct({s32, s64}, {s32, s64})
+ .legalForCartesianProduct({i32, i64}, {f32, f64})
.minScalar(1, s32)
.widenScalarToNextPow2(0)
.clampScalar(0, s32, s64);
}
getActionDefinitionsBuilder({G_UITOFP, G_SITOFP})
- .legalForCartesianProduct({s32, s64}, {s32, s64})
+ .legalForCartesianProduct({f32, f64}, {i32, i64})
//.scalarize(0)
.minScalar(1, s32)
.widenScalarToNextPow2(1)
.clampScalar(1, s32, s64);
getActionDefinitionsBuilder(G_PTRTOINT)
- .legalFor({p0s, p0})
- .customForCartesianProduct({s32, s64}, {p0});
+ .legalFor({p0i, p0})
+ .customForCartesianProduct({i32, i64}, {p0});
getActionDefinitionsBuilder(G_INTTOPTR)
- .legalFor({p0, p0s})
- .customForCartesianProduct({p0}, {s32, s64});
+ .legalFor({p0, p0i})
+ .customForCartesianProduct({p0}, {i32, i64});
- getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, p0s}});
- getActionDefinitionsBuilder(G_PTRMASK).legalFor({{p0, p0s}});
+ getActionDefinitionsBuilder(G_PTR_ADD).legalFor({{p0, p0i}});
+ getActionDefinitionsBuilder(G_PTRMASK).legalFor({{p0, p0i}});
getActionDefinitionsBuilder(G_LOAD)
.legalForTypesWithMemDesc(
@@ -239,7 +246,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
},
[=](const LegalityQuery &Query) {
const LLT VecTy = Query.Types[0];
- return std::pair(0, LLT::scalar(VecTy.getSizeInBits()));
+ return std::pair(0, LLT::integer(VecTy.getSizeInBits()));
});
//.scalarize(0);
@@ -248,12 +255,12 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.lower();
getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
- .legalForTypesWithMemDesc({{s32, p0, s8, 1},
- {s32, p0, s16, 1},
+ .legalForTypesWithMemDesc({{i32, p0, s8, 1},
+ {i32, p0, s16, 1},
- {s64, p0, s8, 1},
- {s64, p0, s16, 1},
- {s64, p0, s32, 1}})
+ {i64, p0, s8, 1},
+ {i64, p0, s16, 1},
+ {i64, p0, s32, 1}})
.clampScalar(0, s32, s64)
.lowerIfMemSizeNotByteSizePow2();
@@ -261,15 +268,15 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
getActionDefinitionsBuilder(G_BZERO).unsupported();
getActionDefinitionsBuilder(G_MEMSET)
- .customForCartesianProduct({p0}, {s8, s32}, {p0s})
+ .customForCartesianProduct({p0}, {s8, s32}, {p0i})
.immIdx(0);
getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
- .customForCartesianProduct({p0}, {p0}, {p0s})
+ .customForCartesianProduct({p0}, {p0}, {p0i})
.immIdx(0);
getActionDefinitionsBuilder(G_MEMCPY_INLINE)
- .customForCartesianProduct({p0}, {p0}, {p0s});
+ .customForCartesianProduct({p0}, {p0}, {p0i});
} else {
getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
.libcall();
@@ -281,13 +288,13 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
// instruction anyway).
getActionDefinitionsBuilder(G_ANYEXT)
- .legalFor({{s64, s32}})
+ .legalFor({{i64, i32}})
//.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64);
getActionDefinitionsBuilder({G_SEXT, G_ZEXT})
- .legalFor({{s64, s32}})
+ .legalFor({{i64, i32}})
//.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64)
@@ -295,28 +302,28 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
if (ST.hasSignExt()) {
getActionDefinitionsBuilder(G_SEXT_INREG)
- .clampScalar(0, s32, s64)
- .customFor({s32, s64});
+ .customFor({i32, i64})
+ .clampScalar(0, s32, s64);
} else {
getActionDefinitionsBuilder(G_SEXT_INREG).lower();
}
getActionDefinitionsBuilder(G_TRUNC)
- .legalFor({{s32, s64}})
+ .legalFor({{i32, i64}})
//.scalarize(0)
.clampScalar(0, s32, s64)
.clampScalar(1, s32, s64)
.lower();
- getActionDefinitionsBuilder(G_FPEXT).legalFor({{s64, s32}}); //.scalarize(0);
+ getActionDefinitionsBuilder(G_FPEXT).legalFor({{f64, f32}}); //.scalarize(0);
getActionDefinitionsBuilder(G_FPTRUNC).legalFor(
- {{s32, s64}}); //.scalarize(0);
+ {{f32, f64}}); //.scalarize(0);
getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
getActionDefinitionsBuilder(G_VAARG)
.customIf(([=](const LegalityQuery &Query) {
- return Query.Types[1] == p0 && (Query.Types[0].isScalar());
+ return Query.Types[1] == p0 && (Query.Types[0].isInteger());
})) // TODO: replace this with typical lowerForCartesianProduct once widen
// and narrow scalar works
.clampScalar(
@@ -325,7 +332,7 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
.widenScalarToNextPow2(0, /*Min*/ 8); // Note: currently not functional
// (not implemented in core)
- getActionDefinitionsBuilder(G_DYN_STACKALLOC).lowerFor({{p0, p0s}});
+ getActionDefinitionsBuilder(G_DYN_STACKALLOC).lowerFor({{p0, p0i}});
getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
@@ -344,7 +351,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
auto &MRI = *Helper.MIRBuilder.getMRI();
auto &MIRBuilder = Helper.MIRBuilder;
- const LLT s1 = LLT::scalar(1);
+ const LLT i1 = LLT::integer(1);
switch (MI.getOpcode()) {
case TargetOpcode::G_LOAD: {
@@ -381,7 +388,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.setMIFlags(MI.getFlags());
} else {
auto Cond = MIRBuilder
- .buildFCmp(CmpInst::Predicate::FCMP_OLT, s1,
+ .buildFCmp(CmpInst::Predicate::FCMP_OLT, i1,
MI.getOperand(1), MI.getOperand(2))
.getReg(0);
@@ -402,7 +409,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
.setMIFlags(MI.getFlags());
} else {
auto Cond = MIRBuilder
- .buildFCmp(CmpInst::Predicate::FCMP_OGT, s1,
+ .buildFCmp(CmpInst::Predicate::FCMP_OGT, i1,
MI.getOperand(1), MI.getOperand(2))
.getReg(0);
@@ -414,7 +421,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
}
case TargetOpcode::G_PTRTOINT: {
auto TmpReg = MRI.createGenericVirtualRegister(
- LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
+ LLT::integer(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
MIRBuilder.buildPtrToInt(TmpReg, MI.getOperand(1));
MIRBuilder.buildAnyExtOrTrunc(MI.getOperand(0), TmpReg);
@@ -423,7 +430,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
}
case TargetOpcode::G_INTTOPTR: {
auto TmpReg = MRI.createGenericVirtualRegister(
- LLT::scalar(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
+ LLT::integer(MIRBuilder.getDataLayout().getPointerSizeInBits(0)));
MIRBuilder.buildAnyExtOrTrunc(TmpReg, MI.getOperand(1));
MIRBuilder.buildIntToPtr(MI.getOperand(0), TmpReg);
@@ -437,17 +444,17 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
- if (!DstTy.isScalar() || !SrcTy.isVector() || SrcTy.getElementType() != s1)
+ if (!DstTy.isScalar() || !SrcTy.isVector() || SrcTy.getElementType() != i1)
return false;
Register ResultReg = MRI.createGenericVirtualRegister(DstTy);
MIRBuilder.buildConstant(ResultReg, 0);
for (unsigned i = 0; i < SrcTy.getNumElements(); i++) {
- auto Elm = MRI.createGenericVirtualRegister(s1);
+ auto Elm = MRI.createGenericVirtualRegister(i1);
auto ExtElm = MRI.createGenericVirtualRegister(DstTy);
auto ShiftedElm = MRI.createGenericVirtualRegister(DstTy);
- auto Idx = MRI.createGenericVirtualRegister(LLT::scalar(8));
+ auto Idx = MRI.createGenericVirtualRegister(LLT::integer(8));
auto NewResultReg = MRI.createGenericVirtualRegister(DstTy);
MIRBuilder.buildConstant(Idx, i);
@@ -491,67 +498,67 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
MIRBuilder.buildOr(
- s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, s1, LHS, RHS).getReg(0),
- MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, s1, LHS, RHS).getReg(0)));
+ i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, i1, LHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_ORD: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
MIRBuilder.buildAnd(
- s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, s1, RHS, RHS).getReg(0),
- MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, s1, LHS, LHS).getReg(0)));
+ i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, i1, RHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OEQ, i1, LHS, LHS).getReg(0)));
break;
}
case CmpInst::FCMP_UNO: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
MIRBuilder.buildOr(
- s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, s1, RHS, RHS).getReg(0),
- MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, s1, LHS, LHS).getReg(0)));
+ i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, i1, RHS, RHS).getReg(0),
+ MIRBuilder.buildFCmp(CmpInst::FCMP_UNE, i1, LHS, LHS).getReg(0)));
break;
}
case CmpInst::FCMP_UEQ: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
buildEqz(
- MIRBuilder, s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_ONE, s1, LHS, RHS).getReg(0)));
+ MIRBuilder, i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_ONE, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_UGT: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
buildEqz(
- MIRBuilder, s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OLE, s1, LHS, RHS).getReg(0)));
+ MIRBuilder, i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLE, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_UGE: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
buildEqz(
- MIRBuilder, s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, s1, LHS, RHS).getReg(0)));
+ MIRBuilder, i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OLT, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_ULT: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
buildEqz(
- MIRBuilder, s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OGE, s1, LHS, RHS).getReg(0)));
+ MIRBuilder, i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGE, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_ULE: {
MIRBuilder.buildAnyExt(
MI.getOperand(0).getReg(),
buildEqz(
- MIRBuilder, s1,
- MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, s1, LHS, RHS).getReg(0)));
+ MIRBuilder, i1,
+ MIRBuilder.buildFCmp(CmpInst::FCMP_OGT, i1, LHS, RHS).getReg(0)));
break;
}
case CmpInst::FCMP_UNE:
@@ -596,7 +603,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
if (MRI.getType(Value.getReg()).getSizeInBits() == 8) {
Register ExtValueReg =
- Helper.MIRBuilder.buildAnyExt(LLT::scalar(32), Value).getReg(0);
+ Helper.MIRBuilder.buildAnyExt(LLT::integer(32), Value).getReg(0);
Value.setReg(ExtValueReg);
return true;
}
@@ -606,7 +613,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
auto *DstMem = MI.memoperands()[0];
auto &Dst = MI.getOperand(0);
- auto &Len = MI.getOperand(1);
+ auto &Len = MI.getOperand(2);
// auto TailCall = I.getOperand(3);
auto PointerWidth = MI.getMF()->getDataLayout().getPointerSizeInBits();
@@ -657,7 +664,7 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
Register ListPtr = MI.getOperand(1).getReg();
LLT PtrTy = MRI.getType(ListPtr);
- LLT IntPtrTy = LLT::scalar(PtrTy.getSizeInBits());
+ LLT IntPtrTy = LLT::integer(PtrTy.getSizeInBits());
const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
const Align PtrAlign = Align(PtrSize);
@@ -715,7 +722,7 @@ bool WebAssemblyLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
MachineFunction &MF = *MI.getMF();
auto Val = MF.getRegInfo().createGenericVirtualRegister(
- LLT::scalar(PointerWidth * 8));
+ LLT::integer(PointerWidth * 8));
MIRBuilder.buildLoad(Val, MI.getOperand(2),
*MF.getMachineMemOperand(
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index da98ccf9d8642..df1bf52bb8eac 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -13,12 +13,9 @@
#include "WebAssemblyRegisterBankInfo.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
-#include "WebAssemblySubtarget.h"
-#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
-#include "llvm/CodeGen/TargetOpcodes.h"
+#include "WebAssemblyRegisterInfo.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
-#include "llvm/IR/Intrinsics.h"
-#include "llvm/Support/ErrorHandling.h"
#define GET_TARGET_REGBANK_IMPL
@@ -35,43 +32,11 @@ enum PartialMappingIdx {
PMI_Min = PMI_I32,
};
-enum ValueMappingIdx {
- InvalidIdx = 0,
- I32Idx = 1,
- I64Idx = 5,
- F32Idx = 9,
- F64Idx = 13
-};
-
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 4 operands as I32
- {&PartMappings[PMI_I32 - PMI_Min], 1},
- {&PartMappings[PMI_I32 - PMI_Min], 1},
- {&PartMappings[PMI_I32 - PMI_Min], 1},
- {&PartMappings[PMI_I32 - PMI_Min], 1},
- // up to 4 operands as I64
- {&PartMappings[PMI_I64 - PMI_Min], 1},
- {&PartMappings[PMI_I64 - PMI_Min], 1},
- {&PartMappings[PMI_I64 - PMI_Min], 1},
- {&PartMappings[PMI_I64 - PMI_Min], 1},
- // up to 4 operands as F32
- {&PartMappings[PMI_F32 - PMI_Min], 1},
- {&PartMappings[PMI_F32 - PMI_Min], 1},
- {&PartMappings[PMI_F32 - PMI_Min], 1},
- {&PartMappings[PMI_F32 - PMI_Min], 1},
- // up to 4 operands as F64
- {&PartMappings[PMI_F64 - PMI_Min], 1},
- {&PartMappings[PMI_F64 - PMI_Min], 1},
- {&PartMappings[PMI_F64 - PMI_Min], 1},
- {&PartMappings[PMI_F64 - PMI_Min], 1}};
-
} // namespace WebAssembly
} // namespace llvm
@@ -80,113 +45,11 @@ 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 WebAssemblySubtarget &STI = MF.getSubtarget<WebAssemblySubtarget>();
- const WebAssemblyRegisterInfo &TRI = *STI.getRegisterInfo();
if ((Opc != TargetOpcode::COPY && !isPreISelGenericOpcode(Opc)) ||
Opc == TargetOpcode::G_PHI) {
@@ -196,498 +59,50 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
return Mapping;
}
- using namespace TargetOpcode;
-
- unsigned NumOperands = MI.getNumOperands();
- const ValueMapping *OperandsMapping = nullptr;
+ const unsigned NumOperands = MI.isCopyLike() ? 1 : MI.getNumOperands();
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();
- }
- }
-
- if (MI.isDebugValue()) {
- const MachineOperand &MO = MI.getOperand(0);
-
- if (!MO.isReg())
- return getInstructionMapping(DefaultMappingID, /*Cost=*/1, nullptr, 0);
-
- auto &CurBankOrClass = MRI.getRegClassOrRegBank(MO.getReg());
-
- const RegisterBank *NewBank;
+ // Track the size and bank of each register. We don't do partial mappings.
+ SmallVector<unsigned, 8> OpSize(NumOperands);
+ SmallVector<WebAssembly::PartialMappingIdx, 8> OpRegBankIdx(NumOperands);
+ for (unsigned Idx = 0; Idx < NumOperands; ++Idx) {
+ auto &MO = MI.getOperand(Idx);
+ if (!MO.isReg() || !MO.getReg())
+ continue;
- if (auto *CurBank = CurBankOrClass.dyn_cast<const RegisterBank *>()) {
- NewBank = CurBank;
- } else if (auto *CurClass =
- CurBankOrClass.dyn_cast<const TargetRegisterClass *>()) {
- getRegBankFromRegClass(*CurClass, MRI.getType(MO.getReg())).dump();
- NewBank = &getRegBankFromRegClass(*CurClass, MRI.getType(MO.getReg()));
- } else {
- llvm_unreachable("Encountered DBG_VALUE with an unmapped register.");
- }
+ LLT Ty = MRI.getType(MO.getReg());
+ if (!Ty.isValid())
+ continue;
- WebAssembly::ValueMappingIdx ValueMappingIdx;
+ OpSize[Idx] = Ty.getSizeInBits().getKnownMinValue();
- switch (NewBank->getID()) {
- case WebAssembly::I32RegBankID:
- ValueMappingIdx = WebAssembly::I32Idx;
- break;
- case WebAssembly::I64RegBankID:
- ValueMappingIdx = WebAssembly::I64Idx;
- break;
- case WebAssembly::F32RegBankID:
- ValueMappingIdx = WebAssembly::F32Idx;
- break;
- case WebAssembly::F64RegBankID:
- ValueMappingIdx = WebAssembly::F64Idx;
- break;
- default:
- llvm_unreachable("Encountered unexpected register bank.");
+ if (Ty.isInteger() || Ty.isPointer()) {
+ OpRegBankIdx[Idx] = OpSize[Idx] == 64 ? WebAssembly::PMI_I64 : WebAssembly::PMI_I32;
+ } else if (Ty.isFloatIEEE()) {
+ OpRegBankIdx[Idx] = OpSize[Idx] == 64 ? WebAssembly::PMI_F64 : WebAssembly::PMI_F32;
}
-
- return getInstructionMapping(
- MappingID, /*Cost=*/1,
- getOperandsMapping({&WebAssembly::ValueMappings[ValueMappingIdx]}),
- NumOperands);
}
- switch (Opc) {
- case G_BR:
- return getInstructionMapping(MappingID, /*Cost=*/1,
- getOperandsMapping({nullptr}), NumOperands);
- case G_TRAP:
- 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);
- }
+ SmallVector<const ValueMapping *, 8> OpdsMapping(NumOperands);
+ for (unsigned Idx = 0; Idx < NumOperands; ++Idx) {
+ if (MI.getOperand(Idx).isReg() && MI.getOperand(Idx).getReg()) {
+ LLT Ty = MRI.getType(MI.getOperand(Idx).getReg());
+ if (!Ty.isValid())
+ continue;
- if (DstReg.id() == WebAssembly::SP64) {
- return getInstructionMapping(
- MappingID, /*Cost=*/1,
- getOperandsMapping(
- {&WebAssembly::ValueMappings[WebAssembly::I64Idx]}),
- 1);
+ if (OpRegBankIdx[Idx] <= 0) {
+ return getInvalidInstructionMapping();
}
- llvm_unreachable("Trying to copy into WASM physical register other "
- "than sp32 or sp64?");
- }
- break;
- }
- case G_INTRINSIC:
- case G_INTRINSIC_W_SIDE_EFFECTS: {
- switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
- default:
- break;
- }
- return getInstructionMapping(DefaultMappingID, /*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];
+ const auto &Mapping = getValueMapping(&WebAssembly::PartMappings[OpRegBankIdx[Idx] - WebAssembly::PMI_Min], 1);
- 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_PTRMASK:
- 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:
- 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:
- case G_ROTR:
- case G_ROTL:
- 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:
- case G_FCANONICALIZE:
- case G_STRICT_FMUL:
- OperandsMapping = &Op0FloatValueMapping;
- break;
- case G_SEXT_INREG:
- OperandsMapping =
- getOperandsMapping({&Op0IntValueMapping, &Op0IntValueMapping, nullptr});
- break;
- case G_FRAME_INDEX:
- OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
- break;
- case G_VASTART:
- OperandsMapping = &Op0IntValueMapping;
- 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_ZEXTLOAD:
- 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({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)
- 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 = Op2Ty.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_FCONSTANT:
- OperandsMapping = getOperandsMapping({&Op0FloatValueMapping, 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_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, &Pointer0ValueMapping});
- break;
- case COPY: {
- Register DstReg = MI.getOperand(0).getReg();
- Register SrcReg = MI.getOperand(1).getReg();
-
- 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: {
- // 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});
+ if (!Mapping.isValid())
+ return getInvalidInstructionMapping();
- } else {
- OperandsMapping =
- getOperandsMapping({&Op0IntValueMapping,
- &WebAssembly::ValueMappings[WebAssembly::I32Idx],
- &Op0IntValueMapping, &Op0IntValueMapping});
+ OpdsMapping[Idx] = &Mapping;
}
- 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();
- return getInstructionMapping(MappingID, /*Cost=*/1, OperandsMapping,
+ return getInstructionMapping(MappingID, /*Cost=*/1, getOperandsMapping(OpdsMapping),
NumOperands);
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 3a7ed93afa32d..35ef7cc56a12a 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -217,6 +217,9 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
basicCheckForEHAndSjLj(this);
initAsmInfo();
+
+ LLT::setUseExtended(true);
+
// Note that we don't use setRequiresStructuredCFG(true). It disables
// optimizations than we're ok with, and want, such as critical edge
// splitting and tail merging.
More information about the llvm-commits
mailing list