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

daisytuner / docc / 29992851043

23 Jul 2026 08:52AM UTC coverage: 64.1% (+0.3%) from 63.787%
29992851043

Pull #866

github

web-flow
Merge f4354fb28 into 2e568810b
Pull Request #866: Add Support for Complex Types

188 of 296 new or added lines in 12 files covered. (63.51%)

272 existing lines in 10 files now uncovered.

42854 of 66855 relevant lines covered (64.1%)

737.97 hits per line

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

57.32
/sdfg/src/codegen/utils.cpp
1
#include "sdfg/codegen/utils.h"
2

3
#include "sdfg/data_flow/data_flow_graph.h"
4
#include "sdfg/data_flow/tasklet.h"
5
#include "sdfg/exceptions.h"
6
#include "sdfg/function.h"
7

8
namespace sdfg {
9
namespace codegen {
10

11
// Constructor
12
PrettyPrinter::PrettyPrinter(int indent, bool frozen)
13
    : owned_stream(std::make_unique<std::stringstream>()), stream(*owned_stream.get()), indentSize(indent),
235✔
14
      frozen_(frozen) {}
235✔
15

16
PrettyPrinter::PrettyPrinter(std::ostream& stream, int indent, bool frozen)
17
    : stream(stream), indentSize(indent), frozen_(frozen) {}
×
18

19
// Set the indentation level
20
void PrettyPrinter::setIndent(int indent) { indentSize = indent; };
528✔
21

22
int PrettyPrinter::indent() const { return indentSize; };
360✔
23

24
int PrettyPrinter::changeIndent(int delta) { return indentSize += delta; };
×
25

26
// Get the underlying string
27
std::string PrettyPrinter::str() const { return owned_stream->str(); };
121✔
28

29
// Clear the stringstream content
30
void PrettyPrinter::clear() {
14✔
31
    owned_stream->str("");
14✔
32
    owned_stream->clear();
14✔
33
}
14✔
34

35
// Overload for manipulators (like std::endl)
36
PrettyPrinter& PrettyPrinter::operator<<(std::ostream& (*manip)(std::ostream&) ) {
1,640✔
37
    if (frozen_) {
1,640✔
38
        throw std::runtime_error("PrettyPrinter is frozen");
×
39
    }
×
40
    stream << manip;
1,640✔
41
    // Reset indent application on new lines
42
    if (manip == static_cast<std::ostream& (*) (std::ostream&)>(std::endl)) {
1,640✔
43
        isNewLine = true;
1,640✔
44
    }
1,640✔
45
    return *this;
1,640✔
46
};
1,640✔
47

48
// Apply indentation only at the beginning of a new line
49
void PrettyPrinter::applyIndent() {
4,859✔
50
    if (isNewLine && indentSize > 0) {
4,859✔
51
        stream << std::setw(indentSize) << "";
1,121✔
52
        isNewLine = false;
1,121✔
53
    }
1,121✔
54
};
4,859✔
55

56
Reference::Reference(const types::IType& reference_) : reference_(reference_.clone()) {};
16✔
57

58
Reference::Reference(
59
    types::StorageType storage_type, size_t alignment, const std::string& initializer, const types::IType& reference_
60
)
61
    : IType(storage_type, alignment, initializer), reference_(reference_.clone()) {};
6✔
62

63
std::unique_ptr<types::IType> Reference::clone() const {
5✔
64
    return std::make_unique<Reference>(this->storage_type(), this->alignment(), this->initializer(), *this->reference_);
5✔
65
};
5✔
66

67
types::TypeID Reference::type_id() const { return types::TypeID::Reference; };
30✔
68

69
types::PrimitiveType Reference::primitive_type() const { return this->reference_->primitive_type(); };
×
70

71
bool Reference::is_symbol() const { return false; };
1✔
72

73
const types::IType& Reference::reference_type() const { return *this->reference_; };
22✔
74

75
bool Reference::operator==(const types::IType& other) const {
4✔
76
    if (auto reference = dynamic_cast<const Reference*>(&other)) {
4✔
77
        return *(this->reference_) == *reference->reference_ && this->alignment_ == reference->alignment_;
4✔
78
    } else {
4✔
79
        return false;
×
80
    }
×
81
};
4✔
82

83
std::string Reference::print() const { return "Reference(" + this->reference_->print() + ")"; };
1✔
84

UNCOV
85
void Reference::replace_symbols(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
×
NEW
86
    this->reference_->replace_symbols(old_expression, new_expression);
×
NEW
87
}
×
88

NEW
89
void Reference::replace_symbols(const symbolic::ExpressionMapping& replacements) {
×
NEW
90
    this->reference_->replace_symbols(replacements);
×
NEW
91
}
×
92

93

94
std::string complex_type_name(types::PrimitiveType prim_type) {
28✔
95
    switch (prim_type) {
28✔
96
        case types::PrimitiveType::CHalf:
4✔
97
            return "__daisy_type_complex_half";
4✔
98
        case types::PrimitiveType::CBFloat:
4✔
99
            return "__daisy_type_complex_bfloat";
4✔
100
        case types::PrimitiveType::CFloat:
9✔
101
            return "__daisy_type_complex_float";
9✔
102
        case types::PrimitiveType::CDouble:
6✔
103
            return "__daisy_type_complex_double";
6✔
104
        case types::PrimitiveType::CFP128:
5✔
105
            return "__daisy_type_complex_fp128";
5✔
NEW
106
        default:
×
NEW
107
            throw InvalidSDFGException("complex_type_name: not a complex primitive type");
×
108
    }
28✔
109
};
28✔
110

111
namespace {
112

113
// Real "compute type" used to evaluate a complex operation component-wise. Narrow element types
114
// (half/bfloat) have limited native arithmetic support, so their components are widened to float
115
// for the computation and narrowed back on assignment.
116
std::string complex_compute_type(types::PrimitiveType prim) {
1✔
117
    switch (prim) {
1✔
NEW
118
        case types::PrimitiveType::CDouble:
×
NEW
119
            return "double";
×
NEW
120
        case types::PrimitiveType::CFP128:
×
NEW
121
            return "__float128";
×
122
        default:
1✔
123
            return "float";
1✔
124
    }
1✔
125
}
1✔
126

127
} // namespace
128

129
std::string complex_computation(const data_flow::Tasklet& tasklet, const Function& function) {
1✔
130
    if (!data_flow::is_complex(tasklet.code())) {
1✔
NEW
131
        throw InvalidSDFGException("complex_computation: tasklet is not a complex operation");
×
NEW
132
    }
×
133

134
    // The operand type (always complex) drives the component compute type.
135
    auto& graph = tasklet.get_parent();
1✔
136
    types::PrimitiveType operand_prim = types::PrimitiveType::CFloat;
1✔
137
    for (auto& iedge : graph.in_edges(tasklet)) {
1✔
138
        operand_prim = iedge.result_type(function)->primitive_type();
1✔
139
        break;
1✔
140
    }
1✔
141
    const std::string re = "(" + complex_compute_type(operand_prim) + ")";
1✔
142

143
    const auto& inputs = tasklet.inputs();
1✔
144
    const std::string& o = tasklet.output();
1✔
145
    const std::string& a = inputs.at(0);
1✔
146

147
    std::stringstream out;
1✔
148
    switch (tasklet.code()) {
1✔
149
        case data_flow::TaskletCode::complex_add: {
1✔
150
            const std::string& b = inputs.at(1);
1✔
151
            out << o << ".x = " << re << a << ".x + " << re << b << ".x; ";
1✔
152
            out << o << ".y = " << re << a << ".y + " << re << b << ".y;";
1✔
153
            break;
1✔
NEW
154
        }
×
NEW
155
        case data_flow::TaskletCode::complex_sub: {
×
NEW
156
            const std::string& b = inputs.at(1);
×
NEW
157
            out << o << ".x = " << re << a << ".x - " << re << b << ".x; ";
×
NEW
158
            out << o << ".y = " << re << a << ".y - " << re << b << ".y;";
×
NEW
159
            break;
×
NEW
160
        }
×
NEW
161
        case data_flow::TaskletCode::complex_mul: {
×
NEW
162
            const std::string& b = inputs.at(1);
×
NEW
163
            out << o << ".x = " << re << a << ".x * " << re << b << ".x - " << re << a << ".y * " << re << b << ".y; ";
×
NEW
164
            out << o << ".y = " << re << a << ".x * " << re << b << ".y + " << re << a << ".y * " << re << b << ".x;";
×
NEW
165
            break;
×
NEW
166
        }
×
NEW
167
        case data_flow::TaskletCode::complex_div: {
×
NEW
168
            const std::string& b = inputs.at(1);
×
NEW
169
            const std::string denom = "(" + re + b + ".x * " + re + b + ".x + " + re + b + ".y * " + re + b + ".y)";
×
NEW
170
            out << o << ".x = (" << re << a << ".x * " << re << b << ".x + " << re << a << ".y * " << re << b
×
NEW
171
                << ".y) / " << denom << "; ";
×
NEW
172
            out << o << ".y = (" << re << a << ".y * " << re << b << ".x - " << re << a << ".x * " << re << b
×
NEW
173
                << ".y) / " << denom << ";";
×
NEW
174
            break;
×
NEW
175
        }
×
NEW
176
        case data_flow::TaskletCode::complex_neg:
×
NEW
177
            out << o << ".x = -" << re << a << ".x; ";
×
NEW
178
            out << o << ".y = -" << re << a << ".y;";
×
NEW
179
            break;
×
NEW
180
        case data_flow::TaskletCode::complex_real:
×
NEW
181
            out << o << " = " << a << ".x;";
×
NEW
182
            break;
×
NEW
183
        case data_flow::TaskletCode::complex_imag:
×
NEW
184
            out << o << " = " << a << ".y;";
×
NEW
185
            break;
×
NEW
186
        case data_flow::TaskletCode::complex_eq: {
×
NEW
187
            const std::string& b = inputs.at(1);
×
NEW
188
            out << o << " = (" << re << a << ".x == " << re << b << ".x && " << re << a << ".y == " << re << b
×
NEW
189
                << ".y);";
×
NEW
190
            break;
×
NEW
191
        }
×
NEW
192
        case data_flow::TaskletCode::complex_ne: {
×
NEW
193
            const std::string& b = inputs.at(1);
×
NEW
194
            out << o << " = !(" << re << a << ".x == " << re << b << ".x && " << re << a << ".y == " << re << b
×
NEW
195
                << ".y);";
×
NEW
196
            break;
×
NEW
197
        }
×
NEW
198
        default:
×
NEW
199
            throw InvalidSDFGException("complex_computation: unhandled complex tasklet code");
×
200
    }
1✔
201
    return out.str();
1✔
202
};
1✔
203

204
std::string complex_support_preamble(bool device) {
22✔
205
    // Element type of the half-precision component. GPU toolchains expose __fp16, matching the
206
    // scalar Half mapping of the CUDA/ROCm language extensions.
207
    const std::string half_elem = device ? "__fp16" : "_Float16";
22✔
208

209
    std::stringstream out;
22✔
210
    out << "/* Complex type support (generated by sdfglib) */" << std::endl;
22✔
211

212
    // Dedicated 2-component vector types with `.x` (real) / `.y` (imaginary) members. Named with a
213
    // reserved prefix so they never collide with a toolchain's native float2/double2 definitions.
214
    out << "typedef struct { float x; float y; } __daisy_type_complex_float;" << std::endl;
22✔
215
    out << "typedef struct { double x; double y; } __daisy_type_complex_double;" << std::endl;
22✔
216
    out << "typedef struct { " << half_elem << " x; " << half_elem << " y; } __daisy_type_complex_half;" << std::endl;
22✔
217
    out << "typedef struct { __bf16 x; __bf16 y; } __daisy_type_complex_bfloat;" << std::endl;
22✔
218
    out << "typedef struct { __float128 x; __float128 y; } __daisy_type_complex_fp128;" << std::endl;
22✔
219

220
    return out.str();
22✔
221
};
22✔
222

223

224
} // namespace codegen
225
} // 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