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

daisytuner / docc / 28592186395

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

push

github

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

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

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

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

3 existing lines in 1 file now uncovered.

39495 of 63567 relevant lines covered (62.13%)

978.6 hits per line

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

69.13
/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() {
106✔
24
    // Add globals to SDFG
25
    this->visit_globals();
106✔
26

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

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

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

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

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

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

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

69
    for (llvm::GlobalObject* GV : visited) {
106✔
70
        if (auto function = llvm::dyn_cast<llvm::Function>(GV)) {
41✔
71
            if (function->isIntrinsic()) {
33✔
72
                continue;
20✔
73
            }
20✔
74
        }
33✔
75

76
        // Add the global to the SDFG
77
        std::string global = GV->getName().str();
21✔
78
        if (global == "llvm.used") {
21✔
79
            continue;
×
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) {
21✔
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) {
15✔
94
            if (!GV->getName().starts_with("__daisy_priv_")) {
×
95
                // Hash module name and append to global name
96
                std::string module_path = GV->getParent()->getName().str();
×
97
                std::string module_name = std::filesystem::path(module_path).stem().string();
×
98
                global = "__daisy_priv_" + utils::normalize_name(module_name) + "_" + utils::normalize_name(global);
×
99
                GV->setName(global);
×
100
                GV->setLinkage(llvm::GlobalValue::ExternalLinkage);
×
101
            }
×
102
        }
×
103

104
        // Globals are pointers but their type is the base type
105
        std::unique_ptr<sdfg::types::IType> global_type;
21✔
106
        if (llvm::GlobalVariable* GVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {
21✔
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 {
13✔
122
            global_type = utils::get_type(
13✔
123
                this->builder_,
13✔
124
                this->anonymous_types_mapping_,
13✔
125
                this->DL_,
13✔
126
                GV->getValueType(),
13✔
127
                utils::get_storage_type(this->target_type_, 0)
13✔
128
            );
13✔
129
        }
13✔
130

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

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

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

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

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

173
    // Aggregate constants (arrays, structs) can also hide ConstantExprs.
174
    if (auto* CA = llvm::dyn_cast<llvm::ConstantAggregate>(V)) {
272✔
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)) {
271✔
181
        if (visited.find(GV) == visited.end()) {
41✔
182
            visited.insert(GV);
41✔
183
        }
41✔
184
    }
41✔
185
}
271✔
186

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

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

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

210
            auto& state = this->builder_.add_state(true, ::docc::utils::get_debug_info(initial_inst));
106✔
211
            this->state_mapping_.at(&block).insert(&state);
106✔
212
            this->pred_mapping_.insert({&state, {}});
106✔
213
        } else if (block.phis().empty()) {
106✔
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
    }
118✔
230

231
    // Add edges for each <pred, succ> pair
232
    for (const llvm::BasicBlock& pred : this->function_) {
118✔
233
        const llvm::Instruction* terminator = pred.getTerminator();
118✔
234
        if (llvm::isa<llvm::UnreachableInst>(terminator) || llvm::isa<llvm::ReturnInst>(terminator)) {
118✔
235
            continue;
107✔
236
        }
107✔
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✔
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✔
357
            throw NotImplementedException(
×
358
                "Unsupported terminator instruction in CFG construction",
×
359
                docc::utils::get_debug_info(*terminator),
×
360
                docc::utils::toIRString(*terminator)
×
361
            );
×
362
        }
×
363
    }
11✔
364
}
106✔
365

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

369
    // Phi Nodes perform double buffering. We need to create per-transition containers.
370
    std::string transition_suffix = utils::get_name(block);
121✔
371
    for (auto& pred : preds) {
121✔
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;
121✔
378
    for (const llvm::Instruction& inst : *block) {
127✔
379
        auto phi_inst = llvm::dyn_cast<const llvm::PHINode>(&inst);
127✔
380
        if (!phi_inst) {
127✔
381
            break;
121✔
382
        }
121✔
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✔
415
            this->builder_.add_container(phi_input, *phi_value_type);
×
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✔
426
            case sdfg::types::TypeID::Pointer: {
×
427
                sdfg::types::Pointer base_ptr_type;
×
428
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
×
429
                this->builder_.add_reference_memlet(
×
430
                    *next_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
×
431
                );
×
432
                break;
×
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✔
441
            }
×
442
            default: {
×
443
                throw NotImplementedException(
×
444
                    "Unsupported phi value type",
×
445
                    ::docc::utils::get_debug_info(*phi_inst),
×
446
                    ::docc::utils::toIRString(*phi_inst)
×
447
                );
×
448
            }
×
449
        }
1✔
450
    }
1✔
451

452
    // Visit instructions
453
    for (const llvm::Instruction& inst : *block) {
225✔
454
        if (llvm::isa<llvm::BranchInst>(inst)) {
225✔
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);
212✔
460
        next_state = &this->builder_.add_state_after(*next_state, true, dbg_info);
212✔
461
        this->pred_mapping_.insert({next_state, preds});
212✔
462
        next_state = &this->visit_instruction(block, &inst, *next_state);
212✔
463
    }
212✔
464
};
121✔
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
) {
212✔
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)) {
212✔
481
        return this->visit_AllocaInst(block, inst, current_state);
×
482
    } else if (auto inst = llvm::dyn_cast<const llvm::GetElementPtrInst>(instruction)) {
212✔
483
        return this->visit_GetElementPtrInst(block, inst, current_state);
3✔
484
    } else if (auto inst = llvm::dyn_cast<const llvm::StoreInst>(instruction)) {
209✔
485
        return this->visit_StoreInst(block, inst, current_state);
13✔
486
    } else if (auto inst = llvm::dyn_cast<const llvm::LoadInst>(instruction)) {
196✔
487
        return this->visit_LoadInst(block, inst, current_state);
13✔
488
    } else if (auto inst = llvm::dyn_cast<const llvm::PHINode>(instruction)) {
183✔
489
        return this->visit_PHINode(block, inst, current_state);
6✔
490
    } else if (auto inst = llvm::dyn_cast<const llvm::ICmpInst>(instruction)) {
177✔
491
        return this->visit_ICmpInst(block, inst, current_state);
10✔
492
    } else if (auto inst = llvm::dyn_cast<const llvm::ReturnInst>(instruction)) {
167✔
493
        return this->visit_ReturnInst(block, inst, current_state);
105✔
494
    } else if (auto inst = llvm::dyn_cast<const llvm::SelectInst>(instruction)) {
105✔
495
        return this->visit_SelectInst(block, inst, current_state);
2✔
496
    } else if (auto inst = llvm::dyn_cast<const llvm::UnreachableInst>(instruction)) {
60✔
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)) {
59✔
502
        return this->visit_UnaryOperator(block, inst, current_state);
5✔
503
    } else if (auto inst = llvm::dyn_cast<const llvm::BinaryOperator>(instruction)) {
54✔
504
        return this->visit_BinaryOperator(block, inst, current_state);
6✔
505
    } else if (auto inst = llvm::dyn_cast<const llvm::CastInst>(instruction)) {
48✔
506
        return this->visit_CastInst(block, inst, current_state);
12✔
507
    } else if (auto inst = llvm::dyn_cast<const llvm::FCmpInst>(instruction)) {
36✔
508
        return this->visit_FCmpInst(block, inst, current_state);
4✔
509
    } else if (auto inst = llvm::dyn_cast<const llvm::CallBase>(instruction)) {
32✔
510
        FunctionLifting lifter(
32✔
511
            this->TLI_,
32✔
512
            this->DL_,
32✔
513
            this->function_,
32✔
514
            this->target_type_,
32✔
515
            this->builder_,
32✔
516
            this->state_mapping_,
32✔
517
            this->pred_mapping_,
32✔
518
            this->constants_mapping_,
32✔
519
            this->anonymous_types_mapping_
32✔
520
        );
32✔
521
        return lifter.visit(block, inst, current_state);
32✔
522
    }
