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

ArkScript-lang / Ark / 20377988123

19 Dec 2025 05:45PM UTC coverage: 92.242% (+1.6%) from 90.661%
20377988123

push

github

SuperFola
chore: addressing cppcheck recommandations

1 of 1 new or added line in 1 file covered. (100.0%)

137 existing lines in 6 files now uncovered.

8430 of 9139 relevant lines covered (92.24%)

245122.08 hits per line

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

94.26
/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp
1
#include <Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp>
2

3
#include <chrono>
4
#include <utility>
5
#include <optional>
6
#include <unordered_map>
7
#include <Proxy/Picosha2.hpp>
8
#include <fmt/ostream.h>
9

10
#include <Ark/Constants.hpp>
11
#include <Ark/Utils/Literals.hpp>
12
#include <Ark/Compiler/IntermediateRepresentation/InstLoc.hpp>
13
#include <Ark/Compiler/Serialization/IntegerSerializer.hpp>
14
#include <Ark/Compiler/Serialization/IEEE754Serializer.hpp>
15

16
namespace Ark::internal
17
{
18
    using namespace literals;
19

20
    IRCompiler::IRCompiler(const unsigned debug) :
712✔
21
        m_logger("IRCompiler", debug)
356✔
22
    {}
712✔
23

24
    void IRCompiler::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
231✔
25
    {
231✔
26
        m_logger.traceStart("process");
231✔
27
        pushFileHeader();
231✔
28
        pushSymbolTable(symbols);
231✔
29
        pushValueTable(values);
231✔
30

31
        // compute a list of unique filenames
32
        for (const auto& page : pages)
4,071✔
33
        {
34
            for (const auto& inst : page)
102,095✔
35
            {
36
                if (std::ranges::find(m_filenames, inst.filename()) == m_filenames.end() && inst.hasValidSourceLocation())
98,255✔
37
                    m_filenames.push_back(inst.filename());
365✔
38
            }
98,255✔
39
        }
3,840✔
40

41
        pushFilenameTable();
231✔
42
        pushInstLocTable(pages);
231✔
43

44
        m_ir = pages;
231✔
45
        compile();
231✔
46

47
        if (m_ir.empty())
231✔
48
        {
49
            // code segment with a single instruction
50
            m_bytecode.push_back(CODE_SEGMENT_START);
×
51
            m_bytecode.push_back(0_u8);
×
52
            m_bytecode.push_back(1_u8);
×
53

54
            m_bytecode.push_back(0_u8);
×
55
            m_bytecode.push_back(HALT);
×
56
            m_bytecode.push_back(0_u8);
×
57
            m_bytecode.push_back(0_u8);
×
58
        }
×
59

60
        // generate a hash of the tables + bytecode
61
        std::vector<unsigned char> hash_out(picosha2::k_digest_size);
231✔
62
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize, m_bytecode.end(), hash_out);
231✔
63
        m_bytecode.insert(m_bytecode.begin() + bytecode::HeaderSize, hash_out.begin(), hash_out.end());
231✔
64

65
        m_logger.traceEnd();
231✔
66
    }
231✔
67

68
    void IRCompiler::dumpToStream(std::ostream& stream) const
