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

daisytuner / docc / 28806128926

06 Jul 2026 04:16PM UTC coverage: 62.96%. First build
28806128926

push

github

web-flow
New LibNodeExpansion pass (#740)

* Switched expansion "pipeline" to new LibNodeExpansionPass that can recursively expand using a LibNodeExpander impl.
- removed access to analysis_manager from expansion methods, as those would currently not be appropriately maintained between nodes
+ New expansion API handles more of the boilerplate code (checking for standalone, creating boundary access nodes, removing old elements)
* Also migrated sdfg-json-to-c.cpp to not using the expansion pipeline anymore
* toStr() for ReduceNodes and giving PyStructuredSDFG access to the output_dir for additional dumping
~ DotVisualizer : Fix on nested sequences.
+ DotVisualizer: visualize empty sequences
* DotVisualizer default-enabled show block/loop ids
 * while MathNodes still have an expand-method, the new infrastructure is based upon "Expander" classes. The MathNodeExpander just redirects to the method for now
 ~ Broadcast node still used old ptr-output semantics
 * updated tensor & blas node expand to new expand API, that handles more of the boilerplate code (checking, removing old nodes, creating standalone-replacement nodes)
 - removed Transpose Node. Was unused and on old ptr-semantics
 + StructuredSDFGBuilder.add_sequence_at, add_for_at, add_map_at
 * switched StructuredSDFGBuilder internally to use ptr of Assignments to express using default assignments when needed
 * updated tests to use the new expand_single_math_node helper function, instead of the method directly
 + pass and expand-single helper functions are ready to use other expanders
 + EinsumExpansionPass is basically the legacy ExpansionPass, only restricted to EinsumNodes. It still needs to run in a pipeline to ensure finding multiple EinsumNodes per block. This is temporary, because Einsum is the only node that already supported splitting a block, which the new expansion disallows, as it should handle it itself when needed (but that part is not yet implemented)

594 of 962 new or added lines in 31 files covered. (61.75%)

40554 of 64412 relevant lines covered (62.96%)

963.11 hits per line

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

0.65
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

541
    // Lift SDFG
542
    auto& TLI = this->FAM_.getResult<llvm::TargetLibraryAnalysis>(this->function_);
×
543
    Lifting lifting(TLI, *external_function, sdfg::FunctionType_CPU);
×
544
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
545
    dump_llvm_function();
×
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");
×
555
    unify_loop_exits_pass.run(builder_canon);
×
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
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

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
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);
×
580
    if (failed) {
×
581
        throw sdfg::InvalidSDFGException("Module is broken after lifting region.");
×
582
    }
×
583

584
    return simplified_sdfg;
×
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);
×
591
    std::unique_ptr<sdfg::SDFG> sdfg = lifting.run();
×
592
    dump_llvm_function();
×
593
    dump_sdfg(*sdfg, "0");
×
594
    sdfg->validate();
×
595

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

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

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

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

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

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

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

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

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

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

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

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

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

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

681
    return simplified_sdfg;
×
682
}
×
683

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

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

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

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

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

713
    /***** SDFG Minimization *****/
714

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

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

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

727
    /***** Structured Loops *****/
728

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

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

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

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

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

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

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

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

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

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

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

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

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

787
    /***** Data Parallelism *****/
788

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

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

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

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

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

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

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

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

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

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

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

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

826
    // Convert for loops into maps and reductions
827
    sdfg::passes::ForClassificationPass map_conversion_pass;
×
828
    map_conversion_pass.run(builder_opt, analysis_manager);
×
829

830
    dump_structured_sdfg(builder_opt.subject(), "15.for_classification");
×
831

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

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

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

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

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

847
void FunctionToSDFG::dump_llvm_function() const {
×
848
    if (args::DOCC_DUMP_SDFG) {
×
849
        auto mod = function_.getParent();
×
850

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

853
        std::filesystem::create_directories(out_dir);
×
854
        std::filesystem::path file = out_dir / (function_.getName().str() + ".ll");
×
855

856
        std::ofstream out(file, std::ofstream::out);
×
857
        if (!out.is_open()) {
×
858
            std::cerr << "Could not open file " << file << " for dumping llvm function." << std::endl;
×
859
            return;
×
860
        }
×
861
        std::string llvm_function_text;
×
862
        llvm::raw_string_ostream llvm_out(llvm_function_text);
×
863
        function_.print(llvm_out);
×
864
        llvm_out.flush();
×
865

866
        out << llvm_function_text << '\n';
×
867
        out.close();
×
868
    }
×
869
}
×
870

871
void FunctionToSDFG::dump_sdfg(const sdfg::SDFG& sdfg, const std::string& step) const {
×
872
    if (args::DOCC_DUMP_SDFG) {
×
873
        auto mod = function_.getParent();
×
874

875
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
876

877
        std::filesystem::create_directories(out_dir);
×
878
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".lifting." + step + ".dot"));
×
879
    }
×
880
}
×
881

882
void FunctionToSDFG::dump_structured_sdfg(const sdfg::StructuredSDFG& sdfg, const std::string& step) const {
×
883
    if (args::DOCC_DUMP_SDFG) {
×
884
        auto mod = function_.getParent();
×
885

886
        auto out_dir = analysis::SDFGRegistry::docc_extract_dir(*mod);
×
887

888
        std::filesystem::create_directories(out_dir);
×
889
        sdfg::serializer::JSONSerializer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".json"));
×
890
        sdfg::visualizer::DotVisualizer::writeToFile(sdfg, out_dir / (sdfg.name() + ".pass." + step + ".dot"));
×
891
    }
×
892
}
×
893

894
} // namespace lifting
895
} // 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