[llvm] [WebAssembly,llvm] Add llvm.wasm.ref.test.func intrinsic, option 2 (PR #147486)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 8 02:02:07 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Hood Chatham (hoodmane)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/147486.diff


11 Files Affected:

- (modified) llvm/include/llvm/CodeGen/ISDOpcodes.h (+1) 
- (modified) llvm/include/llvm/CodeGen/SelectionDAG.h (+8-2) 
- (modified) llvm/include/llvm/CodeGen/SelectionDAGNodes.h (+7-5) 
- (modified) llvm/include/llvm/IR/IntrinsicsWebAssembly.td (+4) 
- (modified) llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp (+6-1) 
- (modified) llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp (+4-2) 
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+7-6) 
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp (+1) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+67) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp (+74) 
- (added) llvm/test/CodeGen/WebAssembly/ref-test-func.ll (+42) 


``````````diff
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<VectorType>(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<ConstantSDNode>(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<ConstantFPSDNode>(Op)) {
     MIB.addFPImm(F->getConstantFPValue());
   } else if (RegisterSDNode *R = dyn_cast<RegisterSDNode>(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..413631e42668c 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<ConstantSDNode>(isT, isO, Elt, VTs);
+    N = newSDNode<ConstantSDNode>(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<SDValue, 4> 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<WebAssembly::BlockType>(
+          CImm.extractBitsAsZExtValue(64, (NumWords - 1) * 64));
+      SmallVector<wasm::ValType, 2> 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<wasm::ValType, 4> Params;
+
+      for (int I = NumWords - 2; I >= 0; I--) {
+        auto Val = CImm.extractBitsAsZExtValue(64, 64 * I);
+        auto ParamType = static_cast<wasm::ValType>(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<wasm::ValType, 4> Returns;
           SmallVector<wasm::ValType, 4> 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<WebAssembly::BlockType>(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

``````````

</details>


https://github.com/llvm/llvm-project/pull/147486


More information about the llvm-commits mailing list