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

daisytuner / docc / 25823993703

13 May 2026 08:16PM UTC coverage: 60.954% (-4.8%) from 65.785%
25823993703

push

github

web-flow
Merge pull request #710 from daisytuner/publish-llvm-frontend

Publish LLVM (C/C++) frontend

2664 of 8267 new or added lines in 27 files covered. (32.22%)

35005 of 57429 relevant lines covered (60.95%)

11126.36 hits per line

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

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

3
#include <llvm/IR/BasicBlock.h>
4
#include <llvm/IR/CFG.h>
5
#include <llvm/IR/Constant.h>
6
#include <llvm/IR/Constants.h>
7
#include <llvm/IR/GlobalObject.h>
8
#include <llvm/IR/InstIterator.h>
9
#include <llvm/Support/Casting.h>
10
#include <llvm/Transforms/Utils/ModuleUtils.h>
11

12
#include <cassert>
13
#include <memory>
14

15
#include "docc/lifting/functions/function_lifting.h"
16
#include "docc/utils.h"
17

18
#include <sdfg/data_flow/library_nodes/stdlib/stdlib.h>
19

20
namespace docc {
21
namespace lifting {
22

23
std::unique_ptr<sdfg::SDFG> Lifting::run() {
102✔
24
    // Add globals to SDFG
25
    this->visit_globals();
102✔
26

27
    // Add arguments to SDFG
28
    this->visit_arguments();
102✔
29

30
    // Add CFG to SDFG
31
    this->visit_cfg();
102✔
32

33
    // Visit blocks to add instructions to SDFG
34
    std::list<sdfg::control_flow::State*> visited;
102✔
35
    std::list<sdfg::control_flow::State*> queue = {
102✔
36
        this->builder_.get_non_const_state(&this->builder_.subject().start_state())
102✔
37
    };
102✔
38
    while (!queue.empty()) {
644✔
39
        sdfg::control_flow::State* state = queue.front();
542✔
40
        queue.pop_front();
542✔
41
        if (std::find(visited.begin(), visited.end(), state) != visited.end()) {
542✔
42
            continue;
27✔
43
        }
27✔
44
        visited.push_back(state);
515✔
45

46
        const llvm::BasicBlock* block = nullptr;
515✔
47
        for (auto& state_mapping_kv : this->state_mapping_) {
603✔
48
            if (state_mapping_kv.second.find(state) != state_mapping_kv.second.end()) {
603✔
49
                assert(block == nullptr);
117✔
50
                block = state_mapping_kv.first;
117✔
51
            }
117✔
52
        }
603✔
53
        if (block != nullptr) {
515✔
54
            this->visit_block(block, *state);
117✔
55
        }
117✔
56

57
        for (auto& neighbor : this->builder_.subject().out_edges(*state)) {
515✔
58
            queue.push_back(this->builder_.get_non_const_state(&neighbor.dst()));
440✔
59
        }
440✔
60
    }
515✔
61

62
    return builder_.move();
102✔
63
};
102✔
64

65
void Lifting::visit_globals() {
102✔
66
    std::unordered_set<llvm::GlobalObject*> visited;
102✔
67
    Lifting::collect_globals(this->function_, visited);
102✔
68

69
    for (llvm::GlobalObject* GV : visited) {
102✔
70
        if (auto function = llvm::dyn_cast<llvm::Function>(GV)) {
37✔
71
            if (function->isIntrinsic()) {
29✔
72
                continue;
17✔
73
            }
17✔
74
        }
29✔
75

76
        // Add the global to the SDFG
77
        std::string global = GV->getName().str();
20✔
78
        if (global == "llvm.used") {
20✔
NEW
79
            continue;
×
NEW
80
        }
×
81

82
        // Make internal/private globals unique by hashing module name
83
        // additionally, sanitize name for C/C++ compatibility
84
        if (GV->getLinkage() == llvm::GlobalValue::InternalLinkage) {
20✔
85
            if (!GV->getName().starts_with("__daisy_int_")) {
6✔
86
                // Hash module name and append to global name
87
                std::string module_path = GV->getParent()->getName().str();
6✔
88
                std::string module_name = std::filesystem::path(module_path).stem().string();
6✔
89
                global = "__daisy_int_" + utils::normalize_name(module_name) + "_" + utils::normalize_name(global);
6✔
90
                GV->setName(global);
6✔
91
                GV->setLinkage(llvm::GlobalValue::ExternalLinkage);
6✔
92
            }
6✔
93
        } else if (GV->getLinkage() == llvm::GlobalValue::PrivateLinkage) {
14✔
NEW
94
            if (!GV->getName().starts_with("__daisy_priv_")) {
×
95
                // Hash module name and append to global name
NEW
96
                std::string module_path = GV->getParent()->getName().str();
×
NEW
97
                std::string module_name = std::filesystem::path(module_path).stem().string();
×
NEW
98
                global = "__daisy_priv_" + utils::normalize_name(module_name) + "_" + utils::normalize_name(global);
×
NEW
99
                GV->setName(global);
×
NEW
100
                GV->setLinkage(llvm::GlobalValue::ExternalLinkage);
×
NEW
101
            }
×
NEW
102
        }
×
103

104
        // Globals are pointers but their type is the base type
105
        std::unique_ptr<sdfg::types::IType> global_type;
20✔
106
        if (llvm::GlobalVariable* GVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {
20✔
107
            auto base_type = utils::get_type(
8✔
108
                this->builder_,
8✔
109
                this->anonymous_types_mapping_,
8✔
110
                this->DL_,
8✔
111
                GV->getValueType(),
8✔
112
                utils::get_storage_type(this->target_type_, 0),
8✔
113
                ""
8✔
114
            );
8✔
115
            global_type = std::make_unique<sdfg::types::Pointer>(
8✔
116
                base_type->storage_type(),
8✔
117
                0,
8✔
118
                base_type->initializer(),
8✔
119
                static_cast<const sdfg::types::IType&>(*base_type)
8✔
120
            );
8✔
121
        } else {
12✔
122
            global_type = utils::get_type(
12✔
123
                this->builder_,
12✔
124
                this->anonymous_types_mapping_,
12✔
125
                this->DL_,
12✔
126
                GV->getValueType(),
12✔
127
                utils::get_storage_type(this->target_type_, 0)
12✔
128
            );
12✔
129
        }
12✔
130

131
        switch (GV->getLinkage()) {
20✔
132
            case llvm::GlobalValue::LinkageTypes::ExternalLinkage:
20✔
133
            case llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage:
20✔
134
            case llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage:
20✔
135
            case llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage:
20✔
136
            case llvm::GlobalValue::LinkageTypes::WeakAnyLinkage:
20✔
137
            case llvm::GlobalValue::LinkageTypes::WeakODRLinkage:
20✔
138
            case llvm::GlobalValue::LinkageTypes::InternalLinkage:
20✔
139
            case llvm::GlobalValue::LinkageTypes::PrivateLinkage: {
20✔
140
                this->builder_.add_external(global, *global_type, sdfg::LinkageType_External);
20✔
141
                break;
20✔
142
            }
20✔
NEW
143
            default: {
×
NEW
144
                throw NotImplementedException(
×
NEW
145
                    "Unsupported linkage type: " + std::to_string(GV->getLinkage()),
×
NEW
146
                    docc::utils::bestEffortLoc(*GV),
×
NEW
147
                    ::docc::utils::toIRString(*GV)
×
NEW
148
                );
×
149
            }
20✔
150
        }
20✔
151

152
        // Make sure the global survives later LLVM optimizations
153
        llvm::appendToUsed(*this->function_.getParent(), {GV});
20✔
154
    }
20✔
155
}
102✔
156

157
void Lifting::collect_globals(llvm::Function& function, std::unordered_set<llvm::GlobalObject*>& globals) {
102✔
158
    for (llvm::Instruction& I : llvm::instructions(function)) {
212✔
159
        for (llvm::Use& U : I.operands()) Lifting::collect_globals(function, U.get(), globals);
250✔
160
    }
212✔
161
}
102✔
162

163
void Lifting::collect_globals(llvm::Function& function, llvm::Value* V, std::unordered_set<llvm::GlobalObject*>& visited) {
252✔
164
    // Drop pointer-casts / addrspace-casts / zero-GEPs.
165
    V = V->stripPointerCasts();
252✔
166

167
    // If it is a ConstantExpr (GEP, bitcast, inttoptr, …) look at its operands.
168
    if (auto* CE = llvm::dyn_cast<llvm::ConstantExpr>(V)) {
252✔
NEW
169
        for (llvm::Value* Op : CE->operands()) Lifting::collect_globals(function, Op, visited);
×
NEW
170
        return;
×
NEW
171
    }
×
172

173
    // Aggregate constants (arrays, structs) can also hide ConstantExprs.
174
    if (auto* CA = llvm::dyn_cast<llvm::ConstantAggregate>(V)) {
252✔
175
        for (llvm::Value* Op : CA->operands()) Lifting::collect_globals(function, Op, visited);
2✔
176
        return;
1✔
177
    }
1✔
178

179
    // Finally, if we have hit a global, remember it (deduplicated with visited).
180
    if (auto* GV = llvm::dyn_cast<llvm::GlobalObject>(V)) {
251✔
181
        if (visited.find(GV) == visited.end()) {
37✔
182
            visited.insert(GV);
37✔
183
        }
37✔
184
    }
37✔
185
}
251✔
186

187
void Lifting::visit_arguments() {
102✔
188
    for (auto& llvm_arg : this->function_.args()) {
129✔
189
        std::string arg = utils::get_name(&llvm_arg);
129✔
190
        auto arg_type = utils::get_type(
129✔
191
            this->builder_,
129✔
192
            this->anonymous_types_mapping_,
129✔
193
            this->DL_,
129✔
194
            llvm_arg.getType(),
129✔
195
            utils::get_storage_type(this->target_type_, 0)
129✔
196
        );
129✔
197
        this->builder_.add_container(arg, *arg_type, true);
129✔
198
    }
129✔
199
};
102✔
200

201
void Lifting::visit_cfg() {
102✔
202
    // Add states for each block and PHI transitions
203
    for (const llvm::BasicBlock& block : this->function_) {
114✔
204
        const llvm::Instruction& initial_inst = *block.begin();
114✔
205
        this->state_mapping_.insert({&block, {}});
114✔
206

207
        if (&block == &this->function_.getEntryBlock()) {
114✔
208
            assert(!block.hasNPredecessorsOrMore(1));
102✔
209

210
            auto& state = this->builder_.add_state(true, ::docc::utils::get_debug_info(initial_inst));
102✔
211
            this->state_mapping_.at(&block).insert(&state);
102✔
212
            this->pred_mapping_.insert({&state, {}});
102✔
213
        } else if (block.phis().empty()) {
102✔
214
            auto& state = this->builder_.add_state(false, ::docc::utils::get_debug_info(initial_inst));
9✔
215
            auto preds = llvm::predecessors(&block);
9✔
216
            std::set<const llvm::BasicBlock*> pred_set(preds.begin(), preds.end());
9✔
217
            this->state_mapping_.at(&block).insert(&state);
9✔
218
            this->pred_mapping_.insert({&state, pred_set});
9✔
219
        } else {
9✔
220
            for (auto PI = llvm::pred_begin(&block), E = llvm::pred_end(&block); PI != E; ++PI) {
9✔
221
                const llvm::BasicBlock* pred = *PI;
6✔
222

223
                auto& state = this->builder_.add_state(false, ::docc::utils::get_debug_info(initial_inst));
6✔
224

225
                this->state_mapping_.at(&block).insert(&state);
6✔
226
                this->pred_mapping_.insert({&state, {pred}});
6✔
227
            }
6✔
228
        }
3✔
229
    }
114✔
230

231
    // Add edges for each <pred, succ> pair
232
    for (const llvm::BasicBlock& pred : this->function_) {
114✔
233
        const llvm::Instruction* terminator = pred.getTerminator();
114✔
234
        if (llvm::isa<llvm::UnreachableInst>(terminator) || llvm::isa<llvm::ReturnInst>(terminator)) {
114✔
235
            continue;
103✔
236
        }
103✔
237

238
        if (auto br_inst = llvm::dyn_cast<const llvm::BranchInst>(terminator)) {
11✔
239
            auto dbg_info = ::docc::utils::get_debug_info(*br_inst);
10✔
240

241
            if (br_inst->isUnconditional()) {
10✔
242
                auto succ = br_inst->getSuccessor(0);
6✔
243
                auto dst_states = this->state_mapping_.at(succ);
6✔
244

245
                const sdfg::control_flow::State* dst_state = nullptr;
6✔
246
                for (auto dst_state_cand : dst_states) {
9✔
247
                    if (this->pred_mapping_.at(dst_state_cand).contains(&pred)) {
9✔
248
                        assert(dst_state == nullptr);
6✔
249
                        dst_state = dst_state_cand;
6✔
250
                    }
6✔
251
                }
9✔
252
                assert(dst_state != nullptr);
6✔
253

254
                for (auto src_state : this->state_mapping_.at(&pred)) {
6✔
255
                    this->builder_.add_edge(*src_state, *dst_state, dbg_info);
6✔
256
                }
6✔
257
            } else {
6✔
258
                auto condition = br_inst->getCondition();
4✔
259

260
                sdfg::symbolic::Expression expression;
4✔
261
                if (utils::is_literal(condition)) {
4✔
NEW
262
                    expression = utils::as_symbol(llvm::dyn_cast<llvm::Constant>(condition));
×
263
                } else {
4✔
264
                    auto symbol = utils::find_const_name_to_sdfg_name(this->constants_mapping_, condition);
4✔
265
                    if (!this->builder_.subject().exists(symbol)) {
4✔
266
                        auto condition_type = utils::get_type(
2✔
267
                            this->builder_,
2✔
268
                            this->anonymous_types_mapping_,
2✔
269
                            this->DL_,
2✔
270
                            condition->getType(),
2✔
271
                            utils::get_storage_type(this->target_type_, 0)
2✔
272
                        );
2✔
273
                        this->builder_.add_container(symbol, *condition_type);
2✔
274

275
                        auto& cond_type = this->builder_.subject().type(symbol);
2✔
276
                        assert(
2✔
277
                            cond_type.type_id() == sdfg::types::TypeID::Scalar &&
2✔
278
                            "BranchInst: Expected scalar type as condition"
2✔
279
                        );
2✔
280
                        auto& cond_scalar_type = static_cast<const sdfg::types::Scalar&>(cond_type);
2✔
281
                        assert(
2✔
282
                            cond_scalar_type.primitive_type() == sdfg::types::PrimitiveType::Bool &&
2✔
283
                            "BranchInst: Expected bool type as condition"
2✔
284
                        );
2✔
285
                    }
2✔
286
                    expression = sdfg::symbolic::symbol(symbol);
4✔
287
                }
4✔
288

289
                auto if_block = br_inst->getSuccessor(0);
4✔
290
                auto if_dst_states = this->state_mapping_.at(if_block);
4✔
291

292
                const sdfg::control_flow::State* if_dst_state = nullptr;
4✔
293
                for (auto if_dst_state_cand : if_dst_states) {
4✔
294
                    if (this->pred_mapping_.at(if_dst_state_cand).contains(&pred)) {
4✔
295
                        assert(if_dst_state == nullptr);
4✔
296
                        if_dst_state = if_dst_state_cand;
4✔
297
                    }
4✔
298
                }
4✔
299
                assert(if_dst_state != nullptr);
4✔
300

301
                auto else_block = br_inst->getSuccessor(1);
4✔
302
                auto else_dst_states = this->state_mapping_.at(else_block);
4✔
303

304
                const sdfg::control_flow::State* else_dst_state = nullptr;
4✔
305
                for (auto else_dst_state_cand : else_dst_states) {
7✔
306
                    if (this->pred_mapping_.at(else_dst_state_cand).contains(&pred)) {
7✔
307
                        assert(else_dst_state == nullptr);
4✔
308
                        else_dst_state = else_dst_state_cand;
4✔
309
                    }
4✔
310
                }
7✔
311
                assert(else_dst_state != nullptr);
4✔
312

313
                for (auto src_state : this->state_mapping_.at(&pred)) {
7✔
314
                    auto sdfg_cond = sdfg::symbolic::Ne(expression, sdfg::symbolic::__false__());
7✔
315
                    this->builder_.add_edge(*src_state, *if_dst_state, sdfg_cond, dbg_info);
7✔
316
                    this->builder_.add_edge(*src_state, *else_dst_state, sdfg::symbolic::Not(sdfg_cond), dbg_info);
7✔
317
                }
7✔
318
            }
4✔
319
        } else if (auto invoke_inst = llvm::dyn_cast<const llvm::InvokeInst>(terminator)) {
10✔
320
            auto dbg_info = ::docc::utils::get_debug_info(*invoke_inst);
1✔
321

322
            auto normal_block = invoke_inst->getNormalDest();
1✔
323
            auto normal_dst_states = this->state_mapping_.at(normal_block);
1✔
324

325
            const sdfg::control_flow::State* normal_dst_state = nullptr;
1✔
326
            for (auto normal_dst_state_cand : normal_dst_states) {
1✔
327
                if (this->pred_mapping_.at(normal_dst_state_cand).contains(&pred)) {
1✔
328
                    assert(normal_dst_state == nullptr);
1✔
329
                    normal_dst_state = normal_dst_state_cand;
1✔
330
                }
1✔
331
            }
1✔
332
            assert(normal_dst_state != nullptr);
1✔
333

334
            auto unwind_block = invoke_inst->getUnwindDest();
1✔
335
            auto unwind_dst_states = this->state_mapping_.at(unwind_block);
1✔
336

337
            const sdfg::control_flow::State* unwind_dst_state = nullptr;
1✔
338
            for (auto unwind_dst_state_cand : unwind_dst_states) {
1✔
339
                if (this->pred_mapping_.at(unwind_dst_state_cand).contains(&pred)) {
1✔
340
                    assert(unwind_dst_state == nullptr);
1✔
341
                    unwind_dst_state = unwind_dst_state_cand;
1✔
342
                }
1✔
343
            }
1✔
344
            assert(unwind_dst_state != nullptr);
1✔
345

346
            // Add unwind symbol
347
            sdfg::types::Scalar unwind_type(sdfg::types::PrimitiveType::Bool);
1✔
348
            std::string unwind_symbol = "__unwind_" + utils::get_name(invoke_inst);
1✔
349
            this->builder_.add_container(unwind_symbol, unwind_type);
1✔
350

351
            auto unwind_cond = sdfg::symbolic::Ne(sdfg::symbolic::symbol(unwind_symbol), sdfg::symbolic::__false__());
1✔
352
            for (auto src_state : this->state_mapping_.at(&pred)) {
1✔
353
                this->builder_.add_edge(*src_state, *normal_dst_state, sdfg::symbolic::Not(unwind_cond), dbg_info);
1✔
354
                this->builder_.add_edge(*src_state, *unwind_dst_state, unwind_cond, dbg_info);
1✔
355
            }
1✔
356
        } else {
1✔
NEW
357
            throw NotImplementedException(
×
NEW
358
                "Unsupported terminator instruction in CFG construction",
×
NEW
359
                docc::utils::get_debug_info(*terminator),
×
NEW
360
                docc::utils::toIRString(*terminator)
×
NEW
361
            );
×
NEW
362
        }
×
363
    }
11✔
364
}
102✔
365

366
void Lifting::visit_block(const llvm::BasicBlock* block, sdfg::control_flow::State& state) {
117✔
367
    std::set<const llvm::BasicBlock*> preds = this->pred_mapping_.at(&state);
117✔
368

369
    // Phi Nodes perform double buffering. We need to create per-transition containers.
370
    std::string transition_suffix = utils::get_name(block);
117✔
371
    for (auto& pred : preds) {
117✔
372
        transition_suffix += "_" + utils::get_name(pred);
16✔
373
    }
16✔
374

375
    // We add states before the actual instruction to ensure that the correct phi values are available for the
376
    // instruction.
377
    sdfg::control_flow::State* next_state = &state;
117✔
378
    for (const llvm::Instruction& inst : *block) {
123✔
379
        auto phi_inst = llvm::dyn_cast<const llvm::PHINode>(&inst);
123✔
380
        if (!phi_inst) {
123✔
381
            break;
117✔
382
        }
117✔
383

384
        // Determine phi value
385
        llvm::Value* phi_value = nullptr;
6✔
386
        for (size_t i = 0; i < phi_inst->getNumIncomingValues(); ++i) {
18✔
387
            if (preds.contains(phi_inst->getIncomingBlock(i))) {
12✔
388
                assert(phi_value == nullptr && "Failed to determine phi value");
6✔
389
                phi_value = phi_inst->getIncomingValue(i);
6✔
390
            }
6✔
391
        }
12✔
392
        assert(phi_value != nullptr && "Failed to determine phi value");
6✔
393

394
        // Constants can be handled without a buffer
395
        if (llvm::isa<llvm::Constant>(phi_value) || llvm::isa<llvm::Argument>(phi_value) ||
6✔
396
            llvm::isa<llvm::GlobalValue>(phi_value)) {
6✔
397
            continue;
5✔
398
        }
5✔
399

400
        // Add state
401
        auto dbg_info = ::docc::utils::get_debug_info(*phi_inst);
1✔
402
        next_state = &this->builder_.add_state_after(*next_state, true, dbg_info);
1✔
403
        this->pred_mapping_.insert({next_state, preds});
1✔
404

405
        // Map Phi value to transition-specific value
406
        auto phi_value_type = utils::get_type(
1✔
407
            this->builder_,
1✔
408
            this->anonymous_types_mapping_,
1✔
409
            this->DL_,
1✔
410
            phi_value->getType(),
1✔
411
            utils::get_storage_type(this->target_type_, 0)
1✔
412
        );
1✔
413
        std::string phi_input = utils::get_name(phi_value);
1✔
414
        if (!this->builder_.subject().exists(phi_input)) {
1✔
NEW
415
            this->builder_.add_container(phi_input, *phi_value_type);
×
NEW
416
        }
×
417
        auto& input_node = this->builder_.add_access(*next_state, phi_input, dbg_info);
1✔
418

419
        std::string phi_transition = phi_input + "_" + transition_suffix;
1✔
420
        if (!this->builder_.subject().exists(phi_transition)) {
1✔
421
            this->builder_.add_container(phi_transition, *phi_value_type);
1✔
422
        }
1✔
423
        auto& output_node = this->builder_.add_access(*next_state, phi_transition, dbg_info);
1✔
424

425
        switch (phi_value_type->type_id()) {
1✔
NEW
426
            case sdfg::types::TypeID::Pointer: {
×
NEW
427
                sdfg::types::Pointer base_ptr_type;
×
NEW
428
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
×
NEW
429
                this->builder_.add_reference_memlet(
×
NEW
430
                    *next_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
×
NEW
431
                );
×
NEW
432
                break;
×
NEW
433
            }
×
434
            case sdfg::types::TypeID::Scalar: {
1✔
435
                auto& tasklet =
1✔
436
                    this->builder_
1✔
437
                        .add_tasklet(*next_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
438
                this->builder_.add_computational_memlet(*next_state, input_node, tasklet, "_in", {}, dbg_info);
1✔
439
                this->builder_.add_computational_memlet(*next_state, tasklet, "__out", output_node, {}, dbg_info);
1✔
440
                break;
1✔
NEW
441
            }
×
NEW
442
            default: {
×
NEW
443
                throw NotImplementedException(
×
NEW
444
                    "Unsupported phi value type",
×
NEW
445
                    ::docc::utils::get_debug_info(*phi_inst),
×
NEW
446
                    ::docc::utils::toIRString(*phi_inst)
×
NEW
447
                );
×
NEW
448
            }
×
449
        }
1✔
450
    }
1✔
451

452
    // Visit instructions
453
    for (const llvm::Instruction& inst : *block) {
218✔
454
        if (llvm::isa<llvm::BranchInst>(inst)) {
218✔
455
            // Branch instructions are handled in the CFG construction.
456
            continue;
13✔
457
        }
13✔
458

459
        auto dbg_info = ::docc::utils::get_debug_info(inst);
205✔
460
        next_state = &this->builder_.add_state_after(*next_state, true, dbg_info);
205✔
461
        this->pred_mapping_.insert({next_state, preds});
205✔
462
        next_state = &this->visit_instruction(block, &inst, *next_state);
205✔
463
    }
205✔
464
};
117✔
465

466
sdfg::control_flow::State& Lifting::visit_instruction(
467
    const llvm::BasicBlock* block, const llvm::Instruction* instruction, sdfg::control_flow::State& current_state
468
) {
205✔
469
    // Constant expressions are converted into states before the instruction is visited.
470
    // ConstantExprVisitor constantexpr_visitor(this->TLI_, this->function_, this->DL_, this->builder_,
471
    //                                          this->target_type_, this->state_mapping_,
472
    //                                          this->pred_mapping_, this->constants_mapping_);
473
    // for (auto& op : instruction->operands()) {
474
    //     if (llvm::ConstantExpr* CE = llvm::dyn_cast<llvm::ConstantExpr>(op)) {
475
    //         constantexpr_visitor.visit(block, CE, current_state, instruction);
476
    //     }
477
    // }
478

479
    // Special instructions
480
    if (auto inst = llvm::dyn_cast<const llvm::AllocaInst>(instruction)) {
205✔
NEW
481
        return this->visit_AllocaInst(block, inst, current_state);
×
482
    } else if (auto inst = llvm::dyn_cast<const llvm::GetElementPtrInst>(instruction)) {
205✔
483
        return this->visit_GetElementPtrInst(block, inst, current_state);
3✔
484
    } else if (auto inst = llvm::dyn_cast<const llvm::StoreInst>(instruction)) {
202✔
485
        return this->visit_StoreInst(block, inst, current_state);
12✔
486
    } else if (auto inst = llvm::dyn_cast<const llvm::LoadInst>(instruction)) {
190✔
487
        return this->visit_LoadInst(block, inst, current_state);
13✔
488
    } else if (auto inst = llvm::dyn_cast<const llvm::PHINode>(instruction)) {
177✔
489
        return this->visit_PHINode(block, inst, current_state);
6✔
490
    } else if (auto inst = llvm::dyn_cast<const llvm::ICmpInst>(instruction)) {
171✔
491
        return this->visit_ICmpInst(block, inst, current_state);
10✔
492
    } else if (auto inst = llvm::dyn_cast<const llvm::ReturnInst>(instruction)) {
161✔
493
        return this->visit_ReturnInst(block, inst, current_state);
102✔
494
    } else if (auto inst = llvm::dyn_cast<const llvm::SelectInst>(instruction)) {
102✔
495
        return this->visit_SelectInst(block, inst, current_state);
2✔
496
    } else if (auto inst = llvm::dyn_cast<const llvm::UnreachableInst>(instruction)) {
57✔
497
        return this->visit_UnreachableInst(block, inst, current_state);
1✔
498
    }
1✔
499

500
    // Operations
501
    if (auto inst = llvm::dyn_cast<const llvm::UnaryOperator>(instruction)) {
56✔
502
        return this->visit_UnaryOperator(block, inst, current_state);
5✔
503
    } else if (auto inst = llvm::dyn_cast<const llvm::BinaryOperator>(instruction)) {
51✔
504
        return this->visit_BinaryOperator(block, inst, current_state);
6✔
505
    } else if (auto inst = llvm::dyn_cast<const llvm::CastInst>(instruction)) {
45✔
506
        return this->visit_CastInst(block, inst, current_state);
12✔
507
    } else if (auto inst = llvm::dyn_cast<const llvm::FCmpInst>(instruction)) {
33✔
508
        return this->visit_FCmpInst(block, inst, current_state);
4✔
509
    } else if (auto inst = llvm::dyn_cast<const llvm::CallBase>(instruction)) {
29✔
510
        FunctionLifting lifter(
29✔
511
            this->TLI_,
29✔
512
            this->DL_,
29✔
513
            this->function_,
29✔
514
            this->target_type_,
29✔
515
            this->builder_,
29✔
516
            this->state_mapping_,
29✔
517
            this->pred_mapping_,
29✔
518
            this->constants_mapping_,
29✔
519
            this->anonymous_types_mapping_
29✔
520
        );
29✔
521
        return lifter.visit(block, inst, current_state);
29✔
522
    }
29✔
523

NEW
524
    std::string instruction_str;
×
NEW
525
    llvm::raw_string_ostream OS(instruction_str);
×
NEW
526
    instruction->print(OS);
×
NEW
527
    throw NotImplementedException(
×
NEW
528
        "Unsupported instruction: " + instruction_str,
×
NEW
529
        ::docc::utils::get_debug_info(*instruction),
×
NEW
530
        ::docc::utils::toIRString(*instruction)
×
NEW
531
    );
×
532
};
56✔
533

534
sdfg::control_flow::State& Lifting::visit_ReturnInst(
535
    const llvm::BasicBlock* block, const llvm::ReturnInst* instruction, sdfg::control_flow::State& current_state
536
) {
102✔
537
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
102✔
538
    if (instruction->getNumOperands() == 0) {
102✔
539
        // Do nothing for void return
540
        assert(this->builder_.subject().out_degree(current_state) == 0);
98✔
541
        return this->builder_.add_return_state_after(current_state, "", dbg_info);
98✔
542
    }
98✔
543

544
    assert(instruction->getNumOperands() == 1);
102✔
545

546
    // Non-void return
547
    auto return_value = instruction->getReturnValue();
4✔
548
    assert(return_value != nullptr);
4✔
549

550
    assert(this->builder_.subject().out_degree(current_state) == 0);
4✔
551

552
    std::string data;
4✔
553
    if (utils::is_literal(return_value)) {
4✔
554
        data = utils::as_initializer(llvm::dyn_cast<llvm::Constant>(return_value));
4✔
555
        auto return_type = utils::get_type(
4✔
556
            this->builder_,
4✔
557
            this->anonymous_types_mapping_,
4✔
558
            this->DL_,
4✔
559
            return_value->getType(),
4✔
560
            utils::get_storage_type(this->target_type_, 0)
4✔
561
        );
4✔
562
        return this->builder_.add_constant_return_state_after(current_state, data, *return_type, dbg_info);
4✔
563
    } else {
4✔
NEW
564
        data = utils::find_const_name_to_sdfg_name(this->constants_mapping_, return_value);
×
NEW
565
        return this->builder_.add_return_state_after(current_state, data, dbg_info);
×
NEW
566
    }
×
567
};
4✔
568

569
sdfg::control_flow::State& Lifting::visit_UnreachableInst(
570
    const llvm::BasicBlock* block, const llvm::UnreachableInst* instruction, sdfg::control_flow::State& current_state
571
) {
1✔
572
    // Unreachable instructions do not have any effect on the SDFG.
573
    // We simply return the current state.
574
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
1✔
575
    assert(this->builder_.subject().out_degree(current_state) == 0);
1✔
576

577
    std::string data;
1✔
578
    llvm::Type* RetTy = this->function_.getReturnType();
1✔
579
    if (RetTy->isVoidTy()) {
1✔
NEW
580
        data = "";
×
581
    } else {
1✔
582
        auto undef = llvm::UndefValue::get(RetTy);
1✔
583
        data = utils::as_initializer(undef);
1✔
584
    }
1✔
585

586
    auto& ret_state =
1✔
587
        this->builder_
1✔
588
            .add_constant_return_state_after(current_state, data, this->builder_.subject().return_type(), dbg_info);
1✔
589
    this->builder_.add_library_node<sdfg::stdlib::UnreachableNode>(ret_state, dbg_info);
1✔
590

591
    return ret_state;
1✔
592
};
1✔
593

594
sdfg::control_flow::State& Lifting::visit_AllocaInst(
595
    const llvm::BasicBlock* block, const llvm::AllocaInst* instruction, sdfg::control_flow::State& current_state
NEW
596
) {
×
597
    // Define Debug
NEW
598
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
×
599

600
    // Define Output
NEW
601
    std::string output = utils::get_name(instruction);
×
NEW
602
    if (!this->builder_.subject().exists(output)) {
×
NEW
603
        auto output_type = utils::get_type(
×
NEW
604
            this->builder_,
×
NEW
605
            this->anonymous_types_mapping_,
×
NEW
606
            this->DL_,
×
NEW
607
            instruction->getType(),
×
NEW
608
            utils::get_storage_type(this->target_type_, 0)
×
NEW
609
        );
×
NEW
610
        this->builder_.add_container(output, *output_type);
×
NEW
611
    }
×
NEW
612
    auto& output_type = this->builder_.subject().type(output);
×
NEW
613
    assert(output_type.type_id() == sdfg::types::TypeID::Pointer && "AllocaInst: Expected pointer type as output");
×
614

NEW
615
    auto alloca_type = utils::get_type(
×
NEW
616
        this->builder_,
×
NEW
617
        this->anonymous_types_mapping_,
×
NEW
618
        this->DL_,
×
NEW
619
        instruction->getAllocatedType(),
×
NEW
620
        utils::get_storage_type(this->target_type_, 0)
×
NEW
621
    );
×
622

623
    // Define array size
NEW
624
    sdfg::symbolic::Expression size_sym = sdfg::symbolic::one();
×
NEW
625
    if (instruction->isArrayAllocation()) {
×
NEW
626
        const llvm::Value* arg_size = instruction->getArraySize();
×
NEW
627
        if (utils::is_literal(arg_size)) {
×
NEW
628
            size_sym = utils::as_symbol(llvm::dyn_cast<const llvm::ConstantData>(arg_size));
×
NEW
629
        } else {
×
NEW
630
            std::string arg_name = utils::find_const_name_to_sdfg_name(this->constants_mapping_, arg_size);
×
NEW
631
            size_sym = sdfg::symbolic::symbol(arg_name);
×
NEW
632
        }
×
NEW
633
    }
×
NEW
634
    size_t type_size = this->DL_.getTypeAllocSize(instruction->getAllocatedType());
×
NEW
635
    sdfg::symbolic::Expression alloca_size = sdfg::symbolic::mul(size_sym, sdfg::symbolic::integer(type_size));
×
636

NEW
637
    auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
×
NEW
638
    auto& lib_node = this->builder_.add_library_node<sdfg::stdlib::AllocaNode>(current_state, dbg_info, alloca_size);
×
639

NEW
640
    sdfg::types::Pointer opaque_ptr;
×
NEW
641
    this->builder_.add_computational_memlet(current_state, lib_node, "_ret", output_node, {}, opaque_ptr, dbg_info);
×
642

NEW
643
    return current_state;
×
NEW
644
};
×
645

646
sdfg::control_flow::State& Lifting::visit_GetElementPtrInst(
647
    const llvm::BasicBlock* block, const llvm::GetElementPtrInst* instruction, sdfg::control_flow::State& current_state
648
) {
3✔
649
    // Define Debug
650
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
3✔
651

652
    // Define Output
653
    std::string output = utils::get_name(instruction);
3✔
654
    if (!this->builder_.subject().exists(output)) {
3✔
655
        auto output_type = utils::get_type(
3✔
656
            this->builder_,
3✔
657
            this->anonymous_types_mapping_,
3✔
658
            this->DL_,
3✔
659
            instruction->getType(),
3✔
660
            utils::get_storage_type(this->target_type_, 0)
3✔
661
        );
3✔
662
        this->builder_.add_container(output, *output_type);
3✔
663
    }
3✔
664
    auto& output_type = this->builder_.subject().type(output);
3✔
665
    assert(output_type.type_id() == sdfg::types::TypeID::Pointer && "GetElementPtrInst: Expected pointer type as output");
3✔
666

667
    // Define Input
668
    auto pointer_operand = instruction->getPointerOperand();
3✔
669
    std::string input;
3✔
670
    if (utils::is_null_pointer(pointer_operand)) {
3✔
NEW
671
        input = sdfg::symbolic::__nullptr__()->get_name();
×
672
    } else {
3✔
673
        input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, pointer_operand);
3✔
674
        auto& input_type = this->builder_.subject().type(input);
3✔
675
        assert(
3✔
676
            input_type.type_id() == sdfg::types::TypeID::Pointer && "GetElementPtrInst: Expected pointer type as input"
3✔
677
        );
3✔
678
    }
3✔
679

680
    // Subset
681
    sdfg::data_flow::Subset subset;
3✔
682
    for (auto it = instruction->idx_begin(); it != instruction->idx_end(); ++it) {
8✔
683
        if (auto const_int = llvm::dyn_cast<llvm::ConstantInt>(*it)) {
5✔
684
            auto start = sdfg::symbolic::integer(const_int->getZExtValue());
3✔
685
            subset.push_back(start);
3✔
686
        } else {
3✔
687
            std::string name = utils::get_name(*it);
2✔
688
            auto start = sdfg::symbolic::symbol(name);
2✔
689
            assert(
2✔
690
                this->builder_.subject().exists(name) &&
2✔
691
                "GetElementPtrInst: Expected index to be a constant or a symbol"
2✔
692
            );
2✔
693
            subset.push_back(start);
2✔
694
        }
2✔
695
    }
5✔
696

697
    // Define Source Element Type
698
    auto source_element_type = utils::get_type(
3✔
699
        this->builder_,
3✔
700
        this->anonymous_types_mapping_,
3✔
701
        this->DL_,
3✔
702
        instruction->getSourceElementType(),
3✔
703
        utils::get_storage_type(this->target_type_, 0)
3✔
704
    );
3✔
705
    sdfg::types::Pointer base_ptr_type(*source_element_type);
3✔
706

707
    auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
3✔
708
    if (utils::is_null_pointer(pointer_operand)) {
3✔
NEW
709
        auto& input_node = this->builder_.add_constant(current_state, input, sdfg::types::Pointer(), dbg_info);
×
NEW
710
        this->builder_.add_reference_memlet(current_state, input_node, output_node, subset, base_ptr_type, dbg_info);
×
711
    } else {
3✔
712
        auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
3✔
713
        this->builder_.add_reference_memlet(current_state, input_node, output_node, subset, base_ptr_type, dbg_info);
3✔
714
    }
3✔
715

716
    return current_state;
3✔
717
};
3✔
718

719
sdfg::control_flow::State& Lifting::visit_LoadInst(
720
    const llvm::BasicBlock* block, const llvm::LoadInst* instruction, sdfg::control_flow::State& current_state
721
) {
13✔
722
    // Define Debug
723
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
13✔
724

725
    // Define Output
726
    std::string output = utils::get_name(instruction);
13✔
727
    if (!this->builder_.subject().exists(output)) {
13✔
728
        auto output_type = utils::get_type(
13✔
729
            this->builder_,
13✔
730
            this->anonymous_types_mapping_,
13✔
731
            this->DL_,
13✔
732
            instruction->getType(),
13✔
733
            utils::get_storage_type(this->target_type_, 0)
13✔
734
        );
13✔
735
        this->builder_.add_container(output, *output_type);
13✔
736
    }
13✔
737
    auto& output_type = this->builder_.subject().type(output);
13✔
738

739
    // Define Input
740
    auto pointer_operand = instruction->getPointerOperand();
13✔
741
    assert(!utils::is_null_pointer(pointer_operand) && "LoadInst: Expected non-null pointer as source");
13✔
742
    std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, pointer_operand);
13✔
743
    assert(this->builder_.subject().exists(input));
13✔
744
    auto& input_type = this->builder_.subject().type(input);
13✔
745
    assert(input_type.type_id() == sdfg::types::TypeID::Pointer && "LoadInst: Expected pointer type as input");
13✔
746

747
    // Define Operation
748
    switch (output_type.type_id()) {
13✔
749
        case sdfg::types::TypeID::Scalar: {
6✔
750
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
6✔
751
            auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
6✔
752
            auto& tasklet =
6✔
753
                this->builder_
6✔
754
                    .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
6✔
755

756
            sdfg::types::Pointer base_ptr_type(output_type);
6✔
757
            this->builder_.add_computational_memlet(
6✔
758
                current_state, input_node, tasklet, "_in", {sdfg::symbolic::zero()}, base_ptr_type, dbg_info
6✔
759
            );
6✔
760
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
6✔
761

762
            return current_state;
6✔
NEW
763
        }
×
764
        case sdfg::types::TypeID::Pointer:
1✔
765
        case sdfg::types::TypeID::Array:
1✔
766
        case sdfg::types::TypeID::Structure: {
7✔
767
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
7✔
768
            auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
7✔
769
            sdfg::types::Pointer input_ptr_type(output_type);
7✔
770
            this->builder_
7✔
771
                .add_dereference_memlet(current_state, input_node, output_node, true, input_ptr_type, dbg_info);
7✔
772

773
            return current_state;
7✔
774
        }
1✔
NEW
775
        default:
×
NEW
776
            throw NotImplementedException(
×
NEW
777
                "Unsupported load instruction",
×
NEW
778
                ::docc::utils::get_debug_info(*instruction),
×
NEW
779
                ::docc::utils::toIRString(*instruction)
×
NEW
780
            );
×
781
    }
13✔
782
};
13✔
783

784
sdfg::control_flow::State& Lifting::visit_StoreInst(
785
    const llvm::BasicBlock* block, const llvm::StoreInst* instruction, sdfg::control_flow::State& current_state
786
) {
12✔
787
    // Define Debug
788
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
12✔
789

790
    // Define Output
791
    auto output_operand = instruction->getPointerOperand();
12✔
792
    assert(!utils::is_null_pointer(output_operand) && "StoreInst: Expected non-null pointer as source");
12✔
793
    std::string output = utils::find_const_name_to_sdfg_name(this->constants_mapping_, output_operand);
12✔
794
    assert(this->builder_.subject().exists(output));
12✔
795
    auto& output_type = this->builder_.subject().type(output);
12✔
796
    assert(output_type.type_id() == sdfg::types::TypeID::Pointer && "StoreInst: Expected pointer type as output");
12✔
797

798
    // Define Input
799
    auto input_operand = instruction->getValueOperand();
12✔
800
    auto input_type = utils::get_type(
12✔
801
        this->builder_,
12✔
802
        this->anonymous_types_mapping_,
12✔
803
        this->DL_,
12✔
804
        input_operand->getType(),
12✔
805
        utils::get_storage_type(this->target_type_, 0)
12✔
806
    );
12✔
807
    // Define Operation
808
    switch (input_type->type_id()) {
12✔
809
        case sdfg::types::TypeID::Scalar: {
6✔
810
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
6✔
811

812
            sdfg::data_flow::AccessNode* input_node;
6✔
813
            if (utils::is_literal(input_operand)) {
6✔
814
                std::string arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(input_operand));
5✔
815
                input_node = &this->builder_.add_constant(current_state, arg, *input_type, dbg_info);
5✔
816
            } else {
5✔
817
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
1✔
818
                input_node = &this->builder_.add_access(current_state, input, dbg_info);
1✔
819
            }
1✔
820
            auto& tasklet =
6✔
821
                this->builder_
6✔
822
                    .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
6✔
823

824
            sdfg::types::Pointer base_ptr_type(*input_type);
6✔
825
            this->builder_.add_computational_memlet(current_state, *input_node, tasklet, "_in", {}, dbg_info);
6✔
826
            this->builder_.add_computational_memlet(
6✔
827
                current_state, tasklet, "__out", output_node, {sdfg::symbolic::zero()}, base_ptr_type, dbg_info
6✔
828
            );
6✔
829

830
            return current_state;
6✔
NEW
831
        }
×
NEW
832
        case sdfg::types::TypeID::Array:
×
833
        case sdfg::types::TypeID::Structure:
4✔
834
        case sdfg::types::TypeID::Pointer: {
6✔
835
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
6✔
836

837
            sdfg::data_flow::AccessNode* input_node;
6✔
838
            if (utils::is_literal(input_operand)) {
6✔
839
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::Constant>(input_operand));
4✔
840
                input_node = &this->builder_.add_constant(current_state, input, *input_type, dbg_info);
4✔
841
            } else {
4✔
842
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
2✔
843
                input_node = &this->builder_.add_access(current_state, input, dbg_info);
2✔
844
            }
2✔
845

846
            sdfg::types::Pointer base_ptr_type;
6✔
847
            sdfg::types::Pointer output_ptr_type(*input_type);
6✔
848
            this->builder_
6✔
849
                .add_dereference_memlet(current_state, *input_node, output_node, false, output_ptr_type, dbg_info);
6✔
850

851
            return current_state;
6✔
852
        }
4✔
NEW
853
        default:
×
NEW
854
            throw NotImplementedException(
×
NEW
855
                "Unsupported store instruction",
×
NEW
856
                ::docc::utils::get_debug_info(*instruction),
×
NEW
857
                ::docc::utils::toIRString(*instruction)
×
NEW
858
            );
×
859
    }
12✔
860
};
12✔
861

