From 5eb65fd57bfeb6cfca7f59936d786ec3f01917fd Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 2 Jul 2025 20:53:56 +0200 Subject: [PATCH 01/22] [WebAssembly,llvm] Add llvm.wasm.ref.test.func intrinsic, option 2 To test whether or not a function pointer has the expected signature. Intended for adding a future clang builtin ` __builtin_wasm_test_function_pointer_signature` so we can test whether calling a function pointer will fail with function signature mismatch. This is an alternative to #147076, where instead of using a ref.test.pseudo instruction with a custom inserter, we teach SelectionDag a type of TargetConstantAP nodes that get converted to a CImm in the MCInst layer. --- llvm/include/llvm/CodeGen/ISDOpcodes.h | 1 + llvm/include/llvm/CodeGen/SelectionDAG.h | 10 ++- llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 12 +-- llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 4 + .../lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 7 +- llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 6 +- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 13 ++-- .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 1 + .../WebAssembly/WebAssemblyISelLowering.cpp | 67 +++++++++++++++++ .../WebAssembly/WebAssemblyMCInstLower.cpp | 74 +++++++++++++++++++ .../test/CodeGen/WebAssembly/ref-test-func.ll | 42 +++++++++++ 11 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 llvm/test/CodeGen/WebAssembly/ref-test-func.ll diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 465e4a0a9d0d8..a9d4e5ade0ba8 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -173,6 +173,7 @@ enum NodeType { /// materialized in registers. TargetConstant, TargetConstantFP, + TargetConstantAP, /// TargetGlobalAddress - Like GlobalAddress, but the DAG does no folding or /// anything else with this node, and this is valid in the target-specific diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 7d8a0c4ce8e45..0a7e3ec24be23 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -683,7 +683,8 @@ class SelectionDAG { LLVM_ABI SDValue getConstant(uint64_t Val, const SDLoc &DL, EVT VT, bool isTarget = false, bool isOpaque = false); LLVM_ABI SDValue getConstant(const APInt &Val, const SDLoc &DL, EVT VT, - bool isTarget = false, bool isOpaque = false); + bool isTarget = false, bool isOpaque = false, + bool isArbitraryPrecision = false); LLVM_ABI SDValue getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, bool isTarget = false, @@ -694,7 +695,8 @@ class SelectionDAG { bool IsOpaque = false); LLVM_ABI SDValue getConstant(const ConstantInt &Val, const SDLoc &DL, EVT VT, - bool isTarget = false, bool isOpaque = false); + bool isTarget = false, bool isOpaque = false, + bool isArbitraryPrecision = false); LLVM_ABI SDValue getIntPtrConstant(uint64_t Val, const SDLoc &DL, bool isTarget = false); LLVM_ABI SDValue getShiftAmountConstant(uint64_t Val, EVT VT, @@ -712,6 +714,10 @@ class SelectionDAG { bool isOpaque = false) { return getConstant(Val, DL, VT, true, isOpaque); } + SDValue getTargetConstantAP(const APInt &Val, const SDLoc &DL, EVT VT, + bool isOpaque = false) { + return getConstant(Val, DL, VT, true, isOpaque, true); + } SDValue getTargetConstant(const ConstantInt &Val, const SDLoc &DL, EVT VT, bool isOpaque = false) { return getConstant(Val, DL, VT, true, isOpaque); diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h index 5d9937f832396..45e57c491181b 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -1742,10 +1742,11 @@ class ConstantSDNode : public SDNode { const ConstantInt *Value; - ConstantSDNode(bool isTarget, bool isOpaque, const ConstantInt *val, - SDVTList VTs) - : SDNode(isTarget ? ISD::TargetConstant : ISD::Constant, 0, DebugLoc(), - VTs), + ConstantSDNode(bool isTarget, bool isOpaque, bool isAPTarget, + const ConstantInt *val, SDVTList VTs) + : SDNode(isAPTarget ? ISD::TargetConstantAP + : (isTarget ? ISD::TargetConstant : ISD::Constant), + 0, DebugLoc(), VTs), Value(val) { assert(!isa(val->getType()) && "Unexpected vector type!"); ConstantSDNodeBits.IsOpaque = isOpaque; @@ -1772,7 +1773,8 @@ class ConstantSDNode : public SDNode { static bool classof(const SDNode *N) { return N->getOpcode() == ISD::Constant || - N->getOpcode() == ISD::TargetConstant; + N->getOpcode() == ISD::TargetConstant || + N->getOpcode() == ISD::TargetConstantAP; } }; diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td index f592ff287a0e3..fb61d8a11e5c0 100644 --- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td +++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td @@ -43,6 +43,10 @@ def int_wasm_ref_is_null_exn : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_exnref_ty], [IntrNoMem], "llvm.wasm.ref.is_null.exn">; +def int_wasm_ref_test_func + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_vararg_ty], + [IntrNoMem], "llvm.wasm.ref.test.func">; + //===----------------------------------------------------------------------===// // Table intrinsics //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 03d3e8eab35d0..95a93d0cefff9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -402,7 +402,12 @@ void InstrEmitter::AddOperand(MachineInstrBuilder &MIB, SDValue Op, AddRegisterOperand(MIB, Op, IIOpNum, II, VRBaseMap, IsDebug, IsClone, IsCloned); } else if (ConstantSDNode *C = dyn_cast(Op)) { - MIB.addImm(C->getSExtValue()); + if (C->getOpcode() == ISD::TargetConstantAP) { + MIB.addCImm( + ConstantInt::get(MF->getFunction().getContext(), C->getAPIntValue())); + } else { + MIB.addImm(C->getSExtValue()); + } } else if (ConstantFPSDNode *F = dyn_cast(Op)) { MIB.addFPImm(F->getConstantFPValue()); } else if (RegisterSDNode *R = dyn_cast(Op)) { diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index f5f4d71236fee..ef5c74610f887 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -968,6 +968,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { // Allow illegal target nodes and illegal registers. if (Node->getOpcode() == ISD::TargetConstant || + Node->getOpcode() == ISD::TargetConstantAP || Node->getOpcode() == ISD::Register) return; @@ -979,10 +980,11 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { for (const SDValue &Op : Node->op_values()) assert((TLI.getTypeAction(*DAG.getContext(), Op.getValueType()) == - TargetLowering::TypeLegal || + TargetLowering::TypeLegal || Op.getOpcode() == ISD::TargetConstant || + Op->getOpcode() == ISD::TargetConstantAP || Op.getOpcode() == ISD::Register) && - "Unexpected illegal type!"); + "Unexpected illegal type!"); #endif // Figure out the correct action; the way to query this varies by opcode diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 2a8bda55fef04..2c1bd246c147a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1664,14 +1664,14 @@ SDValue SelectionDAG::getConstant(uint64_t Val, const SDLoc &DL, EVT VT, } SDValue SelectionDAG::getConstant(const APInt &Val, const SDLoc &DL, EVT VT, - bool isT, bool isO) { - return getConstant(*ConstantInt::get(*Context, Val), DL, VT, isT, isO); + bool isT, bool isO, bool isAP) { + return getConstant(*ConstantInt::get(*Context, Val), DL, VT, isT, isO, isAP); } SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, - EVT VT, bool isT, bool isO) { + EVT VT, bool isT, bool isO, bool isAP) { assert(VT.isInteger() && "Cannot create FP integer constant!"); - + isT |= isAP; EVT EltVT = VT.getScalarType(); const ConstantInt *Elt = &Val; @@ -1760,7 +1760,8 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, assert(Elt->getBitWidth() == EltVT.getSizeInBits() && "APInt size does not match type size!"); - unsigned Opc = isT ? ISD::TargetConstant : ISD::Constant; + unsigned Opc = isAP ? ISD::TargetConstantAP + : (isT ? ISD::TargetConstant : ISD::Constant); SDVTList VTs = getVTList(EltVT); FoldingSetNodeID ID; AddNodeIDNode(ID, Opc, VTs, {}); @@ -1773,7 +1774,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, return SDValue(N, 0); if (!N) { - N = newSDNode(isT, isO, Elt, VTs); + N = newSDNode(isT, isO, isAP, Elt, VTs); CSEMap.InsertNode(N, IP); InsertNode(N); NewSDValueDbgMsg(SDValue(N, 0), "Creating constant: ", this); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index d9b9cf6bcc772..5a3b96743b0ef 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -3255,6 +3255,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, case ISD::HANDLENODE: case ISD::MDNODE_SDNODE: case ISD::TargetConstant: + case ISD::TargetConstantAP: case ISD::TargetConstantFP: case ISD::TargetConstantPool: case ISD::TargetFrameIndex: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index bf2e04caa0a61..ec369eaeae0a5 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -18,6 +18,7 @@ #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" #include "WebAssemblyUtilities.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -794,6 +795,7 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, if (IsIndirect) { // Placeholder for the type index. + // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp MIB.addImm(0); // The table into which this call_indirect indexes. MCSymbolWasm *Table = IsFuncrefCall @@ -2253,6 +2255,71 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, DAG.getTargetExternalSymbol(TlsBase, PtrVT)), 0); } + case Intrinsic::wasm_ref_test_func: { + // First emit the TABLE_GET instruction to convert function pointer ==> + // funcref + MachineFunction &MF = DAG.getMachineFunction(); + auto PtrVT = getPointerTy(MF.getDataLayout()); + MCSymbol *Table = + WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget); + SDValue TableSym = DAG.getMCSymbol(Table, PtrVT); + SDValue FuncRef = + SDValue(DAG.getMachineNode(WebAssembly::TABLE_GET_FUNCREF, DL, + MVT::funcref, TableSym, Op.getOperand(1)), + 0); + + // Encode the signature information into the type index placeholder. + // This gets decoded and converted into the actual type signature in + // WebAssemblyMCInstLower.cpp. + auto NParams = Op.getNumOperands() - 2; + auto Sig = APInt(NParams * 64, 0); + // The return type has to be a BlockType since it can be void. + { + SDValue Operand = Op.getOperand(2); + MVT VT = Operand.getValueType().getSimpleVT(); + WebAssembly::BlockType V; + if (VT == MVT::Untyped) { + V = WebAssembly::BlockType::Void; + } else if (VT == MVT::i32) { + V = WebAssembly::BlockType::I32; + } else if (VT == MVT::i64) { + V = WebAssembly::BlockType::I64; + } else if (VT == MVT::f32) { + V = WebAssembly::BlockType::F32; + } else if (VT == MVT::f64) { + V = WebAssembly::BlockType::F64; + } else { + llvm_unreachable("Unhandled type!"); + } + Sig |= (int64_t)V; + } + for (unsigned i = 3; i < Op.getNumOperands(); ++i) { + SDValue Operand = Op.getOperand(i); + MVT VT = Operand.getValueType().getSimpleVT(); + wasm::ValType V; + if (VT == MVT::i32) { + V = wasm::ValType::I32; + } else if (VT == MVT::i64) { + V = wasm::ValType::I64; + } else if (VT == MVT::f32) { + V = wasm::ValType::F32; + } else if (VT == MVT::f64) { + V = wasm::ValType::F64; + } else { + llvm_unreachable("Unhandled type!"); + } + Sig <<= 64; + Sig |= (int64_t)V; + } + + SmallVector Ops; + Ops.push_back(DAG.getTargetConstantAP( + Sig, DL, EVT::getIntegerVT(*DAG.getContext(), NParams * 64))); + Ops.push_back(FuncRef); + return SDValue( + DAG.getMachineNode(WebAssembly::REF_TEST_FUNCREF, DL, MVT::i32, Ops), + 0); + } } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index cc36244e63ff5..f725ec344d922 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -15,13 +15,17 @@ #include "WebAssemblyMCInstLower.h" #include "MCTargetDesc/WebAssemblyMCAsmInfo.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "TargetInfo/WebAssemblyTargetInfo.h" #include "Utils/WebAssemblyTypeUtilities.h" #include "WebAssemblyAsmPrinter.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyUtilities.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineOperand.h" #include "llvm/IR/Constants.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -196,11 +200,80 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, MCOp = MCOperand::createReg(WAReg); break; } + case llvm::MachineOperand::MO_CImmediate: { + // Lower type index placeholder for ref.test + // Currently this is the only way that CImmediates show up so panic if we + // get confused. + unsigned DescIndex = I - NumVariadicDefs; + if (DescIndex >= Desc.NumOperands) { + llvm_unreachable("unexpected CImmediate operand"); + } + const MCOperandInfo &Info = Desc.operands()[DescIndex]; + if (Info.OperandType != WebAssembly::OPERAND_TYPEINDEX) { + llvm_unreachable("unexpected CImmediate operand"); + } + auto CImm = MO.getCImm()->getValue(); + auto NumWords = CImm.getNumWords(); + // Extract the type data we packed into the CImm in LowerRefTestFuncRef. + // We need to load the words from most significant to least significant + // order because of the way we bitshifted them in from the right. + // The return type needs special handling because it could be void. + auto ReturnType = static_cast( + CImm.extractBitsAsZExtValue(64, (NumWords - 1) * 64)); + SmallVector Returns; + switch (ReturnType) { + case WebAssembly::BlockType::Invalid: + llvm_unreachable("Invalid return type"); + case WebAssembly::BlockType::I32: + Returns = {wasm::ValType::I32}; + break; + case WebAssembly::BlockType::I64: + Returns = {wasm::ValType::I64}; + break; + case WebAssembly::BlockType::F32: + Returns = {wasm::ValType::F32}; + break; + case WebAssembly::BlockType::F64: + Returns = {wasm::ValType::F64}; + break; + case WebAssembly::BlockType::Void: + Returns = {}; + break; + case WebAssembly::BlockType::Exnref: + Returns = {wasm::ValType::EXNREF}; + break; + case WebAssembly::BlockType::Externref: + Returns = {wasm::ValType::EXTERNREF}; + break; + case WebAssembly::BlockType::Funcref: + Returns = {wasm::ValType::FUNCREF}; + break; + case WebAssembly::BlockType::V128: + Returns = {wasm::ValType::V128}; + break; + case WebAssembly::BlockType::Multivalue: { + llvm_unreachable("Invalid return type"); + } + } + SmallVector Params; + + for (int I = NumWords - 2; I >= 0; I--) { + auto Val = CImm.extractBitsAsZExtValue(64, 64 * I); + auto ParamType = static_cast(Val); + Params.push_back(ParamType); + } + MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params)); + break; + } case MachineOperand::MO_Immediate: { unsigned DescIndex = I - NumVariadicDefs; if (DescIndex < Desc.NumOperands) { const MCOperandInfo &Info = Desc.operands()[DescIndex]; + // Replace type index placeholder with actual type index. The type index + // placeholders are Immediates and have an operand type of + // OPERAND_TYPEINDEX or OPERAND_SIGNATURE. if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) { + // Lower type index placeholder for a CALL_INDIRECT instruction SmallVector Returns; SmallVector Params; @@ -228,6 +301,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, break; } if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) { + // Lower type index placeholder for blocks auto BT = static_cast(MO.getImm()); assert(BT != WebAssembly::BlockType::Invalid); if (BT == WebAssembly::BlockType::Multivalue) { diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll new file mode 100644 index 0000000000000..3fc848cd167f9 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -0,0 +1,42 @@ +; RUN: llc < %s -mcpu=mvp -mattr=+reference-types | FileCheck %s + +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: test_function_pointer_signature_void: +; CHECK-NEXT: .functype test_function_pointer_signature_void (i32) -> () +; CHECK-NEXT: .local funcref +; CHECK: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: local.tee 1 +; CHECK-NEXT: ref.test (f32, f64, i32) -> (f32) +; CHECK-NEXT: call use +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: ref.test (f32, f64, i32) -> (i32) +; CHECK-NEXT: call use +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: ref.test (i32, i32, i32) -> (i32) +; CHECK-NEXT: call use +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: ref.test (i32, i32, i32) -> () +; CHECK-NEXT: call use +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: ref.test () -> () +; CHECK-NEXT: call use + +; Function Attrs: nounwind +define void @test_function_pointer_signature_void(ptr noundef %func) local_unnamed_addr #0 { +entry: + %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, float 0.000000e+00, double 0.000000e+00, i32 0) + tail call void @use(i32 noundef %0) #3 + %1 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, float 0.000000e+00, double 0.000000e+00, i32 0) + tail call void @use(i32 noundef %1) #3 + %2 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i32 0, i32 0, i32 0) + tail call void @use(i32 noundef %2) #3 + %3 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) + tail call void @use(i32 noundef %3) #3 + %4 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + tail call void @use(i32 noundef %4) #3 + ret void +} + +declare void @use(i32 noundef) local_unnamed_addr #1 From ce05ea5e2efff83a1ab0aafc0d294909e36f4169 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 12:10:58 +0200 Subject: [PATCH 02/22] Use bit width to decide whether to emit CImm or Imm --- llvm/include/llvm/CodeGen/ISDOpcodes.h | 1 - llvm/include/llvm/CodeGen/SelectionDAG.h | 10 ++-------- llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 12 +++++------- llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 6 +++--- llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 6 ++---- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 13 ++++++------- llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 1 - .../Target/WebAssembly/WebAssemblyISelLowering.cpp | 7 ++++--- .../Target/WebAssembly/WebAssemblyMCInstLower.cpp | 2 +- 9 files changed, 23 insertions(+), 35 deletions(-) diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index a9d4e5ade0ba8..465e4a0a9d0d8 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -173,7 +173,6 @@ enum NodeType { /// materialized in registers. TargetConstant, TargetConstantFP, - TargetConstantAP, /// TargetGlobalAddress - Like GlobalAddress, but the DAG does no folding or /// anything else with this node, and this is valid in the target-specific diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 0a7e3ec24be23..7d8a0c4ce8e45 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -683,8 +683,7 @@ class SelectionDAG { LLVM_ABI SDValue getConstant(uint64_t Val, const SDLoc &DL, EVT VT, bool isTarget = false, bool isOpaque = false); LLVM_ABI SDValue getConstant(const APInt &Val, const SDLoc &DL, EVT VT, - bool isTarget = false, bool isOpaque = false, - bool isArbitraryPrecision = false); + bool isTarget = false, bool isOpaque = false); LLVM_ABI SDValue getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, bool isTarget = false, @@ -695,8 +694,7 @@ class SelectionDAG { bool IsOpaque = false); LLVM_ABI SDValue getConstant(const ConstantInt &Val, const SDLoc &DL, EVT VT, - bool isTarget = false, bool isOpaque = false, - bool isArbitraryPrecision = false); + bool isTarget = false, bool isOpaque = false); LLVM_ABI SDValue getIntPtrConstant(uint64_t Val, const SDLoc &DL, bool isTarget = false); LLVM_ABI SDValue getShiftAmountConstant(uint64_t Val, EVT VT, @@ -714,10 +712,6 @@ class SelectionDAG { bool isOpaque = false) { return getConstant(Val, DL, VT, true, isOpaque); } - SDValue getTargetConstantAP(const APInt &Val, const SDLoc &DL, EVT VT, - bool isOpaque = false) { - return getConstant(Val, DL, VT, true, isOpaque, true); - } SDValue getTargetConstant(const ConstantInt &Val, const SDLoc &DL, EVT VT, bool isOpaque = false) { return getConstant(Val, DL, VT, true, isOpaque); diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h index 45e57c491181b..5d9937f832396 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -1742,11 +1742,10 @@ class ConstantSDNode : public SDNode { const ConstantInt *Value; - ConstantSDNode(bool isTarget, bool isOpaque, bool isAPTarget, - const ConstantInt *val, SDVTList VTs) - : SDNode(isAPTarget ? ISD::TargetConstantAP - : (isTarget ? ISD::TargetConstant : ISD::Constant), - 0, DebugLoc(), VTs), + ConstantSDNode(bool isTarget, bool isOpaque, const ConstantInt *val, + SDVTList VTs) + : SDNode(isTarget ? ISD::TargetConstant : ISD::Constant, 0, DebugLoc(), + VTs), Value(val) { assert(!isa(val->getType()) && "Unexpected vector type!"); ConstantSDNodeBits.IsOpaque = isOpaque; @@ -1773,8 +1772,7 @@ class ConstantSDNode : public SDNode { static bool classof(const SDNode *N) { return N->getOpcode() == ISD::Constant || - N->getOpcode() == ISD::TargetConstant || - N->getOpcode() == ISD::TargetConstantAP; + N->getOpcode() == ISD::TargetConstant; } }; diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 95a93d0cefff9..4d56803ba492a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -402,11 +402,11 @@ void InstrEmitter::AddOperand(MachineInstrBuilder &MIB, SDValue Op, AddRegisterOperand(MIB, Op, IIOpNum, II, VRBaseMap, IsDebug, IsClone, IsCloned); } else if (ConstantSDNode *C = dyn_cast(Op)) { - if (C->getOpcode() == ISD::TargetConstantAP) { + if (C->getAPIntValue().getBitWidth() <= 64) { + MIB.addImm(C->getSExtValue()); + } else { MIB.addCImm( ConstantInt::get(MF->getFunction().getContext(), C->getAPIntValue())); - } else { - MIB.addImm(C->getSExtValue()); } } else if (ConstantFPSDNode *F = dyn_cast(Op)) { MIB.addFPImm(F->getConstantFPValue()); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index ef5c74610f887..f5f4d71236fee 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -968,7 +968,6 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { // Allow illegal target nodes and illegal registers. if (Node->getOpcode() == ISD::TargetConstant || - Node->getOpcode() == ISD::TargetConstantAP || Node->getOpcode() == ISD::Register) return; @@ -980,11 +979,10 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { for (const SDValue &Op : Node->op_values()) assert((TLI.getTypeAction(*DAG.getContext(), Op.getValueType()) == - TargetLowering::TypeLegal || + TargetLowering::TypeLegal || Op.getOpcode() == ISD::TargetConstant || - Op->getOpcode() == ISD::TargetConstantAP || Op.getOpcode() == ISD::Register) && - "Unexpected illegal type!"); + "Unexpected illegal type!"); #endif // Figure out the correct action; the way to query this varies by opcode diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 2c1bd246c147a..2a8bda55fef04 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1664,14 +1664,14 @@ SDValue SelectionDAG::getConstant(uint64_t Val, const SDLoc &DL, EVT VT, } SDValue SelectionDAG::getConstant(const APInt &Val, const SDLoc &DL, EVT VT, - bool isT, bool isO, bool isAP) { - return getConstant(*ConstantInt::get(*Context, Val), DL, VT, isT, isO, isAP); + bool isT, bool isO) { + return getConstant(*ConstantInt::get(*Context, Val), DL, VT, isT, isO); } SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, - EVT VT, bool isT, bool isO, bool isAP) { + EVT VT, bool isT, bool isO) { assert(VT.isInteger() && "Cannot create FP integer constant!"); - isT |= isAP; + EVT EltVT = VT.getScalarType(); const ConstantInt *Elt = &Val; @@ -1760,8 +1760,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, assert(Elt->getBitWidth() == EltVT.getSizeInBits() && "APInt size does not match type size!"); - unsigned Opc = isAP ? ISD::TargetConstantAP - : (isT ? ISD::TargetConstant : ISD::Constant); + unsigned Opc = isT ? ISD::TargetConstant : ISD::Constant; SDVTList VTs = getVTList(EltVT); FoldingSetNodeID ID; AddNodeIDNode(ID, Opc, VTs, {}); @@ -1774,7 +1773,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, return SDValue(N, 0); if (!N) { - N = newSDNode(isT, isO, isAP, Elt, VTs); + N = newSDNode(isT, isO, Elt, VTs); CSEMap.InsertNode(N, IP); InsertNode(N); NewSDValueDbgMsg(SDValue(N, 0), "Creating constant: ", this); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index 5a3b96743b0ef..d9b9cf6bcc772 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -3255,7 +3255,6 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, case ISD::HANDLENODE: case ISD::MDNODE_SDNODE: case ISD::TargetConstant: - case ISD::TargetConstantAP: case ISD::TargetConstantFP: case ISD::TargetConstantPool: case ISD::TargetFrameIndex: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index ec369eaeae0a5..2359d6b04aa97 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -2272,7 +2272,8 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, // This gets decoded and converted into the actual type signature in // WebAssemblyMCInstLower.cpp. auto NParams = Op.getNumOperands() - 2; - auto Sig = APInt(NParams * 64, 0); + auto BitWidth = (NParams + 1) * 64; + auto Sig = APInt(BitWidth, 0); // The return type has to be a BlockType since it can be void. { SDValue Operand = Op.getOperand(2); @@ -2313,8 +2314,8 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, } SmallVector Ops; - Ops.push_back(DAG.getTargetConstantAP( - Sig, DL, EVT::getIntegerVT(*DAG.getContext(), NParams * 64))); + Ops.push_back(DAG.getTargetConstant( + Sig, DL, EVT::getIntegerVT(*DAG.getContext(), BitWidth))); Ops.push_back(FuncRef); return SDValue( DAG.getMachineNode(WebAssembly::REF_TEST_FUNCREF, DL, MVT::i32, Ops), diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index f725ec344d922..4e224c0766146 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -213,7 +213,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, llvm_unreachable("unexpected CImmediate operand"); } auto CImm = MO.getCImm()->getValue(); - auto NumWords = CImm.getNumWords(); + auto NumWords = CImm.getNumWords() - 1; // Extract the type data we packed into the CImm in LowerRefTestFuncRef. // We need to load the words from most significant to least significant // order because of the way we bitshifted them in from the right. From f049ff60419c909200e033b5eeb4f62bd1a5c4dc Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 16:09:32 +0200 Subject: [PATCH 03/22] Try moving to WebAssemblyISelDAGToDAG --- llvm/include/llvm/MC/MCSymbolWasm.h | 1 + .../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 38 ++++++++++ .../WebAssembly/WebAssemblyISelLowering.cpp | 66 ---------------- .../WebAssembly/WebAssemblyMCInstLower.cpp | 76 ++++++------------- .../WebAssembly/WebAssemblyMCInstLower.h | 1 + .../WebAssembly/WebAssemblyUtilities.cpp | 37 +++++++++ .../Target/WebAssembly/WebAssemblyUtilities.h | 7 ++ 7 files changed, 109 insertions(+), 117 deletions(-) diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h index beb6b975a4cc3..b58d6138cd045 100644 --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -12,6 +12,7 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolTableEntry.h" + namespace llvm { class MCSymbolWasm : public MCSymbol { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index ac819cf5c1801..c54787f1f71f7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -15,12 +15,14 @@ #include "WebAssembly.h" #include "WebAssemblyISelLowering.h" #include "WebAssemblyTargetMachine.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/CodeGen/WasmEHFuncInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" // To access function attributes. #include "llvm/IR/IntrinsicsWebAssembly.h" +#include "llvm/MC/MCSymbolWasm.h" #include "llvm/Support/Debug.h" #include "llvm/Support/KnownBits.h" #include "llvm/Support/raw_ostream.h" @@ -189,6 +191,42 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { ReplaceNode(Node, TLSAlign); return; } + case Intrinsic::wasm_ref_test_func: { + // First emit the TABLE_GET instruction to convert function pointer ==> + // funcref + MachineFunction &MF = CurDAG->getMachineFunction(); + auto PtrVT = MVT::getIntegerVT(MF.getDataLayout().getPointerSizeInBits()); + MCSymbol *Table = WebAssembly::getOrCreateFunctionTableSymbol( + MF.getContext(), Subtarget); + SDValue TableSym = CurDAG->getMCSymbol(Table, PtrVT); + SDValue FuncRef = SDValue( + CurDAG->getMachineNode(WebAssembly::TABLE_GET_FUNCREF, DL, + MVT::funcref, TableSym, Node->getOperand(1)), + 0); + + // Encode the signature information into the type index placeholder. + // This gets decoded and converted into the actual type signature in + // WebAssemblyMCInstLower.cpp. + SmallVector Params; + SmallVector Results; + + MVT VT = Node->getOperand(2).getValueType().getSimpleVT(); + if (VT != MVT::Untyped) { + Params.push_back(VT); + } + for (unsigned I = 3; I < Node->getNumOperands(); ++I) { + MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); + Results.push_back(VT); + } + auto Sig = WebAssembly::encodeFunctionSignature(Params, Results); + + SmallVector Ops; + auto SigOp = CurDAG->getTargetConstant( + Sig, DL, EVT::getIntegerVT(*CurDAG->getContext(), Sig.getBitWidth())); + MachineSDNode *RefTestNode = CurDAG->getMachineNode( + WebAssembly::REF_TEST_FUNCREF, DL, MVT::i32, {SigOp, FuncRef}); + ReplaceNode(Node, RefTestNode); + } } break; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 2359d6b04aa97..d664bb0d5b17a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -2255,72 +2255,6 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, DAG.getTargetExternalSymbol(TlsBase, PtrVT)), 0); } - case Intrinsic::wasm_ref_test_func: { - // First emit the TABLE_GET instruction to convert function pointer ==> - // funcref - MachineFunction &MF = DAG.getMachineFunction(); - auto PtrVT = getPointerTy(MF.getDataLayout()); - MCSymbol *Table = - WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget); - SDValue TableSym = DAG.getMCSymbol(Table, PtrVT); - SDValue FuncRef = - SDValue(DAG.getMachineNode(WebAssembly::TABLE_GET_FUNCREF, DL, - MVT::funcref, TableSym, Op.getOperand(1)), - 0); - - // Encode the signature information into the type index placeholder. - // This gets decoded and converted into the actual type signature in - // WebAssemblyMCInstLower.cpp. - auto NParams = Op.getNumOperands() - 2; - auto BitWidth = (NParams + 1) * 64; - auto Sig = APInt(BitWidth, 0); - // The return type has to be a BlockType since it can be void. - { - SDValue Operand = Op.getOperand(2); - MVT VT = Operand.getValueType().getSimpleVT(); - WebAssembly::BlockType V; - if (VT == MVT::Untyped) { - V = WebAssembly::BlockType::Void; - } else if (VT == MVT::i32) { - V = WebAssembly::BlockType::I32; - } else if (VT == MVT::i64) { - V = WebAssembly::BlockType::I64; - } else if (VT == MVT::f32) { - V = WebAssembly::BlockType::F32; - } else if (VT == MVT::f64) { - V = WebAssembly::BlockType::F64; - } else { - llvm_unreachable("Unhandled type!"); - } - Sig |= (int64_t)V; - } - for (unsigned i = 3; i < Op.getNumOperands(); ++i) { - SDValue Operand = Op.getOperand(i); - MVT VT = Operand.getValueType().getSimpleVT(); - wasm::ValType V; - if (VT == MVT::i32) { - V = wasm::ValType::I32; - } else if (VT == MVT::i64) { - V = wasm::ValType::I64; - } else if (VT == MVT::f32) { - V = wasm::ValType::F32; - } else if (VT == MVT::f64) { - V = wasm::ValType::F64; - } else { - llvm_unreachable("Unhandled type!"); - } - Sig <<= 64; - Sig |= (int64_t)V; - } - - SmallVector Ops; - Ops.push_back(DAG.getTargetConstant( - Sig, DL, EVT::getIntegerVT(*DAG.getContext(), BitWidth))); - Ops.push_back(FuncRef); - return SDValue( - DAG.getMachineNode(WebAssembly::REF_TEST_FUNCREF, DL, MVT::i32, Ops), - 0); - } } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 4e224c0766146..6ca046b22f503 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -21,6 +21,7 @@ #include "WebAssemblyAsmPrinter.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyUtilities.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -156,6 +157,29 @@ MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand( return MCOperand::createExpr(Expr); } +MCOperand +WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { + auto NumWords = Sig.getNumWords(); + SmallVector Params; + SmallVector Returns; + + int Idx = NumWords; + + auto GetWord = [&Idx, &Sig]() { + Idx--; + return Sig.extractBitsAsZExtValue(64, 64 * Idx); + }; + int NParams = GetWord(); + for (int I = 0; I < NParams; I++) { + Params.push_back(static_cast(GetWord())); + } + int NReturns = GetWord(); + for (int I = 0; I < NReturns; I++) { + Returns.push_back(static_cast(GetWord())); + } + return lowerTypeIndexOperand(std::move(Params), std::move(Returns)); +} + static void getFunctionReturns(const MachineInstr *MI, SmallVectorImpl &Returns) { const Function &F = MI->getMF()->getFunction(); @@ -212,57 +236,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, if (Info.OperandType != WebAssembly::OPERAND_TYPEINDEX) { llvm_unreachable("unexpected CImmediate operand"); } - auto CImm = MO.getCImm()->getValue(); - auto NumWords = CImm.getNumWords() - 1; - // Extract the type data we packed into the CImm in LowerRefTestFuncRef. - // We need to load the words from most significant to least significant - // order because of the way we bitshifted them in from the right. - // The return type needs special handling because it could be void. - auto ReturnType = static_cast( - CImm.extractBitsAsZExtValue(64, (NumWords - 1) * 64)); - SmallVector Returns; - switch (ReturnType) { - case WebAssembly::BlockType::Invalid: - llvm_unreachable("Invalid return type"); - case WebAssembly::BlockType::I32: - Returns = {wasm::ValType::I32}; - break; - case WebAssembly::BlockType::I64: - Returns = {wasm::ValType::I64}; - break; - case WebAssembly::BlockType::F32: - Returns = {wasm::ValType::F32}; - break; - case WebAssembly::BlockType::F64: - Returns = {wasm::ValType::F64}; - break; - case WebAssembly::BlockType::Void: - Returns = {}; - break; - case WebAssembly::BlockType::Exnref: - Returns = {wasm::ValType::EXNREF}; - break; - case WebAssembly::BlockType::Externref: - Returns = {wasm::ValType::EXTERNREF}; - break; - case WebAssembly::BlockType::Funcref: - Returns = {wasm::ValType::FUNCREF}; - break; - case WebAssembly::BlockType::V128: - Returns = {wasm::ValType::V128}; - break; - case WebAssembly::BlockType::Multivalue: { - llvm_unreachable("Invalid return type"); - } - } - SmallVector Params; - - for (int I = NumWords - 2; I >= 0; I--) { - auto Val = CImm.extractBitsAsZExtValue(64, 64 * I); - auto ParamType = static_cast(Val); - Params.push_back(ParamType); - } - MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params)); + MCOp = lowerEncodedFunctionSignature(MO.getCImm()->getValue()); break; } case MachineOperand::MO_Immediate: { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h index 9f08499e5cde1..34404d93434bb 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h @@ -36,6 +36,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower { MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; MCOperand lowerTypeIndexOperand(SmallVectorImpl &&, SmallVectorImpl &&) const; + MCOperand lowerEncodedFunctionSignature(const APInt &Sig) const; public: WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp index 747ef18df8d65..991c80df57bf3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -195,3 +195,40 @@ bool WebAssembly::canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget) { return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget); } + +APInt WebAssembly::encodeFunctionSignature(SmallVector &Params, + SmallVector &Returns) { + auto toWasmValType = [](MVT VT) { + if (VT == MVT::i32) { + return wasm::ValType::I32; + } + if (VT == MVT::i64) { + return wasm::ValType::I64; + } + if (VT == MVT::f32) { + return wasm::ValType::F32; + } + if (VT == MVT::f64) { + return wasm::ValType::F64; + } + llvm_unreachable("Unhandled type!"); + }; + auto NParams = Params.size(); + auto NReturns = Params.size(); + auto BitWidth = (NParams + NReturns + 2) * 64; + auto Sig = APInt(BitWidth, 0); + + Sig |= NParams; + for (auto &Param : Params) { + auto V = toWasmValType(Param); + Sig <<= 64; + Sig |= (int64_t)V; + } + Sig |= NReturns; + for (auto &Return : Returns) { + auto V = toWasmValType(Return); + Sig <<= 64; + Sig |= (int64_t)V; + } + return Sig; +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h index 046b1b5db2a79..d1e696ff59a69 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -15,6 +15,10 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYUTILITIES_H #define LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYUTILITIES_H +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/CodeGenTypes/MachineValueType.h" #include "llvm/Support/CommandLine.h" namespace llvm { @@ -73,6 +77,9 @@ bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget); /// memory. bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget); +APInt encodeFunctionSignature(SmallVector &Params, + SmallVector &Returns); + } // end namespace WebAssembly } // end namespace llvm From a6582c5e86de0d400ae0a4628118d4a66daeee77 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 16:11:53 +0200 Subject: [PATCH 04/22] Revert some unneeded changes --- llvm/include/llvm/MC/MCSymbolWasm.h | 1 - llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h index b58d6138cd045..beb6b975a4cc3 100644 --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -12,7 +12,6 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolTableEntry.h" - namespace llvm { class MCSymbolWasm : public MCSymbol { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index d664bb0d5b17a..081d09e5b9d31 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -18,7 +18,6 @@ #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" #include "WebAssemblyUtilities.h" -#include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" From 1335a04a8b1ac730dee2c37b14277592389f5754 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 16:23:17 +0200 Subject: [PATCH 05/22] Tidy up --- llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td index fb61d8a11e5c0..c1e4b97e96bc8 100644 --- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td +++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td @@ -45,7 +45,7 @@ def int_wasm_ref_is_null_exn : def int_wasm_ref_test_func : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_vararg_ty], - [IntrNoMem], "llvm.wasm.ref.test.func">; + [IntrNoMem]>; //===----------------------------------------------------------------------===// // Table intrinsics From aff1350d6d5f590bcc1b9b060eb8b6f7978c82e5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 17:55:27 +0200 Subject: [PATCH 06/22] Fix and cleanup --- .../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 45 ++++++++++++++++++- .../WebAssembly/WebAssemblyUtilities.cpp | 37 --------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index c54787f1f71f7..93dbc4f157188 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -120,6 +120,47 @@ static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) { return DAG->getTargetExternalSymbol(SymName, PtrVT); } +static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, + SmallVector &Params, + SmallVector &Returns) { + auto toWasmValType = [&DAG, &DL](MVT VT) { + if (VT == MVT::i32) { + return wasm::ValType::I32; + } + if (VT == MVT::i64) { + return wasm::ValType::I64; + } + if (VT == MVT::f32) { + return wasm::ValType::F32; + } + if (VT == MVT::f64) { + return wasm::ValType::F64; + } + DAG->getContext()->diagnose( + DiagnosticInfoUnsupported(DAG->getMachineFunction().getFunction(), + "Unhandled type!", DL.getDebugLoc())); + }; + auto NParams = Params.size(); + auto NReturns = Returns.size(); + auto BitWidth = (NParams + NReturns + 2) * 64; + auto Sig = APInt(BitWidth, 0); + + Sig |= NParams; + for (auto &Param : Params) { + auto V = toWasmValType(Param); + Sig <<= 64; + Sig |= (int64_t)V; + } + Sig <<= 64; + Sig |= NReturns; + for (auto &Return : Returns) { + auto V = toWasmValType(Return); + Sig <<= 64; + Sig |= (int64_t)V; + } + return Sig; +} + void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { // If we have a custom node, we already have selected! if (Node->isMachineOpcode()) { @@ -218,14 +259,14 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); Results.push_back(VT); } - auto Sig = WebAssembly::encodeFunctionSignature(Params, Results); + auto Sig = encodeFunctionSignature(CurDAG, DL, Params, Results); - SmallVector Ops; auto SigOp = CurDAG->getTargetConstant( Sig, DL, EVT::getIntegerVT(*CurDAG->getContext(), Sig.getBitWidth())); MachineSDNode *RefTestNode = CurDAG->getMachineNode( WebAssembly::REF_TEST_FUNCREF, DL, MVT::i32, {SigOp, FuncRef}); ReplaceNode(Node, RefTestNode); + return; } } break; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp index 991c80df57bf3..747ef18df8d65 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -195,40 +195,3 @@ bool WebAssembly::canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget) { return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget); } - -APInt WebAssembly::encodeFunctionSignature(SmallVector &Params, - SmallVector &Returns) { - auto toWasmValType = [](MVT VT) { - if (VT == MVT::i32) { - return wasm::ValType::I32; - } - if (VT == MVT::i64) { - return wasm::ValType::I64; - } - if (VT == MVT::f32) { - return wasm::ValType::F32; - } - if (VT == MVT::f64) { - return wasm::ValType::F64; - } - llvm_unreachable("Unhandled type!"); - }; - auto NParams = Params.size(); - auto NReturns = Params.size(); - auto BitWidth = (NParams + NReturns + 2) * 64; - auto Sig = APInt(BitWidth, 0); - - Sig |= NParams; - for (auto &Param : Params) { - auto V = toWasmValType(Param); - Sig <<= 64; - Sig |= (int64_t)V; - } - Sig |= NReturns; - for (auto &Return : Returns) { - auto V = toWasmValType(Return); - Sig <<= 64; - Sig |= (int64_t)V; - } - return Sig; -} From ec7746559c24535f33b5036fb9aca40854429b42 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 18:00:50 +0200 Subject: [PATCH 07/22] Separate test cases into their own functions --- .../test/CodeGen/WebAssembly/ref-test-func.ll | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index 3fc848cd167f9..2f6b1f42f47ea 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -2,40 +2,64 @@ target triple = "wasm32-unknown-unknown" -; CHECK-LABEL: test_function_pointer_signature_void: -; CHECK-NEXT: .functype test_function_pointer_signature_void (i32) -> () -; CHECK-NEXT: .local funcref +; CHECK-LABEL: test_fpsig_1: ; CHECK: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: local.tee 1 ; CHECK-NEXT: ref.test (f32, f64, i32) -> (f32) ; CHECK-NEXT: call use -; CHECK-NEXT: local.get 1 +; Function Attrs: nounwind +define void @test_fpsig_1(ptr noundef %func) local_unnamed_addr #0 { +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, float 0.000000e+00, double 0.000000e+00, i32 0) + tail call void @use(i32 noundef %res) #3 + ret void +} + +; CHECK-LABEL: test_fpsig_2: +; CHECK: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table ; CHECK-NEXT: ref.test (f32, f64, i32) -> (i32) ; CHECK-NEXT: call use -; CHECK-NEXT: local.get 1 +define void @test_fpsig_2(ptr noundef %func) local_unnamed_addr #0 { +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, float 0.000000e+00, double 0.000000e+00, i32 0) + tail call void @use(i32 noundef %res) #3 + ret void +} + +; CHECK-LABEL: test_fpsig_3: +; CHECK: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table ; CHECK-NEXT: ref.test (i32, i32, i32) -> (i32) ; CHECK-NEXT: call use -; CHECK-NEXT: local.get 1 +define void @test_fpsig_3(ptr noundef %func) local_unnamed_addr #0 { +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i32 0, i32 0, i32 0) + tail call void @use(i32 noundef %res) #3 + ret void +} + +; CHECK-LABEL: test_fpsig_4: +; CHECK: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table ; CHECK-NEXT: ref.test (i32, i32, i32) -> () ; CHECK-NEXT: call use -; CHECK-NEXT: local.get 1 +define void @test_fpsig_4(ptr noundef %func) local_unnamed_addr #0 { +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) + tail call void @use(i32 noundef %res) #3 + ret void +} + +; CHECK-LABEL: test_fpsig_5: +; CHECK: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table ; CHECK-NEXT: ref.test () -> () ; CHECK-NEXT: call use - -; Function Attrs: nounwind -define void @test_function_pointer_signature_void(ptr noundef %func) local_unnamed_addr #0 { +define void @test_fpsig_5(ptr noundef %func) local_unnamed_addr #0 { entry: - %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, float 0.000000e+00, double 0.000000e+00, i32 0) - tail call void @use(i32 noundef %0) #3 - %1 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, float 0.000000e+00, double 0.000000e+00, i32 0) - tail call void @use(i32 noundef %1) #3 - %2 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i32 0, i32 0, i32 0) - tail call void @use(i32 noundef %2) #3 - %3 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) - tail call void @use(i32 noundef %3) #3 - %4 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) - tail call void @use(i32 noundef %4) #3 + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + tail call void @use(i32 noundef %res) #3 ret void } From e32bb0b2fadfc516de69320fc745fe511dcbdd30 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 8 Jul 2025 18:02:56 +0200 Subject: [PATCH 08/22] Cleanup --- llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h index d1e696ff59a69..046b1b5db2a79 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -15,10 +15,6 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYUTILITIES_H #define LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYUTILITIES_H -#include "llvm/ADT/APInt.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/CodeGenTypes/MachineValueType.h" #include "llvm/Support/CommandLine.h" namespace llvm { @@ -77,9 +73,6 @@ bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget); /// memory. bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget); -APInt encodeFunctionSignature(SmallVector &Params, - SmallVector &Returns); - } // end namespace WebAssembly } // end namespace llvm From 6562073886dcd7e447147f922b0c5d4a462bd7b0 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 21 Jul 2025 16:59:55 +0200 Subject: [PATCH 09/22] try using an Imm if getSignificantBits() < 64 --- llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 4d56803ba492a..768940f64ee0c 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -402,7 +402,7 @@ void InstrEmitter::AddOperand(MachineInstrBuilder &MIB, SDValue Op, AddRegisterOperand(MIB, Op, IIOpNum, II, VRBaseMap, IsDebug, IsClone, IsCloned); } else if (ConstantSDNode *C = dyn_cast(Op)) { - if (C->getAPIntValue().getBitWidth() <= 64) { + if (C->getAPIntValue().getSignificantBits() <= 64) { MIB.addImm(C->getSExtValue()); } else { MIB.addCImm( From c4e921af0309b9ac0ccb4c1eec4e3f3bfa0ad272 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 21 Jul 2025 20:56:22 +0200 Subject: [PATCH 10/22] Fix handling of void functions --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 6 +++++- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 93dbc4f157188..d661049555be3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -145,7 +145,11 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, auto BitWidth = (NParams + NReturns + 2) * 64; auto Sig = APInt(BitWidth, 0); - Sig |= NParams; + // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will + // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we + // always emit a CImm. So xor NParams with 0x7ffffff to ensure + // getSignificantBits() > 64 + Sig |= NParams ^ 0x7ffffff; for (auto &Param : Params) { auto V = toWasmValType(Param); Sig <<= 64; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 6ca046b22f503..dff59f038a6e9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -164,12 +164,15 @@ WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { SmallVector Returns; int Idx = NumWords; - auto GetWord = [&Idx, &Sig]() { Idx--; return Sig.extractBitsAsZExtValue(64, 64 * Idx); }; - int NParams = GetWord(); + // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will + // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we + // always emit a CImm. So xor NParams with 0x7ffffff to ensure + // getSignificantBits() > 64 + int NParams = GetWord() ^ 0x7ffffff; for (int I = 0; I < NParams; I++) { Params.push_back(static_cast(GetWord())); } From 2df9ffc8c8f11f59a698e88a63e10f54c96a7dc8 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:18:03 +0200 Subject: [PATCH 11/22] Apply suggestions from code review Co-authored-by: Derek Schuff --- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index dff59f038a6e9..183bd1b3490ac 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -172,6 +172,7 @@ WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we // always emit a CImm. So xor NParams with 0x7ffffff to ensure // getSignificantBits() > 64 + // See encodeFunctionSignature in WebAssemblyISelDAGtoDAG.cpp int NParams = GetWord() ^ 0x7ffffff; for (int I = 0; I < NParams; I++) { Params.push_back(static_cast(GetWord())); From 802cae7c80579d8775b591a7a67ae2586225b537 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:12:37 +0200 Subject: [PATCH 12/22] Convert diagnostic back to unreachable --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index d661049555be3..96eed5db4e16e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -136,9 +136,8 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, if (VT == MVT::f64) { return wasm::ValType::F64; } - DAG->getContext()->diagnose( - DiagnosticInfoUnsupported(DAG->getMachineFunction().getFunction(), - "Unhandled type!", DL.getDebugLoc())); + LLVM_DEBUG(errs() << "Unhandled type for llvm.wasm.ref.test.func: " << VT << "\n"); + llvm_unreachable("Unhandled type for llvm.wasm.ref.test.func"); }; auto NParams = Params.size(); auto NReturns = Returns.size(); From 1fa17722d6f2b91f6ee1a368a01b42f0804fbf23 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:21:06 +0200 Subject: [PATCH 13/22] Add comment that words are 64 bits --- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 183bd1b3490ac..cace2901a8b1c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -159,6 +159,7 @@ MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand( MCOperand WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { + // For APInt a word is 64 bits on all architectures, see definition in APInt.h auto NumWords = Sig.getNumWords(); SmallVector Params; SmallVector Returns; From e8a0ee103e6f9d9eefeefb0aa64fea13d8b018eb Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:29:27 +0200 Subject: [PATCH 14/22] More idiomatic assertions --- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index cace2901a8b1c..fcef364543eca 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -234,13 +234,9 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, // Currently this is the only way that CImmediates show up so panic if we // get confused. unsigned DescIndex = I - NumVariadicDefs; - if (DescIndex >= Desc.NumOperands) { - llvm_unreachable("unexpected CImmediate operand"); - } + assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand"); const MCOperandInfo &Info = Desc.operands()[DescIndex]; - if (Info.OperandType != WebAssembly::OPERAND_TYPEINDEX) { - llvm_unreachable("unexpected CImmediate operand"); - } + assert(Info.OperandType != WebAssembly::OPERAND_TYPEINDEX && "unexpected CImmediate operand"); MCOp = lowerEncodedFunctionSignature(MO.getCImm()->getValue()); break; } From d8af9456edb388bf400c29ea8a964f68db7f4a0d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:32:51 +0200 Subject: [PATCH 15/22] Generate assertions with utils/update_llc_test_checks.py --- .../test/CodeGen/WebAssembly/ref-test-func.ll | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index 2f6b1f42f47ea..6d57197865104 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -1,62 +1,78 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 ; RUN: llc < %s -mcpu=mvp -mattr=+reference-types | FileCheck %s target triple = "wasm32-unknown-unknown" -; CHECK-LABEL: test_fpsig_1: -; CHECK: local.get 0 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (f32, f64, i32) -> (f32) -; CHECK-NEXT: call use ; Function Attrs: nounwind define void @test_fpsig_1(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_1: +; CHECK: .functype test_fpsig_1 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (f32, f64, i32) -> (f32) +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, float 0.000000e+00, double 0.000000e+00, i32 0) tail call void @use(i32 noundef %res) #3 ret void } -; CHECK-LABEL: test_fpsig_2: -; CHECK: local.get 0 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (f32, f64, i32) -> (i32) -; CHECK-NEXT: call use define void @test_fpsig_2(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_2: +; CHECK: .functype test_fpsig_2 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (f32, f64, i32) -> (i32) +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, float 0.000000e+00, double 0.000000e+00, i32 0) tail call void @use(i32 noundef %res) #3 ret void } -; CHECK-LABEL: test_fpsig_3: -; CHECK: local.get 0 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (i32, i32, i32) -> (i32) -; CHECK-NEXT: call use define void @test_fpsig_3(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_3: +; CHECK: .functype test_fpsig_3 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (i32, i32, i32) -> (i32) +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i32 0, i32 0, i32 0) tail call void @use(i32 noundef %res) #3 ret void } -; CHECK-LABEL: test_fpsig_4: -; CHECK: local.get 0 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (i32, i32, i32) -> () -; CHECK-NEXT: call use define void @test_fpsig_4(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_4: +; CHECK: .functype test_fpsig_4 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (i32, i32, i32) -> () +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) tail call void @use(i32 noundef %res) #3 ret void } -; CHECK-LABEL: test_fpsig_5: -; CHECK: local.get 0 -; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> () -; CHECK-NEXT: call use define void @test_fpsig_5(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_5: +; CHECK: .functype test_fpsig_5 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test () -> () +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return entry: %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) tail call void @use(i32 noundef %res) #3 From e8845c9b2246bc11c1a9e1b63aea72110976e954 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:41:22 +0200 Subject: [PATCH 16/22] Fix --- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index fcef364543eca..34abf6538ef0d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -235,15 +235,17 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, // get confused. unsigned DescIndex = I - NumVariadicDefs; assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand"); - const MCOperandInfo &Info = Desc.operands()[DescIndex]; - assert(Info.OperandType != WebAssembly::OPERAND_TYPEINDEX && "unexpected CImmediate operand"); + auto Operands = Desc.operands(); + const MCOperandInfo &Info = Operands[DescIndex]; + assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX && "unexpected CImmediate operand"); MCOp = lowerEncodedFunctionSignature(MO.getCImm()->getValue()); break; } case MachineOperand::MO_Immediate: { unsigned DescIndex = I - NumVariadicDefs; if (DescIndex < Desc.NumOperands) { - const MCOperandInfo &Info = Desc.operands()[DescIndex]; + auto Operands = Desc.operands(); + const MCOperandInfo &Info = Operands[DescIndex]; // Replace type index placeholder with actual type index. The type index // placeholders are Immediates and have an operand type of // OPERAND_TYPEINDEX or OPERAND_SIGNATURE. From 2dacd980033e81c8bd3511b3b31f039ffbea26b3 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 10:51:28 +0200 Subject: [PATCH 17/22] Fix swapped params and returns --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 8 ++++---- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 96eed5db4e16e..9571b09bbdcb8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -252,17 +252,17 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { // This gets decoded and converted into the actual type signature in // WebAssemblyMCInstLower.cpp. SmallVector Params; - SmallVector Results; + SmallVector Returns; MVT VT = Node->getOperand(2).getValueType().getSimpleVT(); if (VT != MVT::Untyped) { - Params.push_back(VT); + Returns.push_back(VT); } for (unsigned I = 3; I < Node->getNumOperands(); ++I) { MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); - Results.push_back(VT); + Params.push_back(VT); } - auto Sig = encodeFunctionSignature(CurDAG, DL, Params, Results); + auto Sig = encodeFunctionSignature(CurDAG, DL, Params, Returns); auto SigOp = CurDAG->getTargetConstant( Sig, DL, EVT::getIntegerVT(*CurDAG->getContext(), Sig.getBitWidth())); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 34abf6538ef0d..113a19802a49e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -182,7 +182,7 @@ WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { for (int I = 0; I < NReturns; I++) { Returns.push_back(static_cast(GetWord())); } - return lowerTypeIndexOperand(std::move(Params), std::move(Returns)); + return lowerTypeIndexOperand(std::move(Returns), std::move(Params)); } static void getFunctionReturns(const MachineInstr *MI, From 6ad6d2c78fda658d6f7d277f74e1d4025b74f706 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 11:12:28 +0200 Subject: [PATCH 18/22] Support multiple return values --- .../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 37 ++++---- .../WebAssembly/WebAssemblyMCInstLower.cpp | 10 +-- .../test/CodeGen/WebAssembly/ref-test-func.ll | 84 +++++++++++++------ 3 files changed, 84 insertions(+), 47 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 9571b09bbdcb8..60a81b545d087 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -121,8 +121,8 @@ static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) { } static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, - SmallVector &Params, - SmallVector &Returns) { + SmallVector &Returns, + SmallVector &Params) { auto toWasmValType = [&DAG, &DL](MVT VT) { if (VT == MVT::i32) { return wasm::ValType::I32; @@ -148,16 +148,16 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we // always emit a CImm. So xor NParams with 0x7ffffff to ensure // getSignificantBits() > 64 - Sig |= NParams ^ 0x7ffffff; - for (auto &Param : Params) { - auto V = toWasmValType(Param); + Sig |= NReturns ^ 0x7ffffff; + for (auto &Return : Returns) { + auto V = toWasmValType(Return); Sig <<= 64; Sig |= (int64_t)V; } Sig <<= 64; - Sig |= NReturns; - for (auto &Return : Returns) { - auto V = toWasmValType(Return); + Sig |= NParams; + for (auto &Param : Params) { + auto V = toWasmValType(Param); Sig <<= 64; Sig |= (int64_t)V; } @@ -252,17 +252,22 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { // This gets decoded and converted into the actual type signature in // WebAssemblyMCInstLower.cpp. SmallVector Params; - SmallVector Returns; + SmallVector Returns; - MVT VT = Node->getOperand(2).getValueType().getSimpleVT(); - if (VT != MVT::Untyped) { - Returns.push_back(VT); - } - for (unsigned I = 3; I < Node->getNumOperands(); ++I) { + bool IsParam = false; + for (unsigned I = 2; I < Node->getNumOperands(); ++I) { MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); - Params.push_back(VT); + if (VT == MVT::Untyped) { + IsParam = true; + continue; + } + if (IsParam) { + Params.push_back(VT); + } else { + Returns.push_back(VT); + } } - auto Sig = encodeFunctionSignature(CurDAG, DL, Params, Returns); + auto Sig = encodeFunctionSignature(CurDAG, DL, Returns, Params); auto SigOp = CurDAG->getTargetConstant( Sig, DL, EVT::getIntegerVT(*CurDAG->getContext(), Sig.getBitWidth())); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 113a19802a49e..d9eca6c99dc45 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -174,14 +174,14 @@ WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const { // always emit a CImm. So xor NParams with 0x7ffffff to ensure // getSignificantBits() > 64 // See encodeFunctionSignature in WebAssemblyISelDAGtoDAG.cpp - int NParams = GetWord() ^ 0x7ffffff; - for (int I = 0; I < NParams; I++) { - Params.push_back(static_cast(GetWord())); - } - int NReturns = GetWord(); + int NReturns = GetWord() ^ 0x7ffffff; for (int I = 0; I < NReturns; I++) { Returns.push_back(static_cast(GetWord())); } + int NParams = GetWord(); + for (int I = 0; I < NParams; I++) { + Params.push_back(static_cast(GetWord())); + } return lowerTypeIndexOperand(std::move(Returns), std::move(Params)); } diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index 6d57197865104..5d88c1ee96cd9 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -3,80 +3,112 @@ target triple = "wasm32-unknown-unknown" -; Function Attrs: nounwind -define void @test_fpsig_1(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_1: -; CHECK: .functype test_fpsig_1 (i32) -> () +define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_void_void: +; CHECK: .functype test_fpsig_void_void (i32) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (f32, f64, i32) -> (f32) +; CHECK-NEXT: ref.test () -> () ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, float 0.000000e+00, double 0.000000e+00, i32 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func) tail call void @use(i32 noundef %res) #3 ret void } -define void @test_fpsig_2(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_2: -; CHECK: .functype test_fpsig_2 (i32) -> () +define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_return_i32: +; CHECK: .functype test_fpsig_return_i32 (i32) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (f32, f64, i32) -> (i32) +; CHECK-NEXT: ref.test () -> (i32) ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, float 0.000000e+00, double 0.000000e+00, i32 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0) tail call void @use(i32 noundef %res) #3 ret void } -define void @test_fpsig_3(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_3: -; CHECK: .functype test_fpsig_3 (i32) -> () +define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_return_i64: +; CHECK: .functype test_fpsig_return_i64 (i32) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (i32, i32, i32) -> (i32) +; CHECK-NEXT: ref.test () -> (i64) ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i32 0, i32 0, i32 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 0) tail call void @use(i32 noundef %res) #3 ret void } -define void @test_fpsig_4(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_4: -; CHECK: .functype test_fpsig_4 (i32) -> () +define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_return_f32: +; CHECK: .functype test_fpsig_return_f32 (i32) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test (i32, i32, i32) -> () +; CHECK-NEXT: ref.test () -> (f32) ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, i32 0, i32 0, i32 0) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.) tail call void @use(i32 noundef %res) #3 ret void } -define void @test_fpsig_5(ptr noundef %func) local_unnamed_addr #0 { -; CHECK-LABEL: test_fpsig_5: -; CHECK: .functype test_fpsig_5 (i32) -> () +define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_return_f64: +; CHECK: .functype test_fpsig_return_f64 (i32) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table -; CHECK-NEXT: ref.test () -> () +; CHECK-NEXT: ref.test () -> (f64) +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double 0.) + tail call void @use(i32 noundef %res) #3 + ret void +} + + +define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_param_i32: +; CHECK: .functype test_fpsig_param_i32 (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (f64) -> () ; CHECK-NEXT: call use ; CHECK-NEXT: # fallthrough-return entry: - %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison) + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, double 0.) tail call void @use(i32 noundef %res) #3 ret void } + +define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) local_unnamed_addr #0 { +; CHECK-LABEL: test_fpsig_multiple_params_and_returns: +; CHECK: .functype test_fpsig_multiple_params_and_returns (i32) -> () +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get __indirect_function_table +; CHECK-NEXT: ref.test (i64, f32, i64) -> (i32, i64, f32, f64) +; CHECK-NEXT: call use +; CHECK-NEXT: # fallthrough-return +entry: + %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 0, i64 0, float 0., double 0., token poison, i64 0, float 0., i64 0) + tail call void @use(i32 noundef %res) #3 + ret void +} + + declare void @use(i32 noundef) local_unnamed_addr #1 From a9d94207f8e862c883ff0f639d1828b1a34d2001 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 11:13:50 +0200 Subject: [PATCH 19/22] llvm style for loop bound --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 60a81b545d087..82b3f4687bdbc 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -255,7 +255,7 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { SmallVector Returns; bool IsParam = false; - for (unsigned I = 2; I < Node->getNumOperands(); ++I) { + for (unsigned I = 2, E = Node->getNumOperands(); I < E; ++I) { MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); if (VT == MVT::Untyped) { IsParam = true; From 781d7aeefb5a3ad41ddc70f454fd3b9a1e734b29 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 11:16:25 +0200 Subject: [PATCH 20/22] Add comment explaining operand magic number --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 82b3f4687bdbc..659ec11cca9be 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -255,6 +255,9 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { SmallVector Returns; bool IsParam = false; + // Operand 0 is the return register, Operand 1 is the function pointer. + // The remaining operands encode the type of the function we are testing + // for. for (unsigned I = 2, E = Node->getNumOperands(); I < E; ++I) { MVT VT = Node->getOperand(I).getValueType().getSimpleVT(); if (VT == MVT::Untyped) { From 57c7be856ad00796ce14cf6696533dbcb5010e72 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 11:23:17 +0200 Subject: [PATCH 21/22] Test against wasm64 too --- .../test/CodeGen/WebAssembly/ref-test-func.ll | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll index 5d88c1ee96cd9..e3760a07c6445 100644 --- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll +++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll @@ -1,11 +1,11 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 -; RUN: llc < %s -mcpu=mvp -mattr=+reference-types | FileCheck %s - -target triple = "wasm32-unknown-unknown" +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=+reference-types | FileCheck --check-prefixes CHECK,CHK32 %s +; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types | FileCheck --check-prefixes CHECK,CHK64 %s define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_void_void: -; CHECK: .functype test_fpsig_void_void (i32) -> () +; CHK32: .functype test_fpsig_void_void (i32) -> () +; CHK64: .functype test_fpsig_void_void (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -20,7 +20,8 @@ entry: define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_return_i32: -; CHECK: .functype test_fpsig_return_i32 (i32) -> () +; CHK32: .functype test_fpsig_return_i32 (i32) -> () +; CHK64: .functype test_fpsig_return_i32 (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -35,7 +36,8 @@ entry: define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_return_i64: -; CHECK: .functype test_fpsig_return_i64 (i32) -> () +; CHK32: .functype test_fpsig_return_i64 (i32) -> () +; CHK64: .functype test_fpsig_return_i64 (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -50,7 +52,8 @@ entry: define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_return_f32: -; CHECK: .functype test_fpsig_return_f32 (i32) -> () +; CHK32: .functype test_fpsig_return_f32 (i32) -> () +; CHK64: .functype test_fpsig_return_f32 (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -65,7 +68,8 @@ entry: define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_return_f64: -; CHECK: .functype test_fpsig_return_f64 (i32) -> () +; CHK32: .functype test_fpsig_return_f64 (i32) -> () +; CHK64: .functype test_fpsig_return_f64 (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -81,7 +85,8 @@ entry: define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_param_i32: -; CHECK: .functype test_fpsig_param_i32 (i32) -> () +; CHK32: .functype test_fpsig_param_i32 (i32) -> () +; CHK64: .functype test_fpsig_param_i32 (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table @@ -97,7 +102,8 @@ entry: define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) local_unnamed_addr #0 { ; CHECK-LABEL: test_fpsig_multiple_params_and_returns: -; CHECK: .functype test_fpsig_multiple_params_and_returns (i32) -> () +; CHK32: .functype test_fpsig_multiple_params_and_returns (i32) -> () +; CHK64: .functype test_fpsig_multiple_params_and_returns (i64) -> () ; CHECK-NEXT: # %bb.0: # %entry ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.get __indirect_function_table From 0330a8b0e1e0d6f65d6ad5e676344a2c268d86d5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 22 Jul 2025 11:41:38 +0200 Subject: [PATCH 22/22] clang-format --- llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 3 ++- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 659ec11cca9be..a7991319be8c7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -136,7 +136,8 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL, if (VT == MVT::f64) { return wasm::ValType::F64; } - LLVM_DEBUG(errs() << "Unhandled type for llvm.wasm.ref.test.func: " << VT << "\n"); + LLVM_DEBUG(errs() << "Unhandled type for llvm.wasm.ref.test.func: " << VT + << "\n"); llvm_unreachable("Unhandled type for llvm.wasm.ref.test.func"); }; auto NParams = Params.size(); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index d9eca6c99dc45..4a57669835af9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -237,7 +237,8 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand"); auto Operands = Desc.operands(); const MCOperandInfo &Info = Operands[DescIndex]; - assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX && "unexpected CImmediate operand"); + assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX && + "unexpected CImmediate operand"); MCOp = lowerEncodedFunctionSignature(MO.getCImm()->getValue()); break; }