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

daisytuner / docc / 30101107686

24 Jul 2026 02:27PM UTC coverage: 64.202% (-0.001%) from 64.203%
30101107686

Pull #880

github

web-flow
Merge 0c7f8383e into 3fccdde4d
Pull Request #880: Llvm stability

26 of 44 new or added lines in 6 files covered. (59.09%)

42826 of 66705 relevant lines covered (64.2%)

733.04 hits per line

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

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

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

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

29
#include "docc/analysis/sdfg_registry.h"
30
#include "docc/cmd_args.h"
31
#include "docc/lifting/functions/function_lifting.h"
32
#include "docc/lifting/lift_report.h"
33
#include "docc/lifting/lifting.h"
34
#include "docc/utils.h"
35
#include "sdfg/visualizer/dot_visualizer.h"
36

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

44
namespace docc {
45
namespace lifting {
46

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

54
        last_valid = std::move(current);
×
55
        current = std::unique_ptr<llvm::Region>(last_valid->getExpandedRegion());
×
56
    }
×
57

58
    return last_valid;
×
59
}
×
60

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

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

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

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

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

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

117
    return false;
×
118
}
×
119

120
FunctionToSDFG::FunctionToSDFG(llvm::Function& function, llvm::FunctionAnalysisManager& FAM, bool apply_on_linkonce_odr)
121
    : function_(function), FAM_(FAM), sdfg_counter(0), apply_on_linkonce_odr_(apply_on_linkonce_odr) {}
×
122

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

130
    // Attempt lifting the entire function
131
    auto res = this->can_be_applied();
×
132
    if (res.first) {
×
133
        try {
×
134
            auto sdfg = this->apply();
×
135
            if (sdfg) {
×
136
                std::vector<std::unique_ptr<sdfg::StructuredSDFG>> sdfgs;
×
137
                sdfgs.push_back(std::move(sdfg));
×
138
                return sdfgs;
×
139
            }
×
140
        } catch (sdfg::UnstructuredControlFlowException& e) {
×
141
            // Fallthrough
142
            LLVM_DEBUG_PRINTLN("UnstructuredControlFlowException on '" << this->function_.getName() << "': " << e.what());
×
143
        } catch (NotImplementedException& e) {
×
144
            // Fallthrough
145
            LLVM_DEBUG_PRINTLN("NotImplementedException 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

NEW
349
void FunctionToSDFG::register_output_dir(sdfg::StructuredSDFG& sdfg) {
×
NEW
350
    auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*function_.getParent());
×
NEW
351
    sdfg.add_metadata("output_dir", out_dir.string());
×
NEW
352
}
×
353

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

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

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

403
            // Poison
404
            if (llvm::dyn_cast<const llvm::FreezeInst>(&inst)) {
×
405
                return {false, &inst};
×
406
            }
×
407

408
            // Unsafe casts
409
            if (llvm::isa<llvm::AddrSpaceCastInst>(&inst)) {
×
410
                return {false, &inst};
×
411
            } else if (llvm::isa<llvm::BitCastInst>(&inst)) {
×
412
                return {false, &inst};
×
413
            } else if (llvm::isa<llvm::IntToPtrInst>(&inst)) {
×
414
                return {false, &inst};
×
415
            }
×
416

417
            // Atomic operations
418
            if (llvm::isa<const llvm::AtomicRMWInst>(&inst)) {
×
419
                return {false, &inst};
×
420
            } else if (llvm::isa<const llvm::AtomicCmpXchgInst>(&inst)) {
×
421
                return {false, &inst};
×
422
            } else if (llvm::isa<const llvm::FenceInst>(&inst)) {
×
423
                return {false, &inst};
×
424
            }
×
425

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

439
            // Constant expressions
440
            for (const llvm::Use& U : inst.operands()) {
×
441
                if (llvm::isa<llvm::ConstantExpr>(U.get())) {
×
442
                    return {false, &inst};
×
443
                }
×
444
            }
×
445

446
            // Not implemented instructions
447
            if (llvm::isa<const llvm::ExtractValueInst>(&inst)) {
×
448
                return {false, &inst};
×
449
            }
×
450
            if (llvm::isa<const llvm::InsertValueInst>(&inst)) {
×
451
                return {false, &inst};
×
452
            }
×
453
            if (llvm::isa<const llvm::InsertElementInst>(&inst)) {
×
454
                return {false, &inst};
×
455
            }
×
456

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

481
    return {true, nullptr};
×
482
}
×
483

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

488
    // Refactor region into separate function
489
    llvm::SmallVector<llvm::BasicBlock*> blocks;
×
490
    for (auto block : region.blocks()) {
×
491
        blocks.push_back(block);
×
492
    }
×
493

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

510
    // Set name of new function
511
    std::string new_function_name = ::docc::utils::hash_function_name(utils::get_name(external_function));
×
512
    new_function_name = utils::normalize_name(module_name) + utils::normalize_name(new_function_name);
×
513
    new_function_name = "__daisy_" + new_function_name + "_" + std::to_string(sdfg_counter++);
×
514
    external_function->setName(new_function_name);
×
515
    external_function->setLinkage(this->function_.getLinkage());
×
516

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

546
    // Lift SDFG
547
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
548
    Lifting lifting(TLI, *external_function, sdfg::FunctionType_CPU);
×
549
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
550
    dump_llvm_function();
×
551
    dump_sdfg(*sdfg, "0");
×
552
    sdfg->validate();
×
553

554
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
555
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
556
    unify_loop_exits_pass.run(builder_canon);
×
557
    dump_sdfg(builder_canon.subject(), "1");
×
558
    unify_loop_exits_pass.run(builder_canon);
×
559
    dump_sdfg(builder_canon.subject(), "2");
×
560
    unify_loop_exits_pass.run(builder_canon);
×
561
    dump_sdfg(builder_canon.subject(), "3");
×
562
    sdfg = builder_canon.move();
×
563

564
    // Build StructuredSDFG
565
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
566

567
    // Propagate debug info
568
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
569
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
570
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
571

572
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(*external_function));
×
573
    auto structured_sdfg = structured_builder.move();