862
sdfg::control_flow::State& Lifting::visit_PHINode(
863
    const llvm::BasicBlock* block, const llvm::PHINode* instruction, sdfg::control_flow::State& current_state
864
) {
6✔
865
    // Define Debug
866
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
6✔
867

868
    // Define Output
869
    std::string output = utils::get_name(instruction);
6✔
870
    if (!this->builder_.subject().exists(output)) {
6✔
871
        auto output_type = utils::get_type(
1✔
872
            this->builder_,
1✔
873
            this->anonymous_types_mapping_,
1✔
874
            this->DL_,
1✔
875
            instruction->getType(),
1✔
876
            utils::get_storage_type(this->target_type_, 0)
1✔
877
        );
1✔
878
        assert(
1✔
879
            (output_type->type_id() == sdfg::types::TypeID::Pointer ||
1✔
880
             output_type->type_id() == sdfg::types::TypeID::Scalar) &&
1✔
881
            "PHINode: Expected pointer or scalar type as output"
1✔
882
        );
1✔
883
        this->builder_.add_container(output, *output_type);
1✔
884
    }
1✔
885
    auto& output_type = this->builder_.subject().type(output);
6✔
886

887
    // Determine Input
888
    llvm::Value* phi_value = nullptr;
6✔
889
    auto preds = this->pred_mapping_.at(&current_state);
6✔
890
    for (size_t i = 0; i < instruction->getNumIncomingValues(); ++i) {
9✔
891
        if (preds.contains(instruction->getIncomingBlock(i))) {
9✔
892
            phi_value = instruction->getIncomingValue(i);
6✔
893
            break;
6✔
894
        }
6✔
895
    }
9✔
896
    assert(phi_value != nullptr && "PHINode: Failed to determine phi value");
6✔
897

898
    // Handle UndefValue
899
    if (llvm::dyn_cast<llvm::UndefValue>(phi_value)) {
6✔
NEW
900
        return current_state;
×
NEW
901
    }
×
902

903
    // Define unsupported phi value types
904
    if (llvm::isa<llvm::ConstantExpr>(phi_value)) {
6✔
NEW
905
        throw NotImplementedException(
×
NEW
906
            "PHINode: Unsupported phi value type",
×
NEW
907
            ::docc::utils::get_debug_info(*instruction),
×
NEW
908
            ::docc::utils::toIRString(*instruction)
×
NEW
909
        );
×
NEW
910
    }
×
911

912
    // Define Operation
913
    switch (output_type.type_id()) {
6✔
914
        case sdfg::types::TypeID::Pointer: {
2✔
915
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
2✔
916

917
            // Handle nullptr
918
            if (utils::is_literal(phi_value)) {
2✔
919
                sdfg::types::Pointer ptr_type;
1✔
920

921
                auto arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(phi_value));
1✔
922
                auto& input_node = this->builder_.add_constant(current_state, arg, ptr_type, dbg_info);
1✔
923
                this->builder_.add_reference_memlet(current_state, input_node, output_node, {}, ptr_type, dbg_info);
1✔
924

925
                return current_state;
1✔
926
            } else if (auto GV = llvm::dyn_cast<llvm::GlobalValue>(phi_value)) {
1✔
NEW
927
                std::string global_name = utils::get_name(GV);
×
NEW
928
                auto& input_node = this->builder_.add_access(current_state, global_name, dbg_info);
×
929

NEW
930
                sdfg::types::Pointer base_ptr_type;
×
NEW
931
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
×
NEW
932
                this->builder_.add_reference_memlet(
×
NEW
933
                    current_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
×
NEW
934
                );
×
935

NEW
936
                return current_state;
×
937
            } else {
1✔
938
                // Handle local variable
939
                std::string input = utils::get_name(phi_value);
1✔
940
                if (!llvm::isa<llvm::Argument>(phi_value)) {
1✔
NEW
941
                    std::string transition_suffix = utils::get_name(block);
×
NEW
942
                    for (auto& pred : preds) {
×
NEW
943
                        transition_suffix += "_" + utils::get_name(pred);
×
NEW
944
                    }
×
NEW
945
                    input += "_" + transition_suffix;
×
NEW
946
                }
×
947

948
                auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
1✔
949

950
                sdfg::types::Pointer base_ptr_type;
1✔
951
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
952
                this->builder_.add_reference_memlet(
1✔
953
                    current_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
954
                );
1✔
955

956
                return current_state;
1✔
957
            }
1✔
958
        }
2✔
959
        case sdfg::types::TypeID::Scalar: {
4✔
960
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
4✔
961

962
            // Handle literal
963
            if (utils::is_literal(phi_value)) {
4✔
964
                auto arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(phi_value));
1✔
965
                auto& input_node = this->builder_.add_constant(current_state, arg, output_type, dbg_info);
1✔
966
                auto& tasklet =
1✔
967
                    this->builder_
1✔
968
                        .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
969
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in", {}, dbg_info);
1✔
970
                this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
1✔
971

972
                return current_state;
1✔
973
            } else {
3✔
974
                // Handle local variable
975
                std::string phi_transition = utils::get_name(phi_value);
3✔
976
                if (!llvm::isa<llvm::Argument>(phi_value)) {
3✔
977
                    std::string transition_suffix = utils::get_name(block);
1✔
978
                    for (auto& pred : preds) {
1✔
979
                        transition_suffix += "_" + utils::get_name(pred);
1✔
980
                    }
1✔
981
                    phi_transition += "_" + transition_suffix;
1✔
982
                }
1✔
983

984
                auto& input_node = this->builder_.add_access(current_state, phi_transition, dbg_info);
3✔
985
                auto& tasklet =
3✔
986
                    this->builder_
3✔
987
                        .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
3✔
988
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in", {}, dbg_info);
3✔
989
                this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
3✔
990

991
                return current_state;
3✔
992
            }
3✔
993
        }
