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

ArkScript-lang / Ark / 29761103967

20 Jul 2026 04:47PM UTC coverage: 94.115% (-0.3%) from 94.385%
29761103967

Pull #705

github

web-flow
Merge d1df3d7ce into 00cfbe903
Pull Request #705: Feat/inliner

413 of 459 new or added lines in 11 files covered. (89.98%)

11 existing lines in 2 files now uncovered.

10667 of 11334 relevant lines covered (94.12%)

1164822.98 hits per line

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

91.44
/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) :
1,108✔
21
        Pass("IRCompiler", debug)
554✔
22
    {}
1,662✔
23

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

31
        // compute a list of unique filenames
32
        for (const auto& page : pages)
9,217✔
33
        {
34
            for (const auto& inst : page.data)
319,030✔
35
            {
36
                if (std::ranges::find(m_filenames, inst.filename()) == m_filenames.end() && inst.hasValidSourceLocation())
310,220✔
37
                    m_filenames.push_back(inst.filename());
655✔
38
            }
310,220✔
39
        }
8,810✔
40

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

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

47
        if (m_ir.empty())
407✔
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);
407✔
62
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize, m_bytecode.end(), hash_out);
407✔
63
        m_bytecode.insert(m_bytecode.begin() + bytecode::HeaderSize, hash_out.begin(), hash_out.end());
407✔
64

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

68
    void IRCompiler::dumpToStream(std::ostream& stream) const
