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

daisytuner / docc / 26769284515

01 Jun 2026 04:56PM UTC coverage: 61.232% (+0.2%) from 61.05%
26769284515

Pull #731

github

web-flow
Merge 2cd40cdc5 into 1f328beee
Pull Request #731: Fixes for LLVM frontend

4 of 15 new or added lines in 4 files covered. (26.67%)

144 existing lines in 1 file now uncovered.

35649 of 58220 relevant lines covered (61.23%)

10982.72 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
        } catch (sdfg::InvalidSDFGException& e) {
×
145
            // Fallthrough
146
            LLVM_DEBUG_PRINTLN("InvalidSDFGException on '" << this->function_.getName() << "': " << e.what());
×
147
        }
×
148
    } else {
×
149
        if (res.second != nullptr) {
×
150
            sdfg::DebugInfo dbg_info;
×
151
            if (auto* inst = llvm::dyn_cast<llvm::Instruction>(res.second)) {
×
152
                dbg_info = ::docc::utils::get_debug_info(*inst);
×
UNCOV
153
            }
×
UNCOV
154
            LiftingReport::add_failed_lift(dbg_info, "Unsupported instruction", ::docc::utils::toIRString(*res.second));
×
155
        }
×
156

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

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

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

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

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

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

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

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

226
    return sdfgs;
×
UNCOV
227
}
×
228

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

559
    // Build StructuredSDFG
560
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
561

562
    // Propagate debug info
UNCOV
563
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
564
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
565
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
566

UNCOV
567
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(*external_function));
×
568
    auto structured_sdfg = structured_builder.move();
×
569

570
    // Simplify SDFG
571
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
572

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

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

584
    return simplified_sdfg;
×
UNCOV
585
}
×
586

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

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

606
    // Build StructuredSDFG
607
    sdfg::builder::StructuredSDFGBuilder structured_builder(*sdfg);
×
608

609
    // Propagate debug info
UNCOV
610
    sdfg::analysis::AnalysisManager analysis_manager(structured_builder.subject());
×
611
    sdfg::passes::DebugInfoPropagation debug_info_propagation_pass;
×
612
    debug_info_propagation_pass.run(structured_builder, analysis_manager);
×
613

UNCOV
614
    LiftingReport::add_successful_lift(::docc::utils::get_debug_info(this->function_));
×
615
    auto structured_sdfg = structured_builder.move();
×
616

617
    // Simplify SDFG
618
    auto simplified_sdfg = this->simplify(structured_sdfg);
×
619

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

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

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

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

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

658
        // Rename sdfg to match internal function
659
        simplified_sdfg->name(internal_function->getName().str());
×
660

661
        // Prevent further inlining
UNCOV
662
        internal_function->addFnAttr(llvm::Attribute::NoInline);
×
663
        internal_function->addFnAttr(llvm::Attribute::OptimizeNone);
×
664
        llvm::appendToUsed(*internal_function->getParent(), {internal_function});
×
665

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

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

680
    return simplified_sdfg;
×
681
}
×
682