×
574

NEW
575
    register_output_dir(*structured_sdfg);
×
576

577
    // Simplify SDFG
578
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
579

580
    // Prevent further inlining
581
    external_function->addFnAttr(llvm::Attribute::NoInline);
×
582
    external_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
583
    llvm::appendToUsed(*this->function_.getParent(), {external_function});
×
584

585
    bool verify_dbg = false;
×
586
    bool failed = llvm::verifyModule(*this->function_.getParent(), &llvm::errs(), &verify_dbg);
×
587
    if (failed) {
×
588
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
589
    }
×
590

591
    return simplified_sdfg;
×
592
}
×
593

594
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::apply() {
×
595
    // Lift SDFG
596
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
597
    Lifting lifting(TLI, this->function_, sdfg::FunctionType_CPU);
×
598
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
599
    dump_llvm_function();
×
600
    dump_sdfg(*sdfg, "0");
×
601
    sdfg->validate();
×
602

603
    // Increase of graph complexity
604
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
605
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
606
    unify_loop_exits_pass.run(builder_canon);
×
607
    dump_sdfg(builder_canon.subject(), "1");
×
608
    unify_loop_exits_pass.run(builder_canon);
×
609
    dump_sdfg(builder_canon.subject(), "2");
×
610
    unify_loop_exits_pass.run(builder_canon);
×
611
    dump_sdfg(builder_canon.subject(), "3");
×
612
    sdfg = builder_canon.move();
×
613

614
    // Build StructuredSDFG
615
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
616

617
    // Propagate debug info
618
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
619
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
620
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
621

622
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(this->function_));
×
623
    auto structured_sdfg = structured_builder.move();
×
624

NEW
625
    register_output_dir(*structured_sdfg);
×
626

627
    // Simplify SDFG
628
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
629

630
    // If LinkOnceODR, internalize original function