32✔
523

524
    std::string instruction_str;
×
525
    llvm::raw_string_ostream OS(instruction_str);
×
526
    instruction->print(OS);
×
527
    throw NotImplementedException(
×
528
        "Unsupported instruction: " + instruction_str,
×
529
        ::docc::utils::get_debug_info(*instruction),
×
530
        ::docc::utils::toIRString(*instruction)
×
531
    );
×
532
};
59✔
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
) {
105✔
537
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
105✔
538
    if (instruction->getNumOperands() == 0) {
105✔
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);
105✔
545

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

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

552
    std::string data;
7✔
553
    if (utils::is_literal(return_value)) {
7✔
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✔
564
        data = utils::find_const_name_to_sdfg_name(this->constants_mapping_, return_value);
3✔
565
        return this->builder_.add_return_state_after(current_state, data, dbg_info);
3✔
566
    }
3✔
567
};
7✔
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✔
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
596
) {
×
597
    // Define Debug
598
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
×
599

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

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

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

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

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

643
    return current_state;
×
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✔
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✔
709
        auto& input_node = this->builder_.add_constant(current_state, input, sdfg::types::Pointer(), dbg_info);
×
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✔
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✔
775
        default:
×
776
            throw NotImplementedException(
×
777
                "Unsupported load instruction",
×
778
                ::docc::utils::get_debug_info(*instruction),
×
779
                ::docc::utils::toIRString(*instruction)
×
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
) {
13✔
787
    // Define Debug
788
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
13✔
789

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

798
    // Define Input
799
    auto input_operand = instruction->getValueOperand();
13✔
800
    auto input_type = utils::get_type(
13✔
801
        this->builder_,
13✔
802
        this->anonymous_types_mapping_,
13✔
803
        this->DL_,
13✔
804
        input_operand->getType(),
13✔
805
        utils::get_storage_type(this->target_type_, 0)
13✔
806
    );
13✔
807
    // Define Operation
808
    switch (input_type->type_id()) {
13✔
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✔
831
        }
×
832
        case sdfg::types::TypeID::Array:
×
833
        case sdfg::types::TypeID::Structure:
4✔
834
        case sdfg::types::TypeID::Pointer: {
7✔
835
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
7✔
836

837
            sdfg::data_flow::AccessNode* input_node;
7✔
838
            if (utils::is_literal(input_operand)) {
7✔
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);
3✔
843
                if (builder_.subject().type(input).type_id() == sdfg::types::TypeID::Function) {
3✔
844
                    // LLVM IR provides a pointer to a function but in the SDFG its a function. Currently, we cannot
845
                    // handle this.
846
                    throw NotImplementedException(
1✔
847
                        "Storing a function into a pointer currently unsupported",
1✔
848
                        dbg_info,
1✔
849
                        ::docc::utils::toIRString(*instruction)
1✔
850
                    );
1✔
851
                }
1✔
852
                input_node = &this->builder_.add_access(current_state, input, dbg_info);
2✔
853
            }
2✔
854

855
            sdfg::types::Pointer base_ptr_type;
6✔
856
            sdfg::types::Pointer output_ptr_type(*input_type);
6✔
857
            this->builder_
6✔
858
                .add_dereference_memlet(current_state, *input_node, output_node, false, output_ptr_type, dbg_info);
6✔
859

860
            return current_state;
6✔
861
        }
7✔
862
        default:
×
863
            throw NotImplementedException(
×
NEW
864
                "Unsupported store instruction", dbg_info, ::docc::utils::toIRString(*instruction)
×
865
            );
×
866
    }
13✔
867
};
13✔
868