UNCOV
683
std::unique_ptr<sdfg::StructuredSDFG> FunctionToSDFG::simplify(std::unique_ptr<sdfg::StructuredSDFG>& sdfg) {
×
684
    sdfg::builder::StructuredSDFGBuilder builder_opt(sdfg);
×
UNCOV
685
    sdfg::analysis::AnalysisManager analysis_manager(builder_opt.subject());
×
686

687
    dump_structured_sdfg(builder_opt.subject(), "0.init");
×
688

689
    // Optimization Pipelines
690
    sdfg::passes::Pipeline dataflow_simplification = sdfg::passes::Pipeline::dataflow_simplification();
×
691
    sdfg::passes::Pipeline symbolic_simplification = sdfg::passes::Pipeline::symbolic_simplification();
×
692
    sdfg::passes::Pipeline dce = sdfg::passes::Pipeline::dead_code_elimination();
×
UNCOV
693
    sdfg::passes::Pipeline memlet_combine = sdfg::passes::Pipeline::memlet_combine();
×
UNCOV
694
    sdfg::passes::DeadDataElimination dde;
×
695
    sdfg::passes::SymbolPropagation symbol_propagation_pass;
×
696

697
    // Promote tasklets into symbolic assignments
UNCOV
698
    sdfg::passes::SymbolPromotion symbol_promotion_pass;
×
699
    symbol_promotion_pass.run(builder_opt, analysis_manager);
×
700

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

712
    /***** SDFG Minimization *****/
713

714
    // Minimize SDFG by fusing blocks, tasklets and sequences
UNCOV
715
    dataflow_simplification.run(builder_opt, analysis_manager);
×
UNCOV
716
    dde.run(builder_opt, analysis_manager);
×
717
    dce.run(builder_opt, analysis_manager);
×
718

719
    // Minimize SDFG by fusing symbolic expressions
UNCOV
720
    symbolic_simplification.run(builder_opt, analysis_manager);
×
721
    dde.run(builder_opt, analysis_manager);
×
UNCOV
722
    dce.run(builder_opt, analysis_manager);
×
723

UNCOV
724
    dump_structured_sdfg(builder_opt.subject(), "1.dde");
×
725

726
    /***** Structured Loops *****/
727

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

741
    dump_structured_sdfg(builder_opt.subject(), "2.cae");
×
742

743
    // Convert loops into structured loops
UNCOV
744
    sdfg::passes::WhileToForConversion for_conversion_pass;
×
745
    for_conversion_pass.run(builder_opt, analysis_manager);
×
746

747
    // Propagate for simpler indvar usage
UNCOV
748
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
749

750
    dump_structured_sdfg(builder_opt.subject(), "4.symprop");
×
751

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

762
    dump_structured_sdfg(builder_opt.subject(), "5.condelim");
×
763

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

771
    dump_structured_sdfg(builder_opt.subject(), "6.loopnorm");
×
772

773
    // Eliminate symbols correlated to loop iterators
774
    sdfg::passes::SymbolEvolution symbol_evolution_pass;
×
UNCOV
775
    symbol_evolution_pass.run(builder_opt, analysis_manager);
×
776

777
    dump_structured_sdfg(builder_opt.subject(), "7.symbevo");
×
778

779
    // Dead code elimination
UNCOV
780
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
781
    dde.run(builder_opt, analysis_manager);
×
UNCOV
782
    dce.run(builder_opt, analysis_manager);
×
783

UNCOV
784
    dump_structured_sdfg(builder_opt.subject(), "8.dde");
×
785

786
    /***** Data Parallelism *****/
787

788
    // Combine address calculations in memlets
UNCOV
789
    memlet_combine.run(builder_opt, analysis_manager);
×
790

791
    dump_structured_sdfg(builder_opt.subject(), "9.memletcomb");
×
792

793
    // Move code out of loops where possible
794
    sdfg::passes::Pipeline code_motion = sdfg::passes::code_motion();
×
UNCOV
795
    code_motion.run(builder_opt, analysis_manager);
×
796

797
    dump_structured_sdfg(builder_opt.subject(), "10.codemotion");
×
798

799
    // Convert pointer-based iterators to indvar usage
UNCOV
800
    sdfg::passes::PointerEvolution pointer_evolution_pass;
×
801
    pointer_evolution_pass.run(builder_opt, analysis_manager);
×
UNCOV
802
    loop_normalization_pass.run(builder_opt, analysis_manager);
×
803

804
    dump_structured_sdfg(builder_opt.subject(), "11.pointerevo");
×
805

806
    // Convert lib-calls into managed memory
807
    sdfg::passes::Pipeline memory = sdfg::passes::Pipeline::memory();
×
UNCOV
808
    memory.run(builder_opt, analysis_manager);
×
809

810
    dump_structured_sdfg(builder_opt.subject(), "12.memorymgmt");
×
811

UNCOV
812
    sdfg::passes::TypeMinimizationPass type_minimization_pass;
×
813
    type_minimization_pass.run(builder_opt, analysis_manager);
×
UNCOV
814
    type_minimization_pass.run(builder_opt, analysis_manager);
×
815

816
    dump_structured_sdfg(builder_opt.subject(), "13.typemin");
×
817

818
    // Dead code elimination
UNCOV
819
    symbol_propagation_pass.run(builder_opt, analysis_manager);
×
820
    dce.run(builder_opt, analysis_manager);
×
UNCOV
821
    dde.run(builder_opt, analysis_manager);
×
822

823
    dump_structured_sdfg(builder_opt.subject(), "14.dde");
×
824

825
    // Convert for loops into maps
826
    sdfg::passes::For2MapPass map_conversion_pass;
×
UNCOV
827
    map_conversion_pass.run(builder_opt, analysis_manager);
×
828

829
    dump_structured_sdfg(builder_opt.subject(), "15.for2map");
×
830

831
    // Move code out of maps where possible
UNCOV
832
    code_motion.run(builder_opt, analysis_manager);
×
833

834
    dump_structured_sdfg(builder_opt.subject(), "16.codemotion");
×
835

836
    // Dead code elimination
UNCOV
837
    dde.run(builder_opt, analysis_manager);
×
838
    dce.run(builder_opt, analysis_manager);
×
UNCOV
839
    dataflow_simplification.run(builder_opt, analysis_manager);
×
840

841
    dump_structured_sdfg(builder_opt.subject(), "17.dde");
×
842

843
    return builder_opt.move();
×
844
}
×
845

UNCOV
846
void FunctionToSDFG::dump_sdfg(const sdfg::SDFG& sdfg, const std::string& step) const {
×
847
    if (args::DOCC_DUMP_SDFG) {
×
UNCOV
848
        auto mod = function_.getParent();
×
849

850
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
851

852
        std::filesystem::create_directories(out_dir);
×
UNCOV
853
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".lifting." + step + ".dot"));
×
854
    }
×
855
}
×
856

UNCOV
857
void FunctionToSDFG::dump_structured_sdfg(const sdfg::StructuredSDFG& sdfg, const std::string& step) const {
×
858
    if (args::DOCC_DUMP_SDFG) {
×
UNCOV
859
        auto mod = function_.getParent();
×
860

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

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

869
} // namespace lifting
870
} // 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