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

daisytuner / docc / 28177795453

25 Jun 2026 02:33PM UTC coverage: 61.743% (+0.1%) from 61.644%
28177795453

Pull #802

github

web-flow
Merge 08f22df91 into fe9abd1cb
Pull Request #802: adds reduce as new structured loop type

705 of 985 new or added lines in 34 files covered. (71.57%)

5 existing lines in 4 files now uncovered.

38837 of 62901 relevant lines covered (61.74%)

978.19 hits per line

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

0.67
/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/cmd_args.h"
29
#include "docc/lifting/functions/function_lifting.h"
30
#include "docc/lifting/lift_report.h"
31
#include "docc/lifting/lifting.h"
32
#include "docc/utils.h"
33
#include "sdfg/visualizer/dot_visualizer.h"
34

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

42
namespace docc {
43
namespace lifting {
44

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

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

56
    return last_valid;
×
57
}
×
58

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

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

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

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

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

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

115
    return false;
×
116
}
×
117

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

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

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

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

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

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

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

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

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

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

219
        // Region failed, give up
220
        break;
×
221
    }
×
222

223
    return sdfgs;
×
224
}
×
225

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

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

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

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

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

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

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

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

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

344
    return {true, nullptr};
×
345
}
×
346

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

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

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

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

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

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

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

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

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

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

474
    return {true, nullptr};
×
475
}
×
476

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

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

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

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

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

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

546
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
547
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
548
    unify_loop_exits_pass.run(builder_canon);
×
549
    dump_sdfg(builder_canon.subject(), "1");
×
550
    unify_loop_exits_pass.run(builder_canon);
×
551
    dump_sdfg(builder_canon.subject(), "2");
×
552
    unify_loop_exits_pass.run(builder_canon);
×
553
    dump_sdfg(builder_canon.subject(), "3");
×
554
    sdfg = builder_canon.move();
×
555

556
    // Build StructuredSDFG
557
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
558

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

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

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

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

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

581
    return simplified_sdfg;
×
582
}
×
583

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

592
    // Increase of graph complexity
593
    sdfg::builder::SDFGBuilder builder_canon(sdfg);
×
594
    sdfg::passes::UnifyLoopExits unify_loop_exits_pass;
×
595
    unify_loop_exits_pass.run(builder_canon);
×
596
    dump_sdfg(builder_canon.subject(), "1");
×
597
    unify_loop_exits_pass.run(builder_canon);
×
598
    dump_sdfg(builder_canon.subject(), "2");
×
599
    unify_loop_exits_pass.run(builder_canon);
×
600
    dump_sdfg(builder_canon.subject(), "3");
×
601
    sdfg = builder_canon.move();
×
602

603
    // Build StructuredSDFG
604
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
605

606
    // Propagate debug info
607
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
608
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
609
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
610

611
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(this->function_));
×
612
    auto structured_sdfg = structured_builder.move();
×
613

614
    // Simplify SDFG
615
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
616

617
    // If LinkOnceODR, internalize original function
618
    if (this->function_.getLinkage() == llvm::GlobalValue::LinkOnceODRLinkage) {
×
619
        std::string module_path = this->function_.getParent()->getName().str();
×
620
        std::string module_name = std::filesystem::path(module_path).stem().string();
×
621

622
        llvm::Function* internal_function = llvm::Function::Create(
×
623
            this->function_.getFunctionType(),
×
624
            llvm::GlobalValue::ExternalLinkage,
×
625
            "__daisy_odr_" + utils::normalize_name(module_name) + "_" + this->function_.getName(),
×
626
            this->function_.getParent()
×
627
        );
×
628
        internal_function->copyAttributesFrom(&this->function_);
×
629
        internal_function->setComdat(nullptr);
×
630

631
        // Map arguments and the function itself for recursion
632
        llvm::ValueToValueMapTy VMap;
×
633
        auto dest_arg_it = internal_function->arg_begin();
×
634
        for (auto& src_arg : this->function_.args()) {
×
635
            dest_arg_it->setName(src_arg.getName());
×
636
            VMap[&src_arg] = &*dest_arg_it++;
×
637
        }
×
638
        VMap[&this->function_] = internal_function; // Handle recursive calls
×
639

640
        llvm::SmallVector<llvm::ReturnInst*, 8> Returns;
×
641
        // Clone the function body
642
        llvm::CloneFunctionInto(
×
643
            internal_function, &this->function_, VMap, llvm::CloneFunctionChangeType::LocalChangesOnly, Returns
×
644
        );
×
645

646
        // Redirect all uses in this module to the clone
647
        llvm::SmallVector<llvm::Use*, 16> Uses;
×
648
        for (llvm::Use& U : this->function_.uses()) {
×
649
            Uses.push_back(&U);
×
650
        }
×
651
        for (llvm::Use* U : Uses) {
×
652
            U->set(internal_function);
×
653
        }
×
654

655
        // Rename sdfg to match internal function
656
        simplified_sdfg->name(internal_function->getName().str());
×
657

658
        // Prevent further inlining
659
        internal_function->addFnAttr(llvm::Attribute::NoInline);
×
660
        internal_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
661
        llvm::appendToUsed(*internal_function->getParent(), {internal_function});
×
662

663
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
664
    } else {
×
665
        // Prevent further inlining
666
        this->function_.addFnAttr(llvm::Attribute::NoInline);
×
667
        this->function_.addFnAttr(llvm::Attribute::OptimizeNone);
×
668
        llvm::appendToUsed(*this->function_.getParent(), {&this->function_});
×
669
    }