869
sdfg::control_flow::State& Lifting::visit_PHINode(
870
    const llvm::BasicBlock* block, const llvm::PHINode* instruction, sdfg::control_flow::State& current_state
871
) {
6✔
872
    // Define Debug
873
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
6✔
874

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

894
    // Determine Input
895
    llvm::Value* phi_value = nullptr;
6✔
896
    auto preds = this->pred_mapping_.at(&current_state);
6✔
897
    for (size_t i = 0; i < instruction->getNumIncomingValues(); ++i) {
9✔
898
        if (preds.contains(instruction->getIncomingBlock(i))) {
9✔
899
            phi_value = instruction->getIncomingValue(i);
6✔
900
            break;
6✔
901
        }
6✔
902
    }
9✔
903
    assert(phi_value != nullptr && "PHINode: Failed to determine phi value");
6✔
904

905
    // Handle UndefValue
906
    if (llvm::dyn_cast<llvm::UndefValue>(phi_value)) {
6✔
907
        return current_state;
×
908
    }
×
909

910
    // Define unsupported phi value types
911
    if (llvm::isa<llvm::ConstantExpr>(phi_value)) {
6✔
912
        throw NotImplementedException(
×
913
            "PHINode: Unsupported phi value type",
×
914
            ::docc::utils::get_debug_info(*instruction),
×
915
            ::docc::utils::toIRString(*instruction)
×
916
        );
×
917
    }
×
918

919
    // Define Operation
920
    switch (output_type.type_id()) {
6✔
921
        case sdfg::types::TypeID::Pointer: {
2✔
922
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
2✔
923

924
            // Handle nullptr
925
            if (utils::is_literal(phi_value)) {
2✔
926
                sdfg::types::Pointer ptr_type;
1✔
927

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

932
                return current_state;
1✔
933
            } else if (auto GV = llvm::dyn_cast<llvm::GlobalValue>(phi_value)) {
1✔
934
                std::string global_name = utils::get_name(GV);
×
935
                auto& input_node = this->builder_.add_access(current_state, global_name, dbg_info);
×
936

937
                sdfg::types::Pointer base_ptr_type;
×
938
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
×
939
                this->builder_.add_reference_memlet(
×
940
                    current_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
×
941
                );
×
942

943
                return current_state;
×
944
            } else {
1✔
945
                // Handle local variable
946
                std::string input = utils::get_name(phi_value);
1✔
947
                if (!llvm::isa<llvm::Argument>(phi_value)) {
1✔
948
                    std::string transition_suffix = utils::get_name(block);
×
949
                    for (auto& pred : preds) {
×
950
                        transition_suffix += "_" + utils::get_name(pred);
×
951
                    }
×
952
                    input += "_" + transition_suffix;
×
953
                }
×
954

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

957
                sdfg::types::Pointer base_ptr_type;
1✔
958
                sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
959
                this->builder_.add_reference_memlet(
1✔
960
                    current_state, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
961
                );
1✔
962

963
                return current_state;
1✔
964
            }
1✔
965
        }
2✔
966
        case sdfg::types::TypeID::Scalar: {
4✔
967
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
4✔
968

969
            // Handle literal
970
            if (utils::is_literal(phi_value)) {
4✔
971
                auto arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(phi_value));
1✔
972
                auto& input_node = this->builder_.add_constant(current_state, arg, output_type, dbg_info);
1✔
973
                auto& tasklet =
1✔
974
                    this->builder_
1✔
975
                        .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
976
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in", {}, dbg_info);
1✔
977
                this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
1✔
978

979
                return current_state;
1✔
980
            } else {
3✔
981
                // Handle local variable
982
                std::string phi_transition = utils::get_name(phi_value);
3✔
983
                if (!llvm::isa<llvm::Argument>(phi_value)) {
3✔
984
                    std::string transition_suffix = utils::get_name(block);
1✔
985
                    for (auto& pred : preds) {
1✔
986
                        transition_suffix += "_" + utils::get_name(pred);
1✔
987
                    }
1✔
988
                    phi_transition += "_" + transition_suffix;
1✔
989
                }
1✔
990

991
                auto& input_node = this->builder_.add_access(current_state, phi_transition, dbg_info);
3✔
992
                auto& tasklet =
3✔
993
                    this->builder_
3✔
994
                        .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
3✔
995
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in", {}, dbg_info);
3✔
996
                this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
3✔
997

998
                return current_state;
3✔
999
            }
3✔
1000
        }
4✔
1001
        default:
×
1002
            throw NotImplementedException(
×
1003
                "PHINode: Unsupported output type",
×
1004
                ::docc::utils::get_debug_info(*instruction),
×
1005
                ::docc::utils::toIRString(*instruction)
×
1006
            );
×
1007
    }
6✔
1008

1009
    return current_state;
×
1010
};
6✔
1011

1012
sdfg::control_flow::State& Lifting::visit_ICmpInst(
1013
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
1014
) {
10✔
1015
    auto predicate = instruction->getPredicate();
10✔
1016

1017
    // Unsigned comparisions cannot be handled symbolically
1018
    if ((predicate == llvm::CmpInst::Predicate::ICMP_UGT || predicate == llvm::CmpInst::Predicate::ICMP_UGE ||
10✔
1019
         predicate == llvm::CmpInst::Predicate::ICMP_ULT || predicate == llvm::CmpInst::Predicate::ICMP_ULE) &&
10✔
1020
        !instruction->getOperand(0)->getType()->isPointerTy()) {
10✔
1021
        return this->visit_ICmpInst_dataflow(block, instruction, current_state);
×
1022
    } else {
10✔
1023
        return this->visit_ICmpInst_symbolic(block, instruction, current_state);
10✔
1024
    }
10✔
1025
}
10✔
1026

1027
sdfg::control_flow::State& Lifting::visit_ICmpInst_symbolic(
1028
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
1029
) {
10✔
1030
    // Define Debug
1031
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
10✔
1032

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

1052
    auto left_operand = instruction->getOperand(0);
10✔
1053
    auto left_operand_type = utils::get_type(
10✔
1054
        this->builder_,
10✔
1055
        this->anonymous_types_mapping_,
10✔
1056
        this->DL_,
10✔
1057
        left_operand->getType(),
10✔
1058
        utils::get_storage_type(this->target_type_, 0)
10✔
1059
    );
10✔
1060
    assert(
10✔
1061
        (left_operand_type->type_id() == sdfg::types::TypeID::Scalar ||
10✔
1062
         left_operand_type->type_id() == sdfg::types::TypeID::Pointer) &&
10✔
1063
        "ICmpInst: Expected scalar or pointer type as left operand"
10✔
1064
    );
10✔
1065

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

1081
    auto right_operand = instruction->getOperand(1);
10✔
1082
    auto right_operand_type = utils::get_type(
10✔
1083
        this->builder_,
10✔
1084
        this->anonymous_types_mapping_,
10✔
1085
        this->DL_,
10✔
1086
        right_operand->getType(),
10✔
1087
        utils::get_storage_type(this->target_type_, 0)
10✔
1088
    );
10✔
1089
    assert(
10✔
1090
        (right_operand_type->type_id() == sdfg::types::TypeID::Scalar ||
10✔
1091
         right_operand_type->type_id() == sdfg::types::TypeID::Pointer) &&
10✔
1092
        "ICmpInst: Expected scalar or pointer type as right operand"
10✔
1093
    );
10✔
1094

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

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

1149
    auto& state_end = this->builder_.add_state_after(current_state, false, dbg_info);
10✔
1150
    auto& state_cond = this->builder_.add_state(false, dbg_info);
10✔
1151
    this->builder_.add_edge(current_state, state_cond, {{output, condition}}, dbg_info);
10✔
1152
    this->builder_.add_edge(state_cond, state_end, dbg_info);
10✔
1153

1154
    return state_end;
10✔
1155
};
10✔
1156