4✔
NEW
994
        default:
×
NEW
995
            throw NotImplementedException(
×
NEW
996
                "PHINode: Unsupported output type",
×
NEW
997
                ::docc::utils::get_debug_info(*instruction),
×
NEW
998
                ::docc::utils::toIRString(*instruction)
×
NEW
999
            );
×
1000
    }
6✔
1001

NEW
1002
    return current_state;
×
1003
};
6✔
1004

1005
sdfg::control_flow::State& Lifting::visit_ICmpInst(
1006
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
1007
) {
10✔
1008
    auto predicate = instruction->getPredicate();
10✔
1009

1010
    // Unsigned comparisions cannot be handled symbolically
1011
    if ((predicate == llvm::CmpInst::Predicate::ICMP_UGT || predicate == llvm::CmpInst::Predicate::ICMP_UGE ||
10✔
1012
         predicate == llvm::CmpInst::Predicate::ICMP_ULT || predicate == llvm::CmpInst::Predicate::ICMP_ULE) &&
10✔
1013
        !instruction->getOperand(0)->getType()->isPointerTy()) {
10✔
NEW
1014
        return this->visit_ICmpInst_dataflow(block, instruction, current_state);
×
1015
    } else {
10✔
1016
        return this->visit_ICmpInst_symbolic(block, instruction, current_state);
10✔
1017
    }
10✔
1018
}
10✔
1019

