diff --git a/llvm/include/llvm/CodeGen/MachineCFGPrinter.h b/llvm/include/llvm/CodeGen/MachineCFGPrinter.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachineCFGPrinter.h @@ -0,0 +1,138 @@ +//===-- 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); + else + return getCompleteNodeLabel(Node, CFGInfo); + } + + static std::string getGraphName(DOTFuncInfo *CFGInfo) { + return "Machine CFG for '" + CFGInfo->getFunction()->getName().str() + + "' function"; + } +}; +} // namespace llvm diff --git a/llvm/include/llvm/CodeGen/MachinePassRegistry.def b/llvm/include/llvm/CodeGen/MachinePassRegistry.def --- a/llvm/include/llvm/CodeGen/MachinePassRegistry.def +++ b/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, ()) diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/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; diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/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&); diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -101,6 +101,7 @@ MachineBlockFrequencyInfo.cpp MachineBlockPlacement.cpp MachineBranchProbabilityInfo.cpp + MachineCFGPrinter.cpp MachineCombiner.cpp MachineCopyPropagation.cpp MachineCSE.cpp diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -69,6 +69,7 @@ initializeMachineBlockFrequencyInfoPass(Registry); initializeMachineBlockPlacementPass(Registry); initializeMachineBlockPlacementStatsPass(Registry); + initializeMachineCFGPrinterPass(Registry); initializeMachineCSEPass(Registry); initializeMachineCombinerPass(Registry); initializeMachineCopyPropagationPass(Registry); diff --git a/llvm/lib/CodeGen/MachineCFGPrinter.cpp b/llvm/lib/CodeGen/MachineCFGPrinter.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MachineCFGPrinter.cpp @@ -0,0 +1,91 @@ +//===- MachineCFGPrinter.cpp - Pass to print machine function to a dot file +//--===// +// +// 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/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; +} diff --git a/llvm/test/Analysis/DotMachineCFG/AMDGPU/irreducible.mir b/llvm/test/Analysis/DotMachineCFG/AMDGPU/irreducible.mir new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/DotMachineCFG/AMDGPU/irreducible.mir @@ -0,0 +1,138 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=amdgcn-- -run-pass=dot-machine-cfg -o - %s 2>&1 | FileCheck %s --check-prefixes=MCFG +# RUN: llc -mtriple=amdgcn-- -run-pass=dot-machine-cfg -dot-mcfg-only -o - %s 2>&1 | FileCheck %s --check-prefixes=MCFG-ONLY +--- +name: irreducible +tracksRegLiveness: true +machineFunctionInfo: + isEntryFunction: true +body: | + ; MCFG-LABEL: name: irreducible + ; MCFG: bb.0: + ; MCFG-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; MCFG-NEXT: liveins: $vgpr0, $vgpr1, $vgpr2, $sgpr4_sgpr5, $sgpr6_sgpr7, $sgpr8_sgpr9, $sgpr10_sgpr11, $sgpr14, $sgpr15, $sgpr16 + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[DEF:%[0-9]+]]:sreg_32 = IMPLICIT_DEF + ; MCFG-NEXT: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr0 + ; MCFG-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; MCFG-NEXT: S_CMP_EQ_U32 [[DEF]], 0, implicit-def $scc + ; MCFG-NEXT: S_CBRANCH_SCC1 %bb.1, implicit $scc + ; MCFG-NEXT: S_BRANCH %bb.2 + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.1: + ; MCFG-NEXT: successors: %bb.3(0x80000000) + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[PHI:%[0-9]+]]:vgpr_32 = PHI [[V_MOV_B32_e32_]], %bb.0, %4, %bb.5 + ; MCFG-NEXT: [[V_ADD_U32_e64_:%[0-9]+]]:vgpr_32 = V_ADD_U32_e64 [[PHI]], 1, 0, implicit $exec + ; MCFG-NEXT: S_BRANCH %bb.3 + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.2: + ; MCFG-NEXT: successors: %bb.3(0x80000000) + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[PHI1:%[0-9]+]]:vgpr_32 = PHI [[V_MOV_B32_e32_]], %bb.0, %4, %bb.4 + ; MCFG-NEXT: [[V_ADD_U32_e64_1:%[0-9]+]]:vgpr_32 = V_ADD_U32_e64 [[PHI1]], 2, 0, implicit $exec + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.3: + ; MCFG-NEXT: successors: %bb.4(0x80000000) + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[PHI2:%[0-9]+]]:vgpr_32 = PHI [[V_ADD_U32_e64_]], %bb.1, [[V_ADD_U32_e64_1]], %bb.2 + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.4: + ; MCFG-NEXT: successors: %bb.2(0x40000000), %bb.5(0x40000000) + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[V_AND_B32_e32_:%[0-9]+]]:vgpr_32 = V_AND_B32_e32 3, [[COPY]], implicit $exec + ; MCFG-NEXT: [[V_CMP_EQ_U32_e64_:%[0-9]+]]:sreg_64 = V_CMP_EQ_U32_e64 [[V_AND_B32_e32_]], 2, implicit $exec + ; MCFG-NEXT: [[SI_IF:%[0-9]+]]:sreg_64 = SI_IF killed [[V_CMP_EQ_U32_e64_]], %bb.2, implicit-def dead $exec, implicit-def dead $scc, implicit $exec + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.5: + ; MCFG-NEXT: successors: %bb.1(0x40000000), %bb.6(0x40000000) + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: [[V_CMP_EQ_U32_e64_1:%[0-9]+]]:sreg_64 = V_CMP_EQ_U32_e64 [[V_AND_B32_e32_]], 1, implicit $exec + ; MCFG-NEXT: [[SI_IF1:%[0-9]+]]:sreg_64 = SI_IF killed [[V_CMP_EQ_U32_e64_1]], %bb.1, implicit-def dead $exec, implicit-def dead $scc, implicit $exec + ; MCFG-NEXT: {{ $}} + ; MCFG-NEXT: bb.6: + ; MCFG-NEXT: S_ENDPGM 0 + ; MCFG-ONLY-LABEL: name: irreducible + ; MCFG-ONLY: bb.0: + ; MCFG-ONLY-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; MCFG-ONLY-NEXT: liveins: $vgpr0, $vgpr1, $vgpr2, $sgpr4_sgpr5, $sgpr6_sgpr7, $sgpr8_sgpr9, $sgpr10_sgpr11, $sgpr14, $sgpr15, $sgpr16 + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[DEF:%[0-9]+]]:sreg_32 = IMPLICIT_DEF + ; MCFG-ONLY-NEXT: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr0 + ; MCFG-ONLY-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; MCFG-ONLY-NEXT: S_CMP_EQ_U32 [[DEF]], 0, implicit-def $scc + ; MCFG-ONLY-NEXT: S_CBRANCH_SCC1 %bb.1, implicit $scc + ; MCFG-ONLY-NEXT: S_BRANCH %bb.2 + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.1: + ; MCFG-ONLY-NEXT: successors: %bb.3(0x80000000) + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[PHI:%[0-9]+]]:vgpr_32 = PHI [[V_MOV_B32_e32_]], %bb.0, %4, %bb.5 + ; MCFG-ONLY-NEXT: [[V_ADD_U32_e64_:%[0-9]+]]:vgpr_32 = V_ADD_U32_e64 [[PHI]], 1, 0, implicit $exec + ; MCFG-ONLY-NEXT: S_BRANCH %bb.3 + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.2: + ; MCFG-ONLY-NEXT: successors: %bb.3(0x80000000) + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[PHI1:%[0-9]+]]:vgpr_32 = PHI [[V_MOV_B32_e32_]], %bb.0, %4, %bb.4 + ; MCFG-ONLY-NEXT: [[V_ADD_U32_e64_1:%[0-9]+]]:vgpr_32 = V_ADD_U32_e64 [[PHI1]], 2, 0, implicit $exec + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.3: + ; MCFG-ONLY-NEXT: successors: %bb.4(0x80000000) + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[PHI2:%[0-9]+]]:vgpr_32 = PHI [[V_ADD_U32_e64_]], %bb.1, [[V_ADD_U32_e64_1]], %bb.2 + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.4: + ; MCFG-ONLY-NEXT: successors: %bb.2(0x40000000), %bb.5(0x40000000) + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[V_AND_B32_e32_:%[0-9]+]]:vgpr_32 = V_AND_B32_e32 3, [[COPY]], implicit $exec + ; MCFG-ONLY-NEXT: [[V_CMP_EQ_U32_e64_:%[0-9]+]]:sreg_64 = V_CMP_EQ_U32_e64 [[V_AND_B32_e32_]], 2, implicit $exec + ; MCFG-ONLY-NEXT: [[SI_IF:%[0-9]+]]:sreg_64 = SI_IF killed [[V_CMP_EQ_U32_e64_]], %bb.2, implicit-def dead $exec, implicit-def dead $scc, implicit $exec + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.5: + ; MCFG-ONLY-NEXT: successors: %bb.1(0x40000000), %bb.6(0x40000000) + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: [[V_CMP_EQ_U32_e64_1:%[0-9]+]]:sreg_64 = V_CMP_EQ_U32_e64 [[V_AND_B32_e32_]], 1, implicit $exec + ; MCFG-ONLY-NEXT: [[SI_IF1:%[0-9]+]]:sreg_64 = SI_IF killed [[V_CMP_EQ_U32_e64_1]], %bb.1, implicit-def dead $exec, implicit-def dead $scc, implicit $exec + ; MCFG-ONLY-NEXT: {{ $}} + ; MCFG-ONLY-NEXT: bb.6: + ; MCFG-ONLY-NEXT: S_ENDPGM 0 + 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 +... + diff --git a/llvm/test/Analysis/DotMachineCFG/AMDGPU/lit.local.cfg b/llvm/test/Analysis/DotMachineCFG/AMDGPU/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/DotMachineCFG/AMDGPU/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'AMDGPU' in config.root.targets: + config.unsupported = True