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

daisytuner / docc / 26556322966

27 May 2026 03:45PM UTC coverage: 60.869% (-0.02%) from 60.886%
26556322966

push

github

web-flow
Libnode ptr edges (#719)

Migrating SDFGs to treat pointers as inputs to libNodes / Calls as scalars.
A pointer will only appear in an output edge if its actually returned from the function (like malloc).

* Stdlib, Blas and Tensor Matmul nodes were migrated to this new format. Other, currently transitory Tensor Nodes are not yet migrated.
* DOCC version was bumped to incorporate previous docc-llvm versions (up to 0.4.0) that had been counted separately.
! Until all passes consider the use / leak of pointers as uncertainty / hiding potential writes, TensorNodes are declared as general side-effect.
* Lots of utility functions to centralize the creation (and edges) of various libNodes that needed to be changed.
* Fixed & unified docc paths across python and llvm front-ends.
* Skip BlockFusion test that fails to its libNodes currently having side effects
~ Prevent a crash in DotViz when using symbolic offsets into structs
* Removing old ConstProp pass, it is not safe for the new pointer representation and should not be all too critical

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

87 existing lines in 28 files now uncovered.

35225 of 57870 relevant lines covered (60.87%)

11046.32 hits per line

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

40.82
/sdfg/src/data_flow/library_nodes/call_node.cpp
1
#include "sdfg/data_flow/library_nodes/call_node.h"
2

3
namespace sdfg {
4
namespace data_flow {
5

6
CallNode::CallNode(
7
    size_t element_id,
8
    const DebugInfo& debug_info,
9
    const graph::Vertex vertex,
10
    data_flow::DataFlowGraph& parent,
11
    const std::string& callee_name,
12
    const std::vector<std::string>& outputs,
13
    const std::vector<std::string>& inputs,
14
    std::vector<PointerAccessType> ptr_access_meta
15
)
16
    : LibraryNode(
11✔
17
          element_id,
11✔
18
          debug_info,
11✔
19
          vertex,
11✔
20
          parent,
11✔
21
          LibraryNodeType_Call,
11✔
22
          outputs,
11✔
23
          inputs,
11✔
24
          true,
11✔
25
          data_flow::ImplementationType_NONE
11✔
26
      ),
11✔
27
      callee_name_(callee_name), ptr_access_meta_(std::move(ptr_access_meta)) {}
11✔
28

29
const std::string& CallNode::callee_name() const { return this->callee_name_; }
3✔
30

31
bool CallNode::is_void(const Function& sdfg) const { return outputs_.empty() || outputs_.at(0) != "_ret"; }
16✔
32

33
bool CallNode::is_indirect_call(const Function& sdfg) const {
×
34
    auto& type = sdfg.type(this->callee_name_);
×
35
    return dynamic_cast<const types::Pointer*>(&type) != nullptr;
×
36
}
×
37

38
void CallNode::validate(const Function& function) const {
16✔
39
    LibraryNode::validate(function);
16✔
40

41
    if (!function.exists(this->callee_name_)) {
16✔
42
        throw InvalidSDFGException("CallNode: Function '" + this->callee_name_ + "' does not exist.");
×
43
    }
×
44
    auto& type = function.type(this->callee_name_);
16✔
45
    if (!dynamic_cast<const types::Function*>(&type) && !dynamic_cast<const types::Pointer*>(&type)) {
16✔
46
        throw InvalidSDFGException("CallNode: '" + this->callee_name_ + "' is not a function or pointer.");
×
47
    }
×
48

49
    if (auto func_type = dynamic_cast<const types::Function*>(&type)) {
16✔
50
        if (!function.is_external(this->callee_name_)) {
16✔
51
            throw InvalidSDFGException("CallNode: Function '" + this->callee_name_ + "' must be declared.");
×
52
        }
×
53
        if (!func_type->is_var_arg() && inputs_.size() != func_type->num_params()) {
16✔
54
            throw InvalidSDFGException(
×
55
                "CallNode: Number of inputs does not match number of function parameters. Expected " +
×
56
                std::to_string(func_type->num_params()) + ", got " + std::to_string(inputs_.size())
×
57
            );
×
58
        }
×
59
        if (!this->is_void(function) && outputs_.size() < 1) {
16✔
60
            throw InvalidSDFGException(
×
61
                "CallNode: Non-void function must have at least one output to store the return value."
×
62
            );
×
63
        }
×
64
    }
16✔
65
}
16✔
66

67
symbolic::SymbolSet CallNode::symbols() const { return {symbolic::symbol(this->callee_name_)}; }
14✔
68

69
std::unique_ptr<data_flow::DataFlowNode> CallNode::
70
    clone(size_t element_id, const graph::Vertex vertex, data_flow::DataFlowGraph& parent) const {
×
NEW
71
    std::vector<PointerAccessType> ptr_access_meta_clone;
×
NEW
72
    ptr_access_meta_clone.reserve(ptr_access_meta_.size());
×
NEW
73
    for (auto& ptr_access_meta : ptr_access_meta_) {
×
NEW
74
        if (ptr_access_meta) {
×
NEW
75
            ptr_access_meta_clone.push_back(ptr_access_meta->clone());
×
NEW
76
        } else {
×
NEW
77
            ptr_access_meta_clone.push_back(nullptr);
×
NEW
78
        }
×
NEW
79
    }
×
NEW
80
    return std::make_unique<CallNode>(
×
NEW
81
        element_id, debug_info_, vertex, parent, callee_name_, outputs_, inputs_, std::move(ptr_access_meta_clone)
×
NEW
82
    );
×
UNCOV
83
}
×
84

85
PointerAccessType CallNode::pointer_access_type(int input_idx) const {
3✔
86
    if (ptr_access_meta_.size() > input_idx) {
3✔
NEW
87
        return ptr_access_meta_.at(0)->ref();
×
88
    } else {
3✔
89
        return LibraryNode::pointer_access_type(input_idx);
3✔
90
    }
3✔
91
}
3✔
92

93
const std::vector<PointerAccessType>& CallNode::pointer_access_meta() const { return ptr_access_meta_; }
3✔
94

UNCOV
95
std::string CallNode::toStr() const { return LibraryNode::toStr() + "('" + callee_name_ + "')"; }
×
96

NEW
97
void CallNode::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
NEW
98
    for (auto& meta : ptr_access_meta_) {
×
NEW
99
        if (meta) {
×
NEW
100
            meta->replace(old_expression, new_expression);
×
NEW
101
        }
×
NEW
102
    }
×
NEW
103
}
×
104

105
nlohmann::json CallNodeSerializer::serialize(const data_flow::LibraryNode& library_node) {
3✔
106
    const CallNode& node = static_cast<const CallNode&>(library_node);
3✔
107

108
    nlohmann::json j;
3✔
109
    j["code"] = node.code().value();
3✔
110
    j["callee_name"] = node.callee_name();
3✔
111
    j["outputs"] = node.outputs();
3✔
112
    j["inputs"] = node.inputs();
3✔
113
    j["ptr_access_meta"] = PointerAccessMetaSerializer::serialize(node.pointer_access_meta());
3✔
114

115
    return j;
3✔
116
}
3✔
117

118
data_flow::LibraryNode& CallNodeSerializer::deserialize(
119
    const nlohmann::json& j, builder::StructuredSDFGBuilder& builder, structured_control_flow::Block& parent
120
) {
3✔
121
    assert(j.contains("code"));
3✔
122
    assert(j.contains("callee_name"));
3✔
123
    assert(j.contains("outputs"));
3✔
124
    assert(j.contains("inputs"));
3✔
125
    assert(j.contains("debug_info"));
3✔
126

127
    auto code = j["code"].get<std::string>();
3✔
128
    if (code != LibraryNodeType_Call.value()) {
3✔
129
        throw InvalidSDFGException("Invalid library node code");
×
130
    }
×
131

132
    sdfg::serializer::JSONSerializer serializer;
3✔
133
    DebugInfo debug_info = serializer.json_to_debug_info(j["debug_info"]);
3✔
134

135
    std::string callee_name = j["callee_name"].get<std::string>();
3✔
136
    auto outputs = j["outputs"].get<std::vector<std::string>>();
3✔
137
    auto inputs = j["inputs"].get<std::vector<std::string>>();
3✔
138
    auto ptr_access_meta = PointerAccessMetaSerializer::deserialize_list(j.find("ptr_access_meta"), j);
3✔
139

140
    return builder
3✔
141
        .add_library_node<CallNode>(parent, debug_info, callee_name, outputs, inputs, std::move(ptr_access_meta));
3✔
142
}
3✔
143

144
CallNodeDispatcher::CallNodeDispatcher(
145
    codegen::LanguageExtension& language_extension,
146
    const Function& function,
147
    const data_flow::DataFlowGraph& data_flow_graph,
148
    const CallNode& node
149
)
150
    : codegen::LibraryNodeDispatcher(language_extension, function, data_flow_graph, node) {}
×
151

152
void CallNodeDispatcher::dispatch_code_with_edges(
153
    codegen::CodegenOutput& out,
154
    std::vector<codegen::DispatchInput>& inputs,
155
    std::vector<codegen::DispatchOutput>& outputs
156
) {
×
157
    auto& node = static_cast<const CallNode&>(node_);
×
158

NEW
159
    codegen::DispatchOutput* output = nullptr;
×
160
    if (!node.is_void(function_)) {
×
NEW
161
        output = &outputs.at(0);
×
NEW
162
        pre_allocate_output(out, *output, node.output(0));
×
NEW
163
        out.stream << *output->local_name << " = ";
×
164
    }
×
165
    if (node.is_indirect_call(function_)) {
×
166
        // Cast callee to function pointer type
UNCOV
167
        std::string func_ptr_type;
×
168

169
        // Return type
NEW
170
        if (output) {
×
NEW
171
            func_ptr_type = language_extension_.declaration("", *output->out_type) + " (*)";
×
172
        } else {
×
173
            func_ptr_type = "void (*)";
×
174
        }
×
175

176
        // Parameters
177
        func_ptr_type += "(";
×
NEW
178
        for (size_t i = 0; i < inputs.size(); i++) {
×
NEW
179
            auto& input = inputs.at(i);
×
180

NEW
181
            auto in_type = input.edge.result_type(function_);
×
182
            func_ptr_type += language_extension_.declaration("", *in_type);
×
183
            if (i < node.inputs().size() - 1) {
×
184
                func_ptr_type += ", ";
×
185
            }
×
186
        }
×
187
        func_ptr_type += ")";
×
188

189
        if (this->language_extension_.language() == "C") {
×
NEW
190
            out.stream << "((" << func_ptr_type << ") " << node.callee_name() << ")" << "(";
×
191
        } else if (this->language_extension_.language() == "C++") {
×
NEW
192
            out.stream << "reinterpret_cast<" << func_ptr_type << ">(" << node.callee_name() << ")" << "(";
×
193
        }
×
194
    } else {
×
NEW
195
        out.stream << this->language_extension_.external_prefix() << node.callee_name() << "(";
×
196
    }
×
NEW
197
    for (size_t i = 0; i < inputs.size(); ++i) {
×
NEW
198
        out.stream << inputs.at(i).expr;
×
199
        if (i < node.inputs().size() - 1) {
×
NEW
200
            out.stream << ", ";
×
201
        }
×
202
    }
×
NEW
203
    out.stream << ")" << ";";
×
NEW
204
    out.stream << std::endl;
×
UNCOV
205
}
×
206

207
} // namespace data_flow
208
} // namespace sdfg
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