×
670

671
    bool verify_dbg = false;
×
672
    bool failed = llvm::verifyModule(*this->function_.getParent(), &llvm::errs(), &verify_dbg);
×
673
    if (failed) {
×
674
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
675
    }
×
676

677
    return simplified_sdfg;
×
678
}
×
679

680
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::simplify(std::unique_ptr<sdfg::StructuredSDFG>& sdfg) {
×
681
    sdfg::builder::StructuredSDFGBuilder builder_opt(sdfg);
×
682
    sdfg::analysis::AnalysisManager analysis_manager(builder_opt.subject());
×
683

684
    dump_structured_sdfg(builder_opt.subject(), "0.init");
×
685

686
    // Optimization Pipelines
687
    sdfg::passes::Pipeline dataflow_simplification = sdfg::passes::Pipeline::dataflow_simplification();
×
688
    sdfg::passes::Pipeline symbolic_simplification = sdfg::passes::Pipeline::symbolic_simplification();
×
689
    sdfg::passes::Pipeline dce = sdfg::passes::Pipeline::dead_code_elimination();
×
690
    sdfg::passes::Pipeline memlet_combine = sdfg::passes::Pipeline::memlet_combine();
×
691
    sdfg::passes::DeadDataElimination dde;
×
692
    sdfg::passes::SymbolPropagation symbol_propagation_pass;
×
693

694
    // Promote tasklets into symbolic assignments
695
    sdfg::passes::SymbolPromotion symbol_promotion_pass;
×
696
    symbol_promotion_pass.run(builder_opt, analysis_manager);
×
697

698
    // Expand library nodes if requested
699
    if (DOCC_expand == "tenstorrent") {
×
700
        LLVM_DEBUG_PRINTLN("Overriding all library nodes to Tenstorrent");
×
701
        auto pass = sdfg::tenstorrent::MathNodeImplementationOverridePass();
×
702
        bool success = pass.run(builder_opt, analysis_manager);
×
703
    } else if (DOCC_expand != "none") {
×
704
        LLVM_DEBUG_PRINTLN("Expanding all library nodes");
×
705
        auto expansion_pass = sdfg::passes::ExpansionPass();
×
706
        bool expanded = expansion_pass.run(builder_opt, analysis_manager);
×
707
    }
×
708

709
    /***** SDFG Minimization *****/
710

711
    // Minimize SDFG by fusing blocks, tasklets and sequences
712
    dataflow_simplification.run(builder_opt, analysis_manager);
×
713
    dde.run(builder_opt, analysis_manager);
×
714
    dce.run(builder_opt, analysis_manager);
×
715

716
    // Minimize SDFG by fusing symbolic expressions
717
    symbolic_simplification.run(builder_opt, analysis_manager);
×
718
    dde.run(builder_opt, analysis_manager);
×
719
    dce.run(builder_opt, analysis_manager);
×
720

721
    dump_structured_sdfg(builder_opt.subject(), "1.dde");
×
722

723
    /***** Structured Loops *****/
724

725
    // Unify continue/break inside branches
726
    {
×
727
        sdfg::passes::CommonAssignmentElimination common_assignment_elimination;
×
728
        bool applies = false;
×
729
        do {
×
730
            applies = false;
×
731
            applies |= common_assignment_elimination.run(builder_opt, analysis_manager);
×
732
        } while (applies);
×
733
        dde.run(builder_opt, analysis_manager);
×
734
        dce.run(builder_opt, analysis_manager);
×
735
        symbolic_simplification.run(builder_opt, analysis_manager);
×
736
    }
×
737

738
    dump_structured_sdfg(builder_opt.subject(), "2.cae");
×
739

740
    // Convert loops into structured loops
741
    sdfg::passes::WhileToForConversion for_conversion_pass;
×
742
    for_conversion_pass.run(builder_opt, analysis_manager);
×
743

744
    // Propagate for simpler indvar usage
745
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
746

747
    dump_structured_sdfg(builder_opt.subject(), "4.symprop");
×
748

749
    // Eliminate redundant branches
750
    {
×
751
        bool applies = false;
×
752
        sdfg::passes::ConditionEliminationPass condition_elimination_pass;
×
753
        do {
×
754
            applies = false;
×
755
            applies |= condition_elimination_pass.run(builder_opt, analysis_manager);
×
756
        } while (applies);
×
757
    }
×
758

759
    dump_structured_sdfg(builder_opt.subject(), "5.condelim");
×
760

761
    // Normalize loop condition and update (run twice)
762
    sdfg::passes::normalization::LoopNormalFormPass loop_normalization_pass;
×
763
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
764
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
765
    dde.run(builder_opt, analysis_manager);
×
766
    dce.run(builder_opt, analysis_manager);
×
767

768
    dump_structured_sdfg(builder_opt.subject(), "6.loopnorm");
×
769

770
    // Eliminate symbols correlated to loop iterators
771
    sdfg::passes::SymbolEvolution symbol_evolution_pass;
×
772
    symbol_evolution_pass.run(builder_opt, analysis_manager);
×
773

774
    dump_structured_sdfg(builder_opt.subject(), "7.symbevo");
×
775

776
    // Dead code elimination
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(), "8.dde");
×
782

