-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:
parent
e500f8f8a8
commit
32a8260ccc
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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, ())
|
||||
|
|
|
@ -408,6 +408,9 @@ namespace llvm {
|
|||
/// RemoveRedundantDebugValues pass.
|
||||
extern char &RemoveRedundantDebugValuesID;
|
||||
|
||||
/// MachineCFGPrinter pass.
|
||||
extern char &MachineCFGPrinterID;
|
||||
|
||||
/// LiveDebugValues pass
|
||||
extern char &LiveDebugValuesID;
|
||||
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -101,6 +101,7 @@ add_llvm_component_library(LLVMCodeGen
|
|||
MachineBlockFrequencyInfo.cpp
|
||||
MachineBlockPlacement.cpp
|
||||
MachineBranchProbabilityInfo.cpp
|
||||
MachineCFGPrinter.cpp
|
||||
MachineCombiner.cpp
|
||||
MachineCopyPropagation.cpp
|
||||
MachineCSE.cpp
|
||||
|
|
|
@ -69,6 +69,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
|
|||
initializeMachineBlockFrequencyInfoPass(Registry);
|
||||
initializeMachineBlockPlacementPass(Registry);
|
||||
initializeMachineBlockPlacementStatsPass(Registry);
|
||||
initializeMachineCFGPrinterPass(Registry);
|
||||
initializeMachineCSEPass(Registry);
|
||||
initializeMachineCombinerPass(Registry);
|
||||
initializeMachineCopyPropagationPass(Registry);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
if not 'AMDGPU' in config.root.targets:
|
||||
config.unsupported = True
|
Loading…
Reference in New Issue