631
    if (this->function_.getLinkage() == llvm::GlobalValue::LinkOnceODRLinkage) {
×
632
        std::string module_path = this->function_.getParent()->getName().str();
×
633
        std::string module_name = std::filesystem::path(module_path).stem().string();
×
634

635
        llvm::Function* internal_function = llvm::Function::Create(
×
636
            this->function_.getFunctionType(),
×
637
            llvm::GlobalValue::ExternalLinkage,
×
638
            "__daisy_odr_" + utils::normalize_name(module_name) + "_" + this->function_.getName(),
×
639
            this->function_.getParent()
×
640
        );
×
641
        internal_function->copyAttributesFrom(&this->function_);
×
642
        internal_function->setComdat(nullptr);
×
643

644
        // Map arguments and the function itself for recursion
645
        llvm::ValueToValueMapTy VMap;
×
646
        auto dest_arg_it = internal_function->arg_begin();
×
647
        for (auto& src_arg : this->function_.args()) {
×
648
            dest_arg_it->setName(src_arg.getName());
×
649
            VMap[&src_arg] = &*dest_arg_it++;
×
650
        }
×
651
        VMap[&this->function_] = internal_function; // Handle recursive calls
×
652

653
        llvm::SmallVector<llvm::ReturnInst*, 8> Returns;
×
654
        // Clone the function body
655
        llvm::CloneFunctionInto(
×
656
            internal_function, &this->function_, VMap, llvm::CloneFunctionChangeType::LocalChangesOnly, Returns
×
657
        );
×
658

659
        // Redirect all uses in this module to the clone
660
        llvm::SmallVector<llvm::Use*, 16> Uses;
×
661
        for (llvm::Use& U : this->function_.uses()) {
×
662
            Uses.push_back(&U);
×
663
        }
×
664
        for (llvm::Use* U : Uses) {
×
665
            U->set(internal_function);
×
666
        }
×
667

668
        // Rename sdfg to match internal function
669
        simplified_sdfg->name(internal_function->getName().str());
×
670

671
        // Prevent further inlining
672
        internal_function->addFnAttr(llvm::Attribute::NoInline);
×
673
        internal_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
674
        llvm::appendToUsed(*internal_function->getParent(), {internal_function});
×
675

676
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
677
    } else {
×
678
        // Prevent further inlining
679
        this->function_.addFnAttr(llvm::Attribute::NoInline);
×
680
        this->function_.addFnAttr(llvm::Attribute::OptimizeNone);
×
681
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
682
    }
×
683

684
    bool verify_dbg = false;
×
685
    bool failed = llvm::verifyModule(*this->function_.getParent(), &llvm::errs(), &verify_dbg);
×
686
    if (failed) {
×
687
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
688
    }
×
689

690
    return simplified_sdfg;
×
691
}
×
692

