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

daisytuner / sdfglib / 20764569418

06 Jan 2026 10:50PM UTC coverage: 62.168% (+21.4%) from 40.764%
20764569418

push

github

web-flow
Merge pull request #433 from daisytuner/clang-coverage

updates clang coverage flags

14988 of 24109 relevant lines covered (62.17%)

88.57 hits per line

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

92.22
/src/passes/dataflow/tasklet_fusion.cpp
1
#include "sdfg/passes/dataflow/tasklet_fusion.h"
2

3
#include <unordered_set>
4

5
#include "sdfg/analysis/analysis.h"
6
#include "sdfg/analysis/users.h"
7
#include "sdfg/builder/structured_sdfg_builder.h"
8
#include "sdfg/data_flow/access_node.h"
9
#include "sdfg/data_flow/data_flow_graph.h"
10
#include "sdfg/data_flow/memlet.h"
11
#include "sdfg/data_flow/tasklet.h"
12
#include "sdfg/element.h"
13
#include "sdfg/structured_control_flow/block.h"
14
#include "sdfg/structured_control_flow/control_flow_node.h"
15
#include "sdfg/structured_control_flow/if_else.h"
16
#include "sdfg/structured_control_flow/sequence.h"
17
#include "sdfg/structured_control_flow/structured_loop.h"
18
#include "sdfg/types/type.h"
19
#include "sdfg/visitor/structured_sdfg_visitor.h"
20

