[llvm] [TargetVerifier][AMDGPU] Add TargetVerifier. (PR #123609)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 20 06:19:36 PST 2025


https://github.com/jofrn updated https://github.com/llvm/llvm-project/pull/123609

>From 053aac8113194cda51fbbafe98e38c6c54521134 Mon Sep 17 00:00:00 2001
From: jofernau <Joe.Fernau at amd.com>
Date: Mon, 20 Jan 2025 04:51:26 -0800
Subject: [PATCH] [TargetVerifier][AMDGPU] Add TargetVerifier.

This pass verifies the IR for an individual backend. This is
different than Lint because it consolidates all checks for a
given backend in a single pass. A check for Lint may be
undefined behavior across all targets, whereas a check in
TargetVerifier would only pertain to the specified target
but can check more than just undefined behavior such are IR
validity. A use case of this would be to reject programs
with invalid IR while fuzzing.
---
 llvm/include/llvm/IR/Module.h                 |   4 +
 llvm/include/llvm/Target/TargetVerifier.h     |  82 +++++++
 .../TargetVerify/AMDGPUTargetVerifier.h       |  36 +++
 llvm/lib/Analysis/Lint.cpp                    |   9 +-
 llvm/lib/IR/Verifier.cpp                      |  18 +-
 .../Target/AMDGPU/AMDGPUTargetVerifier.cpp    | 220 ++++++++++++++++++
 llvm/lib/Target/AMDGPU/CMakeLists.txt         |   1 +
 llvm/test/CodeGen/AMDGPU/tgt-verify.ll        |  62 +++++
 llvm/tools/llvm-tgt-verify/CMakeLists.txt     |  34 +++
 .../tools/llvm-tgt-verify/llvm-tgt-verify.cpp | 172 ++++++++++++++
 10 files changed, 631 insertions(+), 7 deletions(-)
 create mode 100644 llvm/include/llvm/Target/TargetVerifier.h
 create mode 100644 llvm/include/llvm/Target/TargetVerify/AMDGPUTargetVerifier.h
 create mode 100644 llvm/lib/Target/AMDGPU/AMDGPUTargetVerifier.cpp
 create mode 100644 llvm/test/CodeGen/AMDGPU/tgt-verify.ll
 create mode 100644 llvm/tools/llvm-tgt-verify/CMakeLists.txt
 create mode 100644 llvm/tools/llvm-tgt-verify/llvm-tgt-verify.cpp

diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h
index 12b50fc506516e..7844cb216ed571 100644
--- a/llvm/include/llvm/IR/Module.h
+++ b/llvm/include/llvm/IR/Module.h
@@ -211,6 +211,10 @@ class LLVM_ABI Module {
 /// @name Constructors
 /// @{
 public:
+  /// Is this Module valid as determined by one of the verification passes
+  /// i.e. Lint, Verifier, TargetVerifier.
+  bool IsValid = true;
+
   /// Is this Module using intrinsics to record the position of debugging
   /// information, or non-intrinsic records? See IsNewDbgInfoFormat in
   /// \ref BasicBlock.
diff --git a/llvm/include/llvm/Target/TargetVerifier.h b/llvm/include/llvm/Target/TargetVerifier.h
new file mode 100644
index 00000000000000..e00c6a7b260c98
--- /dev/null
+++ b/llvm/include/llvm/Target/TargetVerifier.h
@@ -0,0 +1,82 @@
+//===-- llvm/Target/TargetVerifier.h - LLVM IR Target Verifier ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines target verifier interfaces that can be used for some
+// validation of input to the system, and for checking that transformations
+// haven't done something bad. In contrast to the Verifier or Lint, the
+// TargetVerifier looks for constructions invalid to a particular target
+// machine.
+//
+// To see what specifically is checked, look at TargetVerifier.cpp or an
+// individual backend's TargetVerifier.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TARGET_VERIFIER_H
+#define LLVM_TARGET_VERIFIER_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm {
+
+class Function;
+
+class TargetVerifierPass : public PassInfoMixin<TargetVerifierPass> {
+public:
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {}
+};
+
+class TargetVerify {
+protected:
+  void WriteValues(ArrayRef<const Value *> Vs) {
+    for (const Value *V : Vs) {
+      if (!V)
+        continue;
+      if (isa<Instruction>(V)) {
+        MessagesStr << *V << '\n';
+      } else {
+        V->printAsOperand(MessagesStr, true, Mod);
+        MessagesStr << '\n';
+      }
+    }
+  }
+
+  /// 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) { MessagesStr << Message << '\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);
+    WriteValues({V1, Vs...});
+  }
+public:
+  Module *Mod;
+  Triple TT;
+
+  std::string Messages;
+  raw_string_ostream MessagesStr;
+
+  TargetVerify(Module *Mod)
+      : Mod(Mod), TT(Triple::normalize(Mod->getTargetTriple())),
+        MessagesStr(Messages) {}
+
+  void run(Function &F) {};
+};
+
+} // namespace llvm
+
+#endif // LLVM_TARGET_VERIFIER_H
diff --git a/llvm/include/llvm/Target/TargetVerify/AMDGPUTargetVerifier.h b/llvm/include/llvm/Target/TargetVerify/AMDGPUTargetVerifier.h
new file mode 100644
index 00000000000000..e6ff57629b1413
--- /dev/null
+++ b/llvm/include/llvm/Target/TargetVerify/AMDGPUTargetVerifier.h
@@ -0,0 +1,36 @@
+//===-- llvm/Target/TargetVerify/AMDGPUTargetVerifier.h - AMDGPU ---*- C++ -*-===//
+////
+//// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+//// See https://llvm.org/LICENSE.txt for license information.
+//// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+////
+////===----------------------------------------------------------------------===//
+////
+//// This file defines target verifier interfaces that can be used for some
+//// validation of input to the system, and for checking that transformations
+//// haven't done something bad. In contrast to the Verifier or Lint, the
+//// TargetVerifier looks for constructions invalid to a particular target
+//// machine.
+////
+//// To see what specifically is checked, look at an individual backend's
+//// TargetVerifier.
+////
+////===----------------------------------------------------------------------===//
+
+#ifndef LLVM_AMDGPU_TARGET_VERIFIER_H
+#define LLVM_AMDGPU_TARGET_VERIFIER_H
+
+#include "llvm/Target/TargetVerifier.h"
+
+namespace llvm {
+
+class Function;
+
+class AMDGPUTargetVerifierPass : public TargetVerifierPass {
+public:
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_AMDGPU_TARGET_VERIFIER_H
diff --git a/llvm/lib/Analysis/Lint.cpp b/llvm/lib/Analysis/Lint.cpp
index e9d96a0c2972ad..1a42131b8ed1f1 100644
--- a/llvm/lib/Analysis/Lint.cpp
+++ b/llvm/lib/Analysis/Lint.cpp
@@ -747,10 +747,13 @@ PreservedAnalyses LintPass::run(Function &F, FunctionAnalysisManager &AM) {
   Lint L(Mod, DL, AA, AC, DT, TLI);
   L.visit(F);
   dbgs() << L.MessagesStr.str();
-  if (LintAbortOnError && !L.MessagesStr.str().empty())
-    report_fatal_error(Twine("Linter found errors, aborting. (enabled by --") +
+  if (!L.MessagesStr.str().empty()) {
+    F.getParent()->IsValid = false;
+    if (LintAbortOnError)
+      report_fatal_error(Twine("Linter found errors, aborting. (enabled by --") +
                            LintAbortOnErrorArgName + ")",
-                       false);
+                         false);
+  }
   return PreservedAnalyses::all();
 }
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 7b6f7b5aa6171a..26616c4de59d2a 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -135,6 +135,10 @@ static cl::opt<bool> VerifyNoAliasScopeDomination(
     cl::desc("Ensure that llvm.experimental.noalias.scope.decl for identical "
              "scopes are not dominating"));
 
+static cl::opt<bool>
+    VerifyAbortOnError("verifier-abort-on-error", cl::init(false),
+                       cl::desc("In the Verifier pass, abort on errors."));
+
 namespace llvm {
 
 struct VerifierSupport {
@@ -7739,16 +7743,22 @@ VerifierAnalysis::Result VerifierAnalysis::run(Function &F,
 
 PreservedAnalyses VerifierPass::run(Module &M, ModuleAnalysisManager &AM) {
   auto Res = AM.getResult<VerifierAnalysis>(M);
-  if (FatalErrors && (Res.IRBroken || Res.DebugInfoBroken))
-    report_fatal_error("Broken module found, compilation aborted!");
+  if (Res.IRBroken || Res.DebugInfoBroken) {
+    M.IsValid = false;
+    if (VerifyAbortOnError && FatalErrors)
+      report_fatal_error("Broken module found, compilation aborted!");
+  }
 
   return PreservedAnalyses::all();
 }
 
 PreservedAnalyses VerifierPass::run(Function &F, FunctionAnalysisManager &AM) {
   auto res = AM.getResult<VerifierAnalysis>(F);
-  if (res.IRBroken && FatalErrors)
-    report_fatal_error("Broken function found, compilation aborted!");
+  if (res.IRBroken) {
+    F.getParent()->IsValid = false;
+    if (VerifyAbortOnError && FatalErrors)
+      report_fatal_error("Broken function found, compilation aborted!");
+  }
 
   return PreservedAnalyses::all();
 }
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetVerifier.cpp b/llvm/lib/Target/AMDGPU/AMDGPUTargetVerifier.cpp
new file mode 100644
index 00000000000000..4e12145d0b45d7
--- /dev/null
+++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetVerifier.cpp
@@ -0,0 +1,220 @@
+#include "llvm/Target/TargetVerify/AMDGPUTargetVerifier.h"
+
+#include "llvm/Analysis/UniformityAnalysis.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IntrinsicsAMDGPU.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+static cl::opt<bool>
+MarkUniform("mark-uniform", cl::desc("Mark instructions as uniform"), cl::init(false));
+
+// Check - We know that cond should be true, if not print an error message.
+#define Check(C, ...)                                                          \
+  do {                                                                         \
+    if (!(C)) {                                                                \
+      TargetVerify::CheckFailed(__VA_ARGS__);                                  \
+      return;                                                                  \
+    }                                                                          \
+  } while (false)
+
+static bool isMFMA(unsigned IID) {
+  switch (IID) {
+    case Intrinsic::amdgcn_mfma_f32_4x4x1f32:
+    case Intrinsic::amdgcn_mfma_f32_4x4x4f16:
+    case Intrinsic::amdgcn_mfma_i32_4x4x4i8:
+    case Intrinsic::amdgcn_mfma_f32_4x4x2bf16:
+
+    case Intrinsic::amdgcn_mfma_f32_16x16x1f32:
+    case Intrinsic::amdgcn_mfma_f32_16x16x4f32:
+    case Intrinsic::amdgcn_mfma_f32_16x16x4f16:
+    case Intrinsic::amdgcn_mfma_f32_16x16x16f16:
+    case Intrinsic::amdgcn_mfma_i32_16x16x4i8:
+    case Intrinsic::amdgcn_mfma_i32_16x16x16i8:
+    case Intrinsic::amdgcn_mfma_f32_16x16x2bf16:
+    case Intrinsic::amdgcn_mfma_f32_16x16x8bf16:
+
+    case Intrinsic::amdgcn_mfma_f32_32x32x1f32:
+    case Intrinsic::amdgcn_mfma_f32_32x32x2f32:
+    case Intrinsic::amdgcn_mfma_f32_32x32x4f16:
+    case Intrinsic::amdgcn_mfma_f32_32x32x8f16:
+    case Intrinsic::amdgcn_mfma_i32_32x32x4i8:
+    case Intrinsic::amdgcn_mfma_i32_32x32x8i8:
+    case Intrinsic::amdgcn_mfma_f32_32x32x2bf16:
+    case Intrinsic::amdgcn_mfma_f32_32x32x4bf16:
+
+    case Intrinsic::amdgcn_mfma_f32_4x4x4bf16_1k:
+    case Intrinsic::amdgcn_mfma_f32_16x16x4bf16_1k:
+    case Intrinsic::amdgcn_mfma_f32_16x16x16bf16_1k:
+    case Intrinsic::amdgcn_mfma_f32_32x32x4bf16_1k:
+    case Intrinsic::amdgcn_mfma_f32_32x32x8bf16_1k:
+
+    case Intrinsic::amdgcn_mfma_f64_16x16x4f64:
+    case Intrinsic::amdgcn_mfma_f64_4x4x4f64:
+
+    case Intrinsic::amdgcn_mfma_i32_16x16x32_i8:
+    case Intrinsic::amdgcn_mfma_i32_32x32x16_i8:
+    case Intrinsic::amdgcn_mfma_f32_16x16x8_xf32:
+    case Intrinsic::amdgcn_mfma_f32_32x32x4_xf32:
+
+    case Intrinsic::amdgcn_mfma_f32_16x16x32_bf8_bf8:
+    case Intrinsic::amdgcn_mfma_f32_16x16x32_bf8_fp8:
+    case Intrinsic::amdgcn_mfma_f32_16x16x32_fp8_bf8:
+    case Intrinsic::amdgcn_mfma_f32_16x16x32_fp8_fp8:
+
+    case Intrinsic::amdgcn_mfma_f32_32x32x16_bf8_bf8:
+    case Intrinsic::amdgcn_mfma_f32_32x32x16_bf8_fp8:
+    case Intrinsic::amdgcn_mfma_f32_32x32x16_fp8_bf8:
+    case Intrinsic::amdgcn_mfma_f32_32x32x16_fp8_fp8:
+      return true;
+    default:
+      return false;
+  }
+}
+
+namespace llvm {
+class AMDGPUTargetVerify : public TargetVerify {
+public:
+  Module *Mod;
+
+  DominatorTree *DT;
+  PostDominatorTree *PDT;
+  UniformityInfo *UA;
+
+  AMDGPUTargetVerify(Module *Mod, DominatorTree *DT, PostDominatorTree *PDT, UniformityInfo *UA)
+    : TargetVerify(Mod), Mod(Mod), DT(DT), PDT(PDT), UA(UA) {}
+
+  void run(Function &F);
+};
+
+static bool IsValidInt(const Type *Ty) {
+  return Ty->isIntegerTy(1) ||
+         Ty->isIntegerTy(8) ||
+         Ty->isIntegerTy(16) ||
+         Ty->isIntegerTy(32) ||
+         Ty->isIntegerTy(64) ||
+         Ty->isIntegerTy(128);
+}
+
+static bool isShader(CallingConv::ID CC) {
+  switch(CC) {
+    case CallingConv::AMDGPU_VS:
+    case CallingConv::AMDGPU_LS:
+    case CallingConv::AMDGPU_HS:
+    case CallingConv::AMDGPU_ES:
+    case CallingConv::AMDGPU_GS:
+    case CallingConv::AMDGPU_PS:
+    case CallingConv::AMDGPU_CS_Chain:
+    case CallingConv::AMDGPU_CS_ChainPreserve:
+    case CallingConv::AMDGPU_CS:
+      return true;
+    default:
+      return false;
+  }
+}
+
+void AMDGPUTargetVerify::run(Function &F) {
+  // Ensure shader calling convention returns void
+  if (isShader(F.getCallingConv()))
+    Check(F.getReturnType() == Type::getVoidTy(F.getContext()), "Shaders must return void");
+
+  for (auto &BB : F) {
+
+    for (auto &I : BB) {
+      if (MarkUniform)
+        outs() << UA->isUniform(&I) << ' ' << I << '\n';
+
+      // Ensure integral types are valid: i8, i16, i32, i64, i128
+      if (I.getType()->isIntegerTy())
+        Check(IsValidInt(I.getType()), "Int type is invalid.", &I);
+      for (unsigned i = 0; i < I.getNumOperands(); ++i)
+        if (I.getOperand(i)->getType()->isIntegerTy())
+          Check(IsValidInt(I.getOperand(i)->getType()),
+                "Int type is invalid.", I.getOperand(i));
+
+      // Ensure alloca array size is constant
+      if (auto *AI = dyn_cast<AllocaInst>(&I))
+      {
+        auto *AS = AI->getArraySize();
+        Check(!isa<Constant>(AS), "Dynamically-sized alloca disallowed");
+      }
+
+      // Ensure no store to const memory
+      if (auto *SI = dyn_cast<StoreInst>(&I))
+      {
+        unsigned AS = SI->getPointerAddressSpace();
+        Check(AS != 4, "Write to const memory", SI);
+      }
+
+      // Ensure no kernel to kernel calls.
+      if (auto *CI = dyn_cast<CallInst>(&I))
+      {
+        CallingConv::ID CalleeCC = CI->getCallingConv();
+        if (CalleeCC == CallingConv::AMDGPU_KERNEL)
+        {
+          CallingConv::ID CallerCC = CI->getParent()->getParent()->getCallingConv();
+          Check(CallerCC != CallingConv::AMDGPU_KERNEL,
+            "A kernel may not call a kernel", CI->getParent()->getParent());
+        }
+      }
+
+      // Ensure MFMA is not in control flow with diverging operands
+      if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
+        if (isMFMA(II->getIntrinsicID())) {
+          bool InControlFlow = false;
+          for (const auto &P : predecessors(&BB))
+            if (!PDT->dominates(&BB, P)) {
+              InControlFlow = true;
+              break;
+            }
+          for (const auto &S : successors(&BB))
+            if (!DT->dominates(&BB, S)) {
+              InControlFlow = true;
+              break;
+            }
+          if (InControlFlow) {
+            // If operands to MFMA are not uniform, MFMA cannot be in control flow
+            bool hasUniformOperands = true;
+            for (unsigned i = 0; i < II->getNumOperands(); i++) {
+              if (!UA->isUniform(II->getOperand(i))) {
+                dbgs() << "Not uniform: " << *II->getOperand(i) << '\n';
+                hasUniformOperands = false;
+              }
+            }
+            if (!hasUniformOperands) Check(false, "MFMA in control flow", II);
+            //else Check(false, "MFMA in control flow (uniform operands)", II);
+          }
+          //else Check(false, "MFMA not in control flow", II);
+        }
+      }
+    }
+  }
+}
+
+PreservedAnalyses AMDGPUTargetVerifierPass::run(Function &F, FunctionAnalysisManager &AM) {
+
+  auto *Mod = F.getParent();
+
+  auto UA = &AM.getResult<UniformityInfoAnalysis>(F);
+  auto *DT = &AM.getResult<DominatorTreeAnalysis>(F);
+  auto *PDT = &AM.getResult<PostDominatorTreeAnalysis>(F);
+
+  AMDGPUTargetVerify TV(Mod, DT, PDT, UA);
+  TV.run(F);
+
+  dbgs() << TV.MessagesStr.str();
+  if (!TV.MessagesStr.str().empty()) {
+    F.getParent()->IsValid = false;
+  }
+
+  return PreservedAnalyses::all();
+}
+} // namespace llvm
diff --git a/llvm/lib/Target/AMDGPU/CMakeLists.txt b/llvm/lib/Target/AMDGPU/CMakeLists.txt
index 97a0d59cfeeda3..a7cbc0415fb3e4 100644
--- a/llvm/lib/Target/AMDGPU/CMakeLists.txt
+++ b/llvm/lib/Target/AMDGPU/CMakeLists.txt
@@ -108,6 +108,7 @@ add_llvm_target(AMDGPUCodeGen
   AMDGPUTargetMachine.cpp
   AMDGPUTargetObjectFile.cpp
   AMDGPUTargetTransformInfo.cpp
+  AMDGPUTargetVerifier.cpp
   AMDGPUUnifyDivergentExitNodes.cpp
   AMDGPUUnifyMetadata.cpp
   R600MachineCFGStructurizer.cpp
diff --git a/llvm/test/CodeGen/AMDGPU/tgt-verify.ll b/llvm/test/CodeGen/AMDGPU/tgt-verify.ll
new file mode 100644
index 00000000000000..f56ff992a56c2f
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/tgt-verify.ll
@@ -0,0 +1,62 @@
+; RUN: not llvm-tgt-verify %s -mtriple=amdgcn |& FileCheck %s
+
+define amdgpu_kernel void @test_mfma_f32_32x32x1f32_vecarg(ptr addrspace(1) %arg) #0 {
+; CHECK: Not uniform: %in.f32 = load <32 x float>, ptr addrspace(1) %gep, align 128
+; CHECK-NEXT: MFMA in control flow
+; CHECK-NEXT:   %mfma = tail call <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float 1.000000e+00, float 2.000000e+00, <32 x float> %in.f32, i32 1, i32 2, i32 3)
+s:
+  %tid = call i32 @llvm.amdgcn.workitem.id.x()
+  %gep = getelementptr inbounds <32 x float>, ptr addrspace(1) %arg, i32 %tid
+  %in.i32 = load <32 x i32>, ptr addrspace(1) %gep
+  %in.f32 = load <32 x float>, ptr addrspace(1) %gep
+
+  %0 = icmp eq <32 x i32> %in.i32, zeroinitializer
+  %div.br = extractelement <32 x i1> %0, i32 0
+  br i1 %div.br, label %if.3, label %else.0
+
+if.3:
+  br label %join
+
+else.0:
+  %mfma = tail call <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float 1.000000e+00, float 2.000000e+00, <32 x float> %in.f32, i32 1, i32 2, i32 3)
+  br label %join
+
+join:
+  ret void
+}
+
+define amdgpu_cs i32 @shader() {
+; CHECK: Shaders must return void
+  ret i32 0
+}
+
+define amdgpu_kernel void @store_const(ptr addrspace(4) %out, i32 %a, i32 %b) {
+; CHECK: Undefined behavior: Write to memory in const addrspace
+; CHECK-NEXT:   store i32 %r, ptr addrspace(4) %out, align 4
+; CHECK-NEXT: Write to const memory
+; CHECK-NEXT:   store i32 %r, ptr addrspace(4) %out, align 4
+  %r = add i32 %a, %b
+  store i32 %r, ptr addrspace(4) %out
+  ret void
+}
+
+define amdgpu_kernel void @kernel_callee(ptr %x) {
+  ret void
+}
+
+define amdgpu_kernel void @kernel_caller(ptr %x) {
+; CHECK: A kernel may not call a kernel
+; CHECK-NEXT: ptr @kernel_caller
+  call amdgpu_kernel void @kernel_callee(ptr %x)
+  ret void
+}
+
+
+; Function Attrs: nounwind
+define i65 @invalid_type(i65 %x) #0 {
+; CHECK: Int type is invalid.
+; CHECK-NEXT: %tmp2 = ashr i65 %x, 64
+entry:
+  %tmp2 = ashr i65 %x, 64
+  ret i65 %tmp2
+}
diff --git a/llvm/tools/llvm-tgt-verify/CMakeLists.txt b/llvm/tools/llvm-tgt-verify/CMakeLists.txt
new file mode 100644
index 00000000000000..fe47c85e6cdce7
--- /dev/null
+++ b/llvm/tools/llvm-tgt-verify/CMakeLists.txt
@@ -0,0 +1,34 @@
+set(LLVM_LINK_COMPONENTS
+  AllTargetsAsmParsers
+  AllTargetsCodeGens
+  AllTargetsDescs
+  AllTargetsInfos
+  Analysis
+  AsmPrinter
+  CodeGen
+  CodeGenTypes
+  Core
+  IRPrinter
+  IRReader
+  MC
+  MIRParser
+  Passes
+  Remarks
+  ScalarOpts
+  SelectionDAG
+  Support
+  Target
+  TargetParser
+  TransformUtils
+  Vectorize
+  )
+
+add_llvm_tool(llvm-tgt-verify
+  llvm-tgt-verify.cpp
+
+  DEPENDS
+  intrinsics_gen
+  SUPPORT_PLUGINS
+  )
+
+export_executable_symbols_for_plugins(llc)
diff --git a/llvm/tools/llvm-tgt-verify/llvm-tgt-verify.cpp b/llvm/tools/llvm-tgt-verify/llvm-tgt-verify.cpp
new file mode 100644
index 00000000000000..68422abd6f4cc0
--- /dev/null
+++ b/llvm/tools/llvm-tgt-verify/llvm-tgt-verify.cpp
@@ -0,0 +1,172 @@
+//===--- llvm-isel-fuzzer.cpp - Fuzzer for instruction selection ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tool to fuzz instruction selection using libFuzzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/InitializePasses.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/Lint.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/StandardInstrumentations.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetVerifier.h"
+
+#include "llvm/Target/TargetVerify/AMDGPUTargetVerifier.h"
+
+#define DEBUG_TYPE "isel-fuzzer"
+
+using namespace llvm;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+static cl::opt<std::string>
+InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-"));
+
+static cl::opt<bool>
+    StacktraceAbort("stacktrace-abort",
+        cl::desc("Turn on stacktrace"), cl::init(false));
+
+static cl::opt<bool>
+    NoLint("no-lint",
+        cl::desc("Turn off Lint"), cl::init(false));
+
+static cl::opt<bool>
+    NoVerify("no-verifier",
+        cl::desc("Turn off Verifier"), cl::init(false));
+
+static cl::opt<char>
+    OptLevel("O",
+             cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+                      "(default = '-O2')"),
+             cl::Prefix, cl::init('2'));
+
+static cl::opt<std::string>
+    TargetTriple("mtriple", cl::desc("Override target triple for module"));
+
+static std::unique_ptr<TargetMachine> TM;
+
+static void handleLLVMFatalError(void *, const char *Message, bool) {
+  if (StacktraceAbort) {
+    dbgs() << "LLVM ERROR: " << Message << "\n"
+           << "Aborting.\n";
+    abort();
+  }
+}
+
+int main(int argc, char **argv) {
+  StringRef ExecName = argv[0];
+  InitLLVM X(argc, argv);
+
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  InitializeAllAsmPrinters();
+  InitializeAllAsmParsers();
+
+  PassRegistry *Registry = PassRegistry::getPassRegistry();
+  initializeCore(*Registry);
+  initializeCodeGen(*Registry);
+  initializeAnalysis(*Registry);
+  initializeTarget(*Registry);
+
+  cl::ParseCommandLineOptions(argc, argv);
+
+  if (TargetTriple.empty()) {
+    errs() << ExecName << ": -mtriple must be specified\n";
+    exit(1);
+  }
+
+  CodeGenOptLevel OLvl;
+  if (auto Level = CodeGenOpt::parseLevel(OptLevel)) {
+    OLvl = *Level;
+  } else {
+    errs() << ExecName << ": invalid optimization level.\n";
+    return 1;
+  }
+  ExitOnError ExitOnErr(std::string(ExecName) + ": error:");
+  TM = ExitOnErr(codegen::createTargetMachineForTriple(
+      Triple::normalize(TargetTriple), OLvl));
+  assert(TM && "Could not allocate target machine!");
+
+  // Make sure we print the summary and the current unit when LLVM errors out.
+  install_fatal_error_handler(handleLLVMFatalError, nullptr);
+
+  LLVMContext Context;
+  SMDiagnostic Err;
+  std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
+  if (!M) {
+    errs() << "Invalid mod\n";
+    return 1;
+  }
+  auto S = Triple::normalize(TargetTriple);
+  M->setTargetTriple(S);
+
+  PassInstrumentationCallbacks PIC;
+  StandardInstrumentations SI(Context, false/*debug PM*/,
+                              false);
+  registerCodeGenCallback(PIC, *TM);
+
+  ModulePassManager MPM;
+  FunctionPassManager FPM;
+  //TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple()));
+
+  MachineFunctionAnalysisManager MFAM;
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager MAM;
+  PassBuilder PB(TM.get(), PipelineTuningOptions(), std::nullopt, &PIC);
+  PB.registerModuleAnalyses(MAM);
+  PB.registerCGSCCAnalyses(CGAM);
+  PB.registerFunctionAnalyses(FAM);
+  PB.registerLoopAnalyses(LAM);
+  PB.registerMachineFunctionAnalyses(MFAM);
+  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM, &MFAM);
+
+  SI.registerCallbacks(PIC, &MAM);
+
+  //FAM.registerPass([&] { return TargetLibraryAnalysis(TLII); });
+
+  Triple TT(M->getTargetTriple());
+  if (!NoLint)
+    FPM.addPass(LintPass());
+  if (!NoVerify)
+    MPM.addPass(VerifierPass());
+  if (TT.isAMDGPU())
+    FPM.addPass(AMDGPUTargetVerifierPass());
+  else if (false) {} // ...
+  else
+    FPM.addPass(TargetVerifierPass());
+  MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+
+  MPM.run(*M, MAM);
+
+  if (!M->IsValid)
+    return 1;
+
+  return 0;
+}



More information about the llvm-commits mailing list