Skip to content

[TargetVerifier][AMDGPU] Add TargetVerifier. #123609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
210b6d8
[TargetVerifier][AMDGPU] Add TargetVerifier.
jofrn Jan 20, 2025
a808efc
Add hook for target verifier in llc,opt
jofrn Feb 3, 2025
64d0018
Run AMDGPUTargetVerifier within AMDGPU pipeline. Move IsValid from
jofrn Apr 16, 2025
fdae302
Remove cmd line options that aren't required. Make error message expl…
jofrn Apr 16, 2025
5ceda58
Return Verifier none status through PreservedAnalyses on fail.
jofrn Apr 19, 2025
99c2906
Rebase update.
jofrn Apr 20, 2025
3ea7eae
Add generic TargetVerifier.
jofrn Apr 20, 2025
f52c4db
Remove store to const check since it is in Lint already
jofrn Apr 20, 2025
5c9a4ab
Add chain followed by unreachable check
jofrn Apr 20, 2025
0ff03f7
Remove mfma check
jofrn Apr 20, 2025
6b84c73
Add registerVerifierPasses to PassBuilder and add the verifier passes…
jofrn Apr 22, 2025
ec3276b
Remove leftovers. Add titles. Add call to registerVerifierCallbacks i…
jofrn Apr 22, 2025
4f00c83
Add pass to legacy PM.
jofrn Apr 24, 2025
3013fc9
Add fam in other projects.
jofrn Apr 26, 2025
8745cd1
Avoid fatal errors in llc.
jofrn Apr 26, 2025
c7bf730
Add tool to build/test.
jofrn Apr 26, 2025
c8dd3db
Cleanup of unrequired functions.
jofrn Apr 28, 2025
2c12e6a
Make virtual.
jofrn Apr 28, 2025
3267b65
Remove from legacy PM. Add to target dependent pipeline.
jofrn Apr 30, 2025
6401b75
Add back to legacy PM.
jofrn Apr 30, 2025
e2f0225
Remove reference to FAM in registerCallbacks and VerifyEach for Targe…
jofrn Apr 30, 2025
b43cec1
Remove references to registry
jofrn Apr 30, 2025
b583b3f
Remove int check
jofrn Apr 30, 2025
2ba9f5d
Remove modifications to Lint/Verifier.
jofrn May 1, 2025
0c57244
Remove llvm-tgt-verify tool.
jofrn May 1, 2025
5fec133
Remove TargetVerifier.cpp
jofrn May 1, 2025
c176578
clang-format
jofrn May 1, 2025
97d43c1
Add VerifyTarget option
jofrn May 1, 2025
ea2f050
Remove AMDGPUTargetVerifier.h
jofrn May 8, 2025
de38b78
Remove analyses.
jofrn May 8, 2025
dd34679
function name style, TargetVerifier comment, option name generality
jofrn May 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions llvm/include/llvm/Target/TargetVerifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===-- 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 an individual backend's
// TargetVerifier.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_TARGET_VERIFIER_H
#define LLVM_TARGET_VERIFIER_H

#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/TargetParser/Triple.h"

namespace llvm {

class Function;

class TargetVerifierPass : public PassInfoMixin<TargetVerifierPass> {
public:
virtual PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) = 0;
};