1020
sdfg::control_flow::State& Lifting::visit_ICmpInst_symbolic(
1021
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
1022
) {
10✔
1023
    // Define Debug
1024
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
10✔
1025

1026
    // Define Output
1027
    std::string output_str = utils::get_name(instruction);
10✔
1028
    if (!this->builder_.subject().exists(output_str)) {
10✔
1029
        auto output_type = utils::get_type(
10✔
1030
            this->builder_,
10✔
1031
            this->anonymous_types_mapping_,
10✔
1032
            this->DL_,
10✔
1033
            instruction->getType(),
10✔
1034
            utils::get_storage_type(this->target_type_, 0)
10✔
1035
        );
10✔
1036
        this->builder_.add_container(output_str, *output_type);
10✔
1037
    }
10✔
1038
    auto& output_type = this->builder_.subject().type(output_str);
10✔
1039
    assert(
10✔
1040
        output_type.type_id() == sdfg::types::TypeID::Scalar &&
10✔
1041
        output_type.primitive_type() == sdfg::types::PrimitiveType::Bool && "ICmpInst: Expected boolean type as output"
10✔
1042
    );
10✔
1043
    sdfg::symbolic::Symbol output = sdfg::symbolic::symbol(output_str);
10✔
1044

1045
    auto left_operand = instruction->getOperand(0);
10✔
1046
    auto left_operand_type = utils::get_type(
10✔
1047
        this->builder_,
10✔
1048
        this->anonymous_types_mapping_,
10✔
1049
        this->DL_,
10✔
1050
        left_operand->getType(),
10✔
1051
        utils::get_storage_type(this->target_type_, 0)
10✔
1052
    );
10✔
1053
    assert(
10✔
1054
        (left_operand_type->type_id() == sdfg::types::TypeID::Scalar ||
10✔
1055
         left_operand_type->type_id() == sdfg::types::TypeID::Pointer) &&
10✔
1056
        "ICmpInst: Expected scalar or pointer type as left operand"
10✔
1057
    );
10✔
1058

1059
    sdfg::symbolic::Expression left_operand_expr;
10✔
1060
    if (llvm::isa<llvm::ConstantInt>(left_operand)) {
10✔
NEW
1061
        left_operand_expr = sdfg::symbolic::integer(llvm::dyn_cast<llvm::ConstantInt>(left_operand)->getSExtValue());
×
1062
    } else if (llvm::isa<llvm::ConstantPointerNull>(left_operand)) {
10✔
NEW
1063
        left_operand_expr = sdfg::symbolic::__nullptr__();
×
1064
    } else {
10✔
1065
        assert(!utils::is_literal(left_operand) && "ICmpInst: Expected constant or symbol as left operand");
10✔
1066
        std::string left_operand_expr_str = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
10✔
1067
        assert(
10✔
1068
            this->builder_.subject().exists(left_operand_expr_str) &&
10✔
1069
            "ICmpInst: Expected input to be a constant or a symbol"
10✔
1070
        );
10✔
1071
        left_operand_expr = sdfg::symbolic::symbol(left_operand_expr_str);
10✔
1072
    }
10✔
1073

1074
    auto right_operand = instruction->getOperand(1);
10✔
1075
    auto right_operand_type = utils::get_type(
10✔
1076
        this->builder_,
10✔
1077
        this->anonymous_types_mapping_,
10✔
1078
        this->DL_,
10✔
1079
        right_operand->getType(),
10✔
1080
        utils::get_storage_type(this->target_type_, 0)
10✔
1081
    );
10✔
1082
    assert(
10✔
1083
        (right_operand_type->type_id() == sdfg::types::TypeID::Scalar ||
10✔
1084
         right_operand_type->type_id() == sdfg::types::TypeID::Pointer) &&
10✔
1085
        "ICmpInst: Expected scalar or pointer type as right operand"
10✔
1086
    );
10✔
1087

1088
    sdfg::symbolic::Expression right_operand_expr;
10✔
1089
    if (llvm::isa<llvm::ConstantInt>(right_operand)) {
10✔
1090
        right_operand_expr = sdfg::symbolic::integer(llvm::dyn_cast<llvm::ConstantInt>(right_operand)->getSExtValue());
1✔
1091
    } else if (llvm::isa<llvm::ConstantPointerNull>(right_operand)) {
9✔
1092
        right_operand_expr = sdfg::symbolic::__nullptr__();
1✔
1093
    } else {
8✔
1094
        assert(!utils::is_literal(right_operand) && "ICmpInst: Expected constant or symbol as right operand");
8✔
1095
        std::string right_operand_expr_str =
8✔
1096
            utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
8✔
1097
        assert(
8✔
1098
            this->builder_.subject().exists(right_operand_expr_str) &&
8✔
1099
            "ICmpInst: Expected input to be a constant or a symbol"
8✔
1100
        );
8✔
1101
        right_operand_expr = sdfg::symbolic::symbol(right_operand_expr_str);
8✔
1102
    }
8✔
1103

1104
    // SDFGs support symbolic expressions for signed integers
1105
    // and pointer comparisons only
1106
    sdfg::symbolic::Condition condition;
10✔
1107
    auto predicate = instruction->getSignedPredicate();
10✔
1108
    switch (predicate) {
10✔
1109
        case llvm::CmpInst::Predicate::ICMP_EQ: {
4✔
1110
            condition = sdfg::symbolic::Eq(left_operand_expr, right_operand_expr);
4✔
1111
            break;
4✔
NEW
1112
        }
×
1113
        case llvm::CmpInst::Predicate::ICMP_NE: {
1✔
1114
            condition = sdfg::symbolic::Ne(left_operand_expr, right_operand_expr);
1✔
1115
            break;
1✔
NEW
1116
        }
×
1117
        case llvm::CmpInst::Predicate::ICMP_SGT: {
1✔
1118
            condition = sdfg::symbolic::Gt(left_operand_expr, right_operand_expr);
1✔
1119
            break;
1✔
NEW
1120
        }
×
1121
        case llvm::CmpInst::Predicate::ICMP_SGE: {
1✔
1122
            condition = sdfg::symbolic::Ge(left_operand_expr, right_operand_expr);
1✔
1123
            break;
1✔
NEW
1124
        }
×
1125
        case llvm::CmpInst::Predicate::ICMP_SLT: {
1✔
1126
            condition = sdfg::symbolic::Lt(left_operand_expr, right_operand_expr);
1✔
1127
            break;
1✔
NEW
1128
        }
×
1129
        case llvm::CmpInst::Predicate::ICMP_SLE: {
2✔
1130
            condition = sdfg::symbolic::Le(left_operand_expr, right_operand_expr);
2✔
1131
            break;
2✔
NEW
1132
        }
×
NEW
1133
        default: {
×
NEW
1134
            throw NotImplementedException(
×
NEW
1135
                "ICmpInst: Unsupported predicate",
×
NEW
1136
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1137
                ::docc::utils::toIRString(*instruction)
×
NEW
1138
            );
×
NEW
1139
        }
×
1140
    }
10✔
1141

1142
    auto& state_end = this->builder_.add_state_after(current_state, false, dbg_info);
10✔
1143
    auto& state_cond = this->builder_.add_state(false, dbg_info);
10✔
1144
    this->builder_.add_edge(current_state, state_cond, {{output, condition}}, dbg_info);
10✔
1145
    this->builder_.add_edge(state_cond, state_end, dbg_info);
10✔
1146

1147
    return state_end;
10✔
1148
};
10✔
1149

1150
sdfg::control_flow::State& Lifting::visit_ICmpInst_dataflow(
1151
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
NEW
1152
) {
×
1153
    // Define Debug
NEW
1154
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
×
1155

1156
    // Define Output
NEW
1157
    std::string output = utils::get_name(instruction);
×
NEW
1158
    if (!this->builder_.subject().exists(output)) {
×
NEW
1159
        auto output_type = utils::get_type(
×
NEW
1160
            this->builder_,
×
NEW
1161
            this->anonymous_types_mapping_,
×
NEW
1162
            this->DL_,
×
NEW
1163
            instruction->getType(),
×
NEW
1164
            utils::get_storage_type(this->target_type_, 0)
×
NEW
1165
        );
×
NEW
1166
        this->builder_.add_container(output, *output_type);
×
NEW
1167
    }
×
NEW
1168
    auto& output_type = this->builder_.subject().type(output);
×
NEW
1169
    assert(output_type.type_id() == sdfg::types::TypeID::Scalar && "ICmpInst: Expected scalar type as output");
×
1170

1171
    // Define Operation
NEW
1172
    sdfg::data_flow::TaskletCode operation;
×
NEW
1173
    switch (instruction->getPredicate()) {
×
NEW
1174
        case llvm::CmpInst::Predicate::ICMP_UGT: {
×
NEW
1175
            operation = sdfg::data_flow::TaskletCode::int_ugt;
×
NEW
1176
            break;
×
NEW
1177
        }
×
NEW
1178
        case llvm::CmpInst::Predicate::ICMP_UGE: {
×
NEW
1179
            operation = sdfg::data_flow::TaskletCode::int_uge;
×
NEW
1180
            break;
×
NEW
1181
        }
×
NEW
1182
        case llvm::CmpInst::Predicate::ICMP_ULT: {
×
NEW
1183
            operation = sdfg::data_flow::TaskletCode::int_ult;
×
NEW
1184
            break;
×
NEW
1185
        }
×
NEW
1186
        case llvm::CmpInst::Predicate::ICMP_ULE: {
×
NEW
1187
            operation = sdfg::data_flow::TaskletCode::int_ule;
×
NEW
1188
            break;
×
NEW
1189
        }
×
NEW
1190
        default: {
×
NEW
1191
            throw NotImplementedException(
×
NEW
1192
                "FCmpInst: Unsupported predicate",
×
NEW
1193
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1194
                ::docc::utils::toIRString(*instruction)
×
NEW
1195
            );
×
NEW
1196
        }
×
NEW
1197
    }
×
1198

NEW
1199
    auto left_operand = instruction->getOperand(0);
×
NEW
1200
    auto right_operand = instruction->getOperand(1);
×
NEW
1201
    assert(
×
NEW
1202
        left_operand->getType()->isIntegerTy() && right_operand->getType()->isIntegerTy() &&
×
NEW
1203
        "ICmpInst: Expected integer types as operands"
×
NEW
1204
    );
×
NEW
1205
    switch (output_type.type_id()) {
×
NEW
1206
        case sdfg::types::TypeID::Scalar: {
×
NEW
1207
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
×
NEW
1208
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
×
NEW
1209
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
×
1210

NEW
1211
            sdfg::data_flow::AccessNode* left_node;
×
NEW
1212
            if (utils::is_literal(left_operand)) {
×
NEW
1213
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
NEW
1214
                auto left_operand_type = utils::get_type(
×
NEW
1215
                    this->builder_,
×
NEW
1216
                    this->anonymous_types_mapping_,
×
NEW
1217
                    this->DL_,
×
NEW
1218
                    left_operand->getType(),
×
NEW
1219
                    utils::get_storage_type(this->target_type_, 0)
×
NEW
1220
                );
×
NEW
1221
                left_node = &this->builder_.add_constant(current_state, input, *left_operand_type, dbg_info);
×
NEW
1222
            } else {
×
NEW
1223
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
×
NEW
1224
                left_node = &this->builder_.add_access(current_state, left_input, dbg_info);
×
NEW
1225
            }
×
1226

NEW
1227
            sdfg::data_flow::AccessNode* right_node;
×
NEW
1228
            if (utils::is_literal(right_operand)) {
×
NEW
1229
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(right_operand));
×
NEW
1230
                auto right_operand_type = utils::get_type(
×
NEW
1231
                    this->builder_,
×
NEW
1232
                    this->anonymous_types_mapping_,
×
NEW
1233
                    this->DL_,
×
NEW
1234
                    right_operand->getType(),
×
NEW
1235
                    utils::get_storage_type(this->target_type_, 0)
×
NEW
1236
                );
×
NEW
1237
                right_node = &this->builder_.add_constant(current_state, input, *right_operand_type, dbg_info);
×
NEW
1238
            } else {
×
NEW
1239
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
×
NEW
1240
                right_node = &this->builder_.add_access(current_state, input, dbg_info);
×
NEW
1241
            }
×
1242

NEW
1243
            this->builder_.add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, dbg_info);
×
NEW
1244
            this->builder_.add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, dbg_info);