29✔
69
    {
29✔
70
        std::size_t index = 0;
29✔
71
        for (const auto& block : m_ir)
112✔
72
        {
73
            if (index == 0)
83✔
74
                // global scope
75
                fmt::println(stream, "global");
29✔
76
            else
77
            {
78
                fmt::println(
162✔
79
                    stream,
54✔
80
                    "page_{} ({} ({} argument{}) {}, {} instructions)",
54✔
81
                    index,
82
                    block.debugName(),
54✔
83
                    block.metadata.argument_count,
54✔
84
                    block.metadata.argument_count == 1 ? "" : "s",
54✔
85
                    block.metadataRepr(),
54✔
86
                    block.data.size());
54✔
87
            }
88

89
            for (const auto& entity : block.data)
2,043✔
90
            {
91
                switch (entity.kind())
1,960✔
92
                {
206✔
93
                    case IR::Kind::Label:
94
                        fmt::println(stream, ".L{}:", entity.label());
206✔
95
                        break;
391✔
96

97
                    case IR::Kind::Goto:
98
                        fmt::println(stream, "\t{} L{}", InstructionNames[entity.inst()], entity.label());
185✔
99
                        break;
199✔
100

101
                    case IR::Kind::GotoWithArg:
102
                        fmt::println(stream, "\t{} L{}, {}", InstructionNames[entity.inst()], entity.label(), entity.primaryArg());
14✔
103
                        break;
1,295✔
104

105
                    case IR::Kind::Opcode:
106
                        fmt::println(stream, "\t{} {}", InstructionNames[entity.inst()], entity.primaryArg());
1,281✔
107
                        break;
1,475✔
108

109
                    case IR::Kind::Opcode2Args:
110
                        fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
194✔
111
                        break;
274✔
112

113
                    case IR::Kind::Opcode3Args:
114
                        fmt::println(stream, "\t{} {}, {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg(), entity.tertiaryArg());
80✔
115
                        break;
80✔
116
                }
1,960✔
117
            }
1,960✔
118

119
            fmt::println(stream, "");
83✔
120
            ++index;
83✔
121
        }
83✔
122
    }
29✔
123

124
    const bytecode_t& IRCompiler::bytecode() const noexcept
407✔
125
    {
407✔
126
        return m_bytecode;
407✔
127
    }
128

129
    void IRCompiler::compile()
407✔
130
    {
407✔
131
        // push the different code segments
132
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
9,217✔
133
        {
134
            IR::Block& page = m_ir[i];
8,810✔
135
            // just in case we got too far, always add a HALT to be sure the
136
            // VM won't do anything crazy
137
            page.data.emplace_back(HALT);
8,810✔
138

139
            // push number of elements
140
            const std::size_t page_size = page.instructionCount();
8,810✔
141
            if (std::cmp_greater(page_size, MaxValue16Bits))
8,810✔
142
            {
NEW
143
                std::string message;
×
NEW
144
                if (i == 0)
×
NEW
145
                    message = fmt::format("Global scope exceeds the maximum number of instructions ({})", MaxValue16Bits);
×
NEW
146
                else if (page.metadata.name.has_value())
×
NEW
147
                    message = fmt::format("Function {} exceeds the maximum number of instructions ({})", page.metadata.name.value(), MaxValue16Bits);
×
148
                else
NEW
149
                    message = fmt::format("Anonymous function at page {} exceeds the maximum number of instructions ({})", i, MaxValue16Bits);
×
150

NEW
151
                throw std::overflow_error(message);
×
NEW
152
            }
×
153

154
            m_bytecode.push_back(CODE_SEGMENT_START);
8,810✔
155
            serializeOn2BytesToVecBE(page_size, m_bytecode);
8,810✔
156

157
            // register labels position
158
            uint16_t pos = 0;
8,810✔
159
            std::unordered_map<IR::label_t, uint16_t> label_to_position;
8,810✔
160
            for (const auto& inst : page.data)
327,840✔
161
            {
162
                switch (inst.kind())
319,030✔
163
                {
49,242✔
164
                    case IR::Kind::Label:
165
                        label_to_position[inst.label()] = pos;
49,242✔
166
                        break;
319,030✔
167

168
                    default:
169
                        ++pos;
269,788✔
170
                }
319,030✔
171
            }
319,030✔
172

173
            for (const auto& inst : page.data)
327,840✔
174
            {
175
                switch (inst.kind())
319,030✔
176
                {
41,192✔
177
                    case IR::Kind::Goto:
178
                        pushWord(Word(inst.inst(), label_to_position[inst.label()]));
41,192✔
179
                        break;
44,514✔
180

181
                    case IR::Kind::GotoWithArg:
182
                        pushWord(Word(inst.inst(), inst.primaryArg(), label_to_position[inst.label()]));
3,322✔
183
                        break;
228,596✔
184

185
                    case IR::Kind::Opcode:
186
                        [[fallthrough]];
187
                    case IR::Kind::Opcode2Args:
188
                        [[fallthrough]];
189
                    case IR::Kind::Opcode3Args:
190
                        pushWord(inst.bytecode());
225,274✔
191
                        break;
274,516✔
192

193
                    default:
194
                        break;
49,242✔
195
                }
319,030✔
196
            }
319,030✔
197
        }
8,810✔
198
    }
407✔
199

200
    void IRCompiler::pushWord(const Word& word)
269,788✔
201
    {
269,788✔
202
        m_bytecode.push_back(word.opcode);
269,788✔
203
        m_bytecode.push_back(word.byte_1);
269,788✔
204
        m_bytecode.push_back(word.byte_2);
269,788✔
205
        m_bytecode.push_back(word.byte_3);
269,788✔
206
    }
269,788✔
207

208
    void IRCompiler::pushFileHeader() noexcept
407✔
209
    {
407✔
210
        /*
211
            Generating headers:
212
                - lang name (to be sure we are executing an ArkScript file)
213
                    on 4 bytes (ark + padding)
214
                - version (major: 2 bytes, minor: 2 bytes, patch: 2 bytes)
215
                - timestamp (8 bytes, unix format)
216
        */
217

218
        m_bytecode.push_back('a');
407✔
219
        m_bytecode.push_back('r');
407✔
220
        m_bytecode.push_back('k');
407✔
221
        m_bytecode.push_back(0_u8);
407✔
222

223
        // push version
224
        for (const int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
1,628✔
225
            serializeOn2BytesToVecBE(n, m_bytecode);
1,221✔
226

227
        // push timestamp
228
        const long long timestamp = std::chrono::duration_cast<std::chrono::seconds>(
407✔
229
                                        std::chrono::system_clock::now().time_since_epoch())
407✔
230
                                        .count();
407✔
231
        for (long i = 0; i < 8; ++i)
3,663✔
232
        {
233
            const long shift = 8 * (7 - i);
3,256✔
234
            const auto ts_byte = static_cast<uint8_t>((timestamp & (0xffLL << shift)) >> shift);
3,256✔
235
            m_bytecode.push_back(ts_byte);
3,256✔
236
        }
3,256✔
237
    }
407✔
238

239
    void IRCompiler::pushSymbolTable(const std::vector<std::string>& symbols)
407✔
240
    {
407✔
241
        const std::size_t symbol_size = symbols.size();
407✔
242
        if (std::cmp_greater(symbol_size, MaxValue16Bits))
407✔
243
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of {}", symbol_size, MaxValue16Bits));
×
244

245
        m_bytecode.push_back(SYM_TABLE_START);
407✔
246
        serializeOn2BytesToVecBE(symbol_size, m_bytecode);
407✔
247

248
        for (const auto& sym : symbols)
15,362✔
249
        {
250
            // push the string, null terminated
251
            std::ranges::transform(sym, std::back_inserter(m_bytecode), [](const char i) {
165,179✔
252
                return static_cast<uint8_t>(i);
150,224✔
253
            });
254
            m_bytecode.push_back(0_u8);
14,955✔
255
        }
14,955✔
256
    }
407✔
257

258
    void IRCompiler::pushValueTable(const std::vector<ValTableElem>& values)
407✔
259
    {
407✔
260
        const std::size_t value_size = values.size();
407✔
261
        if (std::cmp_greater(value_size, MaxValue16Bits))
407✔
262
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of {}", value_size, MaxValue16Bits));
×
263

264
        m_bytecode.push_back(VAL_TABLE_START);
407✔
265
        serializeOn2BytesToVecBE(value_size, m_bytecode);
407✔
266

267
        for (const ValTableElem& val : values)
21,957✔
268
        {
269
            switch (val.type)
21,550✔
270
            {
2,328✔
271
                case ValTableElemType::Number:
272
                {
273
                    m_bytecode.push_back(NUMBER_TYPE);
2,328✔
274
                    const auto n = std::get<double>(val.value);
2,328✔
275
                    const auto [exponent, mantissa] = ieee754::serialize(n);
4,656✔
276
                    serializeToVecLE(exponent, m_bytecode);
2,328✔
277
                    serializeToVecLE(mantissa, m_bytecode);
2,328✔
278
                    break;
279
                }
13,141✔
280

281
                case ValTableElemType::String:
282
                {
283
                    m_bytecode.push_back(STRING_TYPE);
10,813✔
284
                    auto t = std::get<std::string>(val.value);
10,813✔
285
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
203,291✔
286
                        return static_cast<uint8_t>(i);
192,478✔
287
                    });
288
                    break;
289
                }
19,222✔
290

291
                case ValTableElemType::PageAddr:
292
                {
293
                    m_bytecode.push_back(FUNC_TYPE);
8,409✔
294
                    const std::size_t addr = std::get<std::size_t>(val.value);
8,409✔
295
                    serializeOn2BytesToVecBE(addr, m_bytecode);
8,409✔
296
                    break;
297
                }
8,409✔
298
            }
21,550✔
299

300
            m_bytecode.push_back(0_u8);
21,550✔
301
        }
21,550✔
302
    }