18✔
69
    {
18✔
70
        std::size_t index = 0;
18✔
71
        for (const auto& block : m_ir)
63✔
72
        {
73
            fmt::println(stream, "page_{}", index);
45✔
74
            for (const auto& entity : block)
1,282✔
75
            {
76
                switch (entity.kind())
1,237✔
77
                {
130✔
78
                    case IR::Kind::Label:
79
                        fmt::println(stream, ".L{}:", entity.label());
130✔
80
                        break;
240✔
81

82
                    case IR::Kind::Goto:
83
                        fmt::println(stream, "\t{} L{}", InstructionNames[entity.inst()], entity.label());
110✔
84
                        break;
124✔
85

86
                    case IR::Kind::GotoWithArg:
87
                        fmt::println(stream, "\t{} L{}, {}", InstructionNames[entity.inst()], entity.label(), entity.primaryArg());
14✔
88
                        break;
792✔
89

90
                    case IR::Kind::Opcode:
91
                        fmt::println(stream, "\t{} {}", InstructionNames[entity.inst()], entity.primaryArg());
778✔
92
                        break;
903✔
93

94
                    case IR::Kind::Opcode2Args:
95
                        fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
125✔
96
                        break;
205✔
97

98
                    case IR::Kind::Opcode3Args:
99
                        fmt::println(stream, "\t{} {}, {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg(), entity.tertiaryArg());
80✔
100
                        break;
80✔
101
                }
1,237✔
102
            }
1,237✔
103

104
            fmt::println(stream, "");
45✔
105
            ++index;
45✔
106
        }
45✔
107
    }
18✔
108

109
    const bytecode_t& IRCompiler::bytecode() const noexcept
231✔
110
    {
231✔
111
        return m_bytecode;
231✔
112
    }
113

114
    void IRCompiler::compile()
231✔
115
    {
231✔
116
        // push the different code segments
117
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
4,071✔
118
        {
119
            IR::Block& page = m_ir[i];
3,840✔
120
            // just in case we got too far, always add a HALT to be sure the
121
            // VM won't do anything crazy
122
            page.emplace_back(HALT);
3,840✔
123

124
            // push number of elements
125
            const auto page_size = std::ranges::count_if(page, [](const auto& a) {
105,935✔
126
                return a.kind() != IR::Kind::Label;
102,095✔
127
            });
128
            if (std::cmp_greater(page_size, MaxValue16Bits))
3,840✔
UNCOV
129
                throw std::overflow_error(fmt::format("Size of page {} exceeds the maximum size of {}", i, MaxValue16Bits));
×
130

131
            m_bytecode.push_back(CODE_SEGMENT_START);
3,840✔
132
            serializeOn2BytesToVecBE(page_size, m_bytecode);
3,840✔
133

134
            // register labels position
135
            uint16_t pos = 0;
3,840✔
136
            std::unordered_map<IR::label_t, uint16_t> label_to_position;
3,840✔
137
            for (auto& inst : page)
105,935✔
138
            {
139
                switch (inst.kind())
102,095✔
140
                {
14,673✔
141
                    case IR::Kind::Label:
142
                        label_to_position[inst.label()] = pos;
14,673✔
143
                        break;
102,095✔
144

145
                    default:
146
                        ++pos;
87,422✔
147
                }
102,095✔
148
            }
102,095✔
149

150
            for (auto& inst : page)
105,935✔
151
            {
152
                switch (inst.kind())
102,095✔
153
                {
12,704✔
154
                    case IR::Kind::Goto:
155
                        pushWord(Word(inst.inst(), label_to_position[inst.label()]));
12,704✔
156
                        break;
14,028✔
157

158
                    case IR::Kind::GotoWithArg:
159
                        pushWord(Word(inst.inst(), inst.primaryArg(), label_to_position[inst.label()]));
1,324✔
160
                        break;
74,718✔
161

162
                    case IR::Kind::Opcode:
163
                        [[fallthrough]];
164
                    case IR::Kind::Opcode2Args:
165
                        [[fallthrough]];
166
                    case IR::Kind::Opcode3Args:
167
                        pushWord(inst.bytecode());
73,394✔
168
                        break;
88,067✔
169

170
                    default:
171
                        break;
14,673✔
172
                }
102,095✔
173
            }
102,095✔
174
        }
3,840✔
175
    }
231✔
176

177
    void IRCompiler::pushWord(const Word& word)
87,422✔
178
    {
87,422✔
179
        m_bytecode.push_back(word.opcode);
87,422✔
180
        m_bytecode.push_back(word.byte_1);
87,422✔
181
        m_bytecode.push_back(word.byte_2);
87,422✔
182
        m_bytecode.push_back(word.byte_3);
87,422✔
183
    }
87,422✔
184

185
    void IRCompiler::pushFileHeader() noexcept
231✔
186
    {
231✔
187
        /*
188
            Generating headers:
189
                - lang name (to be sure we are executing an ArkScript file)
190
                    on 4 bytes (ark + padding)
191
                - version (major: 2 bytes, minor: 2 bytes, patch: 2 bytes)
192
                - timestamp (8 bytes, unix format)
193
        */
194

195
        m_bytecode.push_back('a');
231✔
196
        m_bytecode.push_back('r');
231✔
197
        m_bytecode.push_back('k');
231✔
198
        m_bytecode.push_back(0_u8);
231✔
199

200
        // push version
201
        for (const int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
924✔
202
            serializeOn2BytesToVecBE(n, m_bytecode);
693✔
203

204
        // push timestamp
205
        const long long timestamp = std::chrono::duration_cast<std::chrono::seconds>(
231✔
206
                                        std::chrono::system_clock::now().time_since_epoch())
231✔
207
                                        .count();
231✔
208
        for (long i = 0; i < 8; ++i)
2,079✔
209
        {
210
            const long shift = 8 * (7 - i);
1,848✔
211
            const auto ts_byte = static_cast<uint8_t>((timestamp & (0xffLL << shift)) >> shift);
1,848✔
212
            m_bytecode.push_back(ts_byte);
1,848✔
213
        }
1,848✔
214
    }
231✔
215

216
    void IRCompiler::pushSymbolTable(const std::vector<std::string>& symbols)
231✔
217
    {
231✔
218
        const std::size_t symbol_size = symbols.size();
231✔
219
        if (std::cmp_greater(symbol_size, MaxValue16Bits))
231✔
UNCOV
220
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of {}", symbol_size, MaxValue16Bits));
×
221

222
        m_bytecode.push_back(SYM_TABLE_START);
231✔
223
        serializeOn2BytesToVecBE(symbol_size, m_bytecode);
231✔
224

225
        for (const auto& sym : symbols)
7,249✔
226
        {
227
            // push the string, null terminated
228
            std::ranges::transform(sym, std::back_inserter(m_bytecode), [](const char i) {
79,323✔
229
                return static_cast<uint8_t>(i);
72,305✔
230
            });
231
            m_bytecode.push_back(0_u8);
7,018✔
232
        }
7,018✔
233
    }
231✔
234

235
    void IRCompiler::pushValueTable(const std::vector<ValTableElem>& values)
231✔
236
    {
231✔
237
        const std::size_t value_size = values.size();
231✔
238
        if (std::cmp_greater(value_size, MaxValue16Bits))
231✔
UNCOV
239
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of {}", value_size, MaxValue16Bits));
×
240

241
        m_bytecode.push_back(VAL_TABLE_START);
231✔
242
        serializeOn2BytesToVecBE(value_size, m_bytecode);
231✔
243

244
        for (const ValTableElem& val : values)
7,221✔
245
        {
246
            switch (val.type)
6,990✔
247
            {
969✔
248
                case ValTableElemType::Number:
249
                {
250
                    m_bytecode.push_back(NUMBER_TYPE);
969✔
251
                    const auto n = std::get<double>(val.value);
969✔
252
                    const auto [exponent, mantissa] = ieee754::serialize(n);
969✔
253
                    serializeToVecLE(exponent, m_bytecode);
969✔
254
                    serializeToVecLE(mantissa, m_bytecode);
969✔
255
                    break;
256
                }
3,381✔
257

258
                case ValTableElemType::String:
259
                {
260
                    m_bytecode.push_back(STRING_TYPE);
2,412✔
261
                    auto t = std::get<std::string>(val.value);
2,412✔
262
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
47,847✔
263
                        return static_cast<uint8_t>(i);
45,435✔
264
                    });
265
                    break;
266
                }
6,021✔
267

268
                case ValTableElemType::PageAddr:
269
                {
270
                    m_bytecode.push_back(FUNC_TYPE);
3,609✔
271
                    const std::size_t addr = std::get<std::size_t>(val.value);
3,609✔
272
                    serializeOn2BytesToVecBE(addr, m_bytecode);
3,609✔
273
                    break;
274
                }
3,609✔
275
            }
6,990✔
276

277
            m_bytecode.push_back(0_u8);
6,990✔
278
        }
6,990✔
279
    }