×
1245

NEW
1246
            return current_state;
×
NEW
1247
        }
×
NEW
1248
        default: {
×
NEW
1249
            throw NotImplementedException(
×
NEW
1250
                "ICmpInst: Unsupported output type",
×
NEW
1251
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1252
                ::docc::utils::toIRString(*instruction)
×
NEW
1253
            );
×
NEW
1254
        }
×
NEW
1255
    }
×
NEW
1256
};
×
1257

1258

1259
sdfg::control_flow::State& Lifting::visit_SelectInst(
1260
    const llvm::BasicBlock* block, const llvm::SelectInst* instruction, sdfg::control_flow::State& current_state
1261
) {
2✔
1262
    // Define Debug
1263
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
2✔
1264

1265
    // Define Output
1266
    std::string output = utils::get_name(instruction);
2✔
1267
    if (!this->builder_.subject().exists(output)) {
2✔
1268
        auto output_type = utils::get_type(
2✔
1269
            this->builder_,
2✔
1270
            this->anonymous_types_mapping_,
2✔
1271
            this->DL_,
2✔
1272
            instruction->getType(),
2✔
1273
            utils::get_storage_type(this->target_type_, 0)
2✔
1274
        );
2✔
1275
        this->builder_.add_container(output, *output_type);
2✔
1276
    }
2✔
1277
    auto& output_type = this->builder_.subject().type(output);
2✔
1278
    assert(
2✔
1279
        (output_type.type_id() == sdfg::types::TypeID::Scalar || output_type.type_id() == sdfg::types::TypeID::Pointer
2✔
1280
        ) &&
2✔
1281
        "SelectInst: Expected scalar, array, or pointer type as output"
2✔
1282
    );
2✔
1283

1284
    // Define Condition
1285
    auto condition = instruction->getCondition();
2✔
1286
    auto condition_type = utils::get_type(
2✔
1287
        this->builder_,
2✔
1288
        this->anonymous_types_mapping_,
2✔
1289
        this->DL_,
2✔
1290
        condition->getType(),
2✔
1291
        utils::get_storage_type(this->target_type_, 0)
2✔
1292
    );
2✔
1293
    assert((condition_type->type_id() == sdfg::types::TypeID::Scalar) && "SelectInst: Expected scalar type as condition");
2✔
1294

1295
    auto value_if = instruction->getTrueValue();
2✔
1296
    auto value_else = instruction->getFalseValue();
2✔
1297
    switch (condition_type->type_id()) {
2✔
1298
        case sdfg::types::TypeID::Scalar: {
2✔
1299
            sdfg::symbolic::Expression condition_sym;
2✔
1300
            if (utils::is_literal(condition)) {
2✔
NEW
1301
                condition_sym = utils::as_symbol(llvm::dyn_cast<const llvm::Constant>(condition));
×
1302
            } else {
2✔
1303
                auto condition_name = utils::find_const_name_to_sdfg_name(this->constants_mapping_, condition);
2✔
1304
                condition_sym = sdfg::symbolic::symbol(condition_name);
2✔
1305
            }
2✔
1306

1307
            auto& state_if = this->builder_.add_state(false, dbg_info);
2✔
1308
            auto& state_else = this->builder_.add_state(false, dbg_info);
2✔
1309
            auto& state_end = this->builder_.add_state_after(current_state, false, dbg_info);
2✔
1310

1311
            this->builder_.add_edge(
2✔
1312
                current_state, state_if, sdfg::symbolic::Ne(condition_sym, sdfg::symbolic::__false__()), dbg_info
2✔
1313
            );
2✔
1314
            this->builder_.add_edge(
2✔
1315
                current_state, state_else, sdfg::symbolic::Eq(condition_sym, sdfg::symbolic::__false__()), dbg_info
2✔
1316
            );
2✔
1317
            this->builder_.add_edge(state_if, state_end, dbg_info);
2✔
1318
            this->builder_.add_edge(state_else, state_end, dbg_info);
2✔
1319

1320
            // If
1321
            {
2✔
1322
                auto& output_node = this->builder_.add_access(state_if, output, dbg_info);
2✔
1323

1324
                switch (output_type.type_id()) {
2✔
1325
                    case sdfg::types::TypeID::Scalar: {
1✔
1326
                        auto& tasklet =
1✔
1327
                            this->builder_
1✔
1328
                                .add_tasklet(state_if, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
1329
                        this->builder_.add_computational_memlet(state_if, tasklet, "__out", output_node, {}, dbg_info);
1✔
1330

1331
                        sdfg::data_flow::AccessNode* input_node;
1✔
1332
                        if (utils::is_literal(value_if)) {
1✔
NEW
1333
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_if));
×
NEW
1334
                            input_node = &this->builder_.add_constant(state_if, input, output_type, dbg_info);
×
1335
                        } else {
1✔
1336
                            std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_if);
1✔
1337
                            input_node = &this->builder_.add_access(state_if, input, dbg_info);
1✔
1338
                        }
1✔
1339
                        this->builder_.add_computational_memlet(state_if, *input_node, tasklet, "_in", {}, dbg_info);
1✔
1340
                        break;
1✔
NEW
1341
                    }
×
1342
                    case sdfg::types::TypeID::Pointer: {
1✔
1343
                        if (utils::is_literal(value_if)) {
1✔
NEW
1344
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_if));
×
NEW
1345
                            auto& input_node = this->builder_.add_constant(state_if, input, output_type, dbg_info);
×
1346

NEW
1347
                            sdfg::types::Pointer ptr_type;
×
NEW
1348
                            this->builder_
×
NEW
1349
                                .add_reference_memlet(state_if, input_node, output_node, {}, ptr_type, dbg_info);
×
1350
                        } else {
1✔
1351
                            std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_if);
1✔
1352
                            auto& input_node = this->builder_.add_access(state_if, input, dbg_info);
1✔
1353

1354
                            sdfg::types::Pointer base_ptr_type;
1✔
1355
                            sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
1356
                            this->builder_.add_reference_memlet(
1✔
1357
                                state_if, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
1358
                            );
1✔
1359
                        }
1✔
1360
                        break;
1✔
NEW
1361
                    }
×
NEW
1362
                    default: {
×
NEW
1363
                        throw NotImplementedException(
×
NEW
1364
                            "SelectInst: Unsupported output type",
×
NEW
1365
                            ::docc::utils::get_debug_info(*instruction),
×
NEW
1366
                            ::docc::utils::toIRString(*instruction)
×
NEW
1367
                        );
×
NEW
1368
                    }
×
1369
                }
2✔
1370
            }
2✔
1371

1372
            // Else
1373
            {
2✔
1374
                auto& output_node = this->builder_.add_access(state_else, output, dbg_info);
2✔
1375

1376
                switch (output_type.type_id()) {
2✔
1377
                    case sdfg::types::TypeID::Scalar: {
1✔
1378
                        auto& tasklet =
1✔
1379
                            this->builder_
1✔
1380
                                .add_tasklet(state_else, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
1381
                        this->builder_.add_computational_memlet(state_else, tasklet, "__out", output_node, {}, dbg_info);
1✔
1382

1383
                        sdfg::data_flow::AccessNode* input_node;
1✔
1384
                        if (utils::is_literal(value_else)) {
1✔
NEW
1385
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_else));
×
NEW
1386
                            input_node = &this->builder_.add_constant(state_else, input, output_type, dbg_info);
×
1387
                        } else {
1✔
1388
                            std::string input =
1✔
1389
                                utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_else);
1✔
1390
                            input_node = &this->builder_.add_access(state_else, input, dbg_info);
1✔
1391
                        }
1✔
1392
                        this->builder_.add_computational_memlet(state_else, *input_node, tasklet, "_in", {}, dbg_info);
1✔
1393

1394
                        break;
1✔
NEW
1395
                    }
×
1396
                    case sdfg::types::TypeID::Pointer: {
1✔
1397
                        if (utils::is_literal(value_else)) {
1✔
NEW
1398
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_else));
×
NEW
1399
                            auto& input_node = this->builder_.add_constant(state_else, input, output_type, dbg_info);
×
1400

NEW
1401
                            sdfg::types::Pointer ptr_type;
×
NEW
1402
                            this->builder_
×
NEW
1403
                                .add_reference_memlet(state_else, input_node, output_node, {}, ptr_type, dbg_info);
×
1404
                        } else {
1✔
1405
                            std::string input =
1✔
1406
                                utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_else);
1✔
1407
                            auto& input_node = this->builder_.add_access(state_else, input, dbg_info);
1✔
1408

1409
                            sdfg::types::Pointer base_ptr_type;
1✔
1410
                            sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
1411
                            this->builder_.add_reference_memlet(
1✔
1412
                                state_else, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
1413
                            );
1✔
1414
                        }
1✔
1415

1416
                        break;
1✔
NEW
1417
                    }
×
NEW
1418
                    default: {
×
NEW
1419
                        throw NotImplementedException(
×
NEW
1420
                            "SelectInst: Unsupported output type",
×
NEW
1421
                            ::docc::utils::get_debug_info(*instruction),
×
NEW
1422
                            ::docc::utils::toIRString(*instruction)
×
NEW
1423
                        );
×
NEW
1424
                    }
×
1425
                }
2✔
1426
            }
2✔
1427

1428
            return state_end;
2✔
1429
        }
2✔
NEW
1430
        default: {
×
NEW
1431
            throw NotImplementedException(
×
NEW
1432
                "SelectInst: Unsupported condition type",
×
NEW
1433
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1434
                ::docc::utils::toIRString(*instruction)
×
NEW
1435
            );
×
1436
        }
2✔
1437
    }
2✔
1438
};
2✔
1439

