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

daisytuner / docc / 26520678771

27 May 2026 03:22PM UTC coverage: 60.864% (-0.02%) from 60.886%
26520678771

Pull #719

github

web-flow
Merge 99c5e4f9d into 707dadcf8
Pull Request #719: Libnode ptr edges

961 of 1749 new or added lines in 52 files covered. (54.95%)

90 existing lines in 29 files now uncovered.

35222 of 57870 relevant lines covered (60.86%)

11043.61 hits per line

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

63.1
/llvm/src/lifting/functions/function_lifting.cpp
1
#include "docc/lifting/functions/function_lifting.h"
2

3
#include "docc/lifting/functions/blas_lifting.h"
4
#include "docc/lifting/functions/intrinsic_lifting.h"
5
#include "docc/lifting/functions/libfunc_lifting.h"
6

7
#include <sdfg/data_flow/library_nodes/call_node.h>
8
#include <sdfg/data_flow/library_nodes/invoke_node.h>
9

10
namespace docc {
11
namespace lifting {
12

13
static const std::unordered_set<std::string> BLACKLISTED_FUNCTIONS = {};
14

15
bool FunctionLifting::is_supported(llvm::TargetLibraryInfo& TLI, const llvm::CallBase* instruction) {
×
16
    if (!llvm::isa<llvm::CallInst>(instruction) && !llvm::isa<llvm::InvokeInst>(instruction)) {
×
17
        LiftingReport::add_failed_lift(
×
18
            docc::utils::get_debug_info(*instruction),
×
19
            "Unsupported call base type",
×
20
            docc::utils::toIRString(*instruction)
×
21
        );
×
22
        LLVM_DEBUG_PRINTLN("Unsupported call base type: " << docc::utils::toIRString(*instruction));
×
23
        return false;
×
24
    }
×
25

26
    // Intrinsic functions
27
    if (auto intrinsic_inst = llvm::dyn_cast<const llvm::IntrinsicInst>(instruction)) {
×
28
        auto iid = intrinsic_inst->getIntrinsicID();
×
29
        if (IntrinsicLifting::is_supported(intrinsic_inst)) {
×
30
            return true;
×
31
        } else {
×
32
            LiftingReport::add_failed_lift(
×
33
                docc::utils::get_debug_info(*instruction),
×
34
                "Unsupported intrinsic: " + llvm::Intrinsic::getName(iid).str(),
×
35
                docc::utils::toIRString(*instruction)
×
36
            );
×
37
            LLVM_DEBUG_PRINTLN("Unsupported intrinsic: " << docc::utils::toIRString(*instruction));
×
38
            return false;
×
39
        }
×
40
    }
×
41

42
    // Operand must be supported
43
    auto operand = instruction->getCalledOperand();
×
44
    if (!operand) {
×
45
        LiftingReport::add_failed_lift(
×
46
            docc::utils::get_debug_info(*instruction),
×
47
            "Unsupported call with no called operand",
×
48
            docc::utils::toIRString(*instruction)
×
49
        );
×
50
        LLVM_DEBUG_PRINTLN("Unsupported call with no called operand: " << docc::utils::toIRString(*instruction));
×
51
        return false;
×
52
    }
×
53

54
    return true;
×
55
}
×
56

57
sdfg::control_flow::State& FunctionLifting::
58
    visit(const llvm::BasicBlock* block, const llvm::CallBase* instruction, sdfg::control_flow::State& current_state) {
29✔
59
    // Intrinsic functions
60
    if (llvm::isa<const llvm::IntrinsicInst>(instruction)) {
29✔
61
        IntrinsicLifting lifter(
17✔
62
            TLI_,
17✔
63
            DL_,
17✔
64
            function_,
17✔
65
            target_type_,
17✔
66
            builder_,
17✔
67
            state_mapping_,
17✔
68
            pred_mapping_,
17✔
69
            constants_mapping_,
17✔
70
            anonymous_types_mapping_
17✔
71
        );
17✔
72
        return lifter.visit(block, instruction, current_state);
17✔
73
    }
17✔
74

75
    // Match known library functions
76
    llvm::Function* called_func = instruction->getCalledFunction();
12✔
77
    if (called_func) {
12✔
78
        // LLVM Library functions
79
        llvm::LibFunc lf;
12✔
80
        if (TLI_.getLibFunc(called_func->getName(), lf) && LibFuncLifting::is_supported(lf)) {
12✔
81
            LibFuncLifting lifter(
4✔
82
                TLI_,
4✔
83
                DL_,
4✔
84
                function_,
4✔
85
                target_type_,
4✔
86
                builder_,
4✔
87
                state_mapping_,
4✔
88
                pred_mapping_,
4✔
89
                constants_mapping_,
4✔
90
                anonymous_types_mapping_
4✔
91
            );
4✔
92
            return lifter.visit(block, instruction, current_state);
4✔
93
        }
4✔
94

95
        // BLAS functions
96
        if (BLASLifting::is_supported(*called_func)) {
8✔
97
            BLASLifting lifter(
4✔
98
                TLI_,
4✔
99
                DL_,
4✔
100
                function_,
4✔
101
                target_type_,
4✔
102
                builder_,
4✔
103
                state_mapping_,
4✔
104
                pred_mapping_,
4✔
105
                constants_mapping_,
4✔
106
                anonymous_types_mapping_
4✔
107
            );
4✔
108
            return lifter.visit(block, instruction, current_state);
4✔
109
        }
4✔
110
    }
8✔
111

112
    if (auto call_inst = llvm::dyn_cast<const llvm::CallInst>(instruction)) {
4✔
113
        return this->visit_call(block, call_inst, current_state);
3✔
114
    } else if (auto invoke_inst = llvm::dyn_cast<const llvm::InvokeInst>(instruction)) {
3✔
115
        return this->visit_invoke(block, invoke_inst, current_state);
1✔
116
    } else {
1✔
117
        throw NotImplementedException(
×
118
            "Unsupported CallBase type in FunctionLifting::visit",
×
119
            docc::utils::get_debug_info(*instruction),
×
120
            docc::utils::toIRString(*instruction)
×
121
        );
×
122
    }
×
123
}
4✔
124

125
sdfg::control_flow::State& FunctionLifting::visit_call(
126
    const llvm::BasicBlock* block, const llvm::CallInst* instruction, sdfg::control_flow::State& current_state
127
) {
3✔
128
    // Define Debug
129
    auto dbg_info = docc::utils::get_debug_info(*instruction);
3✔
130

131
    // Define function name
132
    std::string callee_name;
3✔
133
    llvm::Function* called_func = instruction->getCalledFunction();
3✔
134
    if (called_func) {
3✔
135
        callee_name = called_func->getName().str();
3✔
136
    } else if (instruction->getCalledOperand()) {
3✔
137
        callee_name = utils::get_name(instruction->getCalledOperand());
×
138
    } else {
×
139
        throw NotImplementedException(
×
140
            "Unsupported call instruction with no function or operand",
×
141
            docc::utils::get_debug_info(*instruction),
×
142
            docc::utils::toIRString(*instruction)
×
143
        );
×
144
    }
×
145
    assert(!callee_name.empty() && "Function name is empty");
3✔
146

147
    // Define return type
148
    std::vector<std::string> outputs;
3✔
149
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> out_conns;
3✔
150
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> unique_nodes_out;
3✔
151
    std::unordered_map<std::string, std::unique_ptr<sdfg::types::IType>> types;
3✔
152
    if (!instruction->getType()->isVoidTy()) {
3✔
153
        std::string output = utils::get_name(instruction);
2✔
154
        if (!this->builder_.subject().exists(output)) {
2✔
155
            auto output_type = utils::get_type(
2✔
156
                this->builder_,
2✔
157
                this->anonymous_types_mapping_,
2✔
158
                this->DL_,
2✔
159
                instruction->getType(),
2✔
160
                utils::get_storage_type(this->target_type_, 0)
2✔
161
            );
2✔
162
            this->builder_.add_container(output, *output_type);
2✔
163
        }
2✔
164
        auto output_node = &this->builder_.add_access(current_state, output, dbg_info);
2✔
165

166
        outputs.push_back("_ret");
2✔
167
        out_conns.insert({"_ret", output_node});
2✔
168
        unique_nodes_out.insert({output, output_node});
2✔
169
        types.insert({"_ret", this->builder_.subject().type(output).clone()});
2✔
170
    }
2✔
171

172
    // Define arguments
173
    std::vector<std::string> inputs;
3✔
174
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> in_conns;
3✔
175
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> unique_nodes_in;
3✔
176
    std::vector<sdfg::data_flow::PointerAccessType> ptr_meta;
3✔
177
    size_t i = 0;
3✔
178
    for (auto& op : instruction->args()) {
3✔
179
        std::string conn = "_arg" + std::to_string(i);
3✔
180

181
        auto op_type = utils::get_type(
3✔
182
            this->builder_,
3✔
183
            this->anonymous_types_mapping_,
3✔
184
            this->DL_,
3✔
185
            op->getType(),
3✔
186
            utils::get_storage_type(this->target_type_, 0)
3✔
187
        );
3✔
188
        if (utils::is_literal(op)) {
3✔
189
            std::string value = utils::as_initializer(llvm::dyn_cast<const llvm::ConstantData>(op));
×
190
            auto& const_node = this->builder_.add_constant(current_state, value, *op_type, dbg_info);
×
191

192
            inputs.push_back(conn);
×
193
            in_conns.insert({conn, &const_node});
×
194
            types.insert({conn, std::move(op_type)});
×
195
        } else {
3✔
196
            std::string data = utils::find_const_name_to_sdfg_name(this->constants_mapping_, op);
3✔
197

198
            // Add type
199
            types.insert({conn, op_type->clone()});
3✔
200

201
            // Add to inputs
202
            if (unique_nodes_in.find(data) == unique_nodes_in.end()) {
3✔
203
                auto& data_node_in = this->builder_.add_access(current_state, data, dbg_info);
3✔
204
                unique_nodes_in.insert({data, &data_node_in});
3✔
205
            }
3✔
206
            inputs.push_back(conn);
3✔
207
            in_conns.insert({conn, unique_nodes_in[data]});
3✔
208

209
            sdfg::data_flow::PointerAccessType meta;
3✔
210
            if (op_type->type_id() == sdfg::types::TypeID::Pointer) {
3✔
211
                if (called_func && i < called_func->arg_size()) {
2✔
212
                    auto* arg = called_func->getArg(i);
2✔
213
                    bool onlyReadsMemory = arg->onlyReadsMemory();
2✔
214
                    bool noCapture = arg->hasNoCaptureAttr();
2✔
215

216
                    if (onlyReadsMemory) {
2✔
NEW
217
                        meta = sdfg::data_flow::PointerAccessMeta::create_read_only(SymEngine::null, noCapture);
×
218
                    } else if (noCapture) {
2✔
NEW
219
                        meta = sdfg::data_flow::PointerAccessMeta::create_generic(nullptr, nullptr, noCapture);
×
UNCOV
220
                    }
×
221
                }
2✔
222
            }
2✔
223
            if (meta) {
3✔
NEW
224
                ptr_meta.push_back(std::move(meta));
×
UNCOV
225
            }
×
226
        }
3✔
227

228
        i++;
3✔
229
    }
3✔
230

231
    // Define library node
232
    auto& lib_node =
3✔
233
        this->builder_
3✔
234
            .add_library_node<sdfg::data_flow::CallNode>(current_state, dbg_info, callee_name, outputs, inputs);
3✔
235

236
    // Define in connectors
237
    for (auto& [in_conn, in_node] : in_conns) {
3✔
238
        auto& conn_type = types.at(in_conn);
3✔
239
        this->builder_.add_computational_memlet(current_state, *in_node, lib_node, in_conn, {}, *conn_type, dbg_info);
3✔
240
    }
3✔
241

242
    // Define out connectors
243
    for (auto& [out_conn, out_node] : out_conns) {
3✔
244
        auto& conn_type = types.at(out_conn);
2✔
245
        this->builder_.add_computational_memlet(current_state, lib_node, out_conn, *out_node, {}, *conn_type, dbg_info);
2✔
246
    }
2✔
247

248
    return current_state;
3✔
249
}
3✔
250

251
sdfg::control_flow::State& FunctionLifting::visit_invoke(
252
    const llvm::BasicBlock* block, const llvm::InvokeInst* instruction, sdfg::control_flow::State& current_state
253
) {
1✔
254
    // Define Debug
255
    auto dbg_info = docc::utils::get_debug_info(*instruction);
1✔
256

257
    // Define function name
258
    std::string callee_name;
1✔
259
    llvm::Function* called_func = instruction->getCalledFunction();
1✔
260
    if (called_func) {
1✔
261
        callee_name = called_func->getName().str();
1✔
262
    } else if (instruction->getCalledOperand()) {
1✔
263
        callee_name = utils::get_name(instruction->getCalledOperand());
×
264
    } else {
×
265
        throw NotImplementedException(
×
266
            "Unsupported call instruction with no function or operand",
×
267
            docc::utils::get_debug_info(*instruction),
×
268
            docc::utils::toIRString(*instruction)
×
269
        );
×
270
    }
×
271
    assert(!callee_name.empty() && "Function name is empty");
1✔
272

273
    // Define return type
274
    std::vector<std::string> outputs;
1✔
275
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> out_conns;
1✔
276
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> unique_nodes_out;
1✔
277
    std::unordered_map<std::string, std::unique_ptr<sdfg::types::IType>> types;
1✔
278
    if (!instruction->getType()->isVoidTy()) {
1✔
279
        std::string output = utils::get_name(instruction);
1✔
280
        if (!this->builder_.subject().exists(output)) {
1✔
281
            auto output_type = utils::get_type(
1✔
282
                this->builder_,
1✔
283
                this->anonymous_types_mapping_,
1✔
284
                this->DL_,
1✔
285
                instruction->getType(),
1✔
286
                utils::get_storage_type(this->target_type_, 0)
1✔
287
            );
1✔
288
            this->builder_.add_container(output, *output_type);
1✔
289
        }
1✔
290
        auto output_node = &this->builder_.add_access(current_state, output, dbg_info);
1✔
291

292
        outputs.push_back("_ret");
1✔
293
        out_conns.insert({"_ret", output_node});
1✔
294
        unique_nodes_out.insert({output, output_node});
1✔
295
        types.insert({"_ret", this->builder_.subject().type(output).clone()});
1✔
296
    }
1✔
297

298
    // Define arguments
299
    std::vector<std::string> inputs;
1✔
300
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> in_conns;
1✔
301
    std::unordered_map<std::string, sdfg::data_flow::AccessNode*> unique_nodes_in;
1✔
302
    size_t i = 0;
1✔
303
    for (auto& op : instruction->args()) {
1✔
304
        std::string conn = "_arg" + std::to_string(i);
×
305

306
        auto op_type = utils::get_type(
×
307
            this->builder_,
×
308
            this->anonymous_types_mapping_,
×
309
            this->DL_,
×
310
            op->getType(),
×
311
            utils::get_storage_type(this->target_type_, 0)
×
312
        );
×
313
        if (utils::is_literal(op)) {
×
314
            std::string value = utils::as_initializer(llvm::dyn_cast<const llvm::ConstantData>(op));
×
315
            auto& const_node = this->builder_.add_constant(current_state, value, *op_type, dbg_info);
×
316

317
            inputs.push_back(conn);
×
318
            in_conns.insert({conn, &const_node});
×
319
            types.insert({conn, std::move(op_type)});
×
320
        } else {
×
321
            std::string data = utils::find_const_name_to_sdfg_name(this->constants_mapping_, op);
×
322

323
            // Add type
324
            types.insert({conn, std::move(op_type)});
×
325

326
            if (unique_nodes_in.find(data) == unique_nodes_in.end()) {
×
327
                auto& data_node_in = this->builder_.add_access(current_state, data, dbg_info);
×
328
                unique_nodes_in.insert({data, &data_node_in});
×
329

330
                if (called_func && !called_func->isVarArg() && called_func->getArg(i)->onlyReadsMemory()) {
×
331
                    // No output node needed
332
                } else {
×
333
                    auto& data_node_out = this->builder_.add_access(current_state, data, dbg_info);
×
334
                    unique_nodes_out.insert({data, &data_node_out});
×
335
                }
×
336
            }
×
337

338
            inputs.push_back(conn);
×
339
            in_conns.insert({conn, unique_nodes_in[data]});
×
340

341
            if (called_func && !called_func->isVarArg() && called_func->getArg(i)->onlyReadsMemory()) {
×
342
                // No output connector needed
343
            } else {
×
344
                outputs.push_back(conn);
×
345
                out_conns.insert({conn, unique_nodes_out[data]});
×
346
            }
×
347
        }
×
348

349
        i++;
×
350
    }
×
351

352
    // Define library node
353
    auto& lib_node =
1✔
354
        this->builder_
1✔
355
            .add_library_node<sdfg::data_flow::InvokeNode>(current_state, dbg_info, callee_name, outputs, inputs);
1✔
356

357
    // Define in connectors
358
    for (auto& [in_conn, in_node] : in_conns) {
1✔
359
        auto& conn_type = types.at(in_conn);
×
360
        this->builder_.add_computational_memlet(current_state, *in_node, lib_node, in_conn, {}, *conn_type, dbg_info);
×
361
    }
×
362

363
    // Define out connectors
364
    for (auto& [out_conn, out_node] : out_conns) {
1✔
365
        auto& conn_type = types.at(out_conn);
1✔
366
        this->builder_.add_computational_memlet(current_state, lib_node, out_conn, *out_node, {}, *conn_type, dbg_info);
1✔
367
    }
1✔
368

369
    // Add unwind handling
370
    std::string unwind_container = "__unwind_" + utils::get_name(instruction);
1✔
371
    sdfg::types::Scalar unwind_type(sdfg::types::PrimitiveType::Bool);
1✔
372
    auto& unwind_node = this->builder_.add_access(current_state, unwind_container, dbg_info);
1✔
373
    this->builder_.add_computational_memlet(current_state, lib_node, "_unwind", unwind_node, {}, unwind_type, dbg_info);
1✔
374

375
    return current_state;
1✔
376
}
1✔
377

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

© 2026 Coveralls, Inc