231✔
280

281
    void IRCompiler::pushFilenameTable()
231✔
282
    {
231✔
283
        if (std::cmp_greater(m_filenames.size(), MaxValue16Bits))
231✔
UNCOV
284
            throw std::overflow_error(fmt::format("Too many filenames: {}, exceeds the maximum size of {}", m_filenames.size(), MaxValue16Bits));
×
285

286
        m_bytecode.push_back(FILENAMES_TABLE_START);
231✔
287
        // push number of elements
288
        serializeOn2BytesToVecBE(m_filenames.size(), m_bytecode);
231✔
289

290
        for (const auto& name : m_filenames)
596✔
291
        {
292
            std::ranges::transform(name, std::back_inserter(m_bytecode), [](const char i) {
28,222✔
293
                return static_cast<uint8_t>(i);
27,857✔
294
            });
295
            m_bytecode.push_back(0_u8);
365✔
296
        }
365✔
297
    }
231✔
298

299
    void IRCompiler::pushInstLocTable(const std::vector<IR::Block>& pages)
231✔
300
    {
231✔
301
        std::vector<internal::InstLoc> locations;
231✔
302
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
4,071✔
303
        {
304
            const auto& page = pages[i];
3,840✔
305
            uint16_t ip = 0;
3,840✔
306

307
            for (const auto& inst : page)
102,095✔
308
            {
309
                if (inst.hasValidSourceLocation())
98,255✔
310
                {
311
                    // we are guaranteed to have a value since we listed all existing filenames in IRCompiler::process before,
312
                    // thus we do not have to check if std::ranges::find returned a valid iterator.
313
                    auto file_id = static_cast<uint16_t>(std::distance(m_filenames.begin(), std::ranges::find(m_filenames, inst.filename())));
62,300✔
314

315
                    std::optional<internal::InstLoc> prev = std::nullopt;
62,300✔
316
                    if (!locations.empty())
62,300✔
317
                        prev = locations.back();
62,069✔
318

319
                    // skip redundant instruction location
320
                    if (!(prev.has_value() && prev->filename_id == file_id && prev->line == inst.sourceLine() && prev->page_pointer == i))
62,300✔
321
                        locations.push_back(
25,066✔
322
                            { .page_pointer = static_cast<uint16_t>(i),
100,264✔
323
                              .inst_pointer = ip,
25,066✔
324
                              .filename_id = file_id,
25,066✔
325
                              .line = static_cast<uint32_t>(inst.sourceLine()) });
25,066✔
326
                }
62,300✔
327

328
                if (inst.kind() != IR::Kind::Label)
98,255✔
329
                    ++ip;
83,582✔
330
            }
98,255✔
331
        }
3,840✔
332

333
        m_bytecode.push_back(INST_LOC_TABLE_START);
231✔
334
        serializeOn2BytesToVecBE(locations.size(), m_bytecode);
231✔
335

336
        std::optional<internal::InstLoc> prev = std::nullopt;
231✔
337

338
        for (const auto& loc : locations)
25,297✔
339
        {
340
            serializeOn2BytesToVecBE(loc.page_pointer, m_bytecode);
25,066✔
341
            serializeOn2BytesToVecBE(loc.inst_pointer, m_bytecode);
25,066✔
342
            serializeOn2BytesToVecBE(loc.filename_id, m_bytecode);
25,066✔
343
            serializeToVecBE(loc.line, m_bytecode);
25,066✔
344

345
            prev = loc;
25,066✔
346
        }
25,066✔
347
    }
231✔
348
}
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