1440
sdfg::control_flow::State& Lifting::visit_FCmpInst(
1441
    const llvm::BasicBlock* block, const llvm::FCmpInst* instruction, sdfg::control_flow::State& current_state
1442
) {
4✔
1443
    // Define Debug
1444
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
4✔
1445

1446
    // Define Output
1447
    std::string output = utils::get_name(instruction);
4✔
1448
    if (!this->builder_.subject().exists(output)) {
4✔
1449
        auto output_type = utils::get_type(
4✔
1450
            this->builder_,
4✔
1451
            this->anonymous_types_mapping_,
4✔
1452
            this->DL_,
4✔
1453
            instruction->getType(),
4✔
1454
            utils::get_storage_type(this->target_type_, 0)
4✔
1455
        );
4✔
1456
        this->builder_.add_container(output, *output_type);
4✔
1457
    }
4✔
1458
    auto& output_type = this->builder_.subject().type(output);
4✔
1459
    assert(
4✔
1460
        (output_type.type_id() == sdfg::types::TypeID::Scalar || output_type.type_id() == sdfg::types::TypeID::Structure
4✔
1461
        ) &&
4✔
1462
        "FCmpInst: Expected scalar or structure type as output"
4✔
1463
    );
4✔
1464

1465
    // Define Operation
1466
    sdfg::data_flow::TaskletCode operation;
4✔
1467
    switch (instruction->getPredicate()) {
4✔
NEW
1468
        case llvm::CmpInst::Predicate::FCMP_FALSE: {
×
NEW
1469
            operation = sdfg::data_flow::TaskletCode::assign;
×
NEW
1470
            break;
×
NEW
1471
        }
×
NEW
1472
        case llvm::CmpInst::Predicate::FCMP_OEQ: {
×
NEW
1473
            operation = sdfg::data_flow::TaskletCode::fp_oeq;
×
NEW
1474
            break;
×
NEW
1475
        }
×
1476
        case llvm::CmpInst::Predicate::FCMP_OGT: {
4✔
1477
            operation = sdfg::data_flow::TaskletCode::fp_ogt;
4✔
1478
            break;
4✔
NEW
1479
        }
×
NEW
1480
        case llvm::CmpInst::Predicate::FCMP_OGE: {
×
NEW
1481
            operation = sdfg::data_flow::TaskletCode::fp_oge;
×
NEW
1482
            break;
×
NEW
1483
        }
×
NEW
1484
        case llvm::CmpInst::Predicate::FCMP_OLT: {
×
NEW
1485
            operation = sdfg::data_flow::TaskletCode::fp_olt;
×
NEW
1486
            break;
×
NEW
1487
        }
×
NEW
1488
        case llvm::CmpInst::Predicate::FCMP_OLE: {
×
NEW
1489
            operation = sdfg::data_flow::TaskletCode::fp_ole;
×
NEW
1490
            break;
×
NEW
1491
        }
×
NEW
1492
        case llvm::CmpInst::Predicate::FCMP_ONE: {
×
NEW
1493
            operation = sdfg::data_flow::TaskletCode::fp_one;
×
NEW
1494
            break;
×
NEW
1495
        }
×
NEW
1496
        case llvm::CmpInst::Predicate::FCMP_ORD: {
×
NEW
1497
            operation = sdfg::data_flow::TaskletCode::fp_ord;
×
NEW
1498
            break;
×
NEW
1499
        }
×
NEW
1500
        case llvm::CmpInst::Predicate::FCMP_UEQ: {
×
NEW
1501
            operation = sdfg::data_flow::TaskletCode::fp_ueq;
×
NEW
1502
            break;
×
NEW
1503
        }
×
NEW
1504
        case llvm::CmpInst::Predicate::FCMP_UGT: {
×
NEW
1505
            operation = sdfg::data_flow::TaskletCode::fp_ugt;
×
NEW
1506
            break;
×
NEW
1507
        }
×
NEW
1508
        case llvm::CmpInst::Predicate::FCMP_UGE: {
×
NEW
1509
            operation = sdfg::data_flow::TaskletCode::fp_uge;
×
NEW
1510
            break;
×
NEW
1511
        }
×
NEW
1512
        case llvm::CmpInst::Predicate::FCMP_ULT: {
×
NEW
1513
            operation = sdfg::data_flow::TaskletCode::fp_ult;
×
NEW
1514
            break;
×
NEW
1515
        }
×
NEW
1516
        case llvm::CmpInst::Predicate::FCMP_ULE: {
×
NEW
1517
            operation = sdfg::data_flow::TaskletCode::fp_ule;
×
NEW
1518
            break;
×
NEW
1519
        }
×
NEW
1520
        case llvm::CmpInst::Predicate::FCMP_UNE: {
×
NEW
1521
            operation = sdfg::data_flow::TaskletCode::fp_une;
×
NEW
1522
            break;
×
NEW
1523
        }
×
NEW
1524
        case llvm::CmpInst::Predicate::FCMP_UNO: {
×
NEW
1525
            operation = sdfg::data_flow::TaskletCode::fp_uno;
×
NEW
1526
            break;
×
NEW
1527
        }
×
NEW
1528
        case llvm::CmpInst::Predicate::FCMP_TRUE: {
×
NEW
1529
            operation = sdfg::data_flow::TaskletCode::assign;
×
NEW
1530
            break;
×
NEW
1531
        }
×
NEW
1532
        default: {
×
NEW
1533
            throw NotImplementedException(
×
NEW
1534
                "FCmpInst: Unsupported predicate",
×
NEW
1535
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1536
                ::docc::utils::toIRString(*instruction)
×
NEW
1537
            );
×
NEW
1538
        }
×
1539
    }
4✔
1540

1541
    auto left_operand = instruction->getOperand(0);
4✔
1542
    auto right_operand = instruction->getOperand(1);
4✔
1543
    switch (output_type.type_id()) {
4✔
1544
        case sdfg::types::TypeID::Scalar: {
1✔
1545
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
1✔
1546
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
1✔
1547
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
1✔
1548

1549
            sdfg::data_flow::AccessNode* left_node;
1✔
1550
            if (utils::is_literal(left_operand)) {
1✔
NEW
1551
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
NEW
1552
                auto left_operand_type = utils::get_type(
×
NEW
1553
                    this->builder_,
×
NEW
1554
                    this->anonymous_types_mapping_,
×
NEW
1555
                    this->DL_,
×
NEW
1556
                    left_operand->getType(),
×
NEW
1557
                    utils::get_storage_type(this->target_type_, 0)
×
NEW
1558
                );
×
NEW
1559
                left_node = &this->builder_.add_constant(current_state, input, *left_operand_type, dbg_info);
×
1560
            } else {
1✔
1561
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
1✔
1562
                left_node = &this->builder_.add_access(current_state, left_input, dbg_info);
1✔
1563
            }
1✔
1564

1565
            sdfg::data_flow::AccessNode* right_node;
1✔
1566
            if (utils::is_literal(right_operand)) {
1✔
NEW
1567
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(right_operand));
×
NEW
1568
                auto right_operand_type = utils::get_type(
×
NEW
1569
                    this->builder_,
×
NEW
1570
                    this->anonymous_types_mapping_,
×
NEW
1571
                    this->DL_,
×
NEW
1572
                    right_operand->getType(),
×
NEW
1573
                    utils::get_storage_type(this->target_type_, 0)
×
NEW
1574
                );
×
NEW
1575
                right_node = &this->builder_.add_constant(current_state, input, *right_operand_type, dbg_info);
×
1576
            } else {
1✔
1577
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
1✔
1578
                right_node = &this->builder_.add_access(current_state, input, dbg_info);
1✔
1579
            }
1✔
1580

1581
            this->builder_.add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, dbg_info);
1✔
1582
            this->builder_.add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, dbg_info);
1✔
1583

1584
            return current_state;
1✔
NEW
1585
        }
×
1586
        case sdfg::types::TypeID::Structure: {
3✔
1587
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1588
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1589
            assert(struct_def.is_vector() && "FCmpInst: Expected vector structure type as output");
3✔
1590

1591
            // Define loop
1592
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1593
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1594
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1595
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1596
            sdfg::symbolic::Condition cond =
3✔
1597
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1598
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1599

1600
            auto loop = this->builder_.add_loop(current_state, iter_sym, init, cond, update, dbg_info);
3✔
1601
            auto& body = std::get<1>(loop);
3✔
1602

1603
            // Define body
1604
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1605
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1606
            this->builder_.add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, dbg_info);
3✔
1607

1608
            sdfg::data_flow::AccessNode* left_node;
3✔
1609
            if (utils::is_literal(left_operand)) {
3✔
NEW
1610
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
NEW
1611
                auto left_operand_type = utils::get_type(
×
NEW
1612
                    this->builder_,
×
NEW
1613
                    this->anonymous_types_mapping_,
×
NEW
1614
                    this->DL_,
×
NEW
1615
                    left_operand->getType(),
×
NEW
1616
                    utils::get_storage_type(this->target_type_, 0)
×
NEW
1617
                );
×
NEW
1618
                left_node = &this->builder_.add_constant(body, input, *left_operand_type, dbg_info);
×
1619
            } else {
3✔
1620
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
3✔
1621
                left_node = &this->builder_.add_access(body, left_input, dbg_info);
3✔
1622
            }
3✔
1623

1624
            sdfg::data_flow::AccessNode* right_node;
3✔
1625
            if (utils::is_literal(right_operand)) {
3✔
1626
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(right_operand));
2✔
1627
                auto right_operand_type = utils::get_type(
2✔
1628
                    this->builder_,
2✔
1629
                    this->anonymous_types_mapping_,
2✔
1630
                    this->DL_,
2✔
1631
                    right_operand->getType(),
2✔
1632
                    utils::get_storage_type(this->target_type_, 0)
2✔
1633
                );
2✔
1634
                right_node = &this->builder_.add_constant(body, input, *right_operand_type, dbg_info);
2✔
1635
            } else {
2✔
1636
                std::string right_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
1✔
1637
                right_node = &this->builder_.add_access(body, right_input, dbg_info);
1✔
1638
            }
1✔
1639

1640
            this->builder_.add_computational_memlet(body, *left_node, tasklet, "_in1", {iter_sym}, dbg_info);
3✔
1641
            this->builder_.add_computational_memlet(body, *right_node, tasklet, "_in2", {iter_sym}, dbg_info);
3✔
1642

1643
            return std::get<2>(loop);
3✔
1644
        }
3✔
NEW
1645
        default: {
×
NEW
1646
            throw NotImplementedException(
×
NEW
1647
                "FCmpInst: Unsupported output type",
×
NEW
1648
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1649
                ::docc::utils::toIRString(*instruction)
×
NEW
1650
            );
×
1651
        }
3✔
1652
    }
4✔
1653
};
4✔
1654

1655
sdfg::control_flow::State& Lifting::visit_UnaryOperator(
1656
    const llvm::BasicBlock* block, const llvm::UnaryOperator* instruction, sdfg::control_flow::State& current_state
1657
) {
5✔
1658
    // Define Debug
1659
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
5✔
1660

1661
    // Define Output
1662
    std::string output = utils::get_name(instruction);
5✔
1663
    if (!this->builder_.subject().exists(output)) {
5✔
1664
        auto output_type = utils::get_type(
5✔
1665
            this->builder_,
5✔
1666
            this->anonymous_types_mapping_,
5✔
1667
            this->DL_,
5✔
1668
            instruction->getType(),
5✔
1669
            utils::get_storage_type(this->target_type_, 0)
5✔
1670
        );
5✔
1671
        this->builder_.add_container(output, *output_type);
5✔
1672
    }
5✔
1673
    auto& output_type = this->builder_.subject().type(output);
5✔
1674
    assert(
5✔
1675
        (output_type.type_id() == sdfg::types::TypeID::Scalar || output_type.type_id() == sdfg::types::TypeID::Structure
5✔
1676
        ) &&
5✔
1677
        "UnaryOperator: Expected scalar or structure type as output"
5✔
1678
    );
5✔
1679

1680
    // Define Operation
1681
    sdfg::data_flow::TaskletCode operation;
5✔
1682
    switch (instruction->getOpcode()) {
5✔
1683
        case llvm::Instruction::UnaryOps::FNeg: {
5✔
1684
            operation = sdfg::data_flow::TaskletCode::fp_neg;
5✔
1685
            break;
5✔
NEW
1686
        }
×
NEW
1687
        default: {
×
NEW
1688
            throw NotImplementedException(
×
NEW
1689
                "UnaryOperator: Unsupported opcode",
×
NEW
1690
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1691
                ::docc::utils::toIRString(*instruction)
×
NEW
1692
            );
×
NEW
1693
        }
×
1694
    }
5✔
1695

1696
    auto operand = instruction->getOperand(0);
5✔
1697
    switch (output_type.type_id()) {
5✔
1698
        case sdfg::types::TypeID::Scalar: {
2✔
1699
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
2✔
1700
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1"}, dbg_info);
2✔
1701
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
2✔
1702

1703
            if (utils::is_literal(operand)) {
2✔
1704
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(operand));
1✔
1705
                auto& input_node = this->builder_.add_constant(current_state, input, output_type, dbg_info);
1✔
1706
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in1", {}, dbg_info);
1✔
1707
            } else {
1✔
1708
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, operand);
1✔
1709
                auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
1✔
1710
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in1", {}, dbg_info);
1✔
1711
            }
1✔
1712

1713
            return current_state;
2✔
NEW
1714
        }
×
1715
        case sdfg::types::TypeID::Structure: {
3✔
1716
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1717
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1718
            assert(struct_def.is_vector() && "UnaryOperator: Expected vector structure type as output");
3✔
1719

1720
            // Define loop
1721
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1722
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1723
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1724
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1725
            sdfg::symbolic::Condition cond =
3✔
1726
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1727
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1728

1729
            auto loop = this->builder_.add_loop(current_state, iter_sym, init, cond, update, dbg_info);
3✔
1730
            auto& body = std::get<1>(loop);
3✔
1731

1732
            // Define body
1733
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1734
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1"}, dbg_info);
3✔
1735
            this->builder_.add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, dbg_info);
3✔
1736

1737
            if (utils::is_literal(operand)) {
3✔
1738
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(operand));
2✔
1739
                auto& input_node = this->builder_.add_constant(body, input, output_type, dbg_info);
2✔
1740
                this->builder_.add_computational_memlet(body, input_node, tasklet, "_in1", {iter_sym}, dbg_info);
2✔
1741
            } else {
2✔
1742
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, operand);
1✔
1743
                auto& input_node = this->builder_.add_access(body, input, dbg_info);
1✔
1744
                this->builder_.add_computational_memlet(body, input_node, tasklet, "_in1", {iter_sym}, dbg_info);
1✔
1745
            }
1✔
1746

1747
            return std::get<2>(loop);
3✔
1748
        }
3✔
NEW
1749
        default: {
×
NEW
1750
            throw NotImplementedException(
×
NEW
1751
                "UnaryOperator: Unsupported output type",
×
NEW
1752
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1753
                ::docc::utils::toIRString(*instruction)
×
NEW
1754
            );
×
1755
        }
3✔
1756
    }
5✔
1757
};
5✔
1758

