[llvm] IR: Add verifier plugins for intrinsic verification (PR #159415)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 17 11:03:22 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
@llvm/pr-subscribers-backend-arm
Author: Nicolai Hähnle (nhaehnle)
<details>
<summary>Changes</summary>
Move target-specific logic into target code.
The main motivation is that target intrinsics can be very complex. Verifying them properly can benefit from using target-specific infrastructure that is not available in the core library that contains the verifier. Verifying target intrinsics via a "plugin" solves this issue.
This does mean that full target-specific verification only happens when the target in question was compiled and initialized. One slightly unfortunate side effect is that llvm-as needs to link against targets in order to fully verify the parsed IR assembly. This shouldn't be a real problem due to dynamic linking, so it seems like a reasonable compromise.
I considered the alternative of adding a hook into TargetTransformInfo, as that is how a similar refactoring was done for InstCombine. However, the verifier is invoked in many places e.g. via llvm::verifyModule where TargetTransformInfo may not be readily available.
---
Patch is 54.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/159415.diff
20 Files Affected:
- (modified) llvm/include/llvm/IR/Verifier.h (+117-2)
- (modified) llvm/lib/IR/Verifier.cpp (+266-398)
- (modified) llvm/lib/Target/AArch64/AArch64.h (+2)
- (modified) llvm/lib/Target/AArch64/AArch64TargetMachine.cpp (+1)
- (added) llvm/lib/Target/AArch64/AArch64Verifier.cpp (+70)
- (modified) llvm/lib/Target/AArch64/CMakeLists.txt (+1)
- (modified) llvm/lib/Target/AMDGPU/AMDGPU.h (+2)
- (modified) llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp (+2)
- (added) llvm/lib/Target/AMDGPU/AMDGPUVerifier.cpp (+266)
- (modified) llvm/lib/Target/AMDGPU/CMakeLists.txt (+1)
- (modified) llvm/lib/Target/ARM/ARM.h (+2)
- (modified) llvm/lib/Target/ARM/ARMTargetMachine.cpp (+2)
- (added) llvm/lib/Target/ARM/ARMVerifier.cpp (+58)
- (modified) llvm/lib/Target/ARM/CMakeLists.txt (+1)
- (modified) llvm/lib/Target/NVPTX/CMakeLists.txt (+1)
- (modified) llvm/lib/Target/NVPTX/NVPTX.h (+2)
- (modified) llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp (+2)
- (added) llvm/lib/Target/NVPTX/NVVMVerifier.cpp (+51)
- (modified) llvm/tools/llvm-as/CMakeLists.txt (+2)
- (modified) llvm/tools/llvm-as/llvm-as.cpp (+2)
``````````diff
diff --git a/llvm/include/llvm/IR/Verifier.h b/llvm/include/llvm/IR/Verifier.h
index 8dbb9c8a41d7e..135c6ab4ebb1f 100644
--- a/llvm/include/llvm/IR/Verifier.h
+++ b/llvm/include/llvm/IR/Verifier.h
@@ -21,20 +21,135 @@
#define LLVM_IR_VERIFIER_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/IR/DebugProgramInstruction.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/ModuleSlotTracker.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Printable.h"
#include <utility>
namespace llvm {
class APInt;
+class Attribute;
+class AttributeList;
+class AttributeSet;
+class CallBase;
+class Comdat;
+class DataLayout;
class Function;
class FunctionPass;
class Instruction;
-class MDNode;
+class LLVMContext;
class Module;
+class Triple;
+class VerifierSupport;
class raw_ostream;
-struct VerifierSupport;
+
+/// Base class for IR verifier plugins.
+///
+/// To add a plugin, derive from this class and then instantiate it once.
+class VerifierPlugin {
+public:
+ VerifierPlugin();
+ virtual ~VerifierPlugin();
+
+ /// Called when the verifier finds a call (or invoke) to an intrinsic it
+ /// doesn't understand.
+ ///
+ /// If the plugin recognizes the intrinsic, it should report any verifier
+ /// errors via the given helper object.
+ virtual void verifyIntrinsicCall(CallBase &Call, VerifierSupport &VS) const;
+};
+
+class VerifierSupport {
+public:
+ raw_ostream *OS;
+ const Module &M;
+ ModuleSlotTracker MST;
+ const Triple &TT;
+ const DataLayout &DL;
+ LLVMContext &Context;
+
+ /// Track the brokenness of the module while recursively visiting.
+ bool Broken = false;
+ /// Broken debug info can be "recovered" from by stripping the debug info.
+ bool BrokenDebugInfo = false;
+ /// Whether to treat broken debug info as an error.
+ bool TreatBrokenDebugInfoAsError = true;
+
+ explicit VerifierSupport(raw_ostream *OS, const Module &M);
+
+private:
+ LLVM_ABI void Write(const Module *M);
+ LLVM_ABI void Write(const Value *V);
+ LLVM_ABI void Write(const Value &V);
+ LLVM_ABI void Write(const DbgRecord *DR);
+ LLVM_ABI void Write(DbgVariableRecord::LocationType Type);
+ LLVM_ABI void Write(const Metadata *MD);
+
+ template <class T> void Write(const MDTupleTypedArrayWrapper<T> &MD) {
+ Write(MD.get());
+ }
+
+ LLVM_ABI void Write(const NamedMDNode *NMD);
+ LLVM_ABI void Write(Type *T);
+ LLVM_ABI void Write(const Comdat *C);
+ LLVM_ABI void Write(const APInt *AI);
+ LLVM_ABI void Write(const unsigned i) { *OS << i << '\n'; }
+
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ LLVM_ABI void Write(const Attribute *A);
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ LLVM_ABI void Write(const AttributeSet *AS);
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ LLVM_ABI void Write(const AttributeList *AL);
+ LLVM_ABI void Write(Printable P) { *OS << P << '\n'; }
+
+ template <typename T> void Write(ArrayRef<T> Vs) {
+ for (const T &V : Vs)
+ Write(V);
+ }
+
+ template <typename T1, typename... Ts>
+ void WriteTs(const T1 &V1, const Ts &...Vs) {
+ Write(V1);
+ WriteTs(Vs...);
+ }
+
+ template <typename... Ts> void WriteTs() {}
+
+public:
+ /// A check failed, so printout out the condition and the message.
+ ///
+ /// This provides a nice place to put a breakpoint if you want to see why
+ /// something is not correct.
+ LLVM_ABI void CheckFailed(const Twine &Message);
+
+ /// A check failed (with values to print).
+ ///
+ /// This calls the Message-only version so that the above is easier to set a
+ /// breakpoint on.
+ template <typename T1, typename... Ts>
+ void CheckFailed(const Twine &Message, const T1 &V1, const Ts &...Vs) {
+ CheckFailed(Message);
+ if (OS)
+ WriteTs(V1, Vs...);
+ }
+
+ /// A debug info check failed.
+ LLVM_ABI void DebugInfoCheckFailed(const Twine &Message);
+
+ /// A debug info check failed (with values to print).
+ template <typename T1, typename... Ts>
+ void DebugInfoCheckFailed(const Twine &Message, const T1 &V1,
+ const Ts &...Vs) {
+ DebugInfoCheckFailed(Message);
+ if (OS)
+ WriteTs(V1, Vs...);
+ }
+};
/// Verify that the TBAA Metadatas are valid.
class TBAAVerifier {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c06b60fd2d9a9..0b393f39e75ff 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -119,6 +119,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/ModRef.h"
+#include "llvm/Support/Mutex.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -127,6 +128,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <thread>
#include <utility>
using namespace llvm;
@@ -136,194 +138,307 @@ static cl::opt<bool> VerifyNoAliasScopeDomination(
cl::desc("Ensure that llvm.experimental.noalias.scope.decl for identical "
"scopes are not dominating"));
-namespace llvm {
+namespace {
-struct VerifierSupport {
- raw_ostream *OS;
- const Module &M;
- ModuleSlotTracker MST;
- const Triple &TT;
- const DataLayout &DL;
- LLVMContext &Context;
+class PluginRegistryLock;
+class PluginRegistryReader;
- /// Track the brokenness of the module while recursively visiting.
- bool Broken = false;
- /// Broken debug info can be "recovered" from by stripping the debug info.
- bool BrokenDebugInfo = false;
- /// Whether to treat broken debug info as an error.
- bool TreatBrokenDebugInfoAsError = true;
+/// Registry for verifier plugins.
+///
+/// The registry satifies the following implementation constraints:
+///
+/// * Support dynamically loading and unloading plugins from a thread (e.g.
+/// from dlopen()/dlclose()) while another thread may be in the verifier
+/// * Fast path for iterating over plugins that is lock-free and avoids
+/// cache-line ping pong
+/// * Plugin teardown may happen due to report_fatal_error from a thread that
+/// is currently in the verifier
+///
+/// The implementation achieves this by registering a "reader" object while the
+/// verifier is active. The reader object holds a hazard pointer to the plugins
+/// list that it is currently using.
+class PluginRegistry {
+ friend PluginRegistryReader;
+ friend PluginRegistryLock;
- explicit VerifierSupport(raw_ostream *OS, const Module &M)
- : OS(OS), M(M), MST(&M), TT(M.getTargetTriple()), DL(M.getDataLayout()),
- Context(M.getContext()) {}
+ using List = SmallVector<const VerifierPlugin *>;
-private:
- void Write(const Module *M) {
- *OS << "; ModuleID = '" << M->getModuleIdentifier() << "'\n";
- }
+ sys::Mutex Mutex;
+ SmallVector<PluginRegistryReader *> Readers;
+ List PluginsStorage[2];
+ std::atomic<List *> Plugins;
- void Write(const Value *V) {
- if (V)
- Write(*V);
+ PluginRegistry() {
+ Plugins.store(&PluginsStorage[0], std::memory_order_relaxed);
}
- void Write(const Value &V) {
- if (isa<Instruction>(V)) {
- V.print(*OS, MST);
- *OS << '\n';
- } else {
- V.printAsOperand(*OS, true, MST);
- *OS << '\n';
- }
- }
+ template <typename FnT> void updatePlugins(FnT &&F);
- void Write(const DbgRecord *DR) {
- if (DR) {
- DR->print(*OS, MST, false);
- *OS << '\n';
- }
+public:
+ static PluginRegistry &get() {
+ static PluginRegistry R;
+ return R;
}
- void Write(DbgVariableRecord::LocationType Type) {
- switch (Type) {
- case DbgVariableRecord::LocationType::Value:
- *OS << "value";
- break;
- case DbgVariableRecord::LocationType::Declare:
- *OS << "declare";
- break;
- case DbgVariableRecord::LocationType::Assign:
- *OS << "assign";
- break;
- case DbgVariableRecord::LocationType::End:
- *OS << "end";
- break;
- case DbgVariableRecord::LocationType::Any:
- *OS << "any";
- break;
- };
+ void addPlugin(const VerifierPlugin *P) {
+ updatePlugins([&](List &Plugins) { Plugins.push_back(P); });
}
- void Write(const Metadata *MD) {
- if (!MD)
- return;
- MD->print(*OS, MST, &M);
- *OS << '\n';
+ void removePlugin(const VerifierPlugin *P) {
+ updatePlugins([&](List &Plugins) {
+ Plugins.erase(std::remove(Plugins.begin(), Plugins.end(), P),
+ Plugins.end());
+ });
}
- template <class T> void Write(const MDTupleTypedArrayWrapper<T> &MD) {
- Write(MD.get());
+ void addReader(PluginRegistryReader *R) {
+ if (llvm_is_multithreaded()) {
+ sys::ScopedLock Lock(Mutex);
+ Readers.push_back(R);
+ }
}
- void Write(const NamedMDNode *NMD) {
- if (!NMD)
- return;
- NMD->print(*OS, MST);
- *OS << '\n';
+ void removeReader(PluginRegistryReader *R) {
+ if (llvm_is_multithreaded()) {
+ sys::ScopedLock Lock(Mutex);
+ Readers.erase(std::remove(Readers.begin(), Readers.end(), R),
+ Readers.end());
+ }
}
+};
- void Write(Type *T) {
- if (!T)
- return;
- *OS << ' ' << *T;
- }
+class PluginRegistryReader {
+ friend PluginRegistry;
+ friend PluginRegistryLock;
- void Write(const Comdat *C) {
- if (!C)
- return;
- *OS << *C;
- }
+ std::atomic<PluginRegistry::List *> HazardPtr;
- void Write(const APInt *AI) {
- if (!AI)
- return;
- *OS << *AI << '\n';
+public:
+ PluginRegistryReader() {
+ HazardPtr.store(nullptr, std::memory_order_relaxed);
+ PluginRegistry::get().addReader(this);
}
- void Write(const unsigned i) { *OS << i << '\n'; }
+ ~PluginRegistryReader() { PluginRegistry::get().removeReader(this); }
+};
- // NOLINTNEXTLINE(readability-identifier-naming)
- void Write(const Attribute *A) {
- if (!A)
- return;
- *OS << A->getAsString() << '\n';
- }
+// Thread-safe update of the plugins list. Take the lock, copy & update the
+// list, then wait for all readers to let go of the old version of the list
+// before releasing the lock.
+template <typename FnT> void PluginRegistry::updatePlugins(FnT &&F) {
+ if (llvm_is_multithreaded()) {
+ sys::ScopedLock Lock(Mutex);
+
+ List *OldList = Plugins.load(std::memory_order_relaxed);
+ List *NewList = (OldList == &PluginsStorage[0]) ? &PluginsStorage[1]
+ : &PluginsStorage[0];
+
+ // We're about to write to NewList. Spin wait to ensure no reader is
+ // accessing it.
+ for (auto *R : Readers) {
+ while (R->HazardPtr.load(std::memory_order_seq_cst) == NewList) {
+ // Let's yield to avoid a pathological busy wait. This really should
+ // only happen in the corner case where multiple users of LLVM exist
+ // in the same process and are initialized or torn down concurrently,
+ // so don't sweat the details.
+ std::this_thread::yield();
+ }
+ }
- // NOLINTNEXTLINE(readability-identifier-naming)
- void Write(const AttributeSet *AS) {
- if (!AS)
- return;
- *OS << AS->getAsString() << '\n';
+ *NewList = *OldList;
+ F(*NewList);
+
+ Plugins.store(NewList, std::memory_order_seq_cst);
+ } else {
+ // Avoid unnecessary copies when compiling without multi-threading
+ // support.
+ F(*Plugins.load(std::memory_order_relaxed));
}
+}
- // NOLINTNEXTLINE(readability-identifier-naming)
- void Write(const AttributeList *AL) {
- if (!AL)
- return;
- AL->print(*OS);
+class PluginRegistryLock {
+ PluginRegistryLock(PluginRegistryLock &) = delete;
+ PluginRegistryLock(PluginRegistryLock &&) = delete;
+ PluginRegistryLock &operator=(PluginRegistryLock &) = delete;
+ PluginRegistryLock &operator=(PluginRegistryLock &&) = delete;
+
+ PluginRegistryReader &Reader;
+
+public:
+ explicit PluginRegistryLock(PluginRegistryReader &Reader) : Reader(Reader) {
+ assert(!Reader.HazardPtr &&
+ "cannot have multiple PluginRegistryLocks through the same reader");
+
+ auto &Registry = PluginRegistry::get();
+
+ // The memory order of the initial load is irrelevant since we re-check the
+ // pointer using a sequentially consistent load later.
+ PluginRegistry::List *L = Registry.Plugins.load(std::memory_order_relaxed);
+
+ if (llvm_is_multithreaded()) {
+ for (;;) {
+ Reader.HazardPtr.store(L, std::memory_order_seq_cst);
+
+ PluginRegistry::List *Check =
+ Registry.Plugins.load(std::memory_order_seq_cst);
+ if (Check == L)
+ break;
+
+ L = Check;
+ }
+ } else {
+ Reader.HazardPtr.store(L, std::memory_order_relaxed);
+ }
}
- void Write(Printable P) { *OS << P << '\n'; }
+ ~PluginRegistryLock() {
+ assert(Reader.HazardPtr);
- template <typename T> void Write(ArrayRef<T> Vs) {
- for (const T &V : Vs)
- Write(V);
+ if (llvm_is_multithreaded()) {
+ Reader.HazardPtr.store(nullptr, std::memory_order_seq_cst);
+ } else {
+ Reader.HazardPtr.store(nullptr, std::memory_order_relaxed);
+ }
}
- template <typename T1, typename... Ts>
- void WriteTs(const T1 &V1, const Ts &... Vs) {
- Write(V1);
- WriteTs(Vs...);
+ ArrayRef<const VerifierPlugin *> get() const {
+ return *Reader.HazardPtr.load(std::memory_order_relaxed);
}
+};
- template <typename... Ts> void WriteTs() {}
+} // anonymous namespace
-public:
- /// A check failed, so printout out the condition and the message.
- ///
- /// This provides a nice place to put a breakpoint if you want to see why
- /// something is not correct.
- void CheckFailed(const Twine &Message) {
- if (OS)
- *OS << Message << '\n';
- Broken = true;
+VerifierPlugin::VerifierPlugin() { PluginRegistry::get().addPlugin(this); }
+
+VerifierPlugin::~VerifierPlugin() { PluginRegistry::get().removePlugin(this); }
+
+void VerifierPlugin::verifyIntrinsicCall(CallBase &Call,
+ VerifierSupport &VS) const {}
+
+VerifierSupport::VerifierSupport(raw_ostream *OS, const Module &M)
+ : OS(OS), M(M), MST(&M), TT(M.getTargetTriple()), DL(M.getDataLayout()),
+ Context(M.getContext()) {}
+
+void VerifierSupport::Write(const Module *M) {
+ *OS << "; ModuleID = '" << M->getModuleIdentifier() << "'\n";
+}
+
+void VerifierSupport::Write(const Value *V) {
+ if (V)
+ Write(*V);
+}
+
+void VerifierSupport::Write(const Value &V) {
+ if (isa<Instruction>(V)) {
+ V.print(*OS, MST);
+ *OS << '\n';
+ } else {
+ V.printAsOperand(*OS, true, MST);
+ *OS << '\n';
}
+}
- /// A check failed (with values to print).
- ///
- /// This calls the Message-only version so that the above is easier to set a
- /// breakpoint on.
- template <typename T1, typename... Ts>
- void CheckFailed(const Twine &Message, const T1 &V1, const Ts &... Vs) {
- CheckFailed(Message);
- if (OS)
- WriteTs(V1, Vs...);
- }
-
- /// A debug info check failed.
- void DebugInfoCheckFailed(const Twine &Message) {
- if (OS)
- *OS << Message << '\n';
- Broken |= TreatBrokenDebugInfoAsError;
- BrokenDebugInfo = true;
- }
-
- /// A debug info check failed (with values to print).
- template <typename T1, typename... Ts>
- void DebugInfoCheckFailed(const Twine &Message, const T1 &V1,
- const Ts &... Vs) {
- DebugInfoCheckFailed(Message);
- if (OS)
- WriteTs(V1, Vs...);
+void VerifierSupport::Write(const DbgRecord *DR) {
+ if (DR) {
+ DR->print(*OS, MST, false);
+ *OS << '\n';
}
-};
+}
+
+void VerifierSupport::Write(DbgVariableRecord::LocationType Type) {
+ switch (Type) {
+ case DbgVariableRecord::LocationType::Value:
+ *OS << "value";
+ break;
+ case DbgVariableRecord::LocationType::Declare:
+ *OS << "declare";
+ break;
+ case DbgVariableRecord::LocationType::Assign:
+ *OS << "assign";
+ break;
+ case DbgVariableRecord::LocationType::End:
+ *OS << "end";
+ break;
+ case DbgVariableRecord::LocationType::Any:
+ *OS << "any";
+ break;
+ };
+}
-} // namespace llvm
+void VerifierSupport::Write(const Metadata *MD) {
+ if (!MD)
+ return;
+ MD->print(*OS, MST, &M);
+ *OS << '\n';
+}
+
+void VerifierSupport::Write(const NamedMDNode *NMD) {
+ if (!NMD)
+ return;
+ NMD->print(*OS, MST);
+ *OS << '\n';
+}
+
+void VerifierSupport::Write(Type *T) {
+ if (!T)
+ return;
+ *OS << ' ' << *T;
+}
+
+void VerifierSupport::Write(const Comdat *C) {
+ if (!C)
+ return;
+ *OS << *C;
+}
+
+void VerifierSupport::Write(const APInt *AI) {
+ if (!AI)
+ return;
+ *OS << *AI << '\n';
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+void VerifierSupport::Write(const Attribute *A) {
+ if (!A)
+ return;
+ *OS << A->getAsString() << '\n';
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+void VerifierSupport::Write(const AttributeSet *AS) {
+ if (!AS)
+ return;
+ *OS << AS->getAsString() << '\n';
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+void VerifierSupport::Write(const AttributeList *AL) {
+ if (!AL)
+ return;
+ AL->print(*OS);
+}
+
+void VerifierSupport::CheckFailed(const Twine &Message) {
+ if (OS)
+ *OS << Message << '\n';
+ Broken = true;
+}
+
+/// A debug info check failed.
+void VerifierSupport::DebugInfoCheckFailed(const Twine &Message) {
+ if (OS)
+ *OS << Message << '\n';
+ Broken |= TreatBrokenDebugInfoAsError;
+ BrokenDebugInfo = true;
+}
namespace {
class Verifier : public InstVisitor<Verifier>, VerifierSupport {
friend class InstVisitor<Verifier>;
+
+ PluginRegistryReader PluginsReader;
+
DominatorTree DT;
/// When verifying a basic block, keep track of all of the
@@ -5660,8 +5775,12 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
}
switch (ID) {
- default:
+ default: {
+ PluginRegistryLock Lock(PluginsReader);
+ for (const VerifierPlugin *P : Lock.get())
+ P->verifyIntrinsicCall(Call, *this);
break;
+ }
case Intrinsic::assume: {
for (auto &Elem : Call.bundle_op_infos()) {
unsigned ArgCount = Elem.End - Elem.Begin;
@@ -6549,37 +6668,12 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
break;
}
case Intrinsic::preserve_array_access_index:
- case Intrinsic::preserve_struct_access_index:
- case Intrinsic::aarch64_ldaxr:
- case Intrinsic::aarch64_ldxr:
- case Intrinsic::arm_ldaex:
- case Intrinsic::arm_ldrex: {
+ case Intrinsic::preserve_struct_access_index: {
Type *ElemTy = Call.getParamElementType(0);
Check(ElemTy, "Intrinsic requires elementtype attribute on first argument.",
&Call);
break;
}
- case Intrinsic::aarch64_stlxr:
- case Intrinsic::aarch64_stxr:
- case Intrinsic::arm_stlex:
- case Intrinsic::arm_strex: {
- Type *ElemTy = Call.getAttributes().getParamElementType(1);
- Check(ElemTy,
- "Intrinsic requires elementtype attribute on second argument.",
- &Call);
- break;
- }
- case Intrinsic::aarch64_prefetch: {
- Check(cast<ConstantInt>(Call.getArgOperand(1))->getZExtValue() < 2,
- "write argument to llvm.aarch64.prefetch must be 0 or 1", Call);
- Check(cast<ConstantInt>(Call.getArgOperand(2))->getZExtValue() < 4,
- "target argument to llvm.aarch64.prefetch must be 0-3", Call);
- Check(cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue() < 2,
- "stream argument to llvm.aarch64.prefetch must be 0 or 1", Call);
- Check(cast<ConstantInt>(Call.getArgOperand(4))->getZExtValue() < 2,
- "isdata argument to llvm.aarch64.prefetch must be 0 or 1", Call);
- break;
- }
case Intrinsic::callbr_landingpad: {
const auto *CBR = dyn_cast<CallBrInst>(Call.getOperand(0));
Check(CBR, "intrinstic requires callbr operand", &Call);
@@ -6606,232 +6700,6 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
&Call);
break;
}
- case Intrinsic::amdgcn_cs_chain: {
- auto CallerCC = Call.getCaller()->getCallingConv();
- switch (CallerCC) {
- case CallingConv::AMDGPU_CS:
- case CallingConv::AMDGPU_CS_Chain:
- case CallingConv::AMDGPU_CS_ChainPreserve:
- break;
- default:
- CheckFailed("Intrinsic can only be used from functions with the "
- "amdgpu_cs, amdgpu_cs_chain or amdgpu_cs_chain_preserve "
- "calling conventions",
- &Call);
- break;
- }
-
- Check(Call.param...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/159415
More information about the llvm-commits
mailing list