• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / docc / 25823993703

13 May 2026 08:16PM UTC coverage: 60.954% (-4.8%) from 65.785%
25823993703

push

github

web-flow
Merge pull request #710 from daisytuner/publish-llvm-frontend

Publish LLVM (C/C++) frontend

2664 of 8267 new or added lines in 27 files covered. (32.22%)

35005 of 57429 relevant lines covered (60.95%)

11126.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/llvm/src/analysis/global_cfg_analysis.cpp
1
#include "docc/analysis/global_cfg_analysis.h"
2

3
#include <llvm/Bitcode/BitcodeReader.h>
4
#include <llvm/IR/CFG.h>
5
#include <llvm/IR/Function.h>
6
#include <llvm/IR/Instructions.h>
7
#include <llvm/IR/ModuleSummaryIndex.h>
8
#include <llvm/Support/CommandLine.h>
9
#include <llvm/Support/Error.h>
10
#include <llvm/Support/Format.h>
11
#include <llvm/Support/MemoryBuffer.h>
12
#include <llvm/Support/raw_ostream.h>
13

14
#include <cassert>
15
#include <memory>
16
#include <stack>
17
#include <unordered_map>
18

19
#include "docc/analysis/sdfg_registry.h"
20
#include "sdfg/data_flow/library_nodes/call_node.h"
21
#include "sdfg/targets/offloading/data_offloading_node.h"
22
#include "sdfg/targets/offloading/external_offloading_node.h"
23
#include "sdfg/visitor/structured_sdfg_visitor.h"
24

25
using namespace docc;
26

27
static llvm::ExitOnError ExitOnErr{"[docc_llvm_plugin] error: "};
28

29
llvm::cl::opt<bool> docc_debug_glbl("docc-debug-glbl");
30

31
#define DOCC_DEBUG(X) \
NEW
32
    if (docc_debug_glbl) X
×
33

34
static llvm::cl::opt<bool> IncludeAllIntrinsics(
35
    "docc-include-all-intrinsics",
36
    llvm::cl::init(false),
37
    llvm::cl::desc("Add external call nodes for all intrinsics, e.g. llvm.dbg.value")
38
);
39