1157
sdfg::control_flow::State& Lifting::visit_ICmpInst_dataflow(
1158
    const llvm::BasicBlock* block, const llvm::ICmpInst* instruction, sdfg::control_flow::State& current_state
1159
) {
×
1160
    // Define Debug
1161
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
×
1162

1163
    // Define Output
1164
    std::string output = utils::get_name(instruction);
×
1165
    if (!this->builder_.subject().exists(output)) {
×
1166
        auto output_type = utils::get_type(
×
1167
            this->builder_,
×
1168
            this->anonymous_types_mapping_,
×
1169
            this->DL_,
×
1170
            instruction->getType(),
×
1171
            utils::get_storage_type(this->target_type_, 0)
×
1172
        );
×
1173
        this->builder_.add_container(output, *output_type);
×
1174
    }
×
1175
    auto& output_type = this->builder_.subject().type(output);
×
1176
    assert(output_type.type_id() == sdfg::types::TypeID::Scalar && "ICmpInst: Expected scalar type as output");
×
1177

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

1206
    auto left_operand = instruction->getOperand(0);
×
1207
    auto right_operand = instruction->getOperand(1);
×
1208
    assert(
×
1209
        left_operand->getType()->isIntegerTy() && right_operand->getType()->isIntegerTy() &&
×
1210
        "ICmpInst: Expected integer types as operands"
×
1211
    );
×
1212
    switch (output_type.type_id()) {
×
1213
        case sdfg::types::TypeID::Scalar: {
×
1214
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
×
1215
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
×
1216
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
×
1217

1218
            sdfg::data_flow::AccessNode* left_node;
×
1219
            if (utils::is_literal(left_operand)) {
×
1220
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(left_operand));
×
1221
                auto left_operand_type = utils::get_type(
×
1222
                    this->builder_,
×
1223
                    this->anonymous_types_mapping_,
×
1224
                    this->DL_,
×
1225
                    left_operand->getType(),
×
1226
                    utils::get_storage_type(this->target_type_, 0)
×
1227
                );
×
1228
                left_node = &this->builder_.add_constant(current_state, input, *left_operand_type, dbg_info);
×
1229
            } else {
×
1230
                std::string left_input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, left_operand);
×
1231
                left_node = &this->builder_.add_access(current_state, left_input, dbg_info);
×
1232
            }
×
1233

1234
            sdfg::data_flow::AccessNode* right_node;
×
1235
            if (utils::is_literal(right_operand)) {
×
1236
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(right_operand));
×
1237
                auto right_operand_type = utils::get_type(
×
1238
                    this->builder_,
×
1239
                    this->anonymous_types_mapping_,
×
1240
                    this->DL_,
×
1241
                    right_operand->getType(),
×
1242
                    utils::get_storage_type(this->target_type_, 0)
×
1243
                );
×
1244
                right_node = &this->builder_.add_constant(current_state, input, *right_operand_type, dbg_info);
×
1245
            } else {
×
1246
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
×
1247
                right_node = &this->builder_.add_access(current_state, input, dbg_info);
×
1248
            }
×
1249

1250
            this->builder_.add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, dbg_info);
×
1251
            this->builder_.add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, dbg_info);
×
1252

1253
            return current_state;
×
1254
        }
×
1255
        default: {
×
1256
            throw NotImplementedException(
×
1257
                "ICmpInst: Unsupported output type",
×
1258
                ::docc::utils::get_debug_info(*instruction),
×
1259
                ::docc::utils::toIRString(*instruction)
×
1260
            );
×
1261
        }
×
1262
    }
×
1263
};
×
1264

1265

