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

daisytuner / docc / 26556322966

27 May 2026 03:45PM UTC coverage: 60.869% (-0.02%) from 60.886%
26556322966

push

github

web-flow
Libnode ptr edges (#719)

Migrating SDFGs to treat pointers as inputs to libNodes / Calls as scalars.
A pointer will only appear in an output edge if its actually returned from the function (like malloc).

* Stdlib, Blas and Tensor Matmul nodes were migrated to this new format. Other, currently transitory Tensor Nodes are not yet migrated.
* DOCC version was bumped to incorporate previous docc-llvm versions (up to 0.4.0) that had been counted separately.
! Until all passes consider the use / leak of pointers as uncertainty / hiding potential writes, TensorNodes are declared as general side-effect.
* Lots of utility functions to centralize the creation (and edges) of various libNodes that needed to be changed.
* Fixed & unified docc paths across python and llvm front-ends.
* Skip BlockFusion test that fails to its libNodes currently having side effects
~ Prevent a crash in DotViz when using symbolic offsets into structs
* Removing old ConstProp pass, it is not safe for the new pointer representation and should not be all too critical

961 of 1749 new or added lines in 52 files covered. (54.95%)

87 existing lines in 28 files now uncovered.

35225 of 57870 relevant lines covered (60.87%)

11046.32 hits per line

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

0.68
/llvm/src/lifting/function_to_sdfg.cpp
1
#include "docc/lifting/function_to_sdfg.h"
2

3
#include <llvm/IR/Function.h>
4
#include <llvm/IR/IntrinsicInst.h>
5
#include <llvm/IR/Verifier.h>
6
#include <llvm/Transforms/Utils/Cloning.h>
7
#include <llvm/Transforms/Utils/CodeExtractor.h>
8
#include <llvm/Transforms/Utils/ModuleUtils.h>
9

10
#include <docc/target/tenstorrent/math_node_implementation_override_pass.h>
11
#include <docc/target/tenstorrent/tenstorrent_transform.h>
12
#include <sdfg/analysis/analysis.h>
13
#include <sdfg/analysis/users.h>
14
#include <sdfg/builder/sdfg_builder.h>
15
#include <sdfg/helpers/helpers.h>
16
#include <sdfg/passes/debug_info_propagation.h>
17
#include <sdfg/passes/normalization/loop_normal_form.h>
18
#include <sdfg/passes/opt_pipeline.h>
19
#include <sdfg/passes/pipeline.h>
20
#include <sdfg/passes/schedules/expansion_pass.h>
21
#include <sdfg/passes/structured_control_flow/pointer_evolution.h>
22
#include <sdfg/passes/structured_control_flow/unify_loop_exits.h>
23
#include <sdfg/passes/structured_control_flow/while_to_for_conversion.h>
24
#include <sdfg/passes/symbolic/symbol_promotion.h>
25
#include <sdfg/passes/symbolic/type_minimization.h>
26

27
#include "docc/analysis/sdfg_registry.h"
28
#include "docc/lifting/functions/function_lifting.h"
29
#include "docc/lifting/lift_report.h"
30
#include "docc/lifting/lifting.h"
31
#include "docc/utils.h"
32
#include "sdfg/visualizer/dot_visualizer.h"
33

34
llvm::cl::opt<std::string> DOCC_expand(
35
    "docc-expand",
36
    llvm::cl::desc("Overrides when to expand library nodes"),
37
    llvm::cl::init("none"),
38
    llvm::cl::value_desc("none|all")
39
);
40

41
namespace docc {
42
namespace lifting {
43

44
std::unique_ptr<llvm::Region> FunctionToSDFG::expand_region(std::unique_ptr<llvm::Region>& R) {
×
45
    std::unique_ptr<llvm::Region> current = std::move(R);
×
46
    std::unique_ptr<llvm::Region> last_valid = nullptr;
×
47
    while (current) {
×
48
        auto res = this->can_be_applied(*current);
×
49
        if (!res.first) break;
×
50

51
        last_valid = std::move(current);
×
52
        current = std::unique_ptr<llvm::Region>(last_valid->getExpandedRegion());
×
53
    }
×
54

55
    return last_valid;
×
56
}
×
57

58
bool FunctionToSDFG::is_blacklisted(llvm::Function& F, bool apply_on_linkonce_odr) {
1✔
59
    // Variadic functions
60
    if (F.isVarArg()) {
1✔
61
        return true;
1✔
62
    }
1✔
63

64
    // clang
65
    if (F.getName().starts_with("__clang")) {
×
66
        return true;
×
67
    }
×
68
    // c++, e.g., __cxx_global_var_init
69
    if (F.getName().starts_with("__cxx")) {
×
70
        return true;
×
71
    }
×
72
    // daisy, previously lifted
73
    if (F.getName().starts_with("__daisy")) {
×
74
        return true;
×
75
    }
×
76
    // global ctors
77
    if (F.hasSection() && F.getSection().contains("startup")) {
×
78
        return true;
×
79
    }
×
80
    // global dtors
81
    if (F.hasSection() && F.getSection().contains("exit")) {
×
82
        return true;
×
83
    }
×
84

85
    // aliased functions cannot be modified
86
    llvm::Module* Mod = F.getParent();
×
87
    for (const auto& alias : Mod->aliases()) {
×
88
        if (alias.getAliasee() == &F) {
×
89
            return true;
×
90
        }
×
91
    }
×
92

93
    // LinkOnceODR only if explicitly allowed
94
    if (apply_on_linkonce_odr) {
×
95
        if (F.getLinkage() == llvm::GlobalValue::LinkOnceODRLinkage) {
×
96
            return false;
×
97
        } else {
×
98
            return true;
×
99
        }
×
100
    }
×
101

102
    // Supported linkage: external, internal, private
103
    if (F.getLinkage() != llvm::GlobalValue::ExternalLinkage && F.getLinkage() != llvm::GlobalValue::InternalLinkage &&
×
104
        F.getLinkage() != llvm::GlobalValue::PrivateLinkage) {
×
105
        return true;
×
106
    }
×
107

108
    // No optnone, alwaysinline
109
    // if (F.hasFnAttribute(llvm::Attribute::OptimizeNone) ||
110
    //     F.hasFnAttribute(llvm::Attribute::AlwaysInline)) {
111
    //     return true;
112
    // }
113

114
    return false;
×
115
}
×
116

117
FunctionToSDFG::FunctionToSDFG(llvm::Function& function, llvm::FunctionAnalysisManager& FAM, bool apply_on_linkonce_odr)
118
    : function_(function), FAM_(FAM), sdfg_counter(0), apply_on_linkonce_odr_(apply_on_linkonce_odr) {}
×
119

120
std::vector<std::unique_ptr<sdfg::StructuredSDFG>> FunctionToSDFG::run() {
×
121
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
122
    llvm::LibFunc lf;
×
123
    if (TLI.getLibFunc(this->function_.getName(), lf)) {
×
124
        return {};
×
125
    }
×
126

127
    // Attempt lifting the entire function
128
    auto res = this->can_be_applied();
×
129
    if (res.first) {
×
130
        try {
×
131
            auto sdfg = this->apply();
×
132
            if (sdfg) {
×
133
                std::vector<std::unique_ptr<sdfg::StructuredSDFG>> sdfgs;
×
134
                sdfgs.push_back(std::move(sdfg));
×
135
                return sdfgs;
×
136
            }
×
137
        } catch (sdfg::UnstructuredControlFlowException& e) {
×
138
            // Fallthrough
139
            LLVM_DEBUG_PRINTLN("UnstructuredControlFlowException on '" << this->function_.getName() << "': " << e.what());
×
140
        } catch (NotImplementedException& e) {
×
141
            // Fallthrough
142
            LLVM_DEBUG_PRINTLN("NotImplementedException on '" << this->function_.getName() << "': " << e.what());
×
143
        } catch (sdfg::InvalidSDFGException& e) {
×
144
            // Fallthrough
145
            LLVM_DEBUG_PRINTLN("InvalidSDFGException on '" << this->function_.getName() << "': " << e.what());
×
146
        }
×
147
    } else {
×
148
        if (res.second != nullptr) {
×
149
            sdfg::DebugInfo dbg_info;
×
150
            if (auto* inst = llvm::dyn_cast<llvm::Instruction>(res.second)) {
×
151
                dbg_info = ::docc::utils::get_debug_info(*inst);
×
152
            }
×
153
            LiftingReport::add_failed_lift(dbg_info, "Unsupported instruction", ::docc::utils::toIRString(*res.second));
×
154
        }
×
155

156
        // For LinkOnceODR, we must give up if the entire function cannot be lifted
157
        if (this->function_.getLinkage() == llvm::GlobalValue::LinkOnceODRLinkage) {
×
158
            return {};
×
159
        }
×
160
    }
×
161

162
    // Attempt lifting of Single-Entry-Single-Exit regions
163
    auto& RI = this->FAM_.getResult<llvm::RegionInfoAnalysis>(this->function_);
×
164
    std::list<std::unique_ptr<llvm::Region>> canonical_regions;
×
165
    for (auto& sub : *RI.getTopLevelRegion()) {
×
166
        canonical_regions.push_back(std::move(sub));
×
167
    }
×
168

169
    std::vector<std::unique_ptr<sdfg::StructuredSDFG>> sdfgs;
×
170
    while (!canonical_regions.empty()) {
×
171
        auto canon_region = std::move(canonical_regions.front());
×
172
        canonical_regions.pop_front();
×
173

174
        auto expanded_region = this->expand_region(canon_region);
×
175
        if (!expanded_region) {
×
176
            continue;
×
177
        }
×
178

179
        // Collect subregions
180
        std::list<llvm::Region*> subregions;
×
181
        for (auto& sub : canonical_regions) {
×
182
            if (expanded_region->contains(sub.get())) {
×
183
                subregions.push_back(sub.get());
×
184
            }
×
185
        }
×
186

187
        if (FunctionToSDFG::loop_count(this->function_, *expanded_region, this->FAM_) < 2) {
×
188
            for (auto& subregion : subregions) {
×
189
                for (auto it = canonical_regions.begin(); it != canonical_regions.end(); ++it) {
×
190
                    if (it->get() == subregion) {
×
191
                        canonical_regions.erase(it);
×
192
                        break;
×
193
                    }
×
194
                }
×
195
            }
×
196
            continue;
×
197
        }
×
198

199
        try {
×
200
            auto sdfg = this->apply(*expanded_region);
×
201
            if (sdfg) {
×
202
                sdfgs.push_back(std::move(sdfg));
×
203
                for (auto& subregion : subregions) {
×
204
                    for (auto it = canonical_regions.begin(); it != canonical_regions.end(); ++it) {
×
205
                        if (it->get() == subregion) {
×
206
                            canonical_regions.erase(it);
×
207
                            break;
×
208
                        }
×
209
                    }
×
210
                }
×
211
                continue;
×
212
            }
×
213
        } catch (sdfg::UnstructuredControlFlowException& e) {
×
214
            // Fallthrough
215
        } catch (NotImplementedException& e) {
×
216
            // Fallthrough
217
        } catch (sdfg::InvalidSDFGException& e) {
×
218
            // Fallthrough
219
        }
×
220

221
        // Region failed, give up
222
        break;
×
223
    }
×
224

225
    return sdfgs;
×
226
}
×
227

228
std::pair<bool, llvm::Value*> FunctionToSDFG::can_be_applied(llvm::Region& region) {
×
229
    // Criterion: Regions must be extractable into functions
230
    llvm::SmallVector<llvm::BasicBlock*> blocks;
×
231
    for (auto block : region.blocks()) {
×
232
        blocks.push_back(block);
×
233
    }
×
234
    llvm::CodeExtractor code_extractor(
×
235
        blocks,
×
236
        nullptr, // DT
×
237
        false, // Aggregate args
×
238
        nullptr, // BFI
×
239
        nullptr, // BPI
×
240
        nullptr, // AC
×
241
        false, // Var args
×
242
        false, // Allow alloca
×
243
        nullptr, // Alloc block
×
244
        "" // Suffix
×
245
    );
×
246
    if (!code_extractor.isEligible()) {
×
247
        return {false, nullptr};
×
248
    }
×
249

250
    // Criterion: No unsupported instructions
251
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
252
    for (auto block : region.blocks()) {
×
253
        for (auto& inst : *block) {
×
254
            // TODO: Switch
255
            if (llvm::dyn_cast<const llvm::SwitchInst>(&inst)) {
×
256
                return {false, &inst};
×
257
            }
×
258
            if (auto phi_inst = llvm::dyn_cast<const llvm::PHINode>(&inst)) {
×
259
                for (size_t i = 0; i < phi_inst->getNumIncomingValues(); ++i) {
×
260
                    auto phi_value = phi_inst->getIncomingValue(i);
×
261
                    // Must be first-class type: scalar or pointer
262
                    if (phi_value->getType()->isVectorTy() || phi_value->getType()->isAggregateType()) {
×
263
                        return {false, &inst};
×
264
                    }
×
265
                }
×
266
            }
×
267

268
            // Poison
269
            if (llvm::dyn_cast<const llvm::FreezeInst>(&inst)) {
×
270
                return {false, &inst};
×
271
            }
×
272

273
            // Unsafe casts
274
            if (llvm::isa<llvm::AddrSpaceCastInst>(&inst)) {
×
275
                return {false, &inst};
×
276
            } else if (llvm::isa<llvm::BitCastInst>(&inst)) {
×
277
                return {false, &inst};
×
278
            } else if (llvm::isa<llvm::IntToPtrInst>(&inst)) {
×
279
                return {false, &inst};
×
280
            }
×
281

282
            // Atomic operations
283
            if (llvm::isa<const llvm::AtomicRMWInst>(&inst)) {
×
284
                return {false, &inst};
×
285
            } else if (llvm::isa<const llvm::AtomicCmpXchgInst>(&inst)) {
×
286
                return {false, &inst};
×
287
            } else if (llvm::isa<const llvm::FenceInst>(&inst)) {
×
288
                return {false, &inst};
×
289
            }
×
290

291
            // Function calls
292
            if (auto call_base = llvm::dyn_cast<const llvm::CallBase>(&inst)) {
×
293
                if (!FunctionLifting::is_supported(TLI, call_base)) {
×
294
                    return {false, &inst};
×
295
                }
×
296
            }
×
297
            if (auto landing_pad = llvm::dyn_cast<const llvm::LandingPadInst>(&inst)) {
×
298
                return {false, &inst};
×
299
            }
×
300
            if (auto resume = llvm::dyn_cast<const llvm::ResumeInst>(&inst)) {
×
301
                return {false, &inst};
×
302
            }
×
303

304
            // Constant expressions
305
            for (const llvm::Use& U : inst.operands()) {
×
306
                if (llvm::isa<llvm::ConstantExpr>(U.get())) {
×
307
                    return {false, &inst};
×
308
                }
×
309
            }
×
310

311
            // Not implemented instructions
312
            if (llvm::isa<const llvm::ExtractValueInst>(&inst)) {
×
313
                return {false, &inst};
×
314
            }
×
315
            if (llvm::isa<const llvm::InsertValueInst>(&inst)) {
×
316
                return {false, &inst};
×
317
            }
×
318
            if (llvm::isa<const llvm::InsertElementInst>(&inst)) {
×
319
                return {false, &inst};
×
320
            }
×
321

322
            // Unsupported types
323
            auto output_type = inst.getType();
×
324
            if (output_type->isIntegerTy()) {
×
325
                switch (output_type->getIntegerBitWidth()) {
×
326
                    case 1:
×
327
                    case 8:
×
328
                    case 16:
×
329
                    case 32:
×
330
                    case 64:
×
331
                    case 128:
×
332
                        break;
×
333
                    default: {
×
334
                        return {false, &inst};
×
335
                    }
×
336
                }
×
337
            }
×
338
        }
×
339
        llvm::Instruction* terminator = block->getTerminator();
×
340
        if (!llvm::isa<llvm::UnreachableInst>(terminator) && !llvm::isa<llvm::ReturnInst>(terminator) &&
×
341
            !llvm::isa<llvm::BranchInst>(terminator) && !llvm::isa<llvm::InvokeInst>(terminator)) {
×
342
            return {false, terminator};
×
343
        }
×
344
    }
×
345

346
    return {true, nullptr};
×
347
}
×
348

349
std::pair<bool, llvm::Value*> FunctionToSDFG::can_be_applied() {
×
350
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
351

352
    // Criterion: No unsupported globals' initializers
353
    std::unordered_set<llvm::GlobalObject*> globals;
×
354
    Lifting::collect_globals(this->function_, globals);
×
355
    for (llvm::GlobalObject* GV : globals) {
×
356
        switch (GV->getLinkage()) {
×
357
            case llvm::GlobalValue::LinkageTypes::ExternalLinkage:
×
358
            case llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage:
×
359
            case llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage:
×
360
            case llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage:
×
361
            case llvm::GlobalValue::LinkageTypes::WeakAnyLinkage:
×
362
            case llvm::GlobalValue::LinkageTypes::WeakODRLinkage: {
×
363
                // Always allowed
364
                continue;
×
365
            }
×
366
            case llvm::GlobalValue::LinkageTypes::InternalLinkage: {
×
367
                // Renamed to globally unique and set to external linkage
368
                // Thus, initializer need not be lifted
369
                continue;
×
370
            }
×
371
            case llvm::GlobalValue::LinkageTypes::PrivateLinkage: {
×
372
                // Renamed to globally unique and set to external linkage
373
                // Thus, initializer need not be lifted
374
                continue;
×
375
            }
×
376
            default:
×
377
                return {false, GV};
×
378
        }
×
379
    }
×
380

381
    // Criterion: No unsupported instructions
382
    for (auto& block : this->function_) {
×
383
        for (auto& inst : block) {
×
384
            // TODO: Switch
385
            if (llvm::dyn_cast<const llvm::SwitchInst>(&inst)) {
×
386
                return {false, &inst};
×
387
            }
×
388
            if (auto phi_inst = llvm::dyn_cast<const llvm::PHINode>(&inst)) {
×
389
                for (size_t i = 0; i < phi_inst->getNumIncomingValues(); ++i) {
×
390
                    auto phi_value = phi_inst->getIncomingValue(i);
×
391
                    // Must be first-class type: scalar or pointer
392
                    if (phi_value->getType()->isVectorTy() || phi_value->getType()->isAggregateType()) {
×
393
                        return {false, &inst};
×
394
                    }
×
395
                }
×
396
            }
×
397

398
            // Poison
399
            if (llvm::dyn_cast<const llvm::FreezeInst>(&inst)) {
×
400
                return {false, &inst};
×
401
            }
×
402

403
            // Unsafe casts
404
            if (llvm::isa<llvm::AddrSpaceCastInst>(&inst)) {
×
405
                return {false, &inst};
×
406
            } else if (llvm::isa<llvm::BitCastInst>(&inst)) {
×
407
                return {false, &inst};
×
408
            } else if (llvm::isa<llvm::IntToPtrInst>(&inst)) {
×
409
                return {false, &inst};
×
410
            }
×
411

412
            // Atomic operations
413
            if (llvm::isa<const llvm::AtomicRMWInst>(&inst)) {
×
414
                return {false, &inst};
×
415
            } else if (llvm::isa<const llvm::AtomicCmpXchgInst>(&inst)) {
×
416
                return {false, &inst};
×
417
            } else if (llvm::isa<const llvm::FenceInst>(&inst)) {
×
418
                return {false, &inst};
×
419
            }
×
420

421
            // Function calls
422
            if (auto call_base = llvm::dyn_cast<const llvm::CallBase>(&inst)) {
×
423
                if (!FunctionLifting::is_supported(TLI, call_base)) {
×
424
                    return {false, &inst};
×
425
                }
×
426
            }
×
427
            if (auto landing_pad = llvm::dyn_cast<const llvm::LandingPadInst>(&inst)) {
×
428
                return {false, &inst};
×
429
            }
×
430
            if (auto resume = llvm::dyn_cast<const llvm::ResumeInst>(&inst)) {
×
431
                return {false, &inst};
×
432
            }
×
433

434
            // Constant expressions
435
            for (const llvm::Use& U : inst.operands()) {
×
436
                if (llvm::isa<llvm::ConstantExpr>(U.get())) {
×
437
                    return {false, &inst};
×
438
                }
×
439
            }
×
440

441
            // Not implemented instructions
442
            if (llvm::isa<const llvm::ExtractValueInst>(&inst)) {
×
443
                return {false, &inst};
×
444
            }
×
445
            if (llvm::isa<const llvm::InsertValueInst>(&inst)) {
×
446
                return {false, &inst};
×
447
            }
×
448
            if (llvm::isa<const llvm::InsertElementInst>(&inst)) {
×
449
                return {false, &inst};
×
450
            }
×
451

452
            // Unsupported types
453
            auto output_type = inst.getType();
×
454
            if (output_type->isIntegerTy()) {
×
455
                switch (output_type->getIntegerBitWidth()) {
×
456
                    case 1:
×
457
                    case 8:
×
458
                    case 16:
×
459
                    case 32:
×
460
                    case 64:
×
461
                    case 128:
×
462
                        break;
×
463
                    default: {
×
464
                        return {false, &inst};
×
465
                    }
×
466
                }
×
467
            }
×
468
        }
×
469
        llvm::Instruction* terminator = block.getTerminator();
×
470
        if (!llvm::isa<llvm::UnreachableInst>(terminator) && !llvm::isa<llvm::ReturnInst>(terminator) &&
×
471
            !llvm::isa<llvm::BranchInst>(terminator) && !llvm::isa<llvm::InvokeInst>(terminator)) {
×
472
            return {false, terminator};
×
473
        }
×
474
    }
×
475

476
    return {true, nullptr};
×
477
}
×
478

479
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::apply(llvm::Region& region) {
×
480
    std::filesystem::path module_path = this->function_.getParent()->getName().str();
×
481
    std::string module_name = module_path.stem();
×
482

483
    // Refactor region into separate function
484
    llvm::SmallVector<llvm::BasicBlock*> blocks;
×
485
    for (auto block : region.blocks()) {
×
486
        blocks.push_back(block);
×
487
    }
×
488

489
    llvm::CodeExtractor code_extractor(
×
490
        blocks,
×
491
        nullptr, // DT
×
492
        false, // Aggregate args
×
493
        nullptr, // BFI
×
494
        nullptr, // BPI
×
495
        nullptr, // AC
×
496
        false, // Var args
×
497
        false, // Allow alloca
×
498
        nullptr, // Alloc block
×
499
        "" // Suffix
×
500
    );
×
501
    assert(code_extractor.isEligible());
×
502
    llvm::CodeExtractorAnalysisCache CEAC(this->function_);
×
503
    llvm::Function* external_function = code_extractor.extractCodeRegion(CEAC);
×
504

505
    // Set name of new function
506
    std::string new_function_name = ::docc::utils::hash_function_name(utils::get_name(external_function));
×
507
    new_function_name = utils::normalize_name(module_name) + utils::normalize_name(new_function_name);
×
508
    new_function_name = "__daisy_" + new_function_name + "_" + std::to_string(sdfg_counter++);
×
509
    external_function->setName(new_function_name);
×
510
    external_function->setLinkage(this->function_.getLinkage());
×
511

512
    // Criterion: No unsupported globals' initializers
513
    std::unordered_set<llvm::GlobalObject*> globals;
×
514
    Lifting::collect_globals(*external_function, globals);
×
515
    for (llvm::GlobalObject* GV : globals) {
×
516
        switch (GV->getLinkage()) {
×
517
            case llvm::GlobalValue::LinkageTypes::ExternalLinkage:
×
518
            case llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage:
×
519
            case llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage:
×
520
            case llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage:
×
521
            case llvm::GlobalValue::LinkageTypes::WeakAnyLinkage:
×
522
            case llvm::GlobalValue::LinkageTypes::WeakODRLinkage: {
×
523
                // Always allowed
524
                continue;
×
525
            }
×
526
            case llvm::GlobalValue::LinkageTypes::InternalLinkage: {
×
527
                // Renamed to globally unique and set to external linkage
528
                // Thus, initializer need not be lifted
529
                continue;
×
530
            }
×
531
            case llvm::GlobalValue::LinkageTypes::PrivateLinkage: {
×
532
                // Renamed to globally unique and set to external linkage
533
                // Thus, initializer need not be lifted
534
                continue;
×
535
            }
×
536
            default:
×
537
                return nullptr;
×
538
        }
×
539
    }
×
540

541
    // Lift SDFG
542
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
543
    Lifting lifting(TLI, *external_function, sdfg::FunctionType_CPU);
×
544
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
545
    sdfg->validate();
×
546

547
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
548
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
549
    unify_loop_exits_pass.run(builder_canon);
×
550
    unify_loop_exits_pass.run(builder_canon);
×
551
    unify_loop_exits_pass.run(builder_canon);
×
552
    sdfg = builder_canon.move();
×
553

554
    // Build StructuredSDFG
555
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
556

557
    // Propagate debug info
558
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
559
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
560
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
561

562
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(*external_function));
×
563
    auto structured_sdfg = structured_builder.move();
×
564

565
    // Simplify SDFG
566
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
567

568
    // Prevent further inlining
569
    external_function->addFnAttr(llvm::Attribute::NoInline);
×
570
    external_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
571
    llvm::appendToUsed(*this->function_.getParent(), {external_function});
×
572

573
    bool verify_dbg = false;
×
574
    bool failed = llvm::verifyModule(*this->function_.getParent(), &llvm::errs(), &verify_dbg);
×
575
    if (failed) {
×
576
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
577
    }
×
578

579
    return simplified_sdfg;
×
580
}
×
581

582
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::apply() {
×
583
    // Lift SDFG
584
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
585
    Lifting lifting(TLI, this->function_, sdfg::FunctionType_CPU);
×
586
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
587
    sdfg->validate();
×
588

589
    // Increase of graph complexity
590
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
591
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
592
    unify_loop_exits_pass.run(builder_canon);
×
593
    unify_loop_exits_pass.run(builder_canon);
×
594
    unify_loop_exits_pass.run(builder_canon);
×
595
    sdfg = builder_canon.move();
×
596

597
    // Build StructuredSDFG
598
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
599

600
    // Propagate debug info
601
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
602
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
603
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
604

605
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(this->function_));
×
606
    auto structured_sdfg = structured_builder.move();
×
607

608
    // Simplify SDFG
609
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
610

611
    // If LinkOnceODR, internalize original function
612
    if (this->function_.getLinkage() == llvm::GlobalValue::LinkOnceODRLinkage) {
×
613
        std::string module_path = this->function_.getParent()->getName().str();
×
614
        std::string module_name = std::filesystem::path(module_path).stem().string();
×
615

616
        llvm::Function* internal_function = llvm::Function::Create(
×
617
            this->function_.getFunctionType(),
×
618
            llvm::GlobalValue::ExternalLinkage,
×
619
            "__daisy_odr_" + utils::normalize_name(module_name) + "_" + this->function_.getName(),
×
620
            this->function_.getParent()
×
621
        );
×
622
        internal_function->copyAttributesFrom(&this->function_);
×
623
        internal_function->setComdat(nullptr);
×
624

625
        // Map arguments and the function itself for recursion
626
        llvm::ValueToValueMapTy VMap;
×
627
        auto dest_arg_it = internal_function->arg_begin();
×
628
        for (auto& src_arg : this->function_.args()) {
×
629
            dest_arg_it->setName(src_arg.getName());
×
630
            VMap[&src_arg] = &*dest_arg_it++;
×
631
        }
×
632
        VMap[&this->function_] = internal_function; // Handle recursive calls
×
633

634
        llvm::SmallVector<llvm::ReturnInst*, 8> Returns;
×
635
        // Clone the function body
636
        llvm::CloneFunctionInto(
×
637
            internal_function, &this->function_, VMap, llvm::CloneFunctionChangeType::LocalChangesOnly, Returns
×
638
        );
×
639

640
        // Redirect all uses in this module to the clone
641
        llvm::SmallVector<llvm::Use*, 16> Uses;
×
642
        for (llvm::Use& U : this->function_.uses()) {
×
643
            Uses.push_back(&U);
×
644
        }
×
645
        for (llvm::Use* U : Uses) {
×
646
            U->set(internal_function);
×
647
        }
×
648

649
        // Rename sdfg to match internal function
650
        simplified_sdfg->name(internal_function->getName().str());
×
651

652
        // Prevent further inlining
653
        internal_function->addFnAttr(llvm::Attribute::NoInline);
×
654
        internal_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
655
        llvm::appendToUsed(*internal_function->getParent(), {internal_function});
×
656

657
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
658
    } else {
×
659
        // Prevent further inlining
660
        this->function_.addFnAttr(llvm::Attribute::NoInline);
×
661
        this->function_.addFnAttr(llvm::Attribute::OptimizeNone);
×
662
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
663
    }
×
664

665
    bool verify_dbg = false;
×
666
    bool failed = llvm::verifyModule(*this->function_.getParent(), &llvm::errs(), &verify_dbg);
×
667
    if (failed) {
×
668
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
669
    }
×
670

671
    return simplified_sdfg;
×
672
}
×
673

674
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::simplify(std::unique_ptr<sdfg::StructuredSDFG>& sdfg) {
×
675
    sdfg::builder::StructuredSDFGBuilder builder_opt(sdfg);
×
676
    sdfg::analysis::AnalysisManager analysis_manager(builder_opt.subject());
×
677

NEW
678
    dump_sdfg(builder_opt.subject(), "0.init");
×
679

680
    // Optimization Pipelines
681
    sdfg::passes::Pipeline dataflow_simplification = sdfg::passes::Pipeline::dataflow_simplification();
×
682
    sdfg::passes::Pipeline symbolic_simplification = sdfg::passes::Pipeline::symbolic_simplification();
×
683
    sdfg::passes::Pipeline dce = sdfg::passes::Pipeline::dead_code_elimination();
×
684
    sdfg::passes::Pipeline memlet_combine = sdfg::passes::Pipeline::memlet_combine();
×
685
    sdfg::passes::DeadDataElimination dde;
×
686
    sdfg::passes::SymbolPropagation symbol_propagation_pass;
×
687

688
    // Promote tasklets into symbolic assignments
689
    sdfg::passes::SymbolPromotion symbol_promotion_pass;
×
690
    symbol_promotion_pass.run(builder_opt, analysis_manager);
×
691

692
    // Expand library nodes if requested
693
    if (DOCC_expand == "tenstorrent") {
×
694
        LLVM_DEBUG_PRINTLN("Overriding all library nodes to Tenstorrent");
×
695
        auto pass = sdfg::tenstorrent::MathNodeImplementationOverridePass();
×
696
        bool success = pass.run(builder_opt, analysis_manager);
×
697
    } else if (DOCC_expand != "none") {
×
698
        LLVM_DEBUG_PRINTLN("Expanding all library nodes");
×
699
        auto expansion_pass = sdfg::passes::ExpansionPass();
×
700
        bool expanded = expansion_pass.run(builder_opt, analysis_manager);
×
701
    }
×
702

703
    /***** SDFG Minimization *****/
704

705
    // Minimize SDFG by fusing blocks, tasklets and sequences
706
    dataflow_simplification.run(builder_opt, analysis_manager);
×
707
    dde.run(builder_opt, analysis_manager);
×
708
    dce.run(builder_opt, analysis_manager);
×
709

710
    // Minimize SDFG by fusing symbolic expressions
711
    symbolic_simplification.run(builder_opt, analysis_manager);
×
712
    dde.run(builder_opt, analysis_manager);
×
713
    dce.run(builder_opt, analysis_manager);
×
714

NEW
715
    dump_sdfg(builder_opt.subject(), "1.dde");
×
716

717
    /***** Structured Loops *****/
718

719
    // Unify continue/break inside branches
720
    {
×
721
        sdfg::passes::CommonAssignmentElimination common_assignment_elimination;
×
722
        bool applies = false;
×
723
        do {
×
724
            applies = false;
×
725
            applies |= common_assignment_elimination.run(builder_opt, analysis_manager);
×
726
        } while (applies);
×
727
        dde.run(builder_opt, analysis_manager);
×
728
        dce.run(builder_opt, analysis_manager);
×
729
        symbolic_simplification.run(builder_opt, analysis_manager);
×
730
    }
×
731

NEW
732
    dump_sdfg(builder_opt.subject(), "2.cae");
×
733

734
    // Convert loops into structured loops
735
    sdfg::passes::WhileToForConversion for_conversion_pass;
×
736
    for_conversion_pass.run(builder_opt, analysis_manager);
×
737

738
    // Propagate for simpler indvar usage
739
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
740

NEW
741
    dump_sdfg(builder_opt.subject(), "4.symprop");
×
742

743
    // Eliminate redundant branches
744
    {
×
745
        bool applies = false;
×
746
        sdfg::passes::ConditionEliminationPass condition_elimination_pass;
×
747
        do {
×
748
            applies = false;
×
749
            applies |= condition_elimination_pass.run(builder_opt, analysis_manager);
×
750
        } while (applies);
×
751
    }
×
752

NEW
753
    dump_sdfg(builder_opt.subject(), "5.condelim");
×
754

755
    // Normalize loop condition and update (run twice)
756
    sdfg::passes::normalization::LoopNormalFormPass loop_normalization_pass;
×
757
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
758
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
759
    dde.run(builder_opt, analysis_manager);
×
760
    dce.run(builder_opt, analysis_manager);
×
761

NEW
762
    dump_sdfg(builder_opt.subject(), "6.loopnorm");
×
763

764
    // Eliminate symbols correlated to loop iterators
765
    sdfg::passes::SymbolEvolution symbol_evolution_pass;
×
766
    symbol_evolution_pass.run(builder_opt, analysis_manager);
×
767

NEW
768
    dump_sdfg(builder_opt.subject(), "7.symbevo");
×
769

770
    // Dead code elimination
771
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
772
    dde.run(builder_opt, analysis_manager);
×
773
    dce.run(builder_opt, analysis_manager);
×
774

NEW
775
    dump_sdfg(builder_opt.subject(), "8.dde");
×
776

777
    /***** Data Parallelism *****/
778

779
    // Combine address calculations in memlets
780
    memlet_combine.run(builder_opt, analysis_manager);
×
781

NEW
782
    dump_sdfg(builder_opt.subject(), "9.memletcomb");
×
783

784
    // Move code out of loops where possible
785
    sdfg::passes::Pipeline code_motion = sdfg::passes::code_motion();
×
786
    code_motion.run(builder_opt, analysis_manager);
×
787

NEW
788
    dump_sdfg(builder_opt.subject(), "10.codemotion");
×
789

790
    // Convert pointer-based iterators to indvar usage
791
    sdfg::passes::PointerEvolution pointer_evolution_pass;
×
792
    pointer_evolution_pass.run(builder_opt, analysis_manager);
×
793
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
794

NEW
795
    dump_sdfg(builder_opt.subject(), "11.pointerevo");
×
796

797
    // Convert lib-calls into managed memory
798
    sdfg::passes::Pipeline memory = sdfg::passes::Pipeline::memory();
×
799
    memory.run(builder_opt, analysis_manager);
×
800

NEW
801
    dump_sdfg(builder_opt.subject(), "12.memorymgmt");
×
802

803
    sdfg::passes::TypeMinimizationPass type_minimization_pass;
×
804
    type_minimization_pass.run(builder_opt, analysis_manager);
×
805
    type_minimization_pass.run(builder_opt, analysis_manager);
×
806

NEW
807
    dump_sdfg(builder_opt.subject(), "13.typemin");
×
808

809
    // Dead code elimination
810
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
811
    dce.run(builder_opt, analysis_manager);
×
812
    dde.run(builder_opt, analysis_manager);
×
813

NEW
814
    dump_sdfg(builder_opt.subject(), "14.dde");
×
815

816
    // Convert for loops into maps
817
    sdfg::passes::For2MapPass map_conversion_pass;
×
818
    map_conversion_pass.run(builder_opt, analysis_manager);
×
819

NEW
820
    dump_sdfg(builder_opt.subject(), "15.for2map");
×
821

822
    // Move code out of maps where possible
823
    code_motion.run(builder_opt, analysis_manager);
×
824

NEW
825
    dump_sdfg(builder_opt.subject(), "16.codemotion");
×
826

827
    // Dead code elimination
828
    dde.run(builder_opt, analysis_manager);
×
829
    dce.run(builder_opt, analysis_manager);
×
830
    dataflow_simplification.run(builder_opt, analysis_manager);
×
831

NEW
832
    dump_sdfg(builder_opt.subject(), "17.dde");
×
833

834
    return builder_opt.move();
×
NEW
835
}
×
836

NEW
837
void FunctionToSDFG::dump_sdfg(const sdfg::StructuredSDFG& sdfg, const std::string& step) const {
×
NEW
838
    if (dump_passes) {
×
NEW
839
        auto mod = function_.getParent();
×
840

NEW
841
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
842

NEW
843
        std::filesystem::create_directories(out_dir);
×
NEW
844
        sdfg::serializer::JSONSerializer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".json"));
×
NEW
845
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".dot"));
×
NEW
846
    }
×
UNCOV
847
};
×
848

849
} // namespace lifting
850
} // namespace docc
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