40
namespace docc::analysis {
41

NEW
42
bool GlobalCFGAnalysis::available(AnalysisManager &am) { return SDFGRegistry::is_link_time(am); }
×
43

NEW
44
const std::vector<GlobalCFGNode *> *GlobalCFGAnalysis::getExitPoints(llvm::GlobalValue::GUID id) const {
×
NEW
45
    auto it = ExitPoints_.find(id);
×
NEW
46
    if (it != ExitPoints_.end()) {
×
NEW
47
        return &(it->second);
×
NEW
48
    } else {
×
NEW
49
        return nullptr;
×
NEW
50
    }
×
NEW
51
}
×
52

NEW
53
const std::vector<GlobalCFGNode *> *GlobalCFGAnalysis::getExitPoints(llvm::StringRef Name) const {
×
NEW
54
    return getExitPoints(llvm::GlobalValue::getGUID(Name));
×
NEW
55
}
×
56

57
class GlobalCFGBuilder {
58
public:
59
    GlobalCFGAnalysis &CFG_;
60
    llvm::ModuleSummaryIndex &CombinedIndex_;
61
    llvm::StringMap<std::vector<std::tuple<GlobalCFGNode *, GlobalCFGNode *, int32_t>>> ReturnEdgeTargets_;
62
    llvm::StringMap<std::vector<std::tuple<GlobalCFGNode *>>> ReturnEdgeOrigins_;
63
    SDFGRegistry &registry_;
64

65
    GlobalCFGBuilder(GlobalCFGAnalysis &CFG, llvm::ModuleSummaryIndex &CombinedIndex, SDFGRegistry &registry)
NEW
66
        : CFG_(CFG), CombinedIndex_(CombinedIndex), registry_(registry) {}
×
67

68
    GlobalCFGNode &addNode(uint32_t modId, int64_t funcId = -1L);
69
    void addModule(uint32_t modId, llvm::Module &Mod);
70
    void insertReturnEdges();
71

NEW
72
    void registerFunctionExit(llvm::StringRef func, GlobalCFGNode &node, bool global = true) {
×
NEW
73
        ReturnEdgeOrigins_[func].emplace_back(&node);
×
NEW
74
        node.specialType_ = CfgSpecialType::Return;
×
NEW
75
        if (global) {
×
NEW
76
            CFG_.ExitPoints_[llvm::GlobalValue::getGUID(func)].push_back(&node);
×
NEW
77
        }
×
NEW
78
    }
×
79

80
    void registerCallReturnSite(
81
        llvm::StringRef func, GlobalCFGNode &callNode, GlobalCFGNode &callReturnNode, int32_t step = -1
NEW
82
    ) {
×
NEW
83
        ReturnEdgeTargets_[func].push_back({&callReturnNode, &callNode, step});
×
NEW
84
    }
×
85

NEW
86
    GlobalCFGNode &addNodeGlobal(uint32_t modId, int64_t funcId, llvm::GlobalValue::GUID Id) {
×
NEW
87
        auto &N = addNode(modId, funcId);
×
NEW
88
        N.Id_ = Id;
×
NEW
89
        return N;
×
NEW
90
    }
×
91

NEW
92
    GlobalCFGNode &addNodeInternal(uint32_t modId, int64_t funcId, llvm::BasicBlock &BB) {
×
NEW
93
        auto &N = addNode(modId, funcId);
×
NEW
94
        N.BB_ = &BB;
×
NEW
95
        return N;
×
NEW
96
    };
×
NEW
97
    GlobalCFGNode &addNodeVirtual(uint32_t modId, llvm::Instruction &Inst) {
×
NEW
98
        auto &N = addNode(modId);
×
NEW
99
        N.Inst_ = &Inst;
×
NEW
100
        return N;
×
NEW
101
    };
×
NEW
102
    GlobalCFGNode &addNodeAuxiliary(uint32_t modId, int64_t funcId) {
×
NEW
103
        auto &N = addNode(modId, funcId);
×
104
        // TODO: Do we need any info, e.g. artificial return point?
NEW
105
        return N;
×
NEW
106
    };
×
NEW
107
    GlobalCFGNode &addNodeExternal(uint32_t modId, const std::string &Name) {
×
NEW
108
        auto &N = addNode(modId);
×
NEW
109
        N.Name_ = Name;
×
NEW
110
        return N;
×
NEW
111
    };
×
112
    GlobalCFGEdge &addEdge(
113
        GlobalCFGNode &From,
114
        GlobalCFGNode &To,
115
        EdgeType Type,
116
        int32_t fromEvtIdx = -1,
117
        bool incRet = false,
118
        int32_t toEvtIdx = -1
NEW
119
    ) {
×
NEW
120
        auto [E, Added] = boost::add_edge(From.Vertex_, To.Vertex_, CFG_.Graph_);
×
NEW
121
        assert(Added && "Duplicate edge");
×
NEW
122
        auto It = CFG_.Edges_
×
NEW
123
                      .insert({E, std::make_unique<GlobalCFGEdge>(E, Type, &From, &To, fromEvtIdx, incRet, toEvtIdx)})
×
NEW
124
                      .first;
×
NEW
125
        return *It->second;
×
NEW
126
    }
×
127

128
private:
129
    void addBasicBlockEdges(
130
        uint32_t modId,
131
        int64_t funcId,
132
        llvm::BasicBlock *BB,
133
        GlobalCFGNode *BBNode,
134
        const llvm::DenseMap<llvm::BasicBlock *, GlobalCFGNode *> &ModuleNodes
135
    );
136

137
    void *addFromSdfg(
138
        uint32_t modId,
139
        llvm::Function &func,
140
        llvm::BasicBlock &bb,
141
        GlobalCFGNode &node,
142
        SDFGHolder *sdfg,
143
        llvm::DenseMap<llvm::BasicBlock *, GlobalCFGNode *> &ModuleNodes
144
    );
145

146
    SDFGHolder *find_sdfg(llvm::Module &module, llvm::StringRef name);
147
};
148

NEW
149
SDFGHolder *GlobalCFGBuilder::find_sdfg(llvm::Module &module, llvm::StringRef name) {
×
NEW
150
    auto &sdfgs = registry_.at(module);
×
151

NEW
152
    auto it = sdfgs.find(name.str());
×
153

NEW
154
    if (it != sdfgs.end()) {
×
NEW
155
        return it->second.get();
×
NEW
156
    } else {
×
NEW
157
        return nullptr;
×
NEW
158
    }
×
NEW
159
}
×
160

161
struct SdfgVisState {
162
    const ControlFlowNode &current_scope;
163
    GlobalCFGNode &node;
164
    std::vector<GlobalCFGNode *> calls;
165
};
166

167
class SDFGCfgVisitor : public sdfg::visitor::ActualStructuredSDFGVisitor {
168
private:
169
    uint32_t modId_;
170
    int64_t funcId_;
171
    const GlobalCFGNode &sdfg_root_;
172
    GlobalCFGBuilder &builder_;
173

174
    std::stack<SdfgVisState, std::list<SdfgVisState>> stack_;
175

176
    void add_transfer(
177
        sdfg::data_flow::LibraryNode *transfer_call, bool h2d, const std::string &func_id, const std::string &label
NEW
178
    ) {
×
NEW
179
        auto &state = stack_.top();
×
NEW
180
        auto &prev_node = state.node;
×
NEW
181
        auto &scope = state.current_scope;
×
182

NEW
183
        commit(state);
×
184

NEW
185
        stack_.pop();
×
186

NEW
187
        auto &transfer_node = builder_.addNode(modId_);
×
NEW
188
        transfer_node.Name_ = label;
×
NEW
189
        transfer_node.Id_ = llvm::GlobalValue::getGUID(func_id);
×
NEW
190
        transfer_node.specialType_ = h2d ? CfgSpecialType::H2D : CfgSpecialType::D2H;
×
NEW
191
        builder_.addEdge(prev_node, transfer_node, EdgeType::Sequence, prev_node.evtSteps_);
×
192

NEW
193
        auto &succ_node = builder_.addNode(modId_, funcId_);
×
NEW
194
        succ_node.sdfg_node_ = prev_node.sdfg_node_;
×
195

NEW
196
        stack_.emplace(scope, succ_node);
×
NEW
197
        builder_.addEdge(transfer_node, succ_node, EdgeType::Sequence);
×
NEW
198
    }
×
199

NEW
200
    void add_call(sdfg::data_flow::CallNode *call) {
×
NEW
201
        auto &state = stack_.top();
×
NEW
202
        auto step = state.node.evtSteps_++;
×
NEW
203
        auto &call_target_name = call->callee_name();
×
NEW
204
        auto *known_call = builder_.CFG_.findNodeExternallyVisible(call_target_name);
×
NEW
205
        if (known_call) {
×
NEW
206
            auto edgeType = known_call->modId_ != sdfg_root_.modId_ ? EdgeType::CallCrossModule
×
NEW
207
                                                                    : EdgeType::CallInternal;
×
NEW
208
            builder_.addEdge(state.node, *known_call, edgeType, step, true);
×
209
            //            builder_.registerCallReturnSite(call_target_name, *known_call, state.node, step);
NEW
210
        } else {
×
NEW
211
            auto &callee_node = builder_.addNodeExternal(modId_, call_target_name);
×
NEW
212
            callee_node.Id_ = llvm::GlobalValue::getGUID(call_target_name);
×
NEW
213
            builder_.addEdge(state.node, callee_node, EdgeType::CallExternal, step, true);
×
NEW
214
        }
×
NEW
215
    }
×
216

NEW
217
    void enterScope(sdfg::structured_control_flow::ControlFlowNode &scope) {
×
NEW
218
        auto &current = stack_.top();
×
NEW
219
        auto &parent_node = current.node;
×
220

NEW
221
        auto &new_node = builder_.addNode(modId_, funcId_);
×
NEW
222
        new_node.sdfg_node_ = &scope;
×
223

NEW
224
        builder_.addEdge(parent_node, new_node, EdgeType::Sequence, parent_node.evtSteps_++, true);
×
225

NEW
226
        stack_.emplace(scope, new_node);
×
NEW
227
    }
×
228

NEW
229
    void commit(SdfgVisState &state) {}
×
230

NEW
231
    void leaveScope(sdfg::structured_control_flow::ControlFlowNode &scope) {
×
NEW
232
        assert(&stack_.top().current_scope == &scope);
×
233

NEW
234
        auto &leaving_state = stack_.top();
×
NEW
235
        auto &leaving_node = leaving_state.node;
×
236

NEW
237
        commit(leaving_state);
×
238

NEW
239
        stack_.pop();
×
NEW
240
    }
×
241

242
public:
243
    using sdfg::visitor::ActualStructuredSDFGVisitor::visit;
244

245
    SDFGCfgVisitor(
246
        GlobalCFGBuilder &builder,
247
        GlobalCFGNode &root_node,
248
        const sdfg::structured_control_flow::ControlFlowNode &root_scope,
249
        uint32_t modId
250
    )
NEW
251
        : builder_(builder), sdfg_root_(root_node), modId_(modId), funcId_(root_node.funcId_) {
×
NEW
252
        root_node.sdfg_node_ = &root_scope;
×
NEW
253
        stack_.push(SdfgVisState{.current_scope = root_scope, .node = root_node});
×
NEW
254
    }
×
255

NEW
256
    bool handleStructuredLoop(StructuredLoop &node) override {
×
NEW
257
        enterScope(node);
×
258

NEW
259
        dispatch(node.root());
×
260

NEW
261
        leaveScope(node);
×
262

NEW
263
        return true;
×
NEW
264
    }
×
265

NEW
266
    bool visit(While &node) override {
×
NEW
267
        enterScope(node);
×
268

NEW
269
        dispatch(node.root());
×
270

NEW
271
        leaveScope(node);
×
272

NEW
273
        return true;
×
NEW
274
    }
×
275

NEW
276
    bool visit(Return &node) override {
×
NEW
277
        auto &state = stack_.top();
×
NEW
278
        builder_.registerFunctionExit(sdfg_root_.Name_, state.node);
×
279

NEW
280
        return true;
×
NEW
281
    }
×
282

NEW
283
    bool visit(Block &node) override {
×
NEW
284
        for (auto &n : node.dataflow().nodes()) {
×
NEW
285
            if (auto *external_offload = dynamic_cast<sdfg::offloading::ExternalDataOffloadingNode *>(&n)) {
×
NEW
286
                if (!external_offload->has_transfer()) {
×
NEW
287
                    continue;
×
NEW
288
                }
×
NEW
289
                add_transfer(
×
NEW
290
                    external_offload,
×
NEW
291
                    external_offload->is_h2d(),
×
NEW
292
                    external_offload->callee_name(),
×
NEW
293
                    external_offload->callee_name() + ":" + std::to_string(external_offload->transfer_index())
×
NEW
294
                );
×
NEW
295
            } else if (auto *data_transfer = dynamic_cast<sdfg::offloading::DataOffloadingNode *>(&n)) {
×
NEW
296
                if (!data_transfer->has_transfer()) {
×
NEW
297
                    continue;
×
NEW
298
                }
×
NEW
299
                add_transfer(
×
NEW
300
                    data_transfer, data_transfer->is_h2d(), data_transfer->code().value(), data_transfer->code().value()
×
NEW
301
                );
×
NEW
302
            } else if (auto *call_node = dynamic_cast<sdfg::data_flow::CallNode *>(&n)) {
×
NEW
303
                add_call(call_node);
×
NEW
304
            }
×
NEW
305
        }
×
306

NEW
307
        return true;
×
NEW
308
    }
×
309

NEW
310
    bool visit(Sequence &node) override {
×
NEW
311
        ActualStructuredSDFGVisitor::visit(node);
×
312

NEW
313
        auto &state = stack_.top();
×
NEW
314
        if (stack_.size() == 1 && &state.current_scope == &node) { // we are at the end of root_scope
×
NEW
315
            builder_.registerFunctionExit(sdfg_root_.Name_, state.node);
×
316
            // TODO this is only for implicit returns and will be double if
317
            // there is also an explicit one at the end (for a retVal).
318
            // Future SDFGS should not allow implicit returns!
NEW
319
        }
×
320

NEW
321
        return true;
×
NEW
322
    }
×
323
};
324

325
void *GlobalCFGBuilder::addFromSdfg(
326
    uint32_t modId,
327
    llvm::Function &func,
328
    llvm::BasicBlock &bb,
329
    GlobalCFGNode &node,
330
    SDFGHolder *sdfg_holder,
331
    llvm::DenseMap<llvm::BasicBlock *, GlobalCFGNode *> &ModuleNodes
NEW
332
) {
×
NEW
333
    node.sdfg_ = sdfg_holder;
×
NEW
334
    auto [lock, sdfg] = sdfg_holder->get_for_read();
×
NEW
335
    node.Name_ = sdfg->name();
×
336

NEW
337
    auto &rootSeq = sdfg->root();
×
338

NEW
339
    auto visitor = SDFGCfgVisitor(*this, node, rootSeq, modId);
×
340

NEW
341
    visitor.visit(const_cast<sdfg::structured_control_flow::Sequence &>(rootSeq));
×
342

NEW
343
    return &node;
×
NEW
344
}
×
345

NEW
346
void GlobalCFGBuilder::addModule(uint32_t modId, llvm::Module &Mod) {
×
347
    // Create nodes for all basic blocks in the module
NEW
348
    llvm::DenseMap<llvm::BasicBlock *, GlobalCFGNode *> Nodes;
×
NEW
349
    for (llvm::Function &Func : Mod) {
×
NEW
350
        if (Func.isDeclaration()) continue;
×
351

NEW
352
        auto func_name = Func.getName();
×
353

NEW
354
        auto already_known_entry_node = CFG_.findNodeExternallyVisible(func_name);
×
NEW
355
        auto funcId = already_known_entry_node ? already_known_entry_node->funcId_ : CFG_.next_func_id_++;
×
356

357
        // Handle definitions that might be replaced at link-time
NEW
358
        assert(!Func.isInterposable() && "TODO: interposable linkage");
×
359
        // Ignore thin-link duplicates for cross-module inlining
NEW
360
        if (Func.hasAvailableExternallyLinkage()) {
×
NEW
361
            DOCC_DEBUG(llvm::dbgs() << "  Ignore thin-link duplicate: " << func_name << "\n");
×
NEW
362
            continue;
×
NEW
363
        }
×
364

NEW
365
        DOCC_DEBUG(llvm::dbgs() << " Processing function " << func_name << " in " << Mod.getName() << "\n");
×
366

NEW
367
        auto sdfg = find_sdfg(Mod, func_name);
×
368

NEW
369
        for (llvm::BasicBlock &BB : Func) {
×
NEW
370
            GlobalCFGNode *node = nullptr;
×
371

372
            // Public functions generate symbols, which are resolved by name.
373
            // Symbols with external visibility are known from thin-LTO module summaries.
NEW
374
            if (BB.isEntryBlock()) {
×
NEW
375
                if (already_known_entry_node) { // add the name & BB to already known cross-module impls
×
NEW
376
                    node = already_known_entry_node;
×
NEW
377
                    assert(already_known_entry_node->Id_ && "External symbols have global value summaries");
×
NEW
378
                    assert(
×
NEW
379
                        already_known_entry_node->funcId_ >= 0 && "External symbols should already have funcId assigned"
×
NEW
380
                    );
×
NEW
381
                    if (already_known_entry_node->BB_) { // some other module has already parsed this function, so abort
×
NEW
382
                        break;
×
NEW
383
                    }
×
NEW
384
                } else {
×
NEW
385
                    node = &addNodeInternal(modId, funcId, BB);
×
NEW
386
                }
×
NEW
387
                node->Name_ = func_name;
×
NEW
388
                node->BB_ = &BB;
×
389

NEW
390
                if (sdfg) { // override with SDFG details
×
NEW
391
                    addFromSdfg(modId, Func, BB, *node, sdfg, Nodes);
×
NEW
392
                }
×
NEW
393
            } else if (sdfg) {
×
394
                //                node = &addNodeInternal(modId, funcId, BB); // skipping. Should never be a branch
395
                //                target from outside. rest handled in addFromSdfg node->sdfg_ = sdfg;
NEW
396
            } else {
×
NEW
397
                node = &addNodeInternal(modId, funcId, BB);
×
NEW
398
            }
×
NEW
399
            if (node) {
×
NEW
400
                Nodes.insert({&BB, node});
×
NEW
401
            }
×
NEW
402
        }
×
NEW
403
    }
×
404

NEW
405
    for (auto &&[BB, N] : Nodes) {
×
NEW
406
        if (!N->sdfg_) {
×
NEW
407
            addBasicBlockEdges(modId, N->funcId_, BB, N, Nodes);
×
NEW
408
        }
×
NEW
409
    }
×
NEW
410
}
×
411

412
void GlobalCFGBuilder::addBasicBlockEdges(
413
    uint32_t modId,
414
    int64_t funcId,
415
    llvm::BasicBlock *BB,
416
    GlobalCFGNode *BBNode,
417
    const llvm::DenseMap<llvm::BasicBlock *, GlobalCFGNode *> &ModuleNodes
NEW
418
) {
×
NEW
419
    GlobalCFGNode *PrevNode = BBNode;
×
NEW
420
    bool PrevWasNoReturn = false;
×
NEW
421
    int32_t insnId = -1;
×
422

NEW
423
    auto endCurrentNode = [&](llvm::BasicBlock *BB, llvm::Instruction &From) -> GlobalCFGNode & {
×
NEW
424
        if (PrevNode->BB_ == BB) { // we are terminating a BBNode early
×
NEW
425
            PrevNode->last_bb_insn_idx_ = insnId;
×
NEW
426
            PrevNode->last_insn_interesting_ = true;
×
NEW
427
        }
×
NEW
428
        return *PrevNode;
×
NEW
429
    };
×
NEW
430
    auto addReturnNode = [&](llvm::StringRef FnName, GlobalCFGNode *Fallback) -> GlobalCFGNode * {
×
NEW
431
        GlobalCFGNode *ReturnNode = &addNodeAuxiliary(modId, funcId);
×
NEW
432
        ReturnNode->BB_ = BB;
×
NEW
433
        ReturnNode->first_bb_insn_idx_ = insnId + 1;
×
434
        // Second pass injects return edges
NEW
435
        registerCallReturnSite(FnName, *Fallback, *ReturnNode);
×
NEW
436
        return ReturnNode;
×
NEW
437
    };
×
438

NEW
439
    for (llvm::Instruction &I : *BB) {
×
NEW
440
        ++insnId;
×
NEW
441
        if (PrevWasNoReturn) break;
×
442

NEW
443
        if (auto *Call = llvm::dyn_cast<llvm::CallBase>(&I)) {
×
444
            // Function-pointer call
NEW
445
            llvm::Function *Callee = Call->getCalledFunction();
×
NEW
446
            if (Callee == nullptr) {
×
NEW
447
                GlobalCFGNode &callNode = addNodeVirtual(modId, I);
×
NEW
448
                addEdge(*PrevNode, callNode, EdgeType::CallIndirect, PrevNode->evtSteps_++, true);
×
NEW
449
                continue;
×
NEW
450
            }
×
451

NEW
452
            llvm::StringRef FnName = Callee->getName();
×
453

454
            // Call to function inside the current module
NEW
455
            if (!Callee->isDeclaration()) {
×
NEW
456
                auto *target_bb = &Callee->getEntryBlock();
×
457

NEW
458
                auto it = ModuleNodes.find(target_bb);
×
NEW
459
                if (it == ModuleNodes.end()) {
×
NEW
460
                    llvm::dbgs() << "found no node for entry of " << FnName;
×
NEW
461
                    llvm::dbgs() << " even though it is a same-module call target!\n";
×
462

NEW
463
                    auto &errorNode = addNode(modId);
×
NEW
464
                    errorNode.Name_ = "Broken: " + FnName.str();
×
NEW
465
                    addEdge(*PrevNode, errorNode, EdgeType::CallInternal, PrevNode->evtSteps_++, true);
×
NEW
466
                    continue;
×
NEW
467
                } else {
×
NEW
468
                    auto *CalleeNode = it->second;
×
NEW
469
                    EdgeType edge_type;
×
NEW
470
                    edge_type = EdgeType::CallInternal;
×
471

NEW
472
                    addEdge(endCurrentNode(BB, I), *CalleeNode, edge_type);
×
NEW
473
                    PrevNode = addReturnNode(FnName, CalleeNode);
×
NEW
474
                    continue;
×
NEW
475
                }
×
NEW
476
            }
×
477

478
            // Call to any known entry point
NEW
479
            if (GlobalCFGNode *TargetNode = CFG_.findNodeExternallyVisible(FnName)) {
×
NEW
480
                EdgeType edge_type;
×
NEW
481
                edge_type = EdgeType::CallCrossModule;
×
NEW
482
                addEdge(endCurrentNode(BB, I), *TargetNode, edge_type);
×
NEW
483
                PrevNode = addReturnNode(FnName, TargetNode);
×
NEW
484
                continue;
×
NEW
485
            }
×
486

487
            // Call to intrinsic without side-effects
NEW
488
            if (!IncludeAllIntrinsics && Callee->isIntrinsic()) {
×
NEW
489
                if (I.mayHaveSideEffects()) {
×
NEW
490
                    DOCC_DEBUG(llvm::dbgs() << "llvm intrinsic " << FnName << " suppressed from GCFG\n");
×
NEW
491
                }
×
NEW
492
                continue;
×
NEW
493
            }
×
494

NEW
495
            GlobalCFGNode &CalleeNode = addNodeExternal(modId, FnName.str());
×
NEW
496
            addEdge(*PrevNode, CalleeNode, EdgeType::CallExternal, PrevNode->evtSteps_++, true);
×
NEW
497
        }
×
NEW
498
    }
×
499

NEW
500
    llvm::Instruction *TI = BB->getTerminator();
×
501

NEW
502
    if (auto *Ret = llvm::dyn_cast<llvm::ReturnInst>(TI)) {
×
NEW
503
        llvm::Function *OwnerFunc = BB->getParent();
×
NEW
504
        registerFunctionExit(OwnerFunc->getName(), *PrevNode);
×
NEW
505
        PrevNode->last_insn_interesting_ = true;
×
NEW
506
    } else {
×
NEW
507
        bool important = true;
×
NEW
508
        auto succs = llvm::succ_size(BB);
×
NEW
509
        if (llvm::dyn_cast<llvm::BranchInst>(TI)) {
×
NEW
510
            important = false;
×
NEW
511
        }
×
NEW
512
        if (important) {
×
NEW
513
            PrevNode->last_insn_interesting_ = true;
×
NEW
514
        }
×
515

NEW
516
        if (succs == 0) {
×
NEW
517
            PrevNode->specialType_ = CfgSpecialType::ErrorHandling;
×
NEW
518
        }
×
519

NEW
520
        auto edge_type = succs > 1 ? EdgeType::Branch : EdgeType::Sequence;
×
521

NEW
522
        auto sourceStep = PrevNode->evtSteps_ > 0 ? PrevNode->evtSteps_ : -1;
×
NEW
523
        for (llvm::BasicBlock *SuccBB : llvm::successors(BB)) {
×
NEW
524
            addEdge(*PrevNode, *ModuleNodes.at(SuccBB), edge_type, sourceStep);
×
NEW
525
        }
×
NEW
526
    }
×
NEW
527
}
×
528

NEW
529
void GlobalCFGBuilder::insertReturnEdges() {
×
530
    // Add edges from all collected origins (basic-blocks with ret terminator)
NEW
531
    for (const auto &KV : ReturnEdgeOrigins_) {
×
NEW
532
        llvm::StringRef FnName = KV.first();
×
NEW
533
        auto It = ReturnEdgeTargets_.find(FnName);
×
NEW
534
        if (It == ReturnEdgeTargets_.end()) {
×
NEW
535
            DOCC_DEBUG(llvm::dbgs() << "  No calls to " << FnName << "\n");
×
NEW
536
            continue;
×
NEW
537
        }
×
NEW
538
        for (auto [Origin] : KV.second) {
×
NEW
539
            int32_t retSourceStep = Origin->evtSteps_ > 0 ? Origin->evtSteps_ : -1;
×
NEW
540
            for (auto [Target, Fallback, retTargetStep] : It->second) {
×
NEW
541
                addEdge(*Origin, *Target, EdgeType::Return, retSourceStep, false, retTargetStep);
×
NEW
542
            }
×
NEW
543
        }
×
NEW
544
        ReturnEdgeTargets_.erase(It);
×
NEW
545
    }
×
546
    // For remaining targets, add edges from fallbacks (callee nodes)
NEW
547
    for (const auto &KV : ReturnEdgeTargets_) {
×
NEW
548
        llvm::StringRef FnName = KV.first();
×
NEW
549
        DOCC_DEBUG(llvm::dbgs() << "  No returns from " << FnName << "\n");
×
NEW
550
        for (auto [Target, Fallback, step] : KV.second) {
×
NEW
551
            addEdge(*Fallback, *Target, EdgeType::Return);
×
NEW
552
        }
×
NEW
553
    }
×
NEW
554
}
×
555

NEW
556
GlobalCFGNode &GlobalCFGBuilder::addNode(uint32_t modId, int64_t funcId) {
×
NEW
557
    auto V = boost::add_vertex(CFG_.Graph_);
×
NEW
558
    auto [It, Added] = CFG_.Nodes_.insert({V, std::make_unique<GlobalCFGNode>(V, modId, funcId)});
×
NEW
559
    assert(Added && "Vertex key is unique");
×
NEW
560
    auto &nd = *It->second;
×
NEW
561
    return nd;
×
NEW
562
}
×
563

NEW
564
void GlobalCFGAnalysis::addEntryPoint(llvm::GlobalValue::GUID Id, GlobalCFGNode *Node) {
×
NEW
565
    auto [ItEntrypoint, AddedEntrypoint] = EntryPoints_.insert({Id, Node});
×
NEW
566
    assert(AddedEntrypoint && "Duplicate entry-point");
×
NEW
567
}
×
568

NEW
569
GlobalCFGNode *GlobalCFGAnalysis::findNodeExternallyVisible(llvm::StringRef Name) const {
×
NEW
570
    auto It = EntryPoints_.find(llvm::GlobalValue::getGUID(Name));
×
NEW
571
    if (It == EntryPoints_.end()) return nullptr;
×
NEW
572
    return It->second;
×
NEW
573
}
×
574

575
// llvm::ErrorOr messages are useless, because they lack context:
576
// [docc_llvm_plugin] error: No such file or directory
NEW
577
static std::unique_ptr<llvm::MemoryBuffer> loadFile(llvm::StringRef Path) {
×
NEW
578
    auto FileOrErr = llvm::MemoryBuffer::getFile(Path);
×
NEW
579
    if (std::error_code EC = FileOrErr.getError()) {
×
NEW
580
        std::string File = Path.str();
×
NEW
581
        ExitOnErr(llvm::createStringError(llvm::inconvertibleErrorCode(), "No such file or directory: %s", File.c_str())
×
NEW
582
        );
×
NEW
583
        llvm_unreachable("Fatal error");
×
NEW
584
    }
×
NEW
585
    return std::move(*FileOrErr);
×
NEW
586
}
×
587

NEW
588
static bool isNullHash(const llvm::ModuleHash &Hash) {
×
NEW
589
    for (auto Component : Hash)
×
NEW
590
        if (Component != 0) return false;
×
NEW
591
    return true;
×
NEW
592
}
×
593

NEW
594
void GlobalCFGAnalysis::run(AnalysisManager &am) {
×
NEW
595
    auto &registry = am.get<analysis::SDFGRegistry>();
×
596

NEW
597
    auto CombinedIndex = registry.combined_index_.get();
×
NEW
598
    DOCC_DEBUG(CombinedIndex->print(llvm::dbgs()));
×
599

NEW
600
    DOCC_DEBUG(llvm::dbgs() << "\nBuilding global CFG:\n");
×
NEW
601
    GlobalCFGBuilder Builder(*this, *CombinedIndex, registry);
×
602

NEW
603
    for (auto &Entry : *CombinedIndex) {
×
NEW
604
        llvm::GlobalValue::GUID Id = Entry.first;
×
NEW
605
        llvm::GlobalValueSummary *S = CombinedIndex->getGlobalValueSummary(Id);
×
NEW
606
        if (S->getSummaryKind() == llvm::GlobalValueSummary::FunctionKind) {
×
NEW
607
            auto func_id = next_func_id_++;
×
NEW
608
            GlobalCFGNode &N = Builder.addNodeGlobal(getModuleId(S->modulePath()), func_id, Id);
×
609

NEW
610
            addEntryPoint(Id, &N);
×
NEW
611
        }
×
NEW
612
    }
×
613

614
    // attach known, global SDFGs to EntryNodes
NEW
615
    for (const auto &Entry : CombinedIndex->modulePaths()) {
×
NEW
616
        auto path = Entry.first();
×
617

NEW
618
        for (auto &[name, holder] : registry.at(path)) {
×
NEW
619
            auto guid = llvm::GlobalValue::getGUID(name);
×
NEW
620
            auto it = EntryPoints_.find(guid);
×
NEW
621
            if (it != EntryPoints_.end()) {
×
NEW
622
                it->second->sdfg_ = holder.get();
×
NEW
623
                it->second->Name_ = name;
×
NEW
624
            }
×
NEW
625
        }
×
NEW
626
    }
×
627

628
    //    DOCC_WAIT_FOR_DEBUGGER("GlobalCFGBuilder");
629

NEW
630
    for (const auto &Entry : CombinedIndex->modulePaths()) {
×
NEW
631
        if (isNullHash(Entry.second)) continue; // [Regular LTO] pseudo-module
×
NEW
632
        llvm::StringRef Path = Entry.first();
×
NEW
633
        std::unique_ptr<llvm::Module> Mod = registry.get_module(Path.str(), Ctx_);
×
634

NEW
635
        DOCC_DEBUG(llvm::dbgs() << "  Add module to global CFG: " << Mod->getName() << "\n");
×
NEW
636
        Builder.addModule(getModuleId(Path), *Mod);
×
NEW
637
        DOCC_DEBUG(llvm::dbgs() << "  Global nodes: " << Nodes_.size() << "\n");
×
638

NEW
639
        Modules_.push_back(std::move(Mod));
×
NEW
640
    }
×
641

NEW
642
    DOCC_DEBUG(llvm::dbgs() << "\nAdding return edges in global CFG:\n");
×
NEW
643
    Builder.insertReturnEdges();
×
NEW
644
}
×
645

NEW
646
uint32_t GlobalCFGAnalysis::getModuleId(llvm::StringRef modPath) {
×
NEW
647
    auto it = moduleIds_.find(modPath);
×
NEW
648
    if (it != moduleIds_.end()) {
×
NEW
649
        return it->second;
×
NEW
650
    } else {
×
NEW
651
        auto next = moduleIds_.size();
×
NEW
652
        moduleIds_[modPath] = next;
×
NEW
653
        return next;
×
NEW
654
    }
×
NEW
655
}
×
656

657
} // namespace docc::analysis
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc