25#include "llvm/ADT/DenseSet.h"
26#include "llvm/ADT/DepthFirstIterator.h"
27#include "llvm/ADT/DirectedGraph.h"
28#include "llvm/ADT/PostOrderIterator.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallVectorExtras.h"
31#include "llvm/ADT/TypeSwitch.h"
32#include "llvm/ADT/iterator_range.h"
33#include "llvm/Option/ArgList.h"
34#include "llvm/Support/Casting.h"
35#include "llvm/Support/GraphWriter.h"
36#include "llvm/Support/JSON.h"
37#include "llvm/Support/Path.h"
38#include "llvm/Support/PrettyStackTrace.h"
39#include "llvm/Support/ThreadPool.h"
40#include "llvm/Support/VirtualFileSystem.h"
51static bool fromJSON(
const llvm::json::Value &Params,
54 llvm::json::ObjectMapper O(Params, P);
55 return O.mapOptional(
"system-include-directories",
59static bool fromJSON(
const llvm::json::Value &Params,
62 llvm::json::ObjectMapper O(Params, P);
63 return O.map(
"is-std-library", ModuleEntry.
IsStdlib) &&
65 O.map(
"source-path", ModuleEntry.
SourcePath) &&
66 O.mapOptional(
"local-arguments", ModuleEntry.
LocalArgs);
69static bool fromJSON(
const llvm::json::Value &Params,
71 llvm::json::ObjectMapper O(Params, P);
72 return O.map(
"modules", Manifest.
Modules);
78 auto ParsedOrErr = llvm::json::parse(Buffer);
80 return ParsedOrErr.takeError();
83 llvm::json::Path::Root Root;
84 if (!
fromJSON(*ParsedOrErr, Manifest, Root))
85 return Root.getError();
96 StringRef ManifestPath) {
97 StringRef ManifestDir = llvm::sys::path::parent_path(ManifestPath);
100 auto PrependManifestDir = [&](StringRef Path) {
101 TempPath = ManifestDir;
102 llvm::sys::path::append(TempPath, Path);
103 return std::string(TempPath);
106 for (
auto &Entry : ManifestEntries) {
107 Entry.SourcePath = PrependManifestDir(Entry.SourcePath);
108 if (!Entry.LocalArgs)
111 for (
auto &IncludeDir : Entry.LocalArgs->SystemIncludeDirs)
112 IncludeDir = PrependManifestDir(IncludeDir);
118 llvm::vfs::FileSystem &VFS) {
119 auto MemBufOrErr =
VFS.getBufferForFile(ManifestPath);
121 return llvm::createFileError(ManifestPath, MemBufOrErr.getError());
123 auto ManifestOrErr =
parseManifest((*MemBufOrErr)->getBuffer());
125 return ManifestOrErr.takeError();
126 auto Manifest = std::move(*ManifestOrErr);
135 DerivedArgList &Args =
C.getArgs();
136 const OptTable &Opts =
C.getDriver().getOpts();
137 for (
const auto &Entry : ManifestEntries) {
139 makeInputArg(Args, Opts, Args.MakeArgString(Entry.SourcePath));
140 Inputs.emplace_back(types::TY_CXXModule, InputArg);
145 llvm::DenseMap<StringRef, const StdModuleManifest::Module *>;
151 for (
auto &Entry : ManifestEntries) {
152 [[maybe_unused]]
const bool Inserted =
153 ManifestEntryBySource.try_emplace(Entry.SourcePath, &Entry).second;
155 "Manifest defines multiple modules with the same source path.");
157 return ManifestEntryBySource;
166 if (
const auto It = ManifestEntryBySource.find(II.getFilename());
167 It != ManifestEntryBySource.end())
176 ArgStringList &CC1Args,
179 const DerivedArgList &TCArgs =
183 for (
const auto &IncludeDir : SystemIncludeDirs)
196 for (
auto &Job : Jobs) {
204 auto CC1Args = Job->getArguments();
206 CC1Args.push_back(
"-Wno-reserved-module-identifier");
207 if (Entry->LocalArgs)
209 Entry->LocalArgs->SystemIncludeDirs);
210 Job->replaceArguments(CC1Args);
215static std::optional<std::string>
217 if (
const Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path))
218 return A->getValue();
221 return std::string(Path);
231 return !InputInfos.empty() &&
types::isSrcFile(InputInfos.front().getType());
237class ScanningWorkerPool {
240 deps::DependencyScanningService &ScanningService) {
242 Slots.emplace_back(ScanningService);
245 std::iota(AvailableSlots.begin(), AvailableSlots.end(), 0);
253 [[nodiscard]]
auto scopedAcquire() {
254 std::unique_lock<std::mutex> UL(Lock);
255 CV.wait(UL, [&] {
return !AvailableSlots.empty(); });
256 const size_t Index = AvailableSlots.pop_back_val();
257 auto ReleaseHandle = [
this, Index](WorkerBundle *) { release(Index); };
258 return std::unique_ptr<WorkerBundle, decltype(ReleaseHandle)>(
259 &Slots[Index], ReleaseHandle);
264 void release(
size_t Index) {
266 std::scoped_lock<std::mutex> SL(Lock);
267 AvailableSlots.push_back(Index);
273 struct WorkerBundle {
274 WorkerBundle(deps::DependencyScanningService &ScanningService)
275 : Worker(std::make_unique<deps::DependencyScanningWorker>(
278 std::unique_ptr<deps::DependencyScanningWorker> Worker;
279 llvm::DenseSet<deps::ModuleID> SeenModules;
283 std::condition_variable CV;
284 SmallVector<size_t> AvailableSlots;
285 SmallVector<WorkerBundle, 0> Slots;
291static std::pair<std::unique_ptr<llvm::ThreadPoolInterface>,
292 std::unique_ptr<ScanningWorkerPool>>
294 size_t NumScanInputs,
bool HasStdlibModuleInputs,
299#if LLVM_ENABLE_THREADS
300 std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
303 if (NumScanInputs == 1 || (HasStdlibModuleInputs && NumScanInputs <= 2)) {
304 auto S = llvm::optimal_concurrency(1);
305 ThreadPool = std::make_unique<llvm::SingleThreadExecutor>(std::move(S));
308 auto ThreadPoolStrategy = llvm::optimal_concurrency(
309 NumScanInputs -
static_cast<size_t>(HasStdlibModuleInputs));
310 ThreadPool = std::make_unique<llvm::DefaultThreadPool>(
311 std::move(ThreadPoolStrategy));
312 const size_t MaxConcurrency = ThreadPool->getMaxConcurrency();
313 const size_t MaxConcurrentlyScannedInputs =
315 (HasStdlibModuleInputs && NumScanInputs < MaxConcurrency ? 1 : 0);
316 WorkerCount = std::min(MaxConcurrency, MaxConcurrentlyScannedInputs);
319 auto ThreadPool = std::make_unique<llvm::SingleThreadExecutor>();
320 size_t WorkerCount = 1;
323 return {std::move(ThreadPool),
324 std::make_unique<ScanningWorkerPool>(WorkerCount, ScanningService)};
336struct StdlibModuleScanScheduler {
337 StdlibModuleScanScheduler(
const llvm::DenseMap<ModuleNameAndTriple, size_t>
338 &StdlibModuleScanIndexByID)
339 : StdlibModuleScanIndexByID(StdlibModuleScanIndexByID) {
340 ScheduledScanInputs.reserve(StdlibModuleScanIndexByID.size());
350 std::scoped_lock<std::mutex> Guard(Lock);
351 for (
const auto &ModuleName : NamedModuleDeps) {
352 const auto It = StdlibModuleScanIndexByID.find({ModuleName, Triple});
353 if (It == StdlibModuleScanIndexByID.end())
355 const size_t ScanIndex = It->second;
356 const bool AlreadyScheduled =
357 !ScheduledScanInputs.insert(ScanIndex).second;
358 if (AlreadyScheduled)
360 NewScanInputs.push_back(ScanIndex);
362 return NewScanInputs;
366 const llvm::DenseMap<ModuleNameAndTriple, size_t> &StdlibModuleScanIndexByID;
367 llvm::SmallDenseSet<size_t> ScheduledScanInputs;
377 this->LangOpts = &LangOpts;
383 StandaloneDiags.emplace_back(*LangOpts, StoredDiag);
388 return std::move(StandaloneDiags);
402class StandaloneDiagReporter {
408 OwnedFileMgr = llvm::makeIntrusiveRefCnt<FileManager>(std::move(Opts));
410 llvm::makeIntrusiveRefCnt<SourceManager>(Diags, *OwnedFileMgr);
417 llvm::StringMap<SourceLocation> SrcLocCache;
419 for (
const auto &StandaloneDiag : StandaloneDiags) {
421 getFileManager(), getSourceManager(), StandaloneDiag, SrcLocCache);
434 return *OwnedFileMgr;
450 StandaloneDiagReporter Reporter(Diags);
451 for (
auto &SingleScanDiags : AllScanDiags)
452 Reporter.Report(SingleScanDiags);
457 StringRef OutputDir) {
458 assert(!ID.ModuleName.empty() && !ID.ContextHash.empty() &&
459 "Invalid ModuleID!");
461 llvm::sys::path::append(ExplicitPCMPath, ID.ContextHash,
462 ID.ModuleName +
"-" + ID.ContextHash +
".pcm");
463 return std::string(ExplicitPCMPath);
471 ModuleLookupController(StringRef OutputDir) : OutputDir(OutputDir) {}
473 std::string lookupModuleOutput(
const deps::ModuleDeps &MD,
482 llvm::reportFatalInternalError(
483 "call to lookupModuleOutput with unexpected ModuleOutputKind");
486 std::unique_ptr<DependencyActionController> clone()
const override {
487 return std::make_unique<ModuleLookupController>(OutputDir);
495struct InputDependencies {
497 std::string ModuleName;
504 std::vector<deps::ModuleID> ClangModuleDeps;
510 std::vector<std::string> NamedModuleDeps;
514 std::vector<std::string> FileDeps;
518 std::vector<std::string> BuildArgs;
523 InputDependencies InputDeps;
524 InputDeps.ModuleName = std::move(TUDeps.ID.ModuleName);
525 InputDeps.NamedModuleDeps = std::move(TUDeps.NamedModuleDeps);
526 InputDeps.ClangModuleDeps = std::move(TUDeps.ClangModuleDeps);
527 InputDeps.FileDeps = std::move(TUDeps.FileDeps);
528 assert(TUDeps.Commands.size() == 1 &&
"Expected exactly one command");
529 InputDeps.BuildArgs = std::move(TUDeps.Commands.front().Arguments);
537 CommandLine.reserve(JobArgs.size() + 1);
539 for (
const char *Arg : JobArgs)
540 CommandLine.emplace_back(Arg);
548static std::pair<std::optional<deps::TranslationUnitDeps>,
551 StringRef WorkingDirectory,
552 ModuleLookupController &LookupController) {
553 StandaloneDiagCollector DiagConsumer;
554 std::optional<deps::TranslationUnitDeps> MaybeTUDeps;
558 auto WorkerBundleHandle = WorkerPool.scopedAcquire();
561 if (WorkerBundleHandle->Worker->computeDependencies(
562 WorkingDirectory, CC1CommandLine, DepConsumer, LookupController,
567 return {std::move(MaybeTUDeps), DiagConsumer.takeDiagnostics()};
571struct DependencyScanResult {
573 SmallVector<size_t> ScannedJobIndices;
576 SmallVector<InputDependencies, 0> InputDepsForScannedJobs;
580 SmallVector<deps::ModuleDepsGraph, 0> ModuleDepGraphsForScannedJobs;
583 SmallVector<size_t> UnusedStdlibModuleJobIndices;
586 SmallVector<size_t> NonScannableJobIndices;
598 ArrayRef<std::unique_ptr<Command>> Jobs,
599 llvm::DenseMap<StringRef, const StdModuleManifest::Module *> ManifestLookup,
600 StringRef ModuleCachePath, StringRef WorkingDirectory,
602 llvm::PrettyStackTraceString CrashInfo(
"Performing module dependency scan.");
607 for (
const auto &&[Index, Job] : llvm::enumerate(Jobs)) {
609 ScannableJobIndices.push_back(Index);
611 NonScannableJobIndices.push_back(Index);
618 llvm::DenseMap<ModuleNameAndTriple, size_t> StdlibModuleScanIndexByID;
619 for (
const auto &&[ScanIndex, JobIndex] :
620 llvm::enumerate(ScannableJobIndices)) {
621 const Command &ScanJob = *Jobs[JobIndex];
622 if (
const auto *Entry =
625 [[maybe_unused]]
const bool Inserted =
626 StdlibModuleScanIndexByID.try_emplace(ID, ScanIndex).second;
628 "Multiple jobs build the same module for the same triple.");
630 UserInputScanIndices.push_back(ScanIndex);
635 const size_t NumScanInputs = ScannableJobIndices.size();
636 const bool HasStdlibModuleInputs = !StdlibModuleScanIndexByID.empty();
641 std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
642 std::unique_ptr<ScanningWorkerPool> WorkerPool;
644 NumScanInputs, HasStdlibModuleInputs, ScanningService);
646 StdlibModuleScanScheduler StdlibModuleRegistry(StdlibModuleScanIndexByID);
647 ModuleLookupController LookupController(ModuleCachePath);
655 std::atomic<bool> HasError{
false};
660 ScanOneAndScheduleNew = [&](
size_t ScanIndex) {
661 const size_t JobIndex = ScannableJobIndices[ScanIndex];
662 const Command &Job = *Jobs[JobIndex];
664 Job, *WorkerPool, WorkingDirectory, LookupController);
668 assert(AllScanDiags[ScanIndex].empty() &&
669 "Each slot should be written to at most once.");
670 AllScanDiags[ScanIndex] = std::move(ScanDiags);
673 HasError.store(
true, std::memory_order_relaxed);
678 const auto NewScanInputs = StdlibModuleRegistry.getNewScanInputs(
679 MaybeTUDeps->NamedModuleDeps,
getTriple(Job));
680 for (
const size_t NewScanIndex : NewScanInputs)
682 [&, NewScanIndex]() { ScanOneAndScheduleNew(NewScanIndex); });
684 assert(!AllScanResults[ScanIndex].has_value() &&
685 "Each slot should be written to at most once.");
686 AllScanResults[ScanIndex] = std::move(MaybeTUDeps);
690 for (
const size_t ScanIndex : UserInputScanIndices)
691 ThreadPool->async([&ScanOneAndScheduleNew, ScanIndex]() {
692 ScanOneAndScheduleNew(ScanIndex);
697 if (HasError.load(std::memory_order_relaxed))
701 DependencyScanResult Result;
702 for (
auto &&[JobIndex, MaybeTUDeps] :
703 llvm::zip_equal(ScannableJobIndices, AllScanResults)) {
705 Result.ScannedJobIndices.push_back(JobIndex);
706 Result.ModuleDepGraphsForScannedJobs.push_back(
707 std::move(MaybeTUDeps->ModuleGraph));
708 Result.InputDepsForScannedJobs.push_back(
711 Result.UnusedStdlibModuleJobIndices.push_back(JobIndex);
713 Result.NonScannableJobIndices = std::move(NonScannableJobIndices);
716 llvm::SmallDenseSet<size_t> SeenJobIndices;
717 SeenJobIndices.insert_range(Result.ScannedJobIndices);
718 SeenJobIndices.insert_range(Result.UnusedStdlibModuleJobIndices);
719 SeenJobIndices.insert_range(Result.NonScannableJobIndices);
720 assert(llvm::all_of(llvm::index_range(0, Jobs.size()),
721 [&](
size_t JobIndex) {
722 return SeenJobIndices.contains(JobIndex);
724 "Scan result must partition all jobs");
733using CGNodeBase = llvm::DGNode<CGNode, CGEdge>;
734using CGEdgeBase = llvm::DGEdge<CGNode, CGEdge>;
735using CGBase = llvm::DirectedGraph<CGNode, CGEdge>;
738class CGNode :
public CGNodeBase {
740 enum class NodeKind {
749 CGNode(
const NodeKind K) : Kind(K) {}
750 CGNode(
const CGNode &) =
delete;
751 CGNode(CGNode &&) =
delete;
752 virtual ~CGNode() = 0;
754 NodeKind
getKind()
const {
return Kind; }
759CGNode::~CGNode() =
default;
768class RootNode :
public CGNode {
770 RootNode() : CGNode(
NodeKind::Root) {}
771 ~RootNode()
override =
default;
773 static bool classof(
const CGNode *N) {
774 return N->getKind() == NodeKind::Root;
779class JobNode :
public CGNode {
781 JobNode(std::unique_ptr<Command> &&Job, NodeKind Kind)
782 : CGNode(
Kind), Job(std::move(Job)) {
783 assert(this->Job &&
"Expected valid job!");
785 virtual ~JobNode()
override = 0;
787 std::unique_ptr<Command> Job;
789 static bool classof(
const CGNode *N) {
790 return N->getKind() != NodeKind::Root;
793JobNode::~JobNode() =
default;
796class ClangModuleJobNode :
public JobNode {
798 ClangModuleJobNode(std::unique_ptr<Command> &&Job, deps::ModuleDeps &&MD)
799 : JobNode(std::move(Job),
NodeKind::ClangModuleCC1Job),
801 ~ClangModuleJobNode()
override =
default;
805 static bool classof(
const CGNode *N) {
806 return N->getKind() == NodeKind::ClangModuleCC1Job;
811class ScannedJobNode :
public JobNode {
813 ScannedJobNode(std::unique_ptr<Command> &&Job, InputDependencies &&InputDeps,
815 : JobNode(std::move(Job),
Kind), InputDeps(std::move(InputDeps)) {}
816 ~ScannedJobNode()
override =
default;
818 InputDependencies InputDeps;
820 static bool classof(
const CGNode *N) {
821 return N->getKind() == NodeKind::NamedModuleCC1Job ||
822 N->getKind() == NodeKind::NonModuleCC1Job;
828class NamedModuleJobNode :
public ScannedJobNode {
830 NamedModuleJobNode(std::unique_ptr<Command> &&Job,
831 InputDependencies &&InputDeps)
832 : ScannedJobNode(std::move(Job), std::move(InputDeps),
834 ~NamedModuleJobNode()
override =
default;
836 static bool classof(
const CGNode *N) {
837 return N->getKind() == NodeKind::NamedModuleCC1Job;
843class NonModuleTUJobNode :
public ScannedJobNode {
845 NonModuleTUJobNode(std::unique_ptr<Command> &&Job,
846 InputDependencies &&InputDeps)
847 : ScannedJobNode(std::move(Job), std::move(InputDeps),
849 ~NonModuleTUJobNode()
override =
default;
851 static bool classof(
const CGNode *N) {
852 return N->getKind() == NodeKind::NonModuleCC1Job;
858class ImageJobNode :
public JobNode {
860 ImageJobNode(std::unique_ptr<Command> &&Job)
861 : JobNode(std::move(Job),
NodeKind::ImageJob) {}
862 ~ImageJobNode()
override =
default;
864 static bool classof(
const CGNode *N) {
865 return N->getKind() == NodeKind::ImageJob;
872class MiscJobNode :
public JobNode {
874 MiscJobNode(std::unique_ptr<Command> &&Job)
875 : JobNode(std::move(Job),
NodeKind::MiscJob) {}
876 ~MiscJobNode()
override =
default;
878 static bool classof(
const CGNode *N) {
879 return N->getKind() == NodeKind::MiscJob;
887class CGEdge :
public CGEdgeBase {
889 enum class EdgeKind {
895 CGEdge(CGNode &N, EdgeKind K) : CGEdgeBase(N), Kind(K) {}
897 EdgeKind
getKind()
const {
return Kind; }
908class CompilationGraph :
public CGBase {
910 CompilationGraph() =
default;
911 CompilationGraph(
const CompilationGraph &) =
delete;
912 CompilationGraph(CompilationGraph &&G) =
default;
914 CGNode &getRoot()
const {
915 assert(Root &&
"Root node has not yet been created!");
919 RootNode &createRoot() {
920 assert(!Root &&
"Root node has already been created!");
921 auto &RootRef = createNodeImpl<RootNode>();
926 template <
typename T,
typename... Args> T &createJobNode(Args &&...Arg) {
927 static_assert(std::is_base_of<JobNode, T>::value,
928 "T must be derived from JobNode");
929 return createNodeImpl<T>(std::forward<Args>(Arg)...);
932 CGEdge &createEdge(CGEdge::EdgeKind Kind, CGNode &Src, CGNode &Dst) {
933 auto Edge = std::make_unique<CGEdge>(Dst, Kind);
934 CGEdge &EdgeRef = *Edge;
935 AllEdges.push_back(std::move(Edge));
936 connect(Src, Dst, EdgeRef);
941 using CGBase::addNode;
942 using CGBase::connect;
944 template <
typename T,
typename... Args> T &createNodeImpl(Args &&...Arg) {
945 auto Node = std::make_unique<T>(std::forward<Args>(Arg)...);
947 AllNodes.push_back(std::move(Node));
952 CGNode *Root =
nullptr;
953 SmallVector<std::unique_ptr<CGNode>> AllNodes;
954 SmallVector<std::unique_ptr<CGEdge>> AllEdges;
964template <>
struct GraphTraits<CGNode *> {
989template <>
struct GraphTraits<CompilationGraph *> : GraphTraits<CGNode *> {
1003template <>
struct GraphTraits<const CGNode *> {
1007 return &E->getTargetNode();
1032struct GraphTraits<const CompilationGraph *> : GraphTraits<const CGNode *> {
1053 return "Module Dependency Graph";
1057 return "\tnode [shape=Mrecord, colorscheme=set23, style=filled];\n";
1068 return llvm::TypeSwitch<NodeRef, std::string>(N)
1069 .Case([](
const ClangModuleJobNode *ClangModuleNode) {
1070 const auto &ID = ClangModuleNode->MD.
ID;
1071 return llvm::formatv(
"{0}-{1}", ID.ModuleName, ID.ContextHash).str();
1073 .Case([](
const NamedModuleJobNode *NamedModuleNode) {
1074 return llvm::formatv(
"{0}-{1}", NamedModuleNode->InputDeps.ModuleName,
1078 .Case([](
const NonModuleTUJobNode *NonModuleTUNode) {
1079 const auto &Job = *NonModuleTUNode->Job;
1084 .DefaultUnreachable(
"Unexpected node kind! Is this node hidden?");
1088 return llvm::TypeSwitch<NodeRef, std::string>(N)
1089 .Case([](
const ClangModuleJobNode *ClangModuleNode) {
1090 const auto &ID = ClangModuleNode->MD.
ID;
1091 return llvm::formatv(
"Module type: Clang module \\| Module name: {0} "
1093 ID.ModuleName, ID.ContextHash)
1096 .Case([](
const NamedModuleJobNode *NamedModuleNode) {
1097 const auto &Job = *NamedModuleNode->Job;
1098 return llvm::formatv(
1099 "Filename: {0} \\| Module type: Named module \\| "
1100 "Module name: {1} \\| Triple: {2}",
1102 NamedModuleNode->InputDeps.ModuleName,
getTriple(Job))
1105 .Case([](
const NonModuleTUJobNode *NonModuleTUNode) {
1106 const auto &Job = *NonModuleTUNode->Job;
1107 return llvm::formatv(
"Filename: {0} \\| Triple: {1}",
1111 .DefaultUnreachable(
"Unexpected node kind! Is this node hidden?");
1115 switch (N->getKind()) {
1116 case CGNode::NodeKind::ClangModuleCC1Job:
1117 return "fillcolor=1";
1118 case CGNode::NodeKind::NamedModuleCC1Job:
1119 return "fillcolor=2";
1120 case CGNode::NodeKind::NonModuleCC1Job:
1121 return "fillcolor=3";
1123 llvm_unreachable(
"Unexpected node kind! Is this node hidden?");
1139 :
Base(O, G, IsSimple), EscapedIDByNodeRef(G->size()) {}
1142 auto IsNodeVisible = [&](NodeRef N) {
return !DTraits.isNodeHidden(N, G); };
1143 auto VisibleNodes = llvm::filter_to_vector(nodes(G), IsNodeVisible);
1145 writeNodeDefinitions(VisibleNodes);
1147 writeNodeRelations(VisibleNodes);
1151 using Base::DOTTraits;
1152 using Base::GTraits;
1153 using Base::NodeRef;
1156 for (NodeRef Node : VisibleNodes) {
1157 std::string EscapedNodeID =
1158 DOT::EscapeString(DTraits.getNodeIdentifier(Node, G));
1159 const std::string NodeLabel = DTraits.getNodeLabel(Node, G);
1160 const std::string NodeAttrs = DTraits.getNodeAttributes(Node, G);
1161 O <<
'\t' <<
'"' << EscapedNodeID <<
"\" [" << NodeAttrs <<
", label=\"{ "
1162 << DOT::EscapeString(NodeLabel) <<
" }\"];\n";
1163 EscapedIDByNodeRef.try_emplace(Node, std::move(EscapedNodeID));
1168 auto IsNodeVisible = [&](NodeRef N) {
return !DTraits.isNodeHidden(N, G); };
1169 for (NodeRef Node : VisibleNodes) {
1170 auto DstNodes = llvm::make_range(GTraits::child_begin(Node),
1171 GTraits::child_end(Node));
1172 auto VisibleDstNodes = llvm::make_filter_range(DstNodes, IsNodeVisible);
1173 StringRef EscapedSrcNodeID = EscapedIDByNodeRef.at(Node);
1174 for (NodeRef DstNode : VisibleDstNodes) {
1175 StringRef EscapedTgtNodeID = EscapedIDByNodeRef.at(DstNode);
1176 O <<
'\t' <<
'"' << EscapedSrcNodeID <<
"\" -> \"" << EscapedTgtNodeID
1182 DenseMap<NodeRef, std::string> EscapedIDByNodeRef;
1190 for (
const auto JobIndex : Indices) {
1191 assert(Jobs[JobIndex] &&
"Expected valid job!");
1192 Out.push_back(std::move(Jobs[JobIndex]));
1199 CompilationGraph &Graph,
1201 for (
auto &Job : NonScannableJobs) {
1202 if (Job->getCreator().isLinkJob())
1203 Graph.createJobNode<ImageJobNode>(std::move(Job));
1205 Graph.createJobNode<MiscJobNode>(std::move(Job));
1215 CompilationGraph &Graph,
1218 for (
auto &Job : UnusedStdlibModuleJobs) {
1219 auto &NewNode = Graph.createJobNode<MiscJobNode>(std::move(Job));
1220 StdlibModuleNodesToPrune.push_back(&NewNode);
1222 return StdlibModuleNodesToPrune;
1226static std::unique_ptr<CC1Command>
1229 DerivedArgList &Args =
C.getArgs();
1230 const OptTable &Opts =
C.getDriver().getOpts();
1231 Arg *InputArg =
makeInputArg(Args, Opts,
"<discovered clang module>");
1237 const auto &TCArgs =
C.getArgsForToolChain(&TC, PA->getOffloadingArch(),
1238 PA->getOffloadingDeviceKind());
1241 ArgStringList JobArgs;
1242 JobArgs.reserve(BuildArgs.size());
1243 for (
const auto &Arg : BuildArgs)
1244 JobArgs.push_back(TCArgs.MakeArgString(Arg));
1246 return std::make_unique<CC1Command>(
1248 C.getDriver().getClangProgramPath(), JobArgs,
1261 ArrayRef<std::unique_ptr<Command>> ImportingJobs,
1263 llvm::DenseSet<deps::ModuleID> AlreadySeen;
1264 for (
auto &&[ImportingJob, ModuleDepsGraph] :
1265 llvm::zip_equal(llvm::make_pointee_range(ImportingJobs),
1266 ModuleDepGraphsForScannedJobs)) {
1267 for (
auto &MD : ModuleDepsGraph) {
1268 const auto Inserted = AlreadySeen.insert(MD.
ID).second;
1273 Graph.createJobNode<ClangModuleJobNode>(std::move(ClangModuleJob),
1285 for (
auto &&[Job, InputDeps] : llvm::zip_equal(
1286 llvm::make_pointee_range(ScannedJobs), InputDepsForScannedJobs)) {
1287 const auto &BuildArgs = InputDeps.BuildArgs;
1288 ArgStringList JobArgs;
1289 JobArgs.reserve(BuildArgs.size());
1291 const auto &SourceAction = Job.getSource();
1292 const auto &TC = Job.getCreator().getToolChain();
1294 C.getArgsForToolChain(&TC, SourceAction.getOffloadingArch(),
1295 SourceAction.getOffloadingDeviceKind());
1297 for (
const auto &Arg : BuildArgs)
1298 JobArgs.push_back(TCArgs.MakeArgString(Arg));
1300 Job.replaceArguments(std::move(JobArgs));
1309 CompilationGraph &Graph,
1312 for (
auto &&[Job, InputDeps] :
1313 llvm::zip_equal(ScannedJobs, InputDepsForScannedJobs)) {
1314 if (InputDeps.ModuleName.empty())
1315 Graph.createJobNode<NonModuleTUJobNode>(std::move(Job),
1316 std::move(InputDeps));
1318 Graph.createJobNode<NamedModuleJobNode>(std::move(Job),
1319 std::move(InputDeps));
1323template <
typename LookupT,
typename KeyRangeT>
1325 const LookupT &SrcNodeLookup,
1326 const KeyRangeT &SrcNodeLookupKeys,
1327 CGEdge::EdgeKind Kind) {
1328 for (
const auto &Key : SrcNodeLookupKeys) {
1329 const auto It = SrcNodeLookup.find(Key);
1330 if (It == SrcNodeLookup.end())
1333 auto &SrcNode = *It->second;
1334 Graph.createEdge(Kind, SrcNode, TgtNode);
1340 llvm::DenseMap<StringRef, CGNode *> NodeByOutputFiles;
1341 for (
auto *Node : Graph) {
1342 for (
const auto &Output :
cast<JobNode>(Node)->Job->getOutputFilenames()) {
1343 [[maybe_unused]]
const bool Inserted =
1344 NodeByOutputFiles.try_emplace(Output, Node).second;
1346 "Driver should not produce multiple jobs with identical outputs!");
1350 for (
auto *Node : Graph) {
1351 const auto &InputInfos =
cast<JobNode>(Node)->Job->getInputInfos();
1352 auto InputFilenames = llvm::map_range(
1353 InputInfos, [](
const auto &II) {
return II.getFilename(); });
1356 CGEdge::EdgeKind::Regular);
1366 llvm::DenseMap<deps::ModuleID, CGNode *> ClangModuleNodeByID;
1367 llvm::DenseMap<ModuleNameAndTriple, CGNode *> NamedModuleNodeByID;
1370 bool HasDuplicateModuleError =
false;
1371 for (
auto *Node : Graph) {
1372 llvm::TypeSwitch<CGNode *>(Node)
1373 .Case([&](ClangModuleJobNode *ClangModuleNode) {
1374 [[maybe_unused]]
const bool Inserted =
1375 ClangModuleNodeByID.try_emplace(ClangModuleNode->MD.
ID, Node)
1378 "Multiple Clang module nodes with the same module ID!");
1380 .Case([&](NamedModuleJobNode *NamedModuleNode) {
1381 StringRef ModuleName = NamedModuleNode->InputDeps.ModuleName;
1383 const auto [It, Inserted] = NamedModuleNodeByID.try_emplace(ID, Node);
1390 StringRef PrevFile =
1393 Diags.
Report(diag::err_modules_driver_named_module_redefinition)
1394 << ModuleName << PrevFile << CurFile;
1395 HasDuplicateModuleError =
true;
1399 if (HasDuplicateModuleError)
1403 for (
auto *Node : Graph) {
1404 llvm::TypeSwitch<CGNode *>(Node)
1405 .Case([&](ClangModuleJobNode *ClangModuleNode) {
1408 CGEdge::EdgeKind::ModuleDependency);
1410 .Case([&](ScannedJobNode *NodeWithInputDeps) {
1412 NodeWithInputDeps->InputDeps.ClangModuleDeps,
1413 CGEdge::EdgeKind::ModuleDependency);
1415 StringRef Triple =
getTriple(*NodeWithInputDeps->Job);
1416 const auto NamedModuleDepIDs =
1417 llvm::map_range(NodeWithInputDeps->InputDeps.NamedModuleDeps,
1418 [&](StringRef ModuleName) {
1419 return ModuleNameAndTriple{ModuleName, Triple};
1423 CGEdge::EdgeKind::ModuleDependency);
1437 for (
auto *PrunableJobNodeRoot : UnusedStdlibModuleJobNodes) {
1438 auto ReachableJobNodes =
1439 llvm::map_range(llvm::depth_first(
cast<CGNode>(PrunableJobNodeRoot)),
1440 llvm::CastTo<JobNode>);
1441 auto ReachableNonImageNodes = llvm::make_filter_range(
1442 ReachableJobNodes, [](
auto *N) {
return !llvm::isa<ImageJobNode>(N); });
1443 PrunableJobNodes.insert_range(ReachableNonImageNodes);
1447 llvm::DenseMap<ImageJobNode *, llvm::SmallPtrSet<JobNode *, 4>>
1448 PrunableJobNodesByImageNode;
1449 for (
auto *PrunableJobNode : PrunableJobNodes) {
1450 auto ReachableJobNodes = llvm::depth_first(
cast<CGNode>(PrunableJobNode));
1451 auto ReachableImageJobNodes = llvm::map_range(
1452 llvm::make_filter_range(ReachableJobNodes, llvm::IsaPred<ImageJobNode>),
1453 llvm::CastTo<ImageJobNode>);
1455 for (
auto *ImageNode : ReachableImageJobNodes)
1456 PrunableJobNodesByImageNode[ImageNode].insert(PrunableJobNode);
1461 for (
auto &[ImageNode, PrunableJobNodeInputs] : PrunableJobNodesByImageNode) {
1463 for (
auto *JN : PrunableJobNodeInputs)
1464 llvm::append_range(OutputsToRemove, JN->Job->getOutputFilenames());
1466 auto NewArgs = ImageNode->Job->getArguments();
1467 llvm::erase_if(NewArgs, [&](StringRef Arg) {
1468 return llvm::is_contained(OutputsToRemove, Arg);
1470 ImageNode->Job->replaceArguments(NewArgs);
1474 for (
auto *JN : PrunableJobNodes) {
1477 Graph.removeNode(*JN);
1485 for (
auto *Node : Graph)
1486 for (
auto *Edge : Node->getEdges())
1487 HasIncomingEdge.insert(&Edge->getTargetNode());
1489 auto AllNonRootNodes = llvm::iterator_range(Graph);
1490 auto &Root = Graph.createRoot();
1492 for (
auto *Node : AllNonRootNodes) {
1493 if (HasIncomingEdge.contains(Node))
1495 Graph.createEdge(CGEdge::EdgeKind::Rooted, Root, *Node);
1501 llvm::PrettyStackTraceString CrashInfo(
"Running modules driver.");
1503 auto Jobs =
C.getJobs().takeJobs();
1514 if (!MaybeModuleCachePath) {
1515 Diags.
Report(diag::err_default_modules_cache_not_available);
1519 auto MaybeCWD =
C.getDriver().getVFS().getCurrentWorkingDirectory();
1520 const auto CWD = MaybeCWD ? std::move(*MaybeCWD) :
".";
1523 *MaybeModuleCachePath, CWD, Diags);
1524 if (!MaybeScanResults) {
1525 Diags.
Report(diag::err_dependency_scan_failed);
1528 auto &ScanResult = *MaybeScanResults;
1531 CompilationGraph Graph;
1539 Graph,
C, ScannedJobs,
1540 std::move(ScanResult.ModuleDepGraphsForScannedJobs));
1543 std::move(ScanResult.InputDepsForScannedJobs));
1551 Diags.
Report(diag::remark_printing_module_graph);
1553 llvm::WriteGraph<const CompilationGraph *>(llvm::errs(), &Graph);
1557 llvm::ReversePostOrderTraversal<CompilationGraph *> TopologicallySortedNodes(
1560 "First node in topological order must be the root!");
1561 auto TopologicallySortedJobNodes = llvm::map_range(
1562 llvm::drop_begin(TopologicallySortedNodes), llvm::CastTo<JobNode>);
1563 for (
auto *JN : TopologicallySortedJobNodes)
1564 C.addCommand(std::move(JN->Job));
Defines the Diagnostic-related interfaces.
static Decl::Kind getKind(const Decl *D)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static std::optional< DependencyScanResult > scanDependencies(ArrayRef< std::unique_ptr< Command > > Jobs, llvm::DenseMap< StringRef, const StdModuleManifest::Module * > ManifestLookup, StringRef ModuleCachePath, StringRef WorkingDirectory, DiagnosticsEngine &Diags)
Scans the compilations job list Jobs for module dependencies.
static std::string constructPCMPath(const deps::ModuleID &ID, StringRef OutputDir)
Construct a path for the explicitly built PCM.
static StringRef getTriple(const Command &Job)
static void reportAllScanDiagnostics(SmallVectorImpl< SmallVector< StandaloneDiagnostic, 0 > > &&AllScanDiags, DiagnosticsEngine &Diags)
Report the diagnostics collected during each dependency scan.
static const StdModuleManifest::Module * getManifestEntryForCommand(const Command &Job, const ManifestEntryLookup &ManifestEntryBySource)
Returns the manifest entry corresponding to Job, or nullptr if none exists.
static bool isDependencyScannableJob(const Command &Job)
Returns true if a dependency scan can be performed using Job.
static std::pair< std::unique_ptr< llvm::ThreadPoolInterface >, std::unique_ptr< ScanningWorkerPool > > createOptimalThreadAndWorkerPool(size_t NumScanInputs, bool HasStdlibModuleInputs, deps::DependencyScanningService &ScanningService)
static StringRef getFirstInputFilename(const Command &Job)
static SmallVector< std::unique_ptr< Command > > takeJobsAtIndices(SmallVectorImpl< std::unique_ptr< Command > > &Jobs, ArrayRef< size_t > Indices)
static void pruneUnusedStdlibModuleJobs(CompilationGraph &Graph, ArrayRef< JobNode * > UnusedStdlibModuleJobNodes)
Prunes the compilation graph of any jobs which build Standard library modules not required in this co...
static Expected< StdModuleManifest > parseManifest(StringRef Buffer)
Parses the Standard library module manifest from Buffer.
static void createNodesForScannedJobs(CompilationGraph &Graph, SmallVectorImpl< std::unique_ptr< Command > > &&ScannedJobs, SmallVectorImpl< InputDependencies > &&InputDepsForScannedJobs)
Creates nodes for all jobs which were scanned for dependencies.
std::pair< StringRef, StringRef > ModuleNameAndTriple
static void createAndConnectRoot(CompilationGraph &Graph)
Creates the root node and connects it to all nodes with no incoming edges ensuring that every node in...
static void makeManifestPathsAbsolute(MutableArrayRef< StdModuleManifest::Module > ManifestEntries, StringRef ManifestPath)
Converts each file path in manifest from relative to absolute.
static SmallVector< std::string, 0 > buildCommandLine(const Command &Job)
Constructs the full command line, including the executable, for Job.
static void applyArgsForStdModuleManifestInputs(Compilation &C, const ManifestEntryLookup &ManifestEntryBySource, MutableArrayRef< std::unique_ptr< Command > > Jobs)
Apply command-line modifications specific for inputs originating from the Standard library module man...
static bool createModuleDependencyEdges(CompilationGraph &Graph, DiagnosticsEngine &Diags)
Create edges for module dependencies in Graph.
static std::pair< std::optional< deps::TranslationUnitDeps >, SmallVector< StandaloneDiagnostic, 0 > > scanDependenciesForJob(const Command &Job, ScanningWorkerPool &WorkerPool, StringRef WorkingDirectory, ModuleLookupController &LookupController)
Performs a dependency scan for a single job.
static std::unique_ptr< CC1Command > createClangModulePrecompileJob(Compilation &C, const Command &ImportingJob, const deps::ModuleDeps &MD)
Creates a job for the Clang module described by MD.
static void addSystemIncludeDirsFromManifest(Compilation &C, Command &Job, ArgStringList &CC1Args, ArrayRef< std::string > SystemIncludeDirs)
Adds all SystemIncludeDirs to the CC1Args of Job.
static InputDependencies makeInputDeps(deps::TranslationUnitDeps &&TUDeps)
static void createNodesForNonScannableJobs(CompilationGraph &Graph, SmallVectorImpl< std::unique_ptr< Command > > &&NonScannableJobs)
Creates nodes for all jobs that could not be scanned (e.g. image jobs, ...).
static SmallVector< JobNode * > createNodesForUnusedStdlibModuleJobs(CompilationGraph &Graph, SmallVectorImpl< std::unique_ptr< Command > > &&UnusedStdlibModuleJobs)
Creates nodes for the Standard library module jobs not discovered as dependencies.
static ManifestEntryLookup buildManifestLookupMap(ArrayRef< StdModuleManifest::Module > ManifestEntries)
Builds a mapping from a module's source path to its entry in the manifest.
static void createRegularEdges(CompilationGraph &Graph)
Create edges for regular (non-module) dependencies in Graph.
static void createClangModuleJobsAndNodes(CompilationGraph &Graph, Compilation &C, ArrayRef< std::unique_ptr< Command > > ImportingJobs, SmallVectorImpl< deps::ModuleDepsGraph > &&ModuleDepGraphsForScannedJobs)
Creates a ClangModuleJobNode with associated job for each unique Clang module in ModuleDepGraphsForSc...
static void installScanCommandLines(Compilation &C, MutableArrayRef< std::unique_ptr< Command > > ScannedJobs, ArrayRef< InputDependencies > InputDepsForScannedJobs)
Installs the command lines produced by the dependency scan into ScannedJobs.
static void connectEdgesViaLookup(CompilationGraph &Graph, CGNode &TgtNode, const LookupT &SrcNodeLookup, const KeyRangeT &SrcNodeLookupKeys, CGEdge::EdgeKind Kind)
static bool isCC1Job(const Command &Job)
llvm::DenseMap< StringRef, const StdModuleManifest::Module * > ManifestEntryLookup
static std::optional< std::string > getModuleCachePath(llvm::opt::DerivedArgList &Args)
Computes the -fmodule-cache-path for this compilation.
This file defines functionality to support driver managed builds for compilations which use Clang mod...
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
virtual void EndSourceFile()
Callback to inform the diagnostic client that processing of a source file has ended.
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
virtual void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP=nullptr)
Callback to inform the diagnostic client that processing of a source file is beginning.
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine a...
Concrete class used by the front-end to report problems and issues.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
bool hasSourceManager() const
bool isLastDiagnosticIgnored() const
Determine whether the previous diagnostic was ignored.
SourceManager & getSourceManager() const
Level
The level of the diagnostic, after it has been through mapping.
DiagnosticConsumer * getClient()
Implements support for file system lookup, file system caching, and directory search management.
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
This class handles loading and caching of source files into memory.
FileManager & getFileManager() const
Represents a diagnostic in a form that can be retained until its corresponding source manager is dest...
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
The dependency scanning service contains shared configuration and state that is used by the individua...
TranslationUnitDeps takeTranslationUnitDeps()
Action - Represent an abstract compilation step to perform.
const char * getOffloadingArch() const
void propagateOffloadInfo(const Action *A)
Set the offload info of this action to be the same as the provided action, and propagate it to its de...
OffloadKind getOffloadingDeviceKind() const
Command - An executable path/name and argument vector to execute.
const Action & getSource() const
getSource - Return the Action which caused the creation of this job.
const Tool & getCreator() const
getCreator - Return the Tool which caused the creation of this job.
const llvm::opt::ArgStringList & getArguments() const
const char * getExecutable() const
const std::vector< InputInfo > & getInputInfos() const
Compilation - A set of tasks to perform for a single driver invocation.
static bool getDefaultModuleCachePath(SmallVectorImpl< char > &Result)
Compute the default -fmodule-cache-path.
GraphWriterBase< GraphType, GraphWriter< GraphType > > Base
GraphWriter(llvm::raw_ostream &O, const GraphType &G, bool IsSimple)
const CompilationGraph * GraphType
@ VFS
Remove unused -ivfsoverlay arguments.
ModuleOutputKind
An output from a module compilation, such as the path of the module file.
@ ModuleFile
The module file (.pcm). Required.
void buildStdModuleManifestInputs(ArrayRef< StdModuleManifest::Module > ManifestEntries, Compilation &C, InputList &Inputs)
Constructs compilation inputs for each module listed in the provided Standard library module manifest...
void runModulesDriver(Compilation &C, ArrayRef< StdModuleManifest::Module > ManifestEntries)
Scans the compilation inputs for module dependencies and adjusts the compilation to build and supply ...
llvm::Expected< StdModuleManifest > readStdModuleManifest(llvm::StringRef ManifestPath, llvm::vfs::FileSystem &VFS)
Reads the Standard library module manifest at ManifestPath.
static bool fromJSON(const llvm::json::Value &Params, StdModuleManifest::Module::LocalArguments &LocalArgs, llvm::json::Path P)
bool isSrcFile(ID Id)
isSrcFile - Is this a source file, i.e.
llvm::opt::Arg * makeInputArg(llvm::opt::DerivedArgList &Args, const llvm::opt::OptTable &Opts, StringRef Value, bool Claim=true)
Creates and adds a synthesized input argument.
llvm::SmallVector< InputTy, 16 > InputList
A list of inputs and their types for the given arguments.
NodeKind
A kind of a syntax node, used for implementing casts.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
static bool classof(const OMPClause *T)
@ NumWorkers
'num_workers' clause, allowed on 'parallel', 'kernels', parallel loop', and 'kernels loop' constructs...
U cast(CodeGen::Address addr)
StoredDiagnostic translateStandaloneDiag(FileManager &FileMgr, SourceManager &SrcMgr, const StandaloneDiagnostic &StandaloneDiag, llvm::StringMap< SourceLocation > &SrcLocCache)
Translates StandaloneDiag into a StoredDiagnostic, associating it with the provided FileManager and S...
Diagnostic wrappers for TextAPI types for error reporting.
int const char * function
The configuration knobs for the dependency scanning service.
ModuleID ID
The identifier of the module.
std::vector< ModuleID > ClangModuleDeps
A list of module identifiers this module directly depends on, not including transitive dependencies.
const std::vector< std::string > & getBuildArguments() const
Get (or compute) the compiler invocation that can be used to build this module.
This is used to identify a specific module.
The full dependencies and module graph for a specific input.
static constexpr ResponseFileSupport AtFileUTF8()
std::vector< std::string > SystemIncludeDirs
std::optional< LocalArguments > LocalArgs
The parsed Standard library module manifest.
std::vector< Module > Modules
static bool renderGraphFromBottomUp()
static std::string getNodeIdentifier(NodeRef N, GraphRef)
static std::string getNodeLabel(NodeRef N, GraphRef)
static std::string getGraphName(GraphRef)
const CompilationGraph * GraphRef
static std::string getNodeAttributes(NodeRef N, GraphRef)
static bool isNodeHidden(NodeRef N, GraphRef)
static std::string getGraphProperties(GraphRef)
DOTGraphTraits(bool IsSimple=false)
static ChildEdgeIteratorType child_edge_end(NodeRef N)
mapped_iterator< CGNode::iterator, decltype(&CGGetTargetNode)> ChildIteratorType
static ChildEdgeIteratorType child_edge_begin(NodeRef N)
CGNode::iterator ChildEdgeIteratorType
static ChildIteratorType child_end(NodeRef N)
static NodeRef CGGetTargetNode(CGEdge *E)
static NodeRef getEntryNode(NodeRef N)
static ChildIteratorType child_begin(NodeRef N)
static NodeRef getEntryNode(GraphRef G)
static nodes_iterator nodes_begin(GraphRef G)
CompilationGraph::iterator nodes_iterator
static nodes_iterator nodes_end(GraphRef G)
CompilationGraph * GraphRef
static ChildIteratorType child_begin(NodeRef N)
mapped_iterator< CGNode::const_iterator, decltype(&CGGetTargetNode)> ChildIteratorType
static ChildEdgeIteratorType child_edge_end(NodeRef N)
static ChildEdgeIteratorType child_edge_begin(NodeRef N)
CGNode::const_iterator ChildEdgeIteratorType
static ChildIteratorType child_end(NodeRef N)
static NodeRef getEntryNode(NodeRef N)
static NodeRef CGGetTargetNode(const CGEdge *E)
const CompilationGraph * GraphRef
static nodes_iterator nodes_begin(GraphRef G)
static NodeRef getEntryNode(GraphRef G)
CompilationGraph::const_iterator nodes_iterator
static nodes_iterator nodes_end(GraphRef G)