1266
sdfg::control_flow::State& Lifting::visit_SelectInst(
1267
    const llvm::BasicBlock* block, const llvm::SelectInst* instruction, sdfg::control_flow::State& current_state
1268
) {
2✔
1269
    // Define Debug
1270
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
2✔
1271

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

1291
    // Define Condition
1292
    auto condition = instruction->getCondition();
2✔
1293
    auto condition_type = utils::get_type(
2✔
1294
        this->builder_,
2✔
1295
        this->anonymous_types_mapping_,
2✔
1296
        this->DL_,
2✔
1297
        condition->getType(),
2✔
1298
        utils::get_storage_type(this->target_type_, 0)
2✔
1299
    );
2✔
1300
    assert((condition_type->type_id() == sdfg::types::TypeID::Scalar) && "SelectInst: Expected scalar type as condition");
2✔
1301

1302
    auto value_if = instruction->getTrueValue();
2✔
1303
    auto value_else = instruction->getFalseValue();
2✔
1304
    switch (condition_type->type_id()) {
2✔
1305
        case sdfg::types::TypeID::Scalar: {
2✔
1306
            sdfg::symbolic::Expression condition_sym;
2✔
1307
            if (utils::is_literal(condition)) {
2✔
1308
                condition_sym = utils::as_symbol(llvm::dyn_cast<const llvm::Constant>(condition));
×
1309
            } else {
2✔
1310
                auto condition_name = utils::find_const_name_to_sdfg_name(this->constants_mapping_, condition);
2✔
1311
                condition_sym = sdfg::symbolic::symbol(condition_name);
2✔
1312
            }
2✔
1313

1314
            auto& state_if = this->builder_.add_state(false, dbg_info);
2✔
1315
            auto& state_else = this->builder_.add_state(false, dbg_info);
2✔
1316
            auto& state_end = this->builder_.add_state_after(current_state, false, dbg_info);
2✔
1317

1318
            this->builder_.add_edge(
2✔
1319
                current_state, state_if, sdfg::symbolic::Ne(condition_sym, sdfg::symbolic::__false__()), dbg_info
2✔
1320
            );
2✔
1321
            this->builder_.add_edge(
2✔
1322
                current_state, state_else, sdfg::symbolic::Eq(condition_sym, sdfg::symbolic::__false__()), dbg_info
2✔
1323
            );
2✔
1324
            this->builder_.add_edge(state_if, state_end, dbg_info);
2✔
1325
            this->builder_.add_edge(state_else, state_end, dbg_info);
2✔
1326

1327
            // If
1328
            {
2✔
1329
                auto& output_node = this->builder_.add_access(state_if, output, dbg_info);
2✔
1330

1331
                switch (output_type.type_id()) {
2✔
1332
                    case sdfg::types::TypeID::Scalar: {
1✔
1333
                        auto& tasklet =
1✔
1334
                            this->builder_
1✔
1335
                                .add_tasklet(state_if, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
1336
                        this->builder_.add_computational_memlet(state_if, tasklet, "__out", output_node, {}, dbg_info);
1✔
1337

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

1354
                            sdfg::types::Pointer ptr_type;
×
1355
                            this->builder_
×
1356
                                .add_reference_memlet(state_if, input_node, output_node, {}, ptr_type, dbg_info);
×
1357
                        } else {
1✔
1358
                            std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_if);
1✔
1359
                            auto& input_node = this->builder_.add_access(state_if, input, dbg_info);
1✔
1360

1361
                            sdfg::types::Pointer base_ptr_type;
1✔
1362
                            sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
1363
                            this->builder_.add_reference_memlet(
1✔
1364
                                state_if, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
1365
                            );
1✔
1366
                        }
1✔
1367
                        break;
1✔
1368
                    }
×
1369
                    default: {
×
1370
                        throw NotImplementedException(
×
1371
                            "SelectInst: Unsupported output type",
×
1372
                            ::docc::utils::get_debug_info(*instruction),
×
1373
                            ::docc::utils::toIRString(*instruction)
×
1374
                        );
×
1375
                    }
×
1376
                }
2✔
1377
            }
2✔
1378

1379
            // Else
1380
            {
2✔
1381
                auto& output_node = this->builder_.add_access(state_else, output, dbg_info);
2✔
1382

1383
                switch (output_type.type_id()) {
2✔
1384
                    case sdfg::types::TypeID::Scalar: {
1✔
1385
                        auto& tasklet =
1✔
1386
                            this->builder_
1✔
1387
                                .add_tasklet(state_else, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
1388
                        this->builder_.add_computational_memlet(state_else, tasklet, "__out", output_node, {}, dbg_info);
1✔
1389

1390
                        sdfg::data_flow::AccessNode* input_node;
1✔
1391
                        if (utils::is_literal(value_else)) {
1✔
1392
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_else));
×
1393
                            input_node = &this->builder_.add_constant(state_else, input, output_type, dbg_info);
×
1394
                        } else {
1✔
1395
                            std::string input =
1✔
1396
                                utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_else);
1✔
1397
                            input_node = &this->builder_.add_access(state_else, input, dbg_info);
1✔
1398
                        }
1✔
1399
                        this->builder_.add_computational_memlet(state_else, *input_node, tasklet, "_in", {}, dbg_info);
1✔
1400

1401
                        break;
1✔
1402
                    }
×
1403
                    case sdfg::types::TypeID::Pointer: {
1✔
1404
                        if (utils::is_literal(value_else)) {
1✔
1405
                            std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(value_else));
×
1406
                            auto& input_node = this->builder_.add_constant(state_else, input, output_type, dbg_info);
×
1407

1408
                            sdfg::types::Pointer ptr_type;
×
1409
                            this->builder_
×
1410
                                .add_reference_memlet(state_else, input_node, output_node, {}, ptr_type, dbg_info);
×
1411
                        } else {
1✔
1412
                            std::string input =
1✔
1413
                                utils::find_const_name_to_sdfg_name(this->constants_mapping_, value_else);
1✔
1414
                            auto& input_node = this->builder_.add_access(state_else, input, dbg_info);
1✔
1415

1416
                            sdfg::types::Pointer base_ptr_type;
1✔
1417
                            sdfg::types::Pointer ptr_type(static_cast<const sdfg::types::IType&>(base_ptr_type));
1✔
1418
                            this->builder_.add_reference_memlet(
1✔
1419
                                state_else, input_node, output_node, {sdfg::symbolic::zero()}, ptr_type, dbg_info
1✔
1420
                            );
1✔
1421
                        }
1✔
1422

1423
                        break;
1✔
1424
                    }
×
1425
                    default: {
×
1426
                        throw NotImplementedException(
×
1427
                            "SelectInst: Unsupported output type",
×
1428
                            ::docc::utils::get_debug_info(*instruction),
×
1429
                            ::docc::utils::toIRString(*instruction)
×
1430
                        );
×
1431
                    }
×
1432
                }
2✔
1433
            }
2✔
1434

1435
            return state_end;
2✔
1436
        }
2✔
1437
        default: {
×
1438
            throw NotImplementedException(
×
1439
                "SelectInst: Unsupported condition type",
×
1440
                ::docc::utils::get_debug_info(*instruction),
×
1441
                ::docc::utils::toIRString(*instruction)
×
1442
            );
×
1443
        }
2✔
1444
    }
2✔
1445
};
2✔
1446

1447
sdfg::control_flow::State& Lifting::visit_FCmpInst(
1448
    const llvm::BasicBlock* block, const llvm::FCmpInst* instruction, sdfg::control_flow::State& current_state
1449
) {
4✔
1450
    // Define Debug
1451
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
4✔
1452

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

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

1548
    auto left_operand = instruction->getOperand(0);
4✔
1549
    auto right_operand = instruction->getOperand(1);
4✔
1550
    switch (output_type.type_id()) {
4✔
1551
        case sdfg::types::TypeID::Scalar: {
1✔
1552
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
1✔
1553
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
1✔
1554
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
1✔
1555

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

1572
            sdfg::data_flow::AccessNode* right_node;
1✔
1573
            if (utils::is_literal(right_operand)) {
1✔
1574
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(right_operand));
×
1575
                auto right_operand_type = utils::get_type(
×
1576
                    this->builder_,
×
1577
                    this->anonymous_types_mapping_,
×
1578
                    this->DL_,
×
1579
                    right_operand->getType(),
×
1580
                    utils::get_storage_type(this->target_type_, 0)
×
1581
                );
×
1582
                right_node = &this->builder_.add_constant(current_state, input, *right_operand_type, dbg_info);
×
1583
            } else {
1✔
1584
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, right_operand);
1✔
1585
                right_node = &this->builder_.add_access(current_state, input, dbg_info);
1✔
1586
            }
1✔
1587

1588
            this->builder_.add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, dbg_info);
1✔
1589
            this->builder_.add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, dbg_info);
1✔
1590

1591
            return current_state;
1✔
1592
        }
×
1593
        case sdfg::types::TypeID::Structure: {
3✔
1594
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1595
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1596
            assert(struct_def.is_vector() && "FCmpInst: Expected vector structure type as output");
3✔
1597

1598
            // Define loop
1599
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1600
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1601
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1602
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1603
            sdfg::symbolic::Condition cond =
3✔
1604
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1605
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1606

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

1610
            // Define body
1611
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1612
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1613
            this->builder_.add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, dbg_info);
3✔
1614

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

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

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

1650
            return std::get<2>(loop);
3✔
1651
        }
3✔
1652
        default: {
×
1653
            throw NotImplementedException(
×
1654
                "FCmpInst: Unsupported output type",
×
1655
                ::docc::utils::get_debug_info(*instruction),
×
1656
                ::docc::utils::toIRString(*instruction)
×
1657
            );
×
1658
        }
3✔
1659
    }
4✔
1660
};
4✔
1661

