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

daisytuner / docc / 28592186395

02 Jul 2026 01:03PM UTC coverage: 62.131% (-0.01%) from 62.141%
28592186395

push

github

web-flow
Fix store instruction of a Function into a pointer bug in LLVM frontend (#828)

Safely rejects storing a pointer of a function by not lifting the whole function to a SDFG. Currently, SDFGs cannot express this kind of behavior.
Also, added dumping of the LLVM IR when the -docc-dump-sdfg flag is set.

Now, 13 tests from LLVM test suite compile that previously segfaulted. However, one of those tests still runs into a timeout. Additionally, 6 of those tests pass the execution and 6 fail.

7 of 30 new or added lines in 2 files covered. (23.33%)

3 existing lines in 1 file now uncovered.

39495 of 63567 relevant lines covered (62.13%)

978.6 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();
×
NEW
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();
×
NEW
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");
×
709
        auto expansion_pass = sdfg::passes::ExpansionPass();
×
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

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

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

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

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

NEW
866
        out << llvm_function_text << '\n';
×
NEW
867
        out.close();
×
NEW
868
    }
×
NEW
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
    }
×
NEW
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