407✔
303

304
    void IRCompiler::pushFilenameTable()
407✔
305
    {
407✔
306
        if (std::cmp_greater(m_filenames.size(), MaxValue16Bits))
407✔
307
            throw std::overflow_error(fmt::format("Too many filenames: {}, exceeds the maximum size of {}", m_filenames.size(), MaxValue16Bits));
×
308

309
        m_bytecode.push_back(FILENAMES_TABLE_START);
407✔
310
        // push number of elements
311
        serializeOn2BytesToVecBE(m_filenames.size(), m_bytecode);
407✔
312

313
        for (const auto& name : m_filenames)
1,062✔
314
        {
315
            std::ranges::transform(name, std::back_inserter(m_bytecode), [](const char i) {
50,700✔
316
                return static_cast<uint8_t>(i);
50,045✔
317
            });
318
            m_bytecode.push_back(0_u8);
655✔
319
        }
655✔
320
    }
407✔
321

322
    void IRCompiler::pushInstLocTable(const std::vector<IR::Block>& pages)
407✔
323
    {
407✔
324
        std::vector<internal::InstLoc> locations;
407✔
325
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
9,217✔
326
        {
327
            const auto& page = pages[i];
8,810✔
328
            uint16_t ip = 0;
8,810✔
329

330
            for (const auto& inst : page.data)
319,030✔
331
            {
332
                if (inst.hasValidSourceLocation())
310,220✔
333
                {
334
                    // we are guaranteed to have a value since we listed all existing filenames in IRCompiler::process before,
335
                    // thus we do not have to check if std::ranges::find returned a valid iterator.
336
                    auto file_id = static_cast<uint16_t>(std::distance(m_filenames.begin(), std::ranges::find(m_filenames, inst.filename())));
190,837✔
337

338
                    std::optional<internal::InstLoc> prev = std::nullopt;
190,837✔
339
                    if (!locations.empty())
190,837✔
340
                        prev = locations.back();
190,431✔
341

342
                    // skip redundant instruction location
343
                    if (!(prev.has_value() && prev->filename_id == file_id && prev->line == inst.sourceLine() && prev->page_pointer == i))
190,837✔
344
                        locations.push_back(
79,258✔
345
                            { .page_pointer = static_cast<uint16_t>(i),
317,032✔
346
                              .inst_pointer = ip,
79,258✔
347
                              .filename_id = file_id,
79,258✔
348
                              .line = static_cast<uint32_t>(inst.sourceLine()) });
79,258✔
349
                }
190,837✔
350

351
                if (inst.kind() != IR::Kind::Label)
310,220✔
352
                    ++ip;
260,978✔
353
            }
310,220✔
354
        }
8,810✔
355

356
        m_bytecode.push_back(INST_LOC_TABLE_START);
407✔
357
        serializeOn2BytesToVecBE(locations.size(), m_bytecode);
407✔
358

359
        for (const auto& loc : locations)
79,665✔
360
        {
361
            serializeOn2BytesToVecBE(loc.page_pointer, m_bytecode);
79,258✔
362
            serializeOn2BytesToVecBE(loc.inst_pointer, m_bytecode);
79,258✔
363
            serializeOn2BytesToVecBE(loc.filename_id, m_bytecode);
79,258✔
364
            serializeToVecBE(loc.line, m_bytecode);
79,258✔
365
        }
79,258✔
366
    }
407✔
367
}
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