1662
sdfg::control_flow::State& Lifting::visit_UnaryOperator(
1663
    const llvm::BasicBlock* block, const llvm::UnaryOperator* instruction, sdfg::control_flow::State& current_state
1664
) {
5✔
1665
    // Define Debug
1666
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
5✔
1667

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

1687
    // Define Operation
1688
    sdfg::data_flow::TaskletCode operation;
5✔
1689
    switch (instruction->getOpcode()) {
5✔
1690
        case llvm::Instruction::UnaryOps::FNeg: {
5✔
1691
            operation = sdfg::data_flow::TaskletCode::fp_neg;
5✔
1692
            break;
5✔
1693
        }
×
1694
        default: {
×
1695
            throw NotImplementedException(
×
1696
                "UnaryOperator: Unsupported opcode",
×
1697
                ::docc::utils::get_debug_info(*instruction),
×
1698
                ::docc::utils::toIRString(*instruction)
×
1699
            );
×
1700
        }
×
1701
    }
5✔
1702

1703
    auto operand = instruction->getOperand(0);
5✔
1704
    switch (output_type.type_id()) {
5✔
1705
        case sdfg::types::TypeID::Scalar: {
2✔
1706
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
2✔
1707
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1"}, dbg_info);
2✔
1708
            this->builder_.add_computational_memlet(current_state, tasklet, "__out", output_node, {}, dbg_info);
2✔
1709

1710
            if (utils::is_literal(operand)) {
2✔
1711
                std::string input = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(operand));
1✔
1712
                auto& input_node = this->builder_.add_constant(current_state, input, output_type, dbg_info);
1✔
1713
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in1", {}, dbg_info);
1✔
1714
            } else {
1✔
1715
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, operand);
1✔
1716
                auto& input_node = this->builder_.add_access(current_state, input, dbg_info);
1✔
1717
                this->builder_.add_computational_memlet(current_state, input_node, tasklet, "_in1", {}, dbg_info);
1✔
1718
            }
1✔
1719

1720
            return current_state;
2✔
1721
        }
×
1722
        case sdfg::types::TypeID::Structure: {
3✔
1723
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1724
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1725
            assert(struct_def.is_vector() && "UnaryOperator: Expected vector structure type as output");
3✔
1726

1727
            // Define loop
1728
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1729
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1730
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1731
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1732
            sdfg::symbolic::Condition cond =
3✔
1733
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1734
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1735

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

1739
            // Define body
1740
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1741
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1"}, dbg_info);
3✔
1742
            this->builder_.add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, dbg_info);
3✔
1743

1744
            if (utils::is_literal(operand)) {
3✔
1745
                std::string input = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(operand));
2✔
1746
                auto& input_node = this->builder_.add_constant(body, input, output_type, dbg_info);
2✔
1747
                this->builder_.add_computational_memlet(body, input_node, tasklet, "_in1", {iter_sym}, dbg_info);
2✔
1748
            } else {
2✔
1749
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, operand);
1✔
1750
                auto& input_node = this->builder_.add_access(body, input, dbg_info);
1✔
1751
                this->builder_.add_computational_memlet(body, input_node, tasklet, "_in1", {iter_sym}, dbg_info);
1✔
1752
            }
1✔
1753

1754
            return std::get<2>(loop);
3✔
1755
        }
3✔
1756
        default: {
×
1757
            throw NotImplementedException(
×
1758
                "UnaryOperator: Unsupported output type",
×
1759
                ::docc::utils::get_debug_info(*instruction),
×
1760
                ::docc::utils::toIRString(*instruction)
×
1761
            );
×
1762
        }
3✔
1763
    }
5✔
1764
};
5✔
1765

1766
sdfg::control_flow::State& Lifting::visit_BinaryOperator(
1767
    const llvm::BasicBlock* block, const llvm::BinaryOperator* instruction, sdfg::control_flow::State& current_state
1768
) {
6✔
1769
    // Define Debug
1770
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
6✔
1771

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

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

1889
    auto left_operand = instruction->getOperand(0);
6✔
1890
    auto right_operand = instruction->getOperand(1);
6✔
1891
    switch (output_type.type_id()) {
6✔
1892
        case sdfg::types::TypeID::Scalar: {
3✔
1893
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
3✔
1894
            auto& tasklet = this->builder_.add_tasklet(current_state, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1895

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

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

1930
            this->builder_
3✔
1931
                .add_computational_memlet(current_state, tasklet, "__out", output_node, {}, output_type, dbg_info);
3✔
1932
            this->builder_
3✔
1933
                .add_computational_memlet(current_state, *left_node, tasklet, "_in1", {}, *left_input_type, dbg_info);
3✔
1934
            this->builder_
3✔
1935
                .add_computational_memlet(current_state, *right_node, tasklet, "_in2", {}, *right_input_type, dbg_info);
3✔
1936

1937
            return current_state;
3✔
1938
        }
×
1939
        case sdfg::types::TypeID::Structure: {
3✔
1940
            auto& struct_type = static_cast<const sdfg::types::Structure&>(output_type);
3✔
1941
            auto& struct_def = this->builder_.subject().structure(struct_type.name());
3✔
1942
            assert(struct_def.is_vector() && "FCmpInst: Expected vector structure type as output");
3✔
1943

1944
            // Define loop
1945
            std::string iterator = this->builder_.find_new_name("_i");
3✔
1946
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
3✔
1947
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
3✔
1948
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
3✔
1949
            sdfg::symbolic::Condition cond =
3✔
1950
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
3✔
1951
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
3✔
1952

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

1956
            // Define body
1957
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
3✔
1958
            auto& tasklet = this->builder_.add_tasklet(body, operation, "__out", {"_in1", "_in2"}, dbg_info);
3✔
1959

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

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

1994
            this->builder_
3✔
1995
                .add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, output_type, dbg_info);
3✔
1996
            this->builder_
3✔
1997
                .add_computational_memlet(body, *left_node, tasklet, "_in1", {iter_sym}, *left_input_type, dbg_info);
3✔
1998
            this->builder_
3✔
1999
                .add_computational_memlet(body, *right_node, tasklet, "_in2", {iter_sym}, *right_input_type, dbg_info);
3✔
2000

2001
            return std::get<2>(loop);
3✔
2002
        }
3✔
2003
        default: {
×
2004
            throw NotImplementedException(
×
2005
                "BinaryOperator: Unsupported output type",
×
2006
                ::docc::utils::get_debug_info(*instruction),
×
2007
                ::docc::utils::toIRString(*instruction)
×
2008
            );
×
2009
        }
3✔
2010
    }
6✔
2011
};
6✔
2012