693
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::simplify(std::unique_ptr<sdfg::StructuredSDFG>& sdfg) {
×
694
    sdfg::builder::StructuredSDFGBuilder builder_opt(sdfg);
×
695
    sdfg::analysis::AnalysisManager analysis_manager(builder_opt.subject());
×
696

697
    dump_structured_sdfg(builder_opt.subject(), "0.init");
×
698

699
    // Optimization Pipelines
700
    sdfg::passes::Pipeline dataflow_simplification = sdfg::passes::Pipeline::dataflow_simplification();
×
701
    sdfg::passes::Pipeline symbolic_simplification = sdfg::passes::Pipeline::symbolic_simplification();
×
702
    sdfg::passes::Pipeline dce = sdfg::passes::Pipeline::dead_code_elimination();
×
703
    sdfg::passes::Pipeline memlet_combine = sdfg::passes::Pipeline::memlet_combine();
×
704
    sdfg::passes::DeadDataElimination dde;
×
705
    sdfg::passes::SymbolPropagation symbol_propagation_pass;
×
706

707
    // Promote tasklets into symbolic assignments
708
    sdfg::passes::SymbolPromotion symbol_promotion_pass;
×
709
    symbol_promotion_pass.run(builder_opt, analysis_manager);
×
710

711
    // Expand library nodes if requested
712
    if (DOCC_expand == "tenstorrent") {
×
713
        LLVM_DEBUG_PRINTLN("Overriding all library nodes to Tenstorrent");
×
714
        auto pass = sdfg::tenstorrent::MathNodeImplementationOverridePass();
×
715
        bool success = pass.run(builder_opt, analysis_manager);
×
716
    } else if (DOCC_expand != "none") {
×
717
        LLVM_DEBUG_PRINTLN("Expanding all library nodes");
×
718
        auto expansion_pass = sdfg::passes::LibraryNodeExpansionPass();
×
719
        bool expanded = expansion_pass.run(builder_opt, analysis_manager);
×
720
    }
×
721

722
    /***** SDFG Minimization *****/
723

724
    // Minimize SDFG by fusing blocks, tasklets and sequences
725
    dataflow_simplification.run(builder_opt, analysis_manager);
×
726
    dde.run(builder_opt, analysis_manager);
×
727
    dce.run(builder_opt, analysis_manager);
×
728

729
    // Minimize SDFG by fusing symbolic expressions
730
    symbolic_simplification.run(builder_opt, analysis_manager);
×
731
    dde.run(builder_opt, analysis_manager);
×
732
    dce.run(builder_opt, analysis_manager);
×
733

734
    dump_structured_sdfg(builder_opt.subject(), "1.dde");
×
735

736
    /***** Structured Loops *****/
737

738
    // Unify continue/break inside branches
739
    {
×
740
        sdfg::passes::CommonAssignmentElimination common_assignment_elimination;
×
741
        bool applies = false;
×
742
        do {
×
743
            applies = false;
×
744
            applies |= common_assignment_elimination.run(builder_opt, analysis_manager);
×
745
        } while (applies);
×
746
        dde.run(builder_opt, analysis_manager);
×
747
        dce.run(builder_opt, analysis_manager);
×
748
        symbolic_simplification.run(builder_opt, analysis_manager);
×
749
    }
×
750

751
    dump_structured_sdfg(builder_opt.subject(), "2.cae");
×
752

753
    // Convert loops into structured loops
754
    sdfg::passes::WhileToForConversion for_conversion_pass;
×
755
    for_conversion_pass.run(builder_opt, analysis_manager);
×
756

757
    // Propagate for simpler indvar usage
758
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
759

760
    dump_structured_sdfg(builder_opt.subject(), "4.symprop");
×
761

762
    // Eliminate redundant branches
763
    {
×
764
        bool applies = false;
×
765
        sdfg::passes::ConditionEliminationPass condition_elimination_pass;
×
766
        do {
×
767
            applies = false;
×
768
            applies |= condition_elimination_pass.run(builder_opt, analysis_manager);
×
769
        } while (applies);
×
770
    }
×
771

772
    dump_structured_sdfg(builder_opt.subject(), "5.condelim");
×
773

774
    // Normalize loop condition and update (run twice)
775
    sdfg::passes::normalization::LoopNormalFormPass loop_normalization_pass;
×
776
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
777
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
778
    dde.run(builder_opt, analysis_manager);
×
779
    dce.run(builder_opt, analysis_manager);
×
780

781
    dump_structured_sdfg(builder_opt.subject(), "6.loopnorm");
×
782

783
    // Eliminate symbols correlated to loop iterators
784
    sdfg::passes::SymbolEvolution symbol_evolution_pass;
×
785
    symbol_evolution_pass.run(builder_opt, analysis_manager);
×
786

787
    dump_structured_sdfg(builder_opt.subject(), "7.symbevo");
×
788

789
    // Dead code elimination
790
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
791
    dde.run(builder_opt, analysis_manager);
×
792
    dce.run(builder_opt, analysis_manager);
×
793

794
    dump_structured_sdfg(builder_opt.subject(), "8.dde");
×
795

796
    /***** Data Parallelism *****/
797

798
    // Combine address calculations in memlets
799
    memlet_combine.run(builder_opt, analysis_manager);
×
800

801
    dump_structured_sdfg(builder_opt.subject(), "9.memletcomb");
×
802

803
    // Move code out of loops where possible
804
    sdfg::passes::Pipeline code_motion = sdfg::passes::code_motion();
×
805
    code_motion.run(builder_opt, analysis_manager);
×
806

807
    dump_structured_sdfg(builder_opt.subject(), "10.codemotion");
×
808

809
    // Convert pointer-based iterators to indvar usage
810
    sdfg::passes::PointerEvolution pointer_evolution_pass;
×
811
    pointer_evolution_pass.run(builder_opt, analysis_manager);
×
812
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
813

814
    dump_structured_sdfg(builder_opt.subject(), "11.pointerevo");
×
815

816
    // Convert lib-calls into managed memory
817
    sdfg::passes::Pipeline memory = sdfg::passes::Pipeline::memory();
×
818
    memory.run(builder_opt, analysis_manager);
×
819

820
    dump_structured_sdfg(builder_opt.subject(), "12.memorymgmt");
×
821

822
    sdfg::passes::TypeMinimizationPass type_minimization_pass;
×
823
    type_minimization_pass.run(builder_opt, analysis_manager);
×
824
    type_minimization_pass.run(builder_opt, analysis_manager);
×
825

826
    dump_structured_sdfg(builder_opt.subject(), "13.typemin");
×
827

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

833
    dump_structured_sdfg(builder_opt.subject(), "14.dde");
×
834

835
    // Convert for loops into maps and reductions
836
    sdfg::passes::ForClassificationPass map_conversion_pass;
×
837
    map_conversion_pass.run(builder_opt, analysis_manager);
×
838

839
    dump_structured_sdfg(builder_opt.subject(), "15.for_classification");
×
840

841
    // Move code out of maps where possible
842
    code_motion.run(builder_opt, analysis_manager);
×
843

844
    dump_structured_sdfg(builder_opt.subject(), "16.codemotion");
×
845

846
    // Dead code elimination
847
    dde.run(builder_opt, analysis_manager);
×
848
    dce.run(builder_opt, analysis_manager);
×
849
    dataflow_simplification.run(builder_opt, analysis_manager);
×
850

851
    dump_structured_sdfg(builder_opt.subject(), "17.dde");
×
852

853
    return builder_opt.move();
×
854
}
×
855