783
    /***** Data Parallelism *****/
784

785
    // Combine address calculations in memlets
786
    memlet_combine.run(builder_opt, analysis_manager);
×
787

788
    dump_structured_sdfg(builder_opt.subject(), "9.memletcomb");
×
789

790
    // Move code out of loops where possible
791
    sdfg::passes::Pipeline code_motion = sdfg::passes::code_motion();
×
792
    code_motion.run(builder_opt, analysis_manager);
×
793

794
    dump_structured_sdfg(builder_opt.subject(), "10.codemotion");
×
795

796
    // Convert pointer-based iterators to indvar usage
797
    sdfg::passes::PointerEvolution pointer_evolution_pass;
×
798
    pointer_evolution_pass.run(builder_opt, analysis_manager);
×
799
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
800

801
    dump_structured_sdfg(builder_opt.subject(), "11.pointerevo");
×
802

803
    // Convert lib-calls into managed memory
804
    sdfg::passes::Pipeline memory = sdfg::passes::Pipeline::memory();
×
805
    memory.run(builder_opt, analysis_manager);
×
806

807
    dump_structured_sdfg(builder_opt.subject(), "12.memorymgmt");
×
808

809
    sdfg::passes::TypeMinimizationPass type_minimization_pass;
×
810
    type_minimization_pass.run(builder_opt, analysis_manager);
×
811
    type_minimization_pass.run(builder_opt, analysis_manager);
×
812

813
    dump_structured_sdfg(builder_opt.subject(), "13.typemin");
×
814

815
    // Dead code elimination
816
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
817
    dce.run(builder_opt, analysis_manager);
×
818
    dde.run(builder_opt, analysis_manager);
×
819

820
    dump_structured_sdfg(builder_opt.subject(), "14.dde");
×
821

822
    // Convert for loops into maps and reductions
NEW
823
    sdfg::passes::ForClassificationPass map_conversion_pass;
×
UNCOV
824
    map_conversion_pass.run(builder_opt, analysis_manager);
×
825

NEW
826
    dump_structured_sdfg(builder_opt.subject(), "15.for_classification");
×
827

828
    // Move code out of maps where possible
829
    code_motion.run(builder_opt, analysis_manager);
×
830

831
    dump_structured_sdfg(builder_opt.subject(), "16.codemotion");
×
832

833
    // Dead code elimination
834
    dde.run(builder_opt, analysis_manager);
×
835
    dce.run(builder_opt, analysis_manager);
×
836
    dataflow_simplification.run(builder_opt, analysis_manager);
×
837

838
    dump_structured_sdfg(builder_opt.subject(), "17.dde");
×
839

840
    return builder_opt.move();
×
841
}
×
842

843
void FunctionToSDFG::dump_sdfg(const sdfg::SDFG& sdfg, const std::string& step) const {
×
844
    if (args::DOCC_DUMP_SDFG) {
×
845
        auto mod = function_.getParent();
×
846

847
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
848

849
        std::filesystem::create_directories(out_dir);
×
850
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".lifting." + step + ".dot"));
×
851
    }
×
852
}
×
853

854
void FunctionToSDFG::dump_structured_sdfg(const sdfg::StructuredSDFG& sdfg, const std::string& step) const {
×
855
    if (args::DOCC_DUMP_SDFG) {
×
856
        auto mod = function_.getParent();
×
857

858
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
859

860
        std::filesystem::create_directories(out_dir);
×
861
        sdfg::serializer::JSONSerializer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".json"));
×
862
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".dot"));
×
863
    }
×
864
};
×
865

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

© 2026 Coveralls, Inc