1759
sdfg::control_flow::State& Lifting::visit_BinaryOperator(
1760
    const llvm::BasicBlock* block, const llvm::BinaryOperator* instruction, sdfg::control_flow::State& current_state
1761
) {
6✔
1762
    // Define Debug
1763
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
6✔
1764

1765
    // Define Output
1766
    std::string output = utils::get_name(instruction);
6✔
1767
    if (!this->builder_.subject().exists(output)) {
6✔
1768
        auto output_type = utils::get_type(
6✔
1769
            this->builder_,
6✔
1770
            this->anonymous_types_mapping_,
6✔
1771
            this->DL_,
6✔
1772
            instruction->getType(),
6✔
1773
            utils::get_storage_type(this->target_type_, 0)
6✔
1774
        );
6✔
1775
        this->builder_.add_container(output, *output_type);
6✔
1776
    }
6✔
1777
    auto& output_type = this->builder_.subject().type(output);
6✔
1778
    assert(
6✔
1779
        (output_type.type_id() == sdfg::types::TypeID::Scalar || output_type.type_id() == sdfg::types::TypeID::Structure
6✔
1780
        ) &&
6✔
1781
        "BinaryOperator: Expected scalar or structure type as output"
6✔
1782
    );
6✔
1783

1784
    // Define Operation
1785
    sdfg::data_flow::TaskletCode operation;
6✔
1786
    switch (instruction->getOpcode()) {
6✔
NEW
1787
        case llvm::Instruction::BinaryOps::FAdd: {
×
NEW
1788
            operation = sdfg::data_flow::TaskletCode::fp_add;
×
NEW
1789
            break;
×
NEW
1790
        }
×
NEW
1791
        case llvm::Instruction::BinaryOps::FSub: {
×
NEW
1792
            operation = sdfg::data_flow::TaskletCode::fp_sub;
×
NEW
1793
            break;
×
NEW
1794
        }
×
NEW
1795
        case llvm::Instruction::BinaryOps::FMul: {
×
NEW
1796
            operation = sdfg::data_flow::TaskletCode::fp_mul;
×
NEW
1797
            break;
×
NEW
1798
        }
×
NEW
1799
        case llvm::Instruction::BinaryOps::FDiv: {
×
NEW
1800
            operation = sdfg::data_flow::TaskletCode::fp_div;
×
NEW
1801
            break;
×
NEW
1802
        }
×
NEW
1803
        case llvm::Instruction::BinaryOps::FRem: {
×
NEW
1804
            operation = sdfg::data_flow::TaskletCode::fp_rem;
×
NEW
1805
            break;
×
NEW
1806
        }
×
1807
        case llvm::Instruction::BinaryOps::Add: {
6✔
1808
            operation = sdfg::data_flow::TaskletCode::int_add;
6✔
1809
            break;
6✔
NEW
1810
        }
×
NEW
1811
        case llvm::Instruction::BinaryOps::Sub: {
×
NEW
1812
            operation = sdfg::data_flow::TaskletCode::int_sub;
×
NEW
1813
            break;
×
NEW
1814
        }
×
NEW
1815
        case llvm::Instruction::BinaryOps::Mul: {
×
NEW
1816
            operation = sdfg::data_flow::TaskletCode::int_mul;
×
NEW
1817
            break;
×
NEW
1818
        }
×
NEW
1819
        case llvm::Instruction::BinaryOps::SDiv: {
×
NEW
1820
            operation = sdfg::data_flow::TaskletCode::int_sdiv;
×
NEW
1821
            break;
×
NEW
1822
        }
×
NEW
1823
        case llvm::Instruction::BinaryOps::SRem: {
×
NEW
1824
            operation = sdfg::data_flow::TaskletCode::int_srem;
×
NEW
1825
            break;
×
NEW
1826
        }
×
NEW
1827
        case llvm::Instruction::BinaryOps::UDiv: {
×
NEW
1828
            operation = sdfg::data_flow::TaskletCode::int_udiv;
×
NEW
1829
            break;
×
NEW
1830
        }
×
NEW
1831
        case llvm::Instruction::BinaryOps::URem: {
×
NEW
1832
            operation = sdfg::data_flow::TaskletCode::int_urem;
×
NEW
1833
            break;
×
NEW
1834
        }
×
NEW
1835
        case llvm::Instruction::BinaryOps::And: {
×
NEW
1836
            operation = sdfg::data_flow::TaskletCode::int_and;
×
NEW
1837
            break;
×
NEW
1838
        }
×
NEW
1839
        case llvm::Instruction::BinaryOps::Or: {
×
1840
            // If disjoint attribute, write as add
NEW
1841
            if (auto disjoint_inst = llvm::dyn_cast<llvm::PossiblyDisjointInst>(instruction)) {
×
NEW
1842
                if (disjoint_inst->isDisjoint()) {
×
NEW
1843
                    operation = sdfg::data_flow::TaskletCode::int_add;
×
NEW
1844
                    break;
×
NEW
1845
                }
×
NEW
1846
            }
×
NEW
1847
            operation = sdfg::data_flow::TaskletCode::int_or;
×
NEW
1848
            break;
×
NEW
1849
        }
×
NEW
1850
        case llvm::Instruction::BinaryOps::Xor: {
×
1851
            // If disjoint attribute, write as add
NEW
1852
            if (auto disjoint_inst = llvm::dyn_cast<llvm::PossiblyDisjointInst>(instruction)) {
×
NEW
1853
                if (disjoint_inst->isDisjoint()) {
×
NEW
1854
                    operation = sdfg::data_flow::TaskletCode::int_add;
×
NEW
1855
                    break;
×
NEW
1856
                }
×
NEW
1857
            }
×
NEW
1858
            operation = sdfg::data_flow::TaskletCode::int_xor;
×
NEW
1859
            break;
×
NEW
1860
        }
×
NEW
1861
        case llvm::Instruction::BinaryOps::Shl: {
×
NEW
1862
            operation = sdfg::data_flow::TaskletCode::int_shl;
×
NEW
1863
            break;
×
NEW
1864
        }
×
NEW
1865
        case llvm::Instruction::BinaryOps::LShr: {
×
NEW
1866
            operation = sdfg::data_flow::TaskletCode::int_lshr;
×
NEW
1867
            break;
×
NEW
1868
        }
×
NEW
1869
        case llvm::Instruction::BinaryOps::AShr: {
×
NEW
1870
            operation = sdfg::data_flow::TaskletCode::int_ashr;
×
NEW
1871
            break;
×
NEW
1872
        }
×
NEW
1873
        default: {
×
NEW
1874
            throw NotImplementedException(
×
NEW
1875
                "BinaryOperator: Unsupported opcode",
×
NEW
1876
                ::docc::utils::get_debug_info(*instruction),
×
NEW
1877
                ::docc::utils::toIRString(*instruction)
×
NEW
1878
            );
×
NEW
1879
        }
×
1880
    }
6✔
1881

1882
    auto left_operand = instruction->getOperand(0);
6✔
1883
    auto right_operand = instruction->getOperand(1);
6✔
1884
    switch (output_type.type_id()) {
6✔
1885
        case sdfg::types::TypeID::Scalar: {
3✔
1886
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
3✔
1887
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1888

1889
            sdfg::data_flow::AccessNode* left_node;
3✔
1890
            auto left_input_type = utils::get_type(
3✔
1891
                this->builder_,
3✔
1892
                this->anonymous_types_mapping_,
3✔
1893
                this->DL_,
3✔
1894
                left_operand->getType(),
3✔
1895
                utils::get_storage_type(this->target_type_, 0)
3✔
1896
            );
3✔
1897
            if (utils::is_literal(left_operand)) {
3✔
NEW
1898
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
NEW
1899
                left_node = &this->builder_.add_constant(current_state, input, *left_input_type, dbg_info);
×
1900
            } else {
3✔
1901
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
3✔
1902
                left_node = &this->builder_.add_access(current_state, left_input, dbg_info);
3✔
1903
            }
3✔
1904

1905
            sdfg::data_flow::AccessNode* right_node;
3✔
1906
            auto right_input_type = utils::get_type(
3✔
1907
                this->builder_,
3✔
1908
                this->anonymous_types_mapping_,
3✔
1909
                this->DL_,
3✔
1910
                right_operand->getType(),
3✔
1911
                utils::get_storage_type(this->target_type_, 0)
3✔
1912
            );
3✔
1913
            if (utils::is_literal(right_operand)) {
3✔
1914
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(right_operand));
1✔
1915
                right_node = &this->builder_.add_constant(current_state, input, *right_input_type, dbg_info);
1✔
1916
            } else if (left_operand != right_operand) {
2✔
1917
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
1✔
1918
                right_node = &this->builder_.add_access(current_state, input, dbg_info);
1✔
1919
            } else {
1✔
1920
                right_node = left_node;
1✔
1921
            }
1✔
1922

1923
            this->builder_
3✔
1924
                .add_computational_memlet(current_state, tasklet, "__out", output_node, {}, output_type, dbg_info);
3✔
1925
            this->builder_
3✔
1926
                .add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, *left_input_type, dbg_info);
3✔
1927
            this->builder_
3✔
1928
                .add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, *right_input_type, dbg_info);
3✔
1929

1930
            return current_state;
3✔
NEW
1931
        }
×
1932
        case sdfg::types::TypeID::Structure: {
3✔
1933
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1934
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1935
            assert(struct_def.is_vector() && "FCmpInst: Expected vector structure type as output");
3✔
1936

1937
            // Define loop
1938
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1939
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1940
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1941
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1942
            sdfg::symbolic::Condition cond =
3✔
1943
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1944
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1945

1946
            auto loop = this->builder_.add_loop(current_state, iter_sym, init, cond, update, dbg_info);
3✔
1947
            auto& body = std::get<1>(loop);
3✔
1948

1949
            // Define body
1950
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1951
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1952

1953
            sdfg::data_flow::AccessNode* left_node;
3✔
1954
            auto left_input_type = utils::get_type(
3✔
1955
                this->builder_,
3✔
1956
                this->anonymous_types_mapping_,
3✔
1957
                this->DL_,
3✔
1958
                left_operand->getType(),
3✔
1959
                utils::get_storage_type(this->target_type_, 0)
3✔
1960
            );
3✔
1961
            if (utils::is_literal(left_operand)) {
3✔
NEW
1962
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
NEW
1963
                left_node = &this->builder_.add_constant(body, input, *left_input_type, dbg_info);
×
1964
            } else {
3✔
1965
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
3✔
1966
                left_node = &this->builder_.add_access(body, left_input, dbg_info);
3✔
1967
            }
3✔
1968

1969
            sdfg::data_flow::AccessNode* right_node;
3✔
1970
            auto right_input_type = utils::get_type(
3✔
1971
                this->builder_,
3✔
1972
                this->anonymous_types_mapping_,
3✔
1973
                this->DL_,
3✔
1974
                right_operand->getType(),
3✔
1975
                utils::get_storage_type(this->target_type_, 0)
3✔
1976
            );
3✔
1977
            if (utils::is_literal(right_operand)) {
3✔
1978
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(right_operand));
1✔
1979
                right_node = &this->builder_.add_constant(body, input, *right_input_type, dbg_info);
1✔
1980
            } else if (left_operand != right_operand) {
2✔
1981
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
1✔
1982
                right_node = &this->builder_.add_access(body, input, dbg_info);
1✔
1983
            } else {
1✔
1984
                right_node = left_node;
1✔
1985
            }
1✔
1986

1987
            this->builder_
3✔
1988
                .add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, output_type, dbg_info);
3✔
1989
            this->builder_
3✔
1990
                .add_computational_memlet(body, *left_node, tasklet, "_in1", {iter_sym}, *left_input_type, dbg_info);
3✔
1991
            this->builder_
3✔
1992
                .add_computational_memlet(body, *right_node, tasklet, "_in2", {iter_sym}, *right_input_type, dbg_info);
3✔
1993

1994
            return std::get<2>(loop);
3✔
1995
        }
3✔
NEW
1996
        default: {
×
NEW
1997
            throw NotImplementedException(
×
NEW
1998
                "BinaryOperator: Unsupported output type",
×
NEW
1999
                ::docc::utils::get_debug_info(*instruction),
×
NEW
2000
                ::docc::utils::toIRString(*instruction)
×
NEW
2001
            );
×
2002
        }
3✔
2003
    }
6✔
2004
};
6✔
2005