856
void FunctionToSDFG::dump_llvm_function() const {
×
857
    if (args::DOCC_DUMP_SDFG) {
×
858
        auto mod = function_.getParent();
×
859

860
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
861

862
        std::filesystem::create_directories(out_dir);
×
863
        std::filesystem::path file = out_dir / (function_.getName().str() + ".ll");
×
864

865
        std::ofstream out(file, std::ofstream::out);
×
866
        if (!out.is_open()) {
×
867
            std::cerr << "Could not open file " << file << " for dumping llvm function." << std::endl;
×
868
            return;
×
869
        }
×
870
        std::string llvm_function_text;
×
871
        llvm::raw_string_ostream llvm_out(llvm_function_text);
×
872
        function_.print(llvm_out);
×
873
        llvm_out.flush();
×
874

875
        out << llvm_function_text << '\n';
×
876
        out.close();
×
877
    }
×
878
}
×
879

880
void FunctionToSDFG::dump_sdfg(const sdfg::SDFG& sdfg, const std::string& step) const {
×
881
    if (args::DOCC_DUMP_SDFG) {
×
882
        auto mod = function_.getParent();
×
883

884
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
885

886
        std::filesystem::create_directories(out_dir);
×
887
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".lifting." + step + ".dot"));
×
888
    }
×
889
}
×
890

891
void FunctionToSDFG::dump_structured_sdfg(const sdfg::StructuredSDFG& sdfg, const std::string& step) const {
×
892
    if (args::DOCC_DUMP_SDFG) {
×
893
        auto mod = function_.getParent();
×
894

895
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
896

897
        std::filesystem::create_directories(out_dir);
×
898
        sdfg::serializer::JSONSerializer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".json"));
×
899
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".dot"));
×
900
    }
×
901
}
×
902

903
} // namespace lifting
904
} // 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