-dot-machine-cfg for printing MachineFunction to a dot file

This pass allows a user to dump a MIR function to a dot file
and view it as a graph. It is targeted to provide a similar
functionality as -dot-cfg pass on LLVM-IR. As of now the pass
also support below flags:
-dot-mcfg-only [optional][won't print instructions in the
graph just block name]
-mcfg-dot-filename-prefix [optional][prefix to add to output dot file]
-mcfg-func-name [optional] [specify function name or it's
substring, handy if mir file contains multiple functions and
you need to see graph of just one]

More flags and details can be introduced as per the requirements
in future. This pass is inspired from -dot-cfg IR pass and APIs
are written in almost identical format.

Patch by Yashwant Singh <Yashwant.Singh@amd.com> (yassingh)

Reviewed By: arsenm

Differential Revision: https://reviews.llvm.org/D133709
This commit is contained in:
Christudasan Devadasan 2022-09-22 10:49:46 +05:30
parent e500f8f8a8
commit 32a8260ccc
11 changed files with 380 additions and 58 deletions

View File

@ -119,81 +119,96 @@ struct GraphTraits<DOTFuncInfo *> : public GraphTraits<const BasicBlock *> {
}
};
template <typename BasicBlockT>
std::string SimpleNodeLabelString(const BasicBlockT *Node) {
if (!Node->getName().empty())
return Node->getName().str();
std::string Str;
raw_string_ostream OS(Str);
Node->printAsOperand(OS, false);
return OS.str();
}
template <typename BasicBlockT>
std::string CompleteNodeLabelString(
const BasicBlockT *Node,
function_ref<void(raw_string_ostream &, const BasicBlockT &)>
HandleBasicBlock,
function_ref<void(std::string &, unsigned &, unsigned)>
HandleComment) {
enum { MaxColumns = 80 };
std::string Str;
raw_string_ostream OS(Str);
if (Node->getName().empty()) {
Node->printAsOperand(OS, false);
OS << ':';
}
HandleBasicBlock(OS, *Node);
std::string OutStr = OS.str();
if (OutStr[0] == '\n')
OutStr.erase(OutStr.begin());
unsigned ColNum = 0;
unsigned LastSpace = 0;
for (unsigned i = 0; i != OutStr.length(); ++i) {
if (OutStr[i] == '\n') { // Left justify
OutStr[i] = '\\';
OutStr.insert(OutStr.begin() + i + 1, 'l');
ColNum = 0;
LastSpace = 0;
} else if (OutStr[i] == ';') { // Delete comments!
unsigned Idx = OutStr.find('\n', i + 1); // Find end of line
HandleComment(OutStr, i, Idx);
} else if (ColNum == MaxColumns) { // Wrap lines.
// Wrap very long names even though we can't find a space.
if (!LastSpace)
LastSpace = i;
OutStr.insert(LastSpace, "\\l...");
ColNum = i - LastSpace;
LastSpace = 0;
i += 3; // The loop will advance 'i' again.
} else
++ColNum;
if (OutStr[i] == ' ')
LastSpace = i;
}
return OutStr;
}
template <>
struct DOTGraphTraits<DOTFuncInfo *> : public DefaultDOTGraphTraits {
// Cache for is hidden property
llvm::DenseMap<const BasicBlock *, bool> isOnDeoptOrUnreachablePath;
DenseMap<const BasicBlock *, bool> isOnDeoptOrUnreachablePath;
DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
static std::string getGraphName(DOTFuncInfo *CFGInfo) {
return "CFG for '" + CFGInfo->getFunction()->getName().str() + "' function";
}
static std::string getSimpleNodeLabel(const BasicBlock *Node, DOTFuncInfo *) {
if (!Node->getName().empty())
return Node->getName().str();
std::string Str;
raw_string_ostream OS(Str);
Node->printAsOperand(OS, false);
return OS.str();
}
static void eraseComment(std::string &OutStr, unsigned &I, unsigned Idx) {
OutStr.erase(OutStr.begin() + I, OutStr.begin() + Idx);
--I;
}
static std::string getGraphName(DOTFuncInfo *CFGInfo) {
return "CFG for '" + CFGInfo->getFunction()->getName().str() + "' function";
}
static std::string getSimpleNodeLabel(const BasicBlock *Node, DOTFuncInfo *) {
return SimpleNodeLabelString(Node);
}
static std::string getCompleteNodeLabel(
const BasicBlock *Node, DOTFuncInfo *,
llvm::function_ref<void(raw_string_ostream &, const BasicBlock &)>
function_ref<void(raw_string_ostream &, const BasicBlock &)>
HandleBasicBlock = [](raw_string_ostream &OS,
const BasicBlock &Node) -> void { OS << Node; },
llvm::function_ref<void(std::string &, unsigned &, unsigned)>
function_ref<void(std::string &, unsigned &, unsigned)>
HandleComment = eraseComment) {
enum { MaxColumns = 80 };
std::string Str;
raw_string_ostream OS(Str);
if (Node->getName().empty()) {
Node->printAsOperand(OS, false);
OS << ":";
}
HandleBasicBlock(OS, *Node);
std::string OutStr = OS.str();
if (OutStr[0] == '\n')
OutStr.erase(OutStr.begin());
// Process string output to make it nicer...
unsigned ColNum = 0;
unsigned LastSpace = 0;
for (unsigned i = 0; i != OutStr.length(); ++i) {
if (OutStr[i] == '\n') { // Left justify
OutStr[i] = '\\';
OutStr.insert(OutStr.begin() + i + 1, 'l');
ColNum = 0;
LastSpace = 0;
} else if (OutStr[i] == ';') { // Delete comments!
unsigned Idx = OutStr.find('\n', i + 1); // Find end of line
HandleComment(OutStr, i, Idx);
} else if (ColNum == MaxColumns) { // Wrap lines.
// Wrap very long names even though we can't find a space.
if (!LastSpace)
LastSpace = i;
OutStr.insert(LastSpace, "\\l...");
ColNum = i - LastSpace;
LastSpace = 0;
i += 3; // The loop will advance 'i' again.
} else
++ColNum;
if (OutStr[i] == ' ')
LastSpace = i;
}
return OutStr;
return CompleteNodeLabelString(Node, HandleBasicBlock, HandleComment);
}
std::string getNodeLabel(const BasicBlock *Node, DOTFuncInfo *CFGInfo) {

View File

@ -0,0 +1,92 @@
//===-- MachineCFGPrinter.h -------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/CFGPrinter.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/Support/DOTGraphTraits.h"
namespace llvm {
template <class GraphType> struct GraphTraits;
class DOTMachineFuncInfo {
private:
const MachineFunction *F;
public:
DOTMachineFuncInfo(const MachineFunction *F) : F(F) {}
const MachineFunction *getFunction() const { return this->F; }
};
template <>
struct GraphTraits<DOTMachineFuncInfo *>
: public GraphTraits<const MachineBasicBlock *> {
static NodeRef getEntryNode(DOTMachineFuncInfo *CFGInfo) {
return &(CFGInfo->getFunction()->front());
}
// nodes_iterator/begin/end - Allow iteration over all nodes in the graph
using nodes_iterator = pointer_iterator<MachineFunction::const_iterator>;
static nodes_iterator nodes_begin(DOTMachineFuncInfo *CFGInfo) {
return nodes_iterator(CFGInfo->getFunction()->begin());
}
static nodes_iterator nodes_end(DOTMachineFuncInfo *CFGInfo) {
return nodes_iterator(CFGInfo->getFunction()->end());
}
static size_t size(DOTMachineFuncInfo *CFGInfo) {
return CFGInfo->getFunction()->size();
}
};
template <>
struct DOTGraphTraits<DOTMachineFuncInfo *> : public DefaultDOTGraphTraits {
DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
static void eraseComment(std::string &OutStr, unsigned &I, unsigned Idx) {
OutStr.erase(OutStr.begin() + I, OutStr.begin() + Idx);
--I;
}
static std::string getSimpleNodeLabel(const MachineBasicBlock *Node,
DOTMachineFuncInfo *) {
return SimpleNodeLabelString(Node);
}
static std::string getCompleteNodeLabel(
const MachineBasicBlock *Node, DOTMachineFuncInfo *,
function_ref<void(raw_string_ostream &, const MachineBasicBlock &)>
HandleBasicBlock =
[](raw_string_ostream &OS,
const MachineBasicBlock &Node) -> void { OS << Node; },
function_ref<void(std::string &, unsigned &, unsigned)>
HandleComment = eraseComment) {
return CompleteNodeLabelString(Node, HandleBasicBlock, HandleComment);
}
std::string getNodeLabel(const MachineBasicBlock *Node,
DOTMachineFuncInfo *CFGInfo) {
if (isSimple())
return getSimpleNodeLabel(Node, CFGInfo);
return getCompleteNodeLabel(Node, CFGInfo);
}
static std::string getGraphName(DOTMachineFuncInfo *CFGInfo) {
return "Machine CFG for '" + CFGInfo->getFunction()->getName().str() +
"' function";
}
};
} // namespace llvm

View File

@ -159,6 +159,7 @@ DUMMY_MACHINE_FUNCTION_PASS("reg-usage-collector", RegUsageInfoCollectorPass, ()
DUMMY_MACHINE_FUNCTION_PASS("funclet-layout", FuncletLayoutPass, ())
DUMMY_MACHINE_FUNCTION_PASS("stackmap-liveness", StackMapLivenessPass, ())
DUMMY_MACHINE_FUNCTION_PASS("removeredundantdebugvalues", RemoveRedundantDebugValuesPass, ())
DUMMY_MACHINE_FUNCTION_PASS("dot-machine-cfg", MachineCFGPrinter, ())
DUMMY_MACHINE_FUNCTION_PASS("livedebugvalues", LiveDebugValuesPass, ())
DUMMY_MACHINE_FUNCTION_PASS("early-tailduplication", EarlyTailDuplicatePass, ())
DUMMY_MACHINE_FUNCTION_PASS("opt-phis", OptimizePHIsPass, ())

View File

@ -408,6 +408,9 @@ namespace llvm {
/// RemoveRedundantDebugValues pass.
extern char &RemoveRedundantDebugValuesID;
/// MachineCFGPrinter pass.
extern char &MachineCFGPrinterID;
/// LiveDebugValues pass
extern char &LiveDebugValuesID;

View File

@ -277,6 +277,7 @@ void initializeMachineBlockFrequencyInfoPass(PassRegistry&);
void initializeMachineBlockPlacementPass(PassRegistry&);
void initializeMachineBlockPlacementStatsPass(PassRegistry&);
void initializeMachineBranchProbabilityInfoPass(PassRegistry&);
void initializeMachineCFGPrinterPass(PassRegistry &);
void initializeMachineCSEPass(PassRegistry&);
void initializeMachineCombinerPass(PassRegistry&);
void initializeMachineCopyPropagationPass(PassRegistry&);

View File

@ -101,6 +101,7 @@ add_llvm_component_library(LLVMCodeGen
MachineBlockFrequencyInfo.cpp
MachineBlockPlacement.cpp
MachineBranchProbabilityInfo.cpp
MachineCFGPrinter.cpp
MachineCombiner.cpp
MachineCopyPropagation.cpp
MachineCSE.cpp

View File

@ -69,6 +69,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeMachineBlockFrequencyInfoPass(Registry);
initializeMachineBlockPlacementPass(Registry);
initializeMachineBlockPlacementStatsPass(Registry);
initializeMachineCFGPrinterPass(Registry);
initializeMachineCSEPass(Registry);
initializeMachineCombinerPass(Registry);
initializeMachineCopyPropagationPass(Registry);

View File

@ -0,0 +1,95 @@
//===- MachineCFGPrinter.cpp - DOT Printer for Machine Functions ----------===//
//
// 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 the `-dot-machine-cfg` analysis pass, which emits
// Machine Function in DOT format in file titled `<prefix>.<function-name>.dot.
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/MachineCFGPrinter.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/GraphWriter.h"
using namespace llvm;
#define DEBUG_TYPE "dot-machine-cfg"
static cl::opt<std::string>
MCFGFuncName("mcfg-func-name", cl::Hidden,
cl::desc("The name of a function (or its substring)"
" whose CFG is viewed/printed."));
static cl::opt<std::string> MCFGDotFilenamePrefix(
"mcfg-dot-filename-prefix", cl::Hidden,
cl::desc("The prefix used for the Machine CFG dot file names."));
static cl::opt<bool>
CFGOnly("dot-mcfg-only", cl::init(false), cl::Hidden,
cl::desc("Print only the CFG without blocks body"));
static void writeMCFGToDotFile(MachineFunction &MF) {
std::string Filename =
(MCFGDotFilenamePrefix + "." + MF.getName() + ".dot").str();
errs() << "Writing '" << Filename << "'...";
std::error_code EC;
raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
DOTMachineFuncInfo MCFGInfo(&MF);
if (!EC)
WriteGraph(File, &MCFGInfo, CFGOnly);
else
errs() << " error opening file for writing!";
errs() << '\n';
}
namespace {
class MachineCFGPrinter : public MachineFunctionPass {
public:
static char ID;
MachineCFGPrinter();
bool runOnMachineFunction(MachineFunction &MF) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
MachineFunctionPass::getAnalysisUsage(AU);
}
};
} // namespace
char MachineCFGPrinter::ID = 0;
char &llvm::MachineCFGPrinterID = MachineCFGPrinter::ID;
INITIALIZE_PASS(MachineCFGPrinter, DEBUG_TYPE, "Machine CFG Printer Pass",
false, true)
/// Default construct and initialize the pass.
MachineCFGPrinter::MachineCFGPrinter() : MachineFunctionPass(ID) {
initializeMachineCFGPrinterPass(*PassRegistry::getPassRegistry());
}
bool MachineCFGPrinter::runOnMachineFunction(MachineFunction &MF) {
if (!MCFGFuncName.empty() && !MF.getName().contains(MCFGFuncName))
return false;
errs() << "Writing Machine CFG for function ";
errs().write_escaped(MF.getName()) << '\n';
writeMCFGToDotFile(MF);
return false;
}

View File

@ -0,0 +1,24 @@
# RUN: llc -mtriple=amdgcn-- -run-pass=dot-machine-cfg -mcfg-dot-filename-prefix=%t -mcfg-func-name=func2 -o - %s 2>&1 > /dev/null
# RUN: FileCheck %s -input-file=%t.func2.dot --check-prefix=MCFG
# MCFG-NOT: digraph "Machine CFG for 'func1' function"
name: func1
body: |
bb.0:
$sgpr0 = S_LOAD_DWORD_IMM $sgpr10_sgpr11, 0, 0
$sgpr1 = S_LOAD_DWORD_IMM $sgpr12_sgpr13, 0, 0
S_ENDPGM 0
...
# MCFG: digraph "Machine CFG for 'func2' function"
# MCFG-NEXT: label="Machine CFG for 'func2' function"
# MCFG: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.0:bb.0:\l $sgpr0 = S_LOAD_DWORD_IMM $sgpr12_sgpr13, 0, 0\l $sgpr1 = S_LOAD_DWORD_IMM $sgpr6_sgpr7, 0, 0\l $sgpr2 = S_LOAD_DWORD_IMM $sgpr14_sgpr15, 0, 0\l S_ENDPGM 0\l}"];
---
name: func2
body: |
bb.0:
$sgpr0 = S_LOAD_DWORD_IMM $sgpr12_sgpr13, 0, 0
$sgpr1 = S_LOAD_DWORD_IMM $sgpr6_sgpr7, 0, 0
$sgpr2 = S_LOAD_DWORD_IMM $sgpr14_sgpr15, 0, 0
S_ENDPGM 0
...

View File

@ -0,0 +1,87 @@
# RUN: llc -mtriple=amdgcn-- -run-pass=dot-machine-cfg -mcfg-dot-filename-prefix=%t -o - %s 2>&1 > /dev/null
# RUN: FileCheck %s -input-file=%t.irreducible.dot --check-prefix=MCFG
# RUN: llc -mtriple=amdgcn-- -run-pass=dot-machine-cfg -mcfg-dot-filename-prefix=%t -dot-mcfg-only -o - %s 2>&1 > /dev/null
# RUN: FileCheck %s -input-file=%t.irreducible.dot --check-prefix=MCFG-ONLY
# MCFG: digraph "Machine CFG for 'irreducible' function"
# MCFG-NEXT: label="Machine CFG for 'irreducible' function"
# MCFG: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.0:bb.0:\l successors: %bb.1(0x40000000), %bb.2(0x40000000)\l liveins: $vgpr0, $vgpr1, $vgpr2, $sgpr4_sgpr5, $sgpr6_sgpr7, $sgpr8_sgpr9,\l... $sgpr10_sgpr11, $sgpr14, $sgpr15, $sgpr16\l %0:sreg_32 = IMPLICIT_DEF\l %1:vgpr_32 = COPY $vgpr0\l %2:vgpr_32 = V_MOV_B32_e32 0, implicit $exec\l S_CMP_EQ_U32 %0:sreg_32, 0, implicit-def $scc\l S_CBRANCH_SCC1 %bb.1, implicit $scc\l S_BRANCH %bb.2\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.1:bb.1:\l\l successors: %bb.3(0x80000000)\l\l %3:vgpr_32 = PHI %2:vgpr_32, %bb.0, %4:vgpr_32, %bb.5\l %5:vgpr_32 = V_ADD_U32_e64 %3:vgpr_32, 1, 0, implicit $exec\l S_BRANCH %bb.3\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.2:bb.2:\l\l successors: %bb.3(0x80000000)\l\l %6:vgpr_32 = PHI %2:vgpr_32, %bb.0, %4:vgpr_32, %bb.4\l %7:vgpr_32 = V_ADD_U32_e64 %6:vgpr_32, 2, 0, implicit $exec\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.3:bb.3:\l\l successors: %bb.4(0x80000000)\l\l %4:vgpr_32 = PHI %5:vgpr_32, %bb.1, %7:vgpr_32, %bb.2\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.4:bb.4:\l\l successors: %bb.2(0x40000000), %bb.5(0x40000000)\l\l %8:vgpr_32 = V_AND_B32_e32 3, %1:vgpr_32, implicit $exec\l %9:sreg_64 = V_CMP_EQ_U32_e64 %8:vgpr_32, 2, implicit $exec\l %10:sreg_64 = SI_IF killed %9:sreg_64, %bb.2, implicit-def dead $exec,\l... implicit-def dead $scc, implicit $exec\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.5:bb.5:\l\l successors: %bb.1(0x40000000), %bb.6(0x40000000)\l\l %11:sreg_64 = V_CMP_EQ_U32_e64 %8:vgpr_32, 1, implicit $exec\l %12:sreg_64 = SI_IF killed %11:sreg_64, %bb.1, implicit-def dead $exec,\l... implicit-def dead $scc, implicit $exec\l}"];
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.6:bb.6:\l\l\l S_ENDPGM 0\l}"];
# MCFG-ONLY: digraph "Machine CFG for 'irreducible' function"
# MCFG-ONLY-NEXT: label="Machine CFG for 'irreducible' function"
# MCFG-ONLY: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.0}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.1}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.2}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.3}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.4}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.5}"];
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} -> Node{{[0-9A-Za-z]*}};
# MCFG-ONLY-NEXT: Node{{[0-9A-Za-z]*}} [shape=record,label="{%bb.6}"];
---
name: irreducible
tracksRegLiveness: true
machineFunctionInfo:
isEntryFunction: true
body: |
bb.0:
successors: %bb.1, %bb.2
liveins: $vgpr0, $vgpr1, $vgpr2, $sgpr4_sgpr5, $sgpr6_sgpr7, $sgpr8_sgpr9, $sgpr10_sgpr11, $sgpr14, $sgpr15, $sgpr16
%0:sreg_32 = IMPLICIT_DEF
%2:vgpr_32 = COPY $vgpr0
%3:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
S_CMP_EQ_U32 %0, 0, implicit-def $scc
S_CBRANCH_SCC1 %bb.1, implicit $scc
S_BRANCH %bb.2
bb.1:
%28:vgpr_32 = PHI %3, %bb.0, %49, %bb.5
%29:vgpr_32 = V_ADD_U32_e64 %28, 1, 0, implicit $exec
S_BRANCH %bb.3
bb.2:
%38:vgpr_32 = PHI %3, %bb.0, %49, %bb.4
%39:vgpr_32 = V_ADD_U32_e64 %38, 2, 0, implicit $exec
bb.3:
%49:vgpr_32 = PHI %29, %bb.1, %39, %bb.2
bb.4:
successors: %bb.2, %bb.5
%50:vgpr_32 = V_AND_B32_e32 3, %2, implicit $exec
%51:sreg_64 = V_CMP_EQ_U32_e64 %50, 2, implicit $exec
%52:sreg_64 = SI_IF killed %51:sreg_64, %bb.2, implicit-def dead $exec, implicit-def dead $scc, implicit $exec
bb.5:
successors: %bb.1, %bb.6
%61:sreg_64 = V_CMP_EQ_U32_e64 %50, 1, implicit $exec
%62:sreg_64 = SI_IF killed %61:sreg_64, %bb.1, implicit-def dead $exec, implicit-def dead $scc, implicit $exec
bb.6:
S_ENDPGM 0
...

View File

@ -0,0 +1,2 @@
if not 'AMDGPU' in config.root.targets:
config.unsupported = True