2006
sdfg::control_flow::State& Lifting::visit_CastInst(
2007
    const llvm::BasicBlock* block, const llvm::CastInst* instruction, sdfg::control_flow::State& current_state
2008
) {
12✔
2009
    // Define Debug
2010
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
12✔
2011

2012
    // Define Output
2013
    std::string output = utils::get_name(instruction);
12✔
2014
    if (!this->builder_.subject().exists(output)) {
12✔
2015
        auto output_type = utils::get_type(
12✔
2016
            this->builder_,
12✔
2017
            this->anonymous_types_mapping_,
12✔
2018
            this->DL_,
12✔
2019
            instruction->getType(),
12✔
2020
            utils::get_storage_type(this->target_type_, 0)
12✔
2021
        );
12✔
2022
        this->builder_.add_container(output, *output_type);
12✔
2023
    }
12✔
2024
    auto& output_type = this->builder_.subject().type(output);
12✔
2025
    assert(
12✔
2026
        (output_type.type_id() == sdfg::types::TypeID::Scalar ||
12✔
2027
         output_type.type_id() == sdfg::types::TypeID::Structure ||
12✔
2028
         output_type.type_id() == sdfg::types::TypeID::Pointer) &&
12✔
2029
        "CastInst: Expected scalar, structure or pointer type as output"
12✔
2030
    );
12✔
2031

2032
    // Define Input
2033
    auto input_operand = instruction->getOperand(0);
12✔
2034
    auto input_type = utils::get_type(
12✔
2035
        this->builder_,
12✔
2036
        this->anonymous_types_mapping_,
12✔
2037
        this->DL_,
12✔
2038
        input_operand->getType(),
12✔
2039
        utils::get_storage_type(this->target_type_, 0)
12✔
2040
    );
12✔
2041
    assert(
12✔
2042
        (input_type->type_id() == sdfg::types::TypeID::Scalar ||
12✔
2043
         input_type->type_id() == sdfg::types::TypeID::Structure ||
12✔
2044
         input_type->type_id() == sdfg::types::TypeID::Pointer) &&
12✔
2045
        "CastInst: Expected scalar, structure or pointer type as input"
12✔
2046
    );
12✔
2047

2048
    if (llvm::isa<llvm::AddrSpaceCastInst>(instruction)) {
12✔
NEW
2049
        throw NotImplementedException(
×
NEW
2050
            "CastInst: AddrSpaceCastInst is not supported",
×
NEW
2051
            ::docc::utils::get_debug_info(*instruction),
×
NEW
2052
            ::docc::utils::toIRString(*instruction)
×
NEW
2053
        );
×
2054
    } else if (llvm::isa<llvm::BitCastInst>(instruction)) {
12✔
NEW
2055
        throw NotImplementedException(
×
NEW
2056
            "CastInst: BitCastInst is not supported",
×
NEW
2057
            ::docc::utils::get_debug_info(*instruction),
×
NEW
2058
            ::docc::utils::toIRString(*instruction)
×
NEW
2059
        );
×
2060
    } else if (llvm::isa<llvm::IntToPtrInst>(instruction)) {
12✔
NEW
2061
        throw NotImplementedException(
×
NEW
2062
            "CastInst: IntToPtrInst is not supported",
×
NEW
2063
            ::docc::utils::get_debug_info(*instruction),
×
NEW
2064
            ::docc::utils::toIRString(*instruction)
×
NEW
2065
        );
×
NEW
2066
    }
×
2067

2068
    // Handle ptr to int separately
2069
    if (llvm::isa<llvm::PtrToIntInst>(instruction)) {
12✔
2070
        assert(
1✔
2071
            input_type->type_id() == sdfg::types::TypeID::Pointer &&
1✔
2072
            "CastInst: Expected pointer type as input for PtrToIntInst"
1✔
2073
        );
1✔
2074
        assert(
1✔
2075
            output_type.type_id() == sdfg::types::TypeID::Scalar &&
1✔
2076
            "CastInst: Expected scalar type as output for PtrToIntInst"
1✔
2077
        );
1✔
2078

2079
        sdfg::symbolic::Symbol output_sym = sdfg::symbolic::symbol(output);
1✔
2080
        sdfg::symbolic::Symbol input_sym = SymEngine::null;
1✔
2081
        if (utils::is_literal(input_operand)) {
1✔
NEW
2082
            std::string arg = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
NEW
2083
            input_sym = sdfg::symbolic::symbol(arg);
×
2084
        } else {
1✔
2085
            std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
1✔
2086
            input_sym = sdfg::symbolic::symbol(input);
1✔
2087
        }
1✔
2088

2089
        auto& next_state = this->builder_.add_state_after(current_state, false, dbg_info);
1✔
2090
        this->builder_.add_edge(current_state, next_state, {{output_sym, input_sym}}, dbg_info);
1✔
2091
        return next_state;
1✔
2092
    }
1✔
2093

2094
    switch (output_type.type_id()) {
11✔
2095
        case sdfg::types::TypeID::Scalar: {
11✔
2096
            auto& scalar_type = static_cast<const sdfg::types::Scalar&>(output_type);
11✔
2097
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
11✔
2098

2099
            sdfg::data_flow::AccessNode* input_node;
11✔
2100
            if (utils::is_literal(input_operand)) {
11✔
NEW
2101
                std::string arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
NEW
2102
                input_node = &this->builder_.add_constant(current_state, arg, *input_type, dbg_info);
×
2103
            } else {
11✔
2104
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
11✔
2105
                input_node = &this->builder_.add_access(current_state, input, dbg_info);
11✔
2106
            }
11✔
2107

2108
            // We implement the cast by chosing the memlet types appropriately
2109
            sdfg::types::PrimitiveType in_cast_type;
11✔
2110
            sdfg::types::PrimitiveType out_cast_type;
11✔
2111
            switch (instruction->getOpcode()) {
11✔
2112
                case llvm::Instruction::CastOps::Trunc: {
1✔
2113
                    auto* TI = llvm::cast<llvm::TruncInst>(instruction);
1✔
2114
                    // truncated bits are all zero
2115
                    bool nuw = TI->hasNoUnsignedWrap();
1✔
2116
                    // truncated bits are all sign bits
2117
                    bool nsw = TI->hasNoSignedWrap();
1✔
2118

2119
                    if (nuw && nsw) {
1✔
2120
                        // truncated bits are all zero and sign bit == 0
2121
                        // result fits into smaller signed type
2122
                        // cast between signed types
2123
                        // int64 _in = 42
2124
                        // int32 _out = (int32) _in
NEW
2125
                        in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
×
NEW
2126
                        out_cast_type = sdfg::types::as_signed(output_type.primitive_type());
×
2127
                    } else {
1✔
2128
                        // Truncation: cast between unsigned types (throw away high bits)
2129
                        // int64 _in = 42
2130
                        // int32 _out = (uint32) _in
2131
                        in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
1✔
2132
                        out_cast_type = sdfg::types::as_unsigned(output_type.primitive_type());
1✔
2133
                    }
1✔
2134

2135
                    auto& tasklet =
1✔
2136
                        this->builder_
1✔
2137
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2138

2139
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2140
                    this->builder_.add_computational_memlet(
1✔
2141
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2142
                    );
1✔
2143

2144
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2145
                    this->builder_.add_computational_memlet(
1✔
2146
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2147
                    );
1✔
2148
                    break;
1✔
NEW
2149
                }
×
2150
                case llvm::Instruction::CastOps::ZExt: {
2✔
2151
                    auto* ZI = llvm::dyn_cast<llvm::ZExtInst>(instruction);
2✔
2152
                    assert(ZI && "ZExtInst expected");
2✔
2153
                    if (!ZI->hasNonNeg()) {
2✔
2154
                        // Zero extension: cast between unsigned types
2155
                        // uint32 _in = 42
2156
                        in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
1✔
2157
                        out_cast_type = sdfg::types::as_unsigned(output_type.primitive_type());
1✔
2158
                    } else {
1✔
2159
                        // Zero extension with non-neq
2160
                        // Equivalent to cast with signed types
2161
                        // -> aggressive optimization later
2162
                        in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
1✔
2163
                        out_cast_type = sdfg::types::as_signed(output_type.primitive_type());
1✔
2164
                    }
1✔
2165

2166
                    auto& tasklet =
2✔
2167
                        this->builder_
2✔
2168
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
2✔
2169

2170
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
2✔
2171
                    this->builder_.add_computational_memlet(
2✔
2172
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
2✔
2173
                    );
2✔
2174

2175
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
2✔
2176
                    this->builder_.add_computational_memlet(
2✔
2177
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
2✔
2178
                    );
2✔
2179
                    break;
2✔
2180
                }
2✔
2181
                case llvm::Instruction::CastOps::SExt: {
2✔
2182
                    // Sign extension: cast between signed types
2183
                    // int32 _in = -42
2184
                    in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
2✔
2185
                    out_cast_type = sdfg::types::as_signed(output_type.primitive_type());
2✔
2186

2187
                    if (input_type->primitive_type() == sdfg::types::PrimitiveType::Bool) {
2✔
2188
                        auto& tasklet = this->builder_.add_tasklet(
1✔
2189
                            current_state, sdfg::data_flow::TaskletCode::int_mul, "__out", {"_in1", "_in2"}, dbg_info
1✔
2190
                        );
1✔
2191

2192
                        auto& minus_one_node =
1✔
2193
                            this->builder_
1✔
2194
                                .add_constant(current_state, "-1", sdfg::types::Scalar(out_cast_type), dbg_info);
1✔
2195
                        this->builder_.add_computational_memlet(
1✔
2196
                            current_state,
1✔
2197
                            minus_one_node,
1✔
2198
                            tasklet,
1✔
2199
                            "_in2",
1✔
2200
                            {},
1✔
2201
                            sdfg::types::Scalar(out_cast_type),
1✔
2202
                            dbg_info
1✔
2203
                        );
1✔
2204

2205
                        auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2206
                        this->builder_.add_computational_memlet(
1✔
2207
                            current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2208
                        );
1✔
2209

2210
                        auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2211
                        this->builder_.add_computational_memlet(
1✔
2212
                            current_state, *input_node, tasklet, "_in1", {}, *input_cast_type, dbg_info
1✔
2213
                        );
1✔
2214
                    } else {
1✔
2215
                        auto& tasklet = this->builder_.add_tasklet(
1✔
2216
                            current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info
1✔
2217
                        );
1✔
2218

2219
                        auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2220
                        this->builder_.add_computational_memlet(
1✔
2221
                            current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2222
                        );
1✔
2223

2224
                        auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2225
                        this->builder_.add_computational_memlet(
1✔
2226
                            current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2227
                        );
1✔
2228
                    }
1✔
2229
                    break;
2✔
2230
                }
2✔
2231
                case llvm::Instruction::CastOps::FPToUI: {
1✔
2232
                    // Floating point to unsigned integer
2233
                    // float32 _in = 42.0
2234
                    in_cast_type = input_type->primitive_type();
1✔
2235
                    out_cast_type = sdfg::types::as_unsigned(output_type.primitive_type());
1✔
2236

2237
                    auto& tasklet =
1✔
2238
                        this->builder_
1✔
2239
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2240

2241
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2242
                    this->builder_.add_computational_memlet(
1✔
2243
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2244
                    );
1✔
2245

2246
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2247
                    this->builder_.add_computational_memlet(
1✔
2248
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2249
                    );
1✔
2250
                    break;
1✔
2251
                }
2✔
2252
                case llvm::Instruction::CastOps::FPToSI: {
1✔
2253
                    // Floating point to signed integer
2254
                    // float32 _in = 42.0
2255
                    in_cast_type = input_type->primitive_type();
1✔
2256
                    out_cast_type = sdfg::types::as_signed(output_type.primitive_type());
1✔
2257

2258
                    auto& tasklet =
1✔
2259
                        this->builder_
1✔
2260
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2261

2262
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2263
                    this->builder_.add_computational_memlet(
1✔
2264
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2265
                    );
1✔
2266

2267
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2268
                    this->builder_.add_computational_memlet(
1✔
2269
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2270
                    );
1✔
2271
                    break;
1✔
2272
                }
2✔
2273
                case llvm::Instruction::CastOps::UIToFP: {
1✔
2274
                    auto* UIFP = llvm::cast<llvm::UIToFPInst>(instruction);
1✔
2275
                    assert(UIFP && "UIToFPInst expected");
1✔
2276

2277
                    if (UIFP->hasNonNeg()) {
1✔
2278
                        // Unsigned integer to floating point with non-neg
2279
                        // uint32 _in = 42
NEW
2280
                        in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
×
NEW
2281
                        out_cast_type = output_type.primitive_type();
×
2282
                    } else {
1✔
2283
                        // Unsigned integer to floating point
2284
                        // uint32 _in = 42
2285
                        in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
1✔
2286
                        out_cast_type = output_type.primitive_type();
1✔
2287
                    }
1✔
2288

2289
                    auto& tasklet =
1✔
2290
                        this->builder_
1✔
2291
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2292

2293
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2294
                    this->builder_.add_computational_memlet(
1✔
2295
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2296
                    );
1✔
2297

2298
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2299
                    this->builder_.add_computational_memlet(
1✔
2300
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2301
                    );
1✔
2302
                    break;
1✔
2303
                }
1✔
2304
                case llvm::Instruction::CastOps::SIToFP: {
1✔
2305
                    // Signed integer to floating point
2306
                    // int32 _in = -42
2307
                    in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
1✔
2308
                    out_cast_type = output_type.primitive_type();
1✔
2309

2310
                    auto& tasklet =
1✔
2311
                        this->builder_
1✔
2312
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2313

2314
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2315
                    this->builder_.add_computational_memlet(
1✔
2316
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2317
                    );
1✔
2318

2319
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2320
                    this->builder_.add_computational_memlet(
1✔
2321
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2322
                    );
1✔
2323
                    break;
1✔
2324
                }
1✔
2325
                case llvm::Instruction::CastOps::FPTrunc: {
1✔
2326
                    // Floating point truncation
2327
                    // float64 _in = 42.0
2328
                    in_cast_type = input_type->primitive_type();
1✔
2329
                    out_cast_type = output_type.primitive_type();
1✔
2330

2331
                    auto& tasklet =
1✔
2332
                        this->builder_
1✔
2333
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2334

2335
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2336
                    this->builder_.add_computational_memlet(
1✔
2337
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2338
                    );
1✔
2339

2340
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2341
                    this->builder_.add_computational_memlet(
1✔
2342
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2343
                    );
1✔
2344
                    break;
1✔
2345
                }
1✔
2346
                case llvm::Instruction::CastOps::FPExt: {
1✔
2347
                    // Floating point extension
2348
                    // float32 _in = 42.0
2349
                    in_cast_type = input_type->primitive_type();
1✔
2350
                    out_cast_type = output_type.primitive_type();
1✔
2351

2352
                    auto& tasklet =
1✔
2353
                        this->builder_
1✔
2354
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2355

2356
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2357
                    this->builder_.add_computational_memlet(
1✔
2358
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2359
                    );
1✔
2360

2361
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2362
                    this->builder_.add_computational_memlet(
1✔
2363
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2364
                    );
1✔
2365
                    break;
1✔
2366
                }
1✔
NEW
2367
                default: {
×
NEW
2368
                    throw NotImplementedException(
×
NEW
2369
                        "CastInst: Unsupported cast operation",
×
NEW
2370
                        ::docc::utils::get_debug_info(*instruction),
×
NEW
2371
                        ::docc::utils::toIRString(*instruction)
×
NEW
2372
                    );
×
2373
                }
1✔
2374
            }
11✔
2375
            return current_state;
11✔
2376
        }
11✔
NEW
2377
        case sdfg::types::TypeID::Structure: {
×
NEW
2378
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
×
NEW
2379
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
×
NEW
2380
            assert(struct_def.is_vector() && "CastInst: Expected vector structure type as output");
×
NEW
2381
            auto& vec_elem_type = struct_def.vector_element_type();
×
2382

2383
            // Define loop
NEW
2384
            std::string iterator = this->builder_.find_new_name("_i");
×
NEW
2385
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
×
NEW
2386
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
×
NEW
2387
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
×
NEW
2388
            sdfg::symbolic::Condition cond =
×
NEW
2389
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
×
NEW
2390
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
×
2391

NEW
2392
            auto loop = this->builder_.add_loop(current_state, iter_sym, init, cond, update, dbg_info);
×
NEW
2393
            auto& body = std::get<1>(loop);
×
2394

2395
            // Define body
NEW
2396
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
×
NEW
2397
            auto& tasklet =
×
NEW
2398
                this->builder_.add_tasklet(body, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
×
2399

NEW
2400
            sdfg::data_flow::AccessNode* input_node;
×
NEW
2401
            if (utils::is_literal(input_operand)) {
×
NEW
2402
                std::string arg = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
NEW
2403
                input_node = &this->builder_.add_constant(body, arg, *input_type, dbg_info);
×
NEW
2404
            } else {
×
NEW
2405
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
×
NEW
2406
                input_node = &this->builder_.add_access(body, input, dbg_info);
×
NEW
2407
            }
×
2408

2409
            // We implement the cast by chosing the memlet types appropriately
NEW
2410
            sdfg::types::PrimitiveType in_cast_type;
×
NEW
2411
            sdfg::types::PrimitiveType out_cast_type;
×
NEW
2412
            switch (instruction->getOpcode()) {
×
NEW
2413
                case llvm::Instruction::CastOps::Trunc: {
×
2414
                    // Truncation: cast between unsigned types
2415
                    // int64 _in = 42
2416
                    // int32 _out = (uint32) _in
NEW
2417
                    in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
×
NEW
2418
                    out_cast_type = sdfg::types::as_unsigned(vec_elem_type.primitive_type());
×
NEW
2419
                    break;
×
NEW
2420
                }
×
NEW
2421
                case llvm::Instruction::CastOps::ZExt: {
×
2422
                    // Zero extension: cast between unsigned types
2423
                    // uint32 _in = 42
NEW
2424
                    in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
×
NEW
2425
                    out_cast_type = sdfg::types::as_unsigned(vec_elem_type.primitive_type());
×
NEW
2426
                    break;
×
NEW
2427
                }
×
NEW
2428
                case llvm::Instruction::CastOps::SExt: {
×
2429
                    // Sign extension: cast between signed types
2430
                    // int32 _in = -42
NEW
2431
                    in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
×
NEW
2432
                    out_cast_type = sdfg::types::as_signed(vec_elem_type.primitive_type());
×
NEW
2433
                    break;
×
NEW
2434
                }
×
NEW
2435
                case llvm::Instruction::CastOps::FPToUI: {
×
2436
                    // Floating point to unsigned integer
2437
                    // float32 _in = 42.0
NEW
2438
                    in_cast_type = input_type->primitive_type();
×
NEW
2439
                    out_cast_type = sdfg::types::as_unsigned(vec_elem_type.primitive_type());
×
NEW
2440
                    break;
×
NEW
2441
                }
×
NEW
2442
                case llvm::Instruction::CastOps::FPToSI: {
×
2443
                    // Floating point to signed integer
2444
                    // float32 _in = 42.0
NEW
2445
                    in_cast_type = input_type->primitive_type();
×
NEW
2446
                    out_cast_type = sdfg::types::as_signed(vec_elem_type.primitive_type());
×
NEW
2447
                    break;
×
NEW
2448
                }
×
NEW
2449
                case llvm::Instruction::CastOps::UIToFP: {
×
2450
                    // Unsigned integer to floating point
2451
                    // uint32 _in = 42
NEW
2452
                    in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
×
NEW
2453
                    out_cast_type = vec_elem_type.primitive_type();
×
NEW
2454
                    break;
×
NEW
2455
                }
×
NEW
2456
                case llvm::Instruction::CastOps::SIToFP: {
×
2457
                    // Signed integer to floating point
2458
                    // int32 _in = -42
NEW
2459
                    in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
×
NEW
2460
                    out_cast_type = vec_elem_type.primitive_type();
×
NEW
2461
                    break;
×
NEW
2462
                }
×
NEW
2463
                case llvm::Instruction::CastOps::FPTrunc: {
×
2464
                    // Floating point truncation
2465
                    // float64 _in = 42.0
NEW
2466
                    in_cast_type = input_type->primitive_type();
×
NEW
2467
                    out_cast_type = vec_elem_type.primitive_type();
×
NEW
2468
                    break;
×
NEW
2469
                }
×
NEW
2470
                case llvm::Instruction::CastOps::FPExt: {
×
2471
                    // Floating point extension
2472
                    // float32 _in = 42.0
NEW
2473
                    in_cast_type = input_type->primitive_type();
×
NEW
2474
                    out_cast_type = vec_elem_type.primitive_type();
×
NEW
2475
                    break;
×
NEW
2476
                }
×
NEW
2477
                default: {
×
NEW
2478
                    throw NotImplementedException(
×
NEW
2479
                        "CastInst: Unsupported cast operation",
×
NEW
2480
                        ::docc::utils::get_debug_info(*instruction),
×
NEW
2481
                        ::docc::utils::toIRString(*instruction)
×
NEW
2482
                    );
×
NEW
2483
                }
×
NEW
2484
            }
×
2485

NEW
2486
            auto output_cast_type_scalar = std::make_unique<sdfg::types::Scalar>(out_cast_type);
×
NEW
2487
            auto output_cast_type =
×
NEW
2488
                this->builder_.create_vector_type(*output_cast_type_scalar, struct_def.vector_size());
×
NEW
2489
            this->builder_
×
NEW
2490
                .add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, *output_cast_type, dbg_info);
×
2491

NEW
2492
            auto input_cast_type_scalar = std::make_unique<sdfg::types::Scalar>(in_cast_type);
×
NEW
2493
            auto input_cast_type = this->builder_.create_vector_type(*input_cast_type_scalar, struct_def.vector_size());
×
NEW
2494
            this->builder_
×
NEW
2495
                .add_computational_memlet(body, *input_node, tasklet, "_in", {iter_sym}, *input_cast_type, dbg_info);
×
2496

NEW
2497
            return std::get<2>(loop);
×
NEW
2498
        }
×
NEW
2499
        default: {
×
NEW
2500
            throw NotImplementedException(
×
NEW
2501
                "CastInst: Unsupported output type",
×
NEW
2502
                ::docc::utils::get_debug_info(*instruction),
×
NEW
2503
                ::docc::utils::toIRString(*instruction)
×
NEW
2504
            );
×
NEW
2505
        }
×
2506
    }
11✔
2507
};
11✔
2508

2509

2510
} // namespace lifting
2511
} // 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