-
Notifications
You must be signed in to change notification settings - Fork 17.9k
[WebAssembly][GlobalISel] CallLowering lowerFormalArguments
#180263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ | |
| #include "WebAssemblySubtarget.h" | ||
| #include "WebAssemblyUtilities.h" | ||
| #include "llvm/CodeGen/Analysis.h" | ||
| #include "llvm/CodeGen/FunctionLoweringInfo.h" | ||
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | ||
| #include "llvm/IR/DataLayout.h" | ||
| #include "llvm/IR/DebugLoc.h" | ||
| #include "llvm/IR/Value.h" | ||
|
|
@@ -27,6 +29,21 @@ | |
|
|
||
| using namespace llvm; | ||
|
|
||
| // 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; | ||
| } | ||
|
|
||
| WebAssemblyCallLowering::WebAssemblyCallLowering( | ||
| const WebAssemblyTargetLowering &TLI) | ||
| : CallLowering(&TLI) {} | ||
|
|
@@ -50,13 +67,199 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, | |
| return false; | ||
| } | ||
|
|
||
| static unsigned getWASMArgumentOpcode(MVT ArgType) { | ||
|
QuantumSegfault marked this conversation as resolved.
|
||
| switch (ArgType.SimpleTy) { | ||
| case MVT::i32: | ||
| return WebAssembly::ARGUMENT_i32; | ||
| case MVT::i64: | ||
| return WebAssembly::ARGUMENT_i64; | ||
| case MVT::f32: | ||
| return WebAssembly::ARGUMENT_f32; | ||
| case MVT::f64: | ||
| return WebAssembly::ARGUMENT_f64; | ||
|
|
||
| case MVT::funcref: | ||
| return WebAssembly::ARGUMENT_funcref; | ||
| case MVT::externref: | ||
| return WebAssembly::ARGUMENT_externref; | ||
| case MVT::exnref: | ||
| return WebAssembly::ARGUMENT_exnref; | ||
|
|
||
| case MVT::v16i8: | ||
| return WebAssembly::ARGUMENT_v16i8; | ||
| case MVT::v8i16: | ||
| return WebAssembly::ARGUMENT_v8i16; | ||
| case MVT::v4i32: | ||
| return WebAssembly::ARGUMENT_v4i32; | ||
| case MVT::v2i64: | ||
| return WebAssembly::ARGUMENT_v2i64; | ||
| case MVT::v8f16: | ||
| return WebAssembly::ARGUMENT_v8f16; | ||
| case MVT::v4f32: | ||
| return WebAssembly::ARGUMENT_v4f32; | ||
| case MVT::v2f64: | ||
| return WebAssembly::ARGUMENT_v2f64; | ||
| default: | ||
| break; | ||
| } | ||
| llvm_unreachable("Found unexpected type for WASM argument"); | ||
| } | ||
|
|
||
| bool WebAssemblyCallLowering::lowerFormalArguments( | ||
| MachineIRBuilder &MIRBuilder, const Function &F, | ||
| ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const { | ||
| if (VRegs.empty()) | ||
| return true; // allow only empty signatures for now | ||
| MachineFunction &MF = MIRBuilder.getMF(); | ||
| MachineRegisterInfo &MRI = MF.getRegInfo(); | ||
| 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(); | ||
|
|
||
| return false; | ||
| LLVMContext &Ctx = MIRBuilder.getContext(); | ||
| const CallingConv::ID CallConv = F.getCallingConv(); | ||
|
|
||
| if (!callingConvSupported(CallConv)) { | ||
| return false; | ||
| } | ||
|
|
||
| MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); | ||
| MF.front().addLiveIn(WebAssembly::ARGUMENTS); | ||
|
|
||
| SmallVector<ArgInfo, 8> SplitArgs; | ||
|
|
||
| if (!FLI.CanLowerReturn) { | ||
| insertSRetIncomingArgument(F, SplitArgs, FLI.DemoteRegister, MRI, DL); | ||
| } | ||
|
|
||
| unsigned ArgIdx = 0; | ||
| bool HasSwiftErrorArg = false; | ||
| bool HasSwiftSelfArg = false; | ||
| for (const auto &Arg : F.args()) { | ||
| ArgInfo OrigArg{VRegs[ArgIdx], Arg.getType(), ArgIdx}; | ||
| setArgFlags(OrigArg, ArgIdx + AttributeList::FirstArgIndex, DL, F); | ||
|
|
||
| HasSwiftSelfArg |= Arg.hasSwiftSelfAttr(); | ||
| HasSwiftErrorArg |= Arg.hasSwiftErrorAttr(); | ||
| if (Arg.hasInAllocaAttr()) { | ||
| return false; | ||
| } | ||
| if (Arg.hasNestAttr()) { | ||
| return false; | ||
| } | ||
|
Comment on lines
+169
to
+174
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Untested? What about sret and byval?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, untested...I could add some tests to make sure it fails (which in -global-isel-abort=2 fallback to SelectionDAG, and provide an explicit error message about these not being supported in Wasm). I can test
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually |
||
| splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv()); | ||
| ++ArgIdx; | ||
| } | ||
|
|
||
| unsigned FinalArgIdx = 0; | ||
| for (auto &Arg : SplitArgs) { | ||
|
QuantumSegfault marked this conversation as resolved.
Outdated
|
||
| 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); | ||
| } | ||
| } | ||
|
|
||
| for (unsigned Part = 0; Part < NumParts; ++Part) { | ||
| auto ArgInst = MIRBuilder.buildInstr(getWASMArgumentOpcode(NewVT)) | ||
| .addDef(Arg.Regs[Part]) | ||
| .addImm(FinalArgIdx); | ||
|
|
||
| constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst, | ||
| ArgInst->getDesc(), ArgInst->getOperand(0), 0); | ||
| MFI->addParam(NewVT); | ||
| ++FinalArgIdx; | ||
| } | ||
|
|
||
| if (NumParts != 1 || OrigVT != NewVT) { | ||
| buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT, | ||
| Arg.Flags[0]); | ||
| } | ||
| } | ||
|
|
||
| // 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); | ||
|
QuantumSegfault marked this conversation as resolved.
Outdated
|
||
| if (CallConv == CallingConv::Swift) { | ||
| if (!HasSwiftSelfArg) { | ||
| MFI->addParam(PtrVT); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally should keep this in terms of LLT
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So...like?: const LLT PtrLLT = LLT::pointer(0, DL.getPointerSizeInBits(0));
const MVT PtrVT = getMVTForLLT(PtrLLT);
if (!HasSwiftSelfArg) {
MFI->addParam(PtrVT);Again, MFI->addParam is a Wasm specific extension to MachineFunctionInfo marking the Wasm-level signature of the function. We need an MVT. The only question is how we obtain it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean in the perfect future, the MFI would be in terms of LLT. As in, the LLT:MVT compatibility decisions would be made inside of MFI and would pass in LLT
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not at all familiar with LLTs or how they should be used in GISel, but if most type logic in GISel should use LLTs, maybe we need to extend the infrastructure for handling wasm signatures to use LLTs too. Either reimplement it, or have some way to convert or reconcile them. Signatures are fairly fundamental to wasm, but the existing code that reasons about signatures was added after most of the backend was already there, so it may well the case that more code in the existing backend should handle them explicitly anyway.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tricky...so now SelectionDAG (and FastISel?) needs to be reworked to accept LLT... I can look into it (unless you meant a separate overload?). What's the reason for having MFI->addParam take in an LLT instead?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LLTs do now express float vs. int. Eventually all MVT usage should be replaced with LLT
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! So even ISelDAG and FastISel should be moving towards LLT? Wait... FINALLY! float vs int. Does that mean all those ugly heuristics to guess whether a select, PHI, load, etc. is floating or integer are no longer necessary?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That probably won't happen. I more meant there should be GIsel native infrastructure instead of reusing the DAG pieces
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And mostly yes. There's also still migration work to use the float typed LLTs (IIRC there's still a target hook to opt in for the moment)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like support got reverted cause of build errors on old GCC :/ Reland hasn't been merged yet #188502 |
||
| } | ||
| if (!HasSwiftErrorArg) { | ||
|
QuantumSegfault marked this conversation as resolved.
|
||
| 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, 0); | ||
|
QuantumSegfault marked this conversation as resolved.
Outdated
|
||
| auto PtrLLT = LLT::pointer(0, DL.getPointerSizeInBits(0)); | ||
| Register VarargVreg = MF.getRegInfo().createGenericVirtualRegister(PtrLLT); | ||
|
|
||
| MFI->setVarargBufferVreg(VarargVreg); | ||
|
|
||
| auto ArgInst = MIRBuilder.buildInstr(getWASMArgumentOpcode(PtrVT)) | ||
|
QuantumSegfault marked this conversation as resolved.
Outdated
|
||
| .addDef(VarargVreg) | ||
| .addImm(FinalArgIdx); | ||
|
|
||
| constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst, | ||
| ArgInst->getDesc(), ArgInst->getOperand(0), 0); | ||
|
|
||
| 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, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.