class TargetVerify {
protected:
void writeValues(ArrayRef<const Value *> Vs) {
for (const Value *V : Vs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rename this to something more accurate because currently I don't know where it writes to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be written to anywhere, but MessagesStr is currently being written to dbgs().

dbgs() << TV.MessagesStr.str();

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;

bool IsValid = true;

TargetVerify(Module *Mod)
: Mod(Mod), TT(Mod->getTargetTriple()), MessagesStr(Messages) {}

virtual bool run(Function &F) = 0;
};

} // namespace llvm

#endif // LLVM_TARGET_VERIFIER_H
7 changes: 7 additions & 0 deletions llvm/lib/Target/AMDGPU/AMDGPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "llvm/Pass.h"
#include "llvm/Support/AMDGPUAddrSpace.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetVerifier.h"

namespace llvm {

Expand Down Expand Up @@ -530,6 +531,12 @@ extern char &GCNRewritePartialRegUsesID;
void initializeAMDGPUWaitSGPRHazardsLegacyPass(PassRegistry &);
extern char &AMDGPUWaitSGPRHazardsLegacyID;

FunctionPass *createAMDGPUTargetVerifierLegacyPass(bool FatalErrors);
void initializeAMDGPUTargetVerifierLegacyPassPass(PassRegistry &);
struct AMDGPUTargetVerifierPass : public TargetVerifierPass {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) override;
};

namespace AMDGPU {
enum TargetIndex {
TI_CONSTDATA_START,
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@ static cl::opt<bool> HasClosedWorldAssumption(
cl::desc("Whether has closed-world assumption at link time"),
cl::init(false), cl::Hidden);

static cl::opt<bool> VerifyTarget("amdgpu-verify-tgt",
cl::desc("Enable the target verifier"));

extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAMDGPUTarget() {
// Register the target
RegisterTargetMachine<R600TargetMachine> X(getTheR600Target());
Expand Down Expand Up @@ -1376,6 +1379,8 @@ bool AMDGPUPassConfig::addGCPasses() {
//===----------------------------------------------------------------------===//

bool GCNPassConfig::addPreISel() {
if (VerifyTarget)
addPass(createAMDGPUTargetVerifierLegacyPass(false));
AMDGPUPassConfig::addPreISel();

if (TM->getOptLevel() > CodeGenOptLevel::None)
Expand Down Expand Up @@ -1975,6 +1980,9 @@ AMDGPUCodeGenPassBuilder::AMDGPUCodeGenPassBuilder(
}

void AMDGPUCodeGenPassBuilder::addIRPasses(AddIRPass &addPass) const {
if (VerifyTarget)
addPass(AMDGPUTargetVerifierPass());

if (RemoveIncompatibleFunctions && TM.getTargetTriple().isAMDGCN())
addPass(AMDGPURemoveIncompatibleFunctionsPass(TM));

Expand Down
170 changes: 170 additions & 0 deletions llvm/lib/Target/AMDGPU/AMDGPUTargetVerifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//===-- AMDGPUTargetVerifier.cpp - 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.
//
//===----------------------------------------------------------------------===//

#include "AMDGPU.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/Debug.h"

#include "llvm/Support/raw_ostream.h"

using namespace llvm;

// Check - We know that cond should be true, if not print an error message.
#define Check(C, ...) \
do { \
if (!(C)) { \
TargetVerify::checkFailed(__VA_ARGS__); \
} \
} while (false)

namespace llvm {

class AMDGPUTargetVerify : public TargetVerify {
public:
AMDGPUTargetVerify(Module *Mod) : TargetVerify(Mod) {}
bool run(Function &F) override;
};

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;
}
}

bool AMDGPUTargetVerify::run(Function &F) {
// Ensure shader calling convention returns void
if (isShader(F.getCallingConv()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on previous discussion with @arsenm , this check should go to the IR verifier.

Check(F.getReturnType() == Type::getVoidTy(F.getContext()),
"Shaders must return void");

for (auto &BB : F) {

for (auto &I : BB) {

if (auto *CI = dyn_cast<CallInst>(&I)) {
// Ensure no kernel to kernel calls.
CallingConv::ID CalleeCC = CI->getCallingConv();
if (CalleeCC == CallingConv::AMDGPU_KERNEL) {
CallingConv::ID CallerCC =
CI->getParent()->getParent()->getCallingConv();
Check(CallerCC != CallingConv::AMDGPU_KERNEL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I already disallow this in the IR verifier.

"A kernel may not call a kernel", CI->getParent()->getParent());
}

// Ensure chain intrinsics are followed by unreachables.
if (CI->getIntrinsicID() == Intrinsic::amdgcn_cs_chain)
Check(isa_and_present<UnreachableInst>(CI->getNextNode()),
"llvm.amdgcn.cs.chain must be followed by unreachable", CI);
}
}
}

dbgs() << MessagesStr.str();
if (!MessagesStr.str().empty()) {
IsValid = false;
return false;
}
return true;
}

PreservedAnalyses AMDGPUTargetVerifierPass::run(Function &F,
FunctionAnalysisManager &AM) {
auto *Mod = F.getParent();

AMDGPUTargetVerify TV(Mod);
TV.run(F);

dbgs() << TV.MessagesStr.str();
if (!TV.MessagesStr.str().empty()) {
TV.IsValid = false;
return PreservedAnalyses::none();
}
return PreservedAnalyses::all();
}

struct AMDGPUTargetVerifierLegacyPass : public FunctionPass {
static char ID;

std::unique_ptr<AMDGPUTargetVerify> TV;
bool FatalErrors = false;

AMDGPUTargetVerifierLegacyPass(bool FatalErrors)
: FunctionPass(ID), FatalErrors(FatalErrors) {
initializeAMDGPUTargetVerifierLegacyPassPass(
*PassRegistry::getPassRegistry());
}

bool doInitialization(Module &M) override {
TV = std::make_unique<AMDGPUTargetVerify>(&M);
return false;
}

bool runOnFunction(Function &F) override {
if (!TV->run(F)) {
errs() << "in function " << F.getName() << '\n';
if (FatalErrors)
report_fatal_error("broken function found, compilation aborted!");
else
errs() << "broken function found, compilation aborted!\n";
}
return false;
}

bool doFinalization(Module &M) override {
bool IsValid = true;
for (Function &F : M)
if (F.isDeclaration())
IsValid &= TV->run(F);

if (!IsValid) {
if (FatalErrors)
report_fatal_error("broken module found, compilation aborted!");
else
errs() << "broken module found, compilation aborted!\n";
}
return false;
}

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
};
char AMDGPUTargetVerifierLegacyPass::ID = 0;
FunctionPass *createAMDGPUTargetVerifierLegacyPass(bool FatalErrors) {
return new AMDGPUTargetVerifierLegacyPass(FatalErrors);
}
} // namespace llvm
INITIALIZE_PASS(AMDGPUTargetVerifierLegacyPass, "amdgpu-tgtverifier",
"AMDGPU Target Verifier", false, false)
1 change: 1 addition & 0 deletions llvm/lib/Target/AMDGPU/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ add_llvm_target(AMDGPUCodeGen
AMDGPUTargetMachine.cpp
AMDGPUTargetObjectFile.cpp
AMDGPUTargetTransformInfo.cpp
AMDGPUTargetVerifier.cpp
AMDGPUWaitSGPRHazards.cpp
AMDGPUUnifyDivergentExitNodes.cpp
AMDGPUUnifyMetadata.cpp
Expand Down
6 changes: 6 additions & 0 deletions llvm/test/CodeGen/AMDGPU/tgt-verify-llc-fail.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
; RUN: llc -mtriple=amdgcn -mcpu=gfx900 -amdgpu-verify-tgt -o - < %s 2>&1 | FileCheck %s

define amdgpu_cs i32 @nonvoid_shader() {
; CHECK: Shaders must return void
ret i32 0
}
6 changes: 6 additions & 0 deletions llvm/test/CodeGen/AMDGPU/tgt-verify-llc-pass.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
; RUN: llc -mtriple=amdgcn -mcpu=gfx900 -amdgpu-verify-tgt %s -o - 2>&1 | FileCheck %s --allow-empty

define amdgpu_cs void @void_shader() {
; CHECK-NOT: Shaders must return void
ret void
}
Loading