[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