From 5eb65fd57bfeb6cfca7f59936d786ec3f01917fd Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 2 Jul 2025 20:53:56 +0200 Subject: [PATCH 1/8] [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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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