Index: llvm/include/llvm/CodeGen/MachineCFGPrinter.h =================================================================== --- /dev/null +++ llvm/include/llvm/CodeGen/MachineCFGPrinter.h @@ -0,0 +1,137 @@ +//===-- 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/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/Support/DOTGraphTraits.h" + +namespace llvm { + +template struct GraphTraits; +class DOTFuncInfo { +private: + const MachineFunction *F; + +public: + DOTFuncInfo(const MachineFunction *F) : F(F) {} + + const MachineFunction *getFunction() const { return this->F; } +}; + +template <> +struct GraphTraits + : public GraphTraits { + static NodeRef getEntryNode(DOTFuncInfo *CFGInfo) { + return &(CFGInfo->getFunction()->front()); + } + + // nodes_iterator/begin/end - Allow iteration over all nodes in the graph + using nodes_iterator = pointer_iterator; + + static nodes_iterator nodes_begin(DOTFuncInfo *CFGInfo) { + return nodes_iterator(CFGInfo->getFunction()->begin()); + } + + static nodes_iterator nodes_end(DOTFuncInfo *CFGInfo) { + return nodes_iterator(CFGInfo->getFunction()->end()); + } + + static size_t size(DOTFuncInfo *CFGInfo) { + return CFGInfo->getFunction()->size(); + } +}; + +template <> +struct DOTGraphTraits : public DefaultDOTGraphTraits { + + DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} + + static std::string getSimpleNodeLabel(const MachineBasicBlock *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 getCompleteNodeLabel( + const MachineBasicBlock *Node, DOTFuncInfo *, + llvm::function_ref + HandleMachineBasicBlock = + [](raw_string_ostream &OS, + const MachineBasicBlock &Node) -> void { OS << Node; }, + llvm::function_ref + HandleComment = eraseComment) { + + enum { MaxColumns = 80 }; + std::string Str; + raw_string_ostream OS(Str); + + if (Node->getName().empty()) { + Node->printAsOperand(OS, false); + OS << ":"; + } + + HandleMachineBasicBlock(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; + } + std::string getNodeLabel(const MachineBasicBlock *Node, + DOTFuncInfo *CFGInfo) { + if (isSimple()) + return getSimpleNodeLabel(Node, CFGInfo); + + return getCompleteNodeLabel(Node, CFGInfo); + } + + static std::string getGraphName(DOTFuncInfo *CFGInfo) { + return "Machine CFG for '" + CFGInfo->getFunction()->getName().str() + + "' function"; + } +}; +} // namespace llvm Index: llvm/include/llvm/CodeGen/MachinePassRegistry.def =================================================================== --- llvm/include/llvm/CodeGen/MachinePassRegistry.def +++ llvm/include/llvm/CodeGen/MachinePassRegistry.def @@ -159,6 +159,7 @@ 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, ()) Index: llvm/include/llvm/CodeGen/Passes.h =================================================================== --- llvm/include/llvm/CodeGen/Passes.h +++ llvm/include/llvm/CodeGen/Passes.h @@ -408,6 +408,9 @@ /// RemoveRedundantDebugValues pass. extern char &RemoveRedundantDebugValuesID; + /// MachineCFGPrinter pass. + extern char &MachineCFGPrinterID; + /// LiveDebugValues pass extern char &LiveDebugValuesID; Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -277,6 +277,7 @@ void initializeMachineBlockPlacementPass(PassRegistry&); void initializeMachineBlockPlacementStatsPass(PassRegistry&); void initializeMachineBranchProbabilityInfoPass(PassRegistry&); +void initializeMachineCFGPrinterPass(PassRegistry &); void initializeMachineCSEPass(PassRegistry&); void initializeMachineCombinerPass(PassRegistry&); void initializeMachineCopyPropagationPass(PassRegistry&); Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -101,6 +101,7 @@ MachineBlockFrequencyInfo.cpp MachineBlockPlacement.cpp MachineBranchProbabilityInfo.cpp + MachineCFGPrinter.cpp MachineCombiner.cpp MachineCopyPropagation.cpp MachineCSE.cpp Index: llvm/lib/CodeGen/CodeGen.cpp =================================================================== --- llvm/lib/CodeGen/CodeGen.cpp +++ llvm/lib/CodeGen/CodeGen.cpp @@ -69,6 +69,7 @@ initializeMachineBlockFrequencyInfoPass(Registry); initializeMachineBlockPlacementPass(Registry); initializeMachineBlockPlacementStatsPass(Registry); + initializeMachineCFGPrinterPass(Registry); initializeMachineCSEPass(Registry); initializeMachineCombinerPass(Registry); initializeMachineCopyPropagationPass(Registry); Index: llvm/lib/CodeGen/MachineCFGPrinter.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/MachineCFGPrinter.cpp @@ -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 `..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 + MCFGFuncName("mcfg-func-name", cl::Hidden, + cl::desc("The name of a function (or its substring)" + " whose CFG is viewed/printed.")); + +static cl::opt MCFGDotFilenamePrefix( + "mcfg-dot-filename-prefix", cl::Hidden, + cl::desc("The prefix used for the Machine CFG dot file names.")); + +static cl::opt + 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); + + DOTFuncInfo 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; +} Index: llvm/test/Analysis/DotMachineCFG/AMDGPU/functions.mir =================================================================== --- /dev/null +++ llvm/test/Analysis/DotMachineCFG/AMDGPU/functions.mir @@ -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 +... Index: llvm/test/Analysis/DotMachineCFG/AMDGPU/irreducible.mir =================================================================== --- /dev/null +++ llvm/test/Analysis/DotMachineCFG/AMDGPU/irreducible.mir @@ -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); %bb.1(50.00%),\l... %bb.2(50.00%)\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; predecessors: %bb.0, %bb.5\l successors: %bb.3(0x80000000); %bb.3(100.00%)\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; predecessors: %bb.0, %bb.4\l successors: %bb.3(0x80000000); %bb.3(100.00%)\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; predecessors: %bb.1, %bb.2\l successors: %bb.4(0x80000000); %bb.4(100.00%)\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; predecessors: %bb.3\l successors: %bb.2(0x40000000), %bb.5(0x40000000); %bb.2(50.00%),\l... %bb.5(50.00%)\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; predecessors: %bb.4\l successors: %bb.1(0x40000000), %bb.6(0x40000000); %bb.1(50.00%),\l... %bb.6(50.00%)\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; predecessors: %bb.5\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 +... + Index: llvm/test/Analysis/DotMachineCFG/AMDGPU/lit.local.cfg =================================================================== --- /dev/null +++ llvm/test/Analysis/DotMachineCFG/AMDGPU/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'AMDGPU' in config.root.targets: + config.unsupported = True