2013
sdfg::control_flow::State& Lifting::visit_CastInst(
2014
    const llvm::BasicBlock* block, const llvm::CastInst* instruction, sdfg::control_flow::State& current_state
2015
) {
12✔
2016
    // Define Debug
2017
    auto dbg_info = ::docc::utils::get_debug_info(*instruction);
12✔
2018

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

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

2055
    if (llvm::isa<llvm::AddrSpaceCastInst>(instruction)) {
12✔
2056
        throw NotImplementedException(
×
2057
            "CastInst: AddrSpaceCastInst is not supported",
×
2058
            ::docc::utils::get_debug_info(*instruction),
×
2059
            ::docc::utils::toIRString(*instruction)
×
2060
        );
×
2061
    } else if (llvm::isa<llvm::BitCastInst>(instruction)) {
12✔
2062
        throw NotImplementedException(
×
2063
            "CastInst: BitCastInst is not supported",
×
2064
            ::docc::utils::get_debug_info(*instruction),
×
2065
            ::docc::utils::toIRString(*instruction)
×
2066
        );
×
2067
    } else if (llvm::isa<llvm::IntToPtrInst>(instruction)) {
12✔
2068
        throw NotImplementedException(
×
2069
            "CastInst: IntToPtrInst is not supported",
×
2070
            ::docc::utils::get_debug_info(*instruction),
×
2071
            ::docc::utils::toIRString(*instruction)
×
2072
        );
×
2073
    }
×
2074

2075
    // Handle ptr to int separately
2076
    if (llvm::isa<llvm::PtrToIntInst>(instruction)) {
12✔
2077
        assert(
1✔
2078
            input_type->type_id() == sdfg::types::TypeID::Pointer &&
1✔
2079
            "CastInst: Expected pointer type as input for PtrToIntInst"
1✔
2080
        );
1✔
2081
        assert(
1✔
2082
            output_type.type_id() == sdfg::types::TypeID::Scalar &&
1✔
2083
            "CastInst: Expected scalar type as output for PtrToIntInst"
1✔
2084
        );
1✔
2085

2086
        sdfg::symbolic::Symbol output_sym = sdfg::symbolic::symbol(output);
1✔
2087
        sdfg::symbolic::Symbol input_sym = SymEngine::null;
1✔
2088
        if (utils::is_literal(input_operand)) {
1✔
2089
            std::string arg = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
2090
            input_sym = sdfg::symbolic::symbol(arg);
×
2091
        } else {
1✔
2092
            std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
1✔
2093
            input_sym = sdfg::symbolic::symbol(input);
1✔
2094
        }
1✔
2095

2096
        auto& next_state = this->builder_.add_state_after(current_state, false, dbg_info);
1✔
2097
        this->builder_.add_edge(current_state, next_state, {{output_sym, input_sym}}, dbg_info);
1✔
2098
        return next_state;
1✔
2099
    }
1✔
2100

2101
    switch (output_type.type_id()) {
11✔
2102
        case sdfg::types::TypeID::Scalar: {
11✔
2103
            auto& scalar_type = static_cast<const sdfg::types::Scalar&>(output_type);
11✔
2104
            auto& output_node = this->builder_.add_access(current_state, output, dbg_info);
11✔
2105

2106
            sdfg::data_flow::AccessNode* input_node;
11✔
2107
            if (utils::is_literal(input_operand)) {
11✔
2108
                std::string arg = utils::as_literal(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
2109
                input_node = &this->builder_.add_constant(current_state, arg, *input_type, dbg_info);
×
2110
            } else {
11✔
2111
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
11✔
2112
                input_node = &this->builder_.add_access(current_state, input, dbg_info);
11✔
2113
            }
11✔
2114

2115
            // We implement the cast by chosing the memlet types appropriately
2116
            sdfg::types::PrimitiveType in_cast_type;
11✔
2117
            sdfg::types::PrimitiveType out_cast_type;
11✔
2118
            switch (instruction->getOpcode()) {
11✔
2119
                case llvm::Instruction::CastOps::Trunc: {
1✔
2120
                    auto* TI = llvm::cast<llvm::TruncInst>(instruction);
1✔
2121
                    // truncated bits are all zero
2122
                    bool nuw = TI->hasNoUnsignedWrap();
1✔
2123
                    // truncated bits are all sign bits
2124
                    bool nsw = TI->hasNoSignedWrap();
1✔
2125

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

2142
                    auto& tasklet =
1✔
2143
                        this->builder_
1✔
2144
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2145

2146
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2147
                    this->builder_.add_computational_memlet(
1✔
2148
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2149
                    );
1✔
2150

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

2173
                    auto& tasklet =
2✔
2174
                        this->builder_
2✔
2175
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
2✔
2176

2177
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
2✔
2178
                    this->builder_.add_computational_memlet(
2✔
2179
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
2✔
2180
                    );
2✔
2181

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

2194
                    if (input_type->primitive_type() == sdfg::types::PrimitiveType::Bool) {
2✔
2195
                        auto& tasklet = this->builder_.add_tasklet(
1✔
2196
                            current_state, sdfg::data_flow::TaskletCode::int_mul, "__out", {"_in1", "_in2"}, dbg_info
1✔
2197
                        );
1✔
2198

2199
                        auto& minus_one_node =
1✔
2200
                            this->builder_
1✔
2201
                                .add_constant(current_state, "-1", sdfg::types::Scalar(out_cast_type), dbg_info);
1✔
2202
                        this->builder_.add_computational_memlet(
1✔
2203
                            current_state,
1✔
2204
                            minus_one_node,
1✔
2205
                            tasklet,
1✔
2206
                            "_in2",
1✔
2207
                            {},
1✔
2208
                            sdfg::types::Scalar(out_cast_type),
1✔
2209
                            dbg_info
1✔
2210
                        );
1✔
2211

2212
                        auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2213
                        this->builder_.add_computational_memlet(
1✔
2214
                            current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2215
                        );
1✔
2216

2217
                        auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2218
                        this->builder_.add_computational_memlet(
1✔
2219
                            current_state, *input_node, tasklet, "_in1", {}, *input_cast_type, dbg_info
1✔
2220
                        );
1✔
2221
                    } else {
1✔
2222
                        auto& tasklet = this->builder_.add_tasklet(
1✔
2223
                            current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info
1✔
2224
                        );
1✔
2225

2226
                        auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2227
                        this->builder_.add_computational_memlet(
1✔
2228
                            current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2229
                        );
1✔
2230

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

2244
                    auto& tasklet =
1✔
2245
                        this->builder_
1✔
2246
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2247

2248
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2249
                    this->builder_.add_computational_memlet(
1✔
2250
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2251
                    );
1✔
2252

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

2265
                    auto& tasklet =
1✔
2266
                        this->builder_
1✔
2267
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2268

2269
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2270
                    this->builder_.add_computational_memlet(
1✔
2271
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2272
                    );
1✔
2273

2274
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2275
                    this->builder_.add_computational_memlet(
1✔
2276
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2277
                    );
1✔
2278
                    break;
1✔
2279
                }
2✔
2280
                case llvm::Instruction::CastOps::UIToFP: {
1✔
2281
                    auto* UIFP = llvm::cast<llvm::UIToFPInst>(instruction);
1✔
2282
                    assert(UIFP && "UIToFPInst expected");
1✔
2283

2284
                    if (UIFP->hasNonNeg()) {
1✔
2285
                        // Unsigned integer to floating point with non-neg
2286
                        // uint32 _in = 42
2287
                        in_cast_type = sdfg::types::as_signed(input_type->primitive_type());
×
2288
                        out_cast_type = output_type.primitive_type();
×
2289
                    } else {
1✔
2290
                        // Unsigned integer to floating point
2291
                        // uint32 _in = 42
2292
                        in_cast_type = sdfg::types::as_unsigned(input_type->primitive_type());
1✔
2293
                        out_cast_type = output_type.primitive_type();
1✔
2294
                    }
1✔
2295

2296
                    auto& tasklet =
1✔
2297
                        this->builder_
1✔
2298
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2299

2300
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2301
                    this->builder_.add_computational_memlet(
1✔
2302
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2303
                    );
1✔
2304

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

2317
                    auto& tasklet =
1✔
2318
                        this->builder_
1✔
2319
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2320

2321
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2322
                    this->builder_.add_computational_memlet(
1✔
2323
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2324
                    );
1✔
2325

2326
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2327
                    this->builder_.add_computational_memlet(
1✔
2328
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2329
                    );
1✔
2330
                    break;
1✔
2331
                }
1✔
2332
                case llvm::Instruction::CastOps::FPTrunc: {
1✔
2333
                    // Floating point truncation
2334
                    // float64 _in = 42.0
2335
                    in_cast_type = input_type->primitive_type();
1✔
2336
                    out_cast_type = output_type.primitive_type();
1✔
2337

2338
                    auto& tasklet =
1✔
2339
                        this->builder_
1✔
2340
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2341

2342
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2343
                    this->builder_.add_computational_memlet(
1✔
2344
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2345
                    );
1✔
2346

2347
                    auto input_cast_type = std::make_unique<sdfg::types::Scalar>(in_cast_type);
1✔
2348
                    this->builder_.add_computational_memlet(
1✔
2349
                        current_state, *input_node, tasklet, "_in", {}, *input_cast_type, dbg_info
1✔
2350
                    );
1✔
2351
                    break;
1✔
2352
                }
1✔
2353
                case llvm::Instruction::CastOps::FPExt: {
1✔
2354
                    // Floating point extension
2355
                    // float32 _in = 42.0
2356
                    in_cast_type = input_type->primitive_type();
1✔
2357
                    out_cast_type = output_type.primitive_type();
1✔
2358

2359
                    auto& tasklet =
1✔
2360
                        this->builder_
1✔
2361
                            .add_tasklet(current_state, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
1✔
2362

2363
                    auto output_cast_type = std::make_unique<sdfg::types::Scalar>(out_cast_type);
1✔
2364
                    this->builder_.add_computational_memlet(
1✔
2365
                        current_state, tasklet, "__out", output_node, {}, *output_cast_type, dbg_info
1✔
2366
                    );
1✔
2367

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

2390
            // Define loop
2391
            std::string iterator = this->builder_.find_new_name("_i");
×
2392
            this->builder_.add_container(iterator, sdfg::types::Scalar(sdfg::types::PrimitiveType::Int64));
×
2393
            sdfg::symbolic::Symbol iter_sym = sdfg::symbolic::symbol(iterator);
×
2394
            sdfg::symbolic::Expression init = sdfg::symbolic::zero();
×
2395
            sdfg::symbolic::Condition cond =
×
2396
                sdfg::symbolic::Lt(iter_sym, sdfg::symbolic::integer(struct_def.vector_size()));
×
2397
            sdfg::symbolic::Expression update = SymEngine::add(iter_sym, sdfg::symbolic::one());
×
2398

2399
            auto loop = this->builder_.add_loop(current_state, iter_sym, init, cond, update, dbg_info);
×
2400
            auto& body = std::get<1>(loop);
×
2401

2402
            // Define body
2403
            auto& output_node = this->builder_.add_access(body, output, dbg_info);
×
2404
            auto& tasklet =
×
2405
                this->builder_.add_tasklet(body, sdfg::data_flow::TaskletCode::assign, "__out", {"_in"}, dbg_info);
×
2406

2407
            sdfg::data_flow::AccessNode* input_node;
×
2408
            if (utils::is_literal(input_operand)) {
×
2409
                std::string arg = utils::as_initializer(llvm::dyn_cast<llvm::ConstantData>(input_operand));
×
2410
                input_node = &this->builder_.add_constant(body, arg, *input_type, dbg_info);
×
2411
            } else {
×
2412
                std::string input = utils::find_const_name_to_sdfg_name(this->constants_mapping_, input_operand);
×
2413
                input_node = &this->builder_.add_access(body, input, dbg_info);
×
2414
            }
×
2415

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

2493
            auto output_cast_type_scalar = std::make_unique<sdfg::types::Scalar>(out_cast_type);
×
2494
            auto output_cast_type =
×
2495
                this->builder_.create_vector_type(*output_cast_type_scalar, struct_def.vector_size());
×
2496
            this->builder_
×
2497
                .add_computational_memlet(body, tasklet, "__out", output_node, {iter_sym}, *output_cast_type, dbg_info);
×
2498

2499
            auto input_cast_type_scalar = std::make_unique<sdfg::types::Scalar>(in_cast_type);
×
2500
            auto input_cast_type = this->builder_.create_vector_type(*input_cast_type_scalar, struct_def.vector_size());
×
2501
            this->builder_
×
2502
                .add_computational_memlet(body, *input_node, tasklet, "_in", {iter_sym}, *input_cast_type, dbg_info);
×
2503

2504
            return std::get<2>(loop);
×
2505
        }
×
2506
        default: {
×
2507
            throw NotImplementedException(
×
2508
                "CastInst: Unsupported output type",
×
2509
                ::docc::utils::get_debug_info(*instruction),
×
2510
                ::docc::utils::toIRString(*instruction)
×
2511
            );
×
2512
        }
×
2513
    }
11✔
2514
};
11✔
2515

2516

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

© 2026 Coveralls, Inc