21
namespace sdfg {
22
namespace passes {
23

24
bool TaskletFusion::container_allowed_accesses(const std::string& container, const Element* allowed_read_and_writes) {
7✔
25
    for (auto* user : this->users_analysis_.uses(container)) {
13✔
26
        if ((user->use() == analysis::Use::READ || user->use() == analysis::Use::WRITE) &&
13✔
27
            user->element() != allowed_read_and_writes) {
13✔
28
            return false;
2✔
29
        }
2✔
30
    }
13✔
31
    return true;
5✔
32
}
7✔
33

34
TaskletFusion::TaskletFusion(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager)
35
    : visitor::NonStoppingStructuredSDFGVisitor(builder, analysis_manager),
8✔
36
      users_analysis_(analysis_manager.get<analysis::Users>()) {};
8✔
37

38
bool TaskletFusion::accept(structured_control_flow::Block& block) {
10✔
39
    bool applied = false;
10✔
40
    auto& dfg = block.dataflow();
10✔
41

42
    // Fuse "input" assignments:
43
    for (auto* access_node : dfg.data_nodes()) {
36✔
44
        // Require exactly one in edge and at leat one out edge
45
        if (dfg.in_degree(*access_node) != 1 || dfg.out_degree(*access_node) == 0) {
36✔
46
            continue;
27✔
47
        }
27✔
48

49
        // The in edge must provide a scalar
50
        auto& iedge = *dfg.in_edges(*access_node).begin();
9✔
51
        if (iedge.base_type().type_id() != types::TypeID::Scalar) {
9✔
52
            continue;
×
53
        }
×
54
        types::PrimitiveType iedge_base_type = iedge.base_type().primitive_type();
9✔
55

56
        // The source of the in edge must be a tasklet with an assignment
57
        auto* assign_tasklet = dynamic_cast<data_flow::Tasklet*>(&iedge.src());
9✔
58
        if (!assign_tasklet || assign_tasklet->code() != data_flow::TaskletCode::assign ||
9✔
59
            dfg.in_degree(*assign_tasklet) != 1) {
9✔
60
            continue;
3✔
61
        }
3✔
62
        auto& assign_tasklet_iedge = *dfg.in_edges(*assign_tasklet).begin();
6✔
63

64
        // All out edges must provide scalars and their destination must be a tasklet
65
        std::unordered_set<data_flow::Memlet*> oedges;
6✔
66
        for (auto& oedge : dfg.out_edges(*access_node)) {
6✔
67
            if (oedge.base_type().type_id() == types::TypeID::Scalar &&
6✔
68
                oedge.base_type().primitive_type() == iedge_base_type &&
6✔
69
                dynamic_cast<data_flow::Tasklet*>(&oedge.dst())) {
6✔
70
                oedges.insert(&oedge);
6✔
71
            }
6✔
72
        }
6✔
73
        if (oedges.size() != dfg.out_degree(*access_node)) {
6✔
74
            continue;
×
75
        }
×
76

77
        // The result tye of the in edge of the assignment tasklet must match with the base types
78
        auto& result_type = assign_tasklet_iedge.result_type(this->builder_.subject());
6✔
79
        if (result_type.type_id() != types::TypeID::Scalar || result_type.primitive_type() != iedge_base_type) {
6✔
80
            continue;
1✔
81
        }
1✔
82

83
        // Container is only read and written in this access node (SSA)
84
        if (!this->container_allowed_accesses(access_node->data(), access_node)) {
5✔
85
            continue;
1✔
86
        }
1✔
87

88
        // Replace each out edge with an edge from the input access node from the assignment tasklet to the out edges's
89
        // tasklet
90
        for (auto* oedge : oedges) {
4✔
91
            auto& tasklet = dynamic_cast<data_flow::Tasklet&>(oedge->dst());
4✔
92
            auto& new_access_node = dynamic_cast<data_flow::AccessNode&>(assign_tasklet_iedge.src());
4✔
93

94
            // Prevent multiple access nodes with same container having in edges to the out edges's tasklet
95
            std::unordered_set<data_flow::Memlet*> other_iedges;
4✔
96
            for (auto& other_iedge : dfg.in_edges(tasklet)) {
8✔
97
                other_iedges.insert(&other_iedge);
8✔
98
            }
8✔
99
            for (auto* other_iedge : other_iedges) {
8✔
100
                if (dynamic_cast<data_flow::ConstantNode*>(&other_iedge->src())) {
8✔
101
                    continue;
×
102
                } else if (auto* other_access_node = dynamic_cast<data_flow::AccessNode*>(&other_iedge->src())) {
8✔
103
                    if (other_access_node == access_node || other_access_node->data() != new_access_node.data()) {
8✔
104
                        continue;
6✔
105
                    }
6✔
106
                    this->builder_.add_memlet(
2✔
107
                        block,
2✔
108
                        new_access_node,
2✔
109
                        other_iedge->src_conn(),
2✔
110
                        tasklet,
2✔
111
                        other_iedge->dst_conn(),
2✔
112
                        other_iedge->subset(),
2✔
113
                        other_iedge->base_type(),
2✔
114
                        other_iedge->debug_info()
2✔
115
                    );
2✔
116
                    this->builder_.remove_memlet(block, *other_iedge);
2✔
117
                    new_access_node
2✔
118
                        .set_debug_info(DebugInfo::merge(new_access_node.debug_info(), other_access_node->debug_info())
2✔
119
                        );
2✔
120
                    if (dfg.in_degree(*other_access_node) == 0 && dfg.out_degree(*other_access_node) == 0) {
2✔
121
                        this->builder_.remove_node(block, *other_access_node);
1✔
122
                    }
1✔
123
                }
2✔
124
            }
8✔
125

126
            DebugInfo debug_info = DebugInfo::merge(
4✔
127
                assign_tasklet_iedge.debug_info(),
4✔
128
                DebugInfo::merge(
4✔
129
                    assign_tasklet->debug_info(),
4✔
130
                    DebugInfo::merge(iedge.debug_info(), DebugInfo::merge(access_node->debug_info(), oedge->debug_info()))
4✔
131
                )
4✔
132
            );
4✔
133
            this->builder_.add_memlet(
4✔
134
                block,
4✔
135
                new_access_node,
4✔
136
                assign_tasklet_iedge.src_conn(),
4✔
137
                tasklet,
4✔
138
                oedge->dst_conn(),
4✔
139
                assign_tasklet_iedge.subset(),
4✔
140
                assign_tasklet_iedge.base_type(),
4✔
141
                debug_info
4✔
142
            );
4✔
143
            this->builder_.remove_memlet(block, *oedge);
4✔
144
        }
4✔
145

146
        // Remove the obsolete memlets
147
        this->builder_.remove_memlet(block, assign_tasklet_iedge);
4✔
148
        this->builder_.remove_memlet(block, iedge);
4✔
149

150
        // Remove the obsolete nodes
151
        this->builder_.remove_node(block, *assign_tasklet);
4✔
152
        this->builder_.remove_node(block, *access_node);
4✔
153

154
        applied = true;
4✔
155
    }
4✔
156

157
    // Fuse "output" assignments:
158
    for (auto* access_node : dfg.data_nodes()) {
31✔
159
        // Require exactly one in and one out edge
160
        if (dfg.in_degree(*access_node) != 1 || dfg.out_degree(*access_node) != 1) {
31✔
161
            continue;
26✔
162
        }
26✔
163

164
        // The in edge must provide a scalar
165
        auto& iedge = *dfg.in_edges(*access_node).begin();
5✔
166
        if (iedge.base_type().type_id() != types::TypeID::Scalar) {
5✔
167
            continue;
×
168
        }
×
169

170
        // The out edge must provide a scalar
171
        auto& oedge = *dfg.out_edges(*access_node).begin();
5✔
172
        if (oedge.base_type().type_id() != types::TypeID::Scalar) {
5✔
173
            continue;
×
174
        }
×
175

176
        // The source of the in edge must be a tasklet
177
        auto* tasklet = dynamic_cast<data_flow::Tasklet*>(&iedge.src());
5✔
178
        if (!tasklet) {
5✔
179
            continue;
×
180
        }
×
181

182
        // The destination of the out edge must be a tasklet with an assignment
183
        auto* assign_tasklet = dynamic_cast<data_flow::Tasklet*>(&oedge.dst());
5✔
184
        if (!assign_tasklet || assign_tasklet->code() != data_flow::TaskletCode::assign ||
5✔
185
            dfg.out_degree(*assign_tasklet) != 1) {
5✔
186
            continue;
2✔
187
        }
2✔
188
        auto& assign_tasklet_oedge = *dfg.out_edges(*assign_tasklet).begin();
3✔
189

190
        // Types must match
191
        if (iedge.base_type().primitive_type() != oedge.base_type().primitive_type()) {
3✔
192
            continue;
×
193
        }
×
194
        auto& result_type = assign_tasklet_oedge.result_type(this->builder_.subject());
3✔
195
        if (result_type.type_id() != types::TypeID::Scalar ||
3✔
196
            result_type.primitive_type() != iedge.base_type().primitive_type()) {
3✔
197
            continue;
1✔
198
        }
1✔
199

200
        // Container is only read and written in this access node (SSA)
201
        if (!this->container_allowed_accesses(access_node->data(), access_node)) {
2✔
202
            continue;
1✔
203
        }
1✔
204

205
        // Replace the in edge with an edge from the in edge's tasklet to the output access node
206
        DebugInfo debug_info = DebugInfo::merge(
1✔
207
            iedge.debug_info(),
1✔
208
            DebugInfo::merge(
1✔
209
                access_node->debug_info(),
1✔
210
                DebugInfo::merge(
1✔
211
                    oedge.debug_info(),
1✔
212
                    DebugInfo::merge(assign_tasklet->debug_info(), assign_tasklet_oedge.debug_info())
1✔
213
                )
1✔
214
            )
1✔
215
        );
1✔
216
        this->builder_.add_memlet(
1✔
217
            block,
1✔
218
            *tasklet,
1✔
219
            iedge.src_conn(),
1✔
220
            dynamic_cast<data_flow::AccessNode&>(assign_tasklet_oedge.dst()),
1✔
221
            assign_tasklet_oedge.dst_conn(),
1✔
222
            assign_tasklet_oedge.subset(),
1✔
223
            assign_tasklet_oedge.base_type(),
1✔
224
            debug_info
1✔
225
        );
1✔
226
        this->builder_.remove_memlet(block, iedge);
1✔
227

228
        // Remove obsolete memlets
229
        this->builder_.remove_memlet(block, oedge);
1✔
230
        this->builder_.remove_memlet(block, assign_tasklet_oedge);
1✔
231

232
        // Remove obsolete nodes
233
        this->builder_.remove_node(block, *access_node);
1✔
234
        this->builder_.remove_node(block, *assign_tasklet);
1✔
235

236
        applied = true;
1✔
237
    }
1✔
238

239
    return applied;
10✔
240
}
10✔
241

242
} // namespace passes
243
} // namespace sdfg
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