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