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

ArkScript-lang / Ark / 11629611787

01 Nov 2024 12:48PM UTC coverage: 77.319% (+0.3%) from 77.042%
11629611787

push

github

SuperFola
feat(tests): adding first test for IR generation and optimization

5209 of 6737 relevant lines covered (77.32%)

9473.78 hits per line

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

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

3
#include <chrono>
4
#include <utility>
5
#include <unordered_map>
6
#include <picosha2.h>
7
#include <fmt/ostream.h>
8

9
#include <Ark/Constants.hpp>
10
#include <Ark/Literals.hpp>
11

12
namespace Ark::internal
13
{
14
    using namespace literals;
15

16
    IRCompiler::IRCompiler(const unsigned debug) :
200✔
17
        m_logger("IRCompiler", debug)
100✔
18
    {}
200✔
19

20
    void IRCompiler::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
27✔
21
    {
27✔
22
        m_logger.traceStart("process");
27✔
23
        pushFileHeader();
27✔
24
        pushSymAndValTables(symbols, values);
27✔
25

26
        m_ir = pages;
27✔
27
        compile();
27✔
28

29
        if (m_ir.empty())
27✔
30
        {
31
            // code segment with a single instruction
32
            m_bytecode.push_back(CODE_SEGMENT_START);
×
33
            m_bytecode.push_back(0_u8);
×
34
            m_bytecode.push_back(1_u8);
×
35

36
            m_bytecode.push_back(0_u8);
×
37
            m_bytecode.push_back(HALT);
×
38
            m_bytecode.push_back(0_u8);
×
39
            m_bytecode.push_back(0_u8);
×
40
        }
×
41

42
        constexpr std::size_t header_size = 18;
27✔
43

44
        // generate a hash of the tables + bytecode
45
        std::vector<unsigned char> hash_out(picosha2::k_digest_size);
27✔
46
        picosha2::hash256(m_bytecode.begin() + header_size, m_bytecode.end(), hash_out);
27✔
47
        m_bytecode.insert(m_bytecode.begin() + header_size, hash_out.begin(), hash_out.end());
27✔
48

49
        m_logger.traceEnd();
27✔
50
    }
27✔
51

52
    void IRCompiler::dumpToStream(std::ostream& stream) const
1✔
53
    {
1✔
54
        std::size_t index = 0;
1✔
55
        for (const auto& block : m_ir)
3✔
56
        {
57
            fmt::println(stream, "page_{}", index);
2✔
58
            for (const auto entity : block)
37✔
59
            {
60
                switch (entity.kind())
35✔
61
                {
4✔
62
                    case IR::Kind::Label:
63
                        fmt::println(stream, ".L{}:", entity.label());
4✔
64
                        break;
6✔
65

66
                    case IR::Kind::Goto:
67
                        fmt::println(stream, "\tGOTO L{}", entity.label());
2✔
68
                        break;
4✔
69

70
                    case IR::Kind::GotoIfTrue:
71
                        fmt::println(stream, "\tGOTO_IF_TRUE L{}", entity.label());
2✔
72
                        break;
2✔
73

74
                    case IR::Kind::GotoIfFalse:
75
                        fmt::println(stream, "\tGOTO_IF_FALSE L{}", entity.label());
×
76
                        break;
24✔
77

78
                    case IR::Kind::Opcode:
79
                        fmt::println(stream, "\t{} {}", InstructionNames[entity.inst()], entity.primaryArg());
24✔
80
                        break;
27✔
81

82
                    case IR::Kind::Opcode2Args:
83
                        fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
3✔
84
                        break;
3✔
85
                }
35✔
86
            }
35✔
87

88
            fmt::println(stream, "");
2✔
89
            ++index;
2✔
90
        }
2✔
91
    }
1✔
92

93
    const bytecode_t& IRCompiler::bytecode() const noexcept
27✔
94
    {
27✔
95
        return m_bytecode;
27✔
96
    }
97

98
    void IRCompiler::compile()
27✔
99
    {
27✔
100
        // push the different code segments
101
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
160✔
102
        {
103
            IR::Block& page = m_ir[i];
133✔
104
            // just in case we got too far, always add a HALT to be sure the
105
            // VM won't do anything crazy
106
            page.emplace_back(HALT);
133✔
107

108
            // push number of elements
109
            const auto page_size = std::ranges::count_if(page, [](const auto& a) {
8,340✔
110
                return a.kind() != IR::Kind::Label;
8,207✔
111
            });
112
            if (std::cmp_greater(page_size, std::numeric_limits<uint16_t>::max()))
133✔
113
                throw std::overflow_error(fmt::format("Size of page {} exceeds the maximum size of 2^16 - 1", i));
×
114

115
            m_bytecode.push_back(CODE_SEGMENT_START);
133✔
116
            m_bytecode.push_back(static_cast<uint8_t>((page_size & 0xff00) >> 8));
133✔
117
            m_bytecode.push_back(static_cast<uint8_t>(page_size & 0x00ff));
133✔
118

119
            // register labels position
120
            uint16_t pos = 0;
133✔
121
            std::unordered_map<IR::label_t, uint16_t> label_to_position;
133✔
122
            for (auto inst : page)
8,340✔
123
            {
124
                switch (inst.kind())
8,207✔
125
                {
680✔
126
                    case IR::Kind::Label:
127
                        label_to_position[inst.label()] = pos;
680✔
128
                        break;
8,207✔
129

130
                    default:
131
                        ++pos;
7,527✔
132
                }
8,207✔
133
            }
8,207✔
134

135
            for (auto inst : page)
8,340✔
136
            {
137
                switch (inst.kind())
8,207✔
138
                {
329✔
139
                    case IR::Kind::Goto:
140
                        pushWord(Word(JUMP, label_to_position[inst.label()]));
329✔
141
                        break;
637✔
142

143
                    case IR::Kind::GotoIfTrue:
144
                        pushWord(Word(POP_JUMP_IF_TRUE, label_to_position[inst.label()]));
308✔
145
                        break;
359✔
146

147
                    case IR::Kind::GotoIfFalse:
148
                        pushWord(Word(POP_JUMP_IF_FALSE, label_to_position[inst.label()]));
51✔
149
                        break;
6,890✔
150

151
                    case IR::Kind::Opcode:
152
                        [[fallthrough]];
153
                    case IR::Kind::Opcode2Args:
154
                        pushWord(inst.bytecode());
6,839✔
155
                        break;
7,519✔
156

157
                    default:
158
                        break;
680✔
159
                }
8,207✔
160
            }
8,207✔
161
        }
133✔
162
    }
27✔
163

164
    void IRCompiler::pushWord(const Word& word)
7,527✔
165
    {
7,527✔
166
        m_bytecode.push_back(word.opcode);
7,527✔
167
        m_bytecode.push_back(word.byte_1);
7,527✔
168
        m_bytecode.push_back(word.byte_2);
7,527✔
169
        m_bytecode.push_back(word.byte_3);
7,527✔
170
    }
7,527✔
171

172
    void IRCompiler::pushFileHeader() noexcept
27✔
173
    {
27✔
174
        /*
175
            Generating headers:
176
                - lang name (to be sure we are executing an ArkScript file)
177
                    on 4 bytes (ark + padding)
178
                - version (major: 2 bytes, minor: 2 bytes, patch: 2 bytes)
179
                - timestamp (8 bytes, unix format)
180
        */
181

182
        m_bytecode.push_back('a');
27✔
183
        m_bytecode.push_back('r');
27✔
184
        m_bytecode.push_back('k');
27✔
185
        m_bytecode.push_back(0_u8);
27✔
186

187
        // push version
188
        for (const int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
108✔
189
        {
190
            m_bytecode.push_back(static_cast<uint8_t>((n & 0xff00) >> 8));
81✔
191
            m_bytecode.push_back(static_cast<uint8_t>(n & 0x00ff));
81✔
192
        }
81✔
193

194
        // push timestamp
195
        const long long timestamp = std::chrono::duration_cast<std::chrono::seconds>(
27✔
196
                                        std::chrono::system_clock::now().time_since_epoch())
27✔
197
                                        .count();
27✔
198
        for (long i = 0; i < 8; ++i)
243✔
199
        {
200
            const long shift = 8 * (7 - i);
216✔
201
            const auto ts_byte = static_cast<uint8_t>((timestamp & (0xffLL << shift)) >> shift);
216✔
202
            m_bytecode.push_back(ts_byte);
216✔
203
        }
216✔
204
    }
27✔
205

206
    void IRCompiler::pushSymAndValTables(const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
27✔
207
    {
27✔
208
        const std::size_t symbol_size = symbols.size();
27✔
209
        if (symbol_size > std::numeric_limits<uint16_t>::max())
27✔
210
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of 2^16 - 1", symbol_size));
×
211

212
        m_bytecode.push_back(SYM_TABLE_START);
27✔
213
        m_bytecode.push_back(static_cast<uint8_t>((symbol_size & 0xff00) >> 8));
27✔
214
        m_bytecode.push_back(static_cast<uint8_t>(symbol_size & 0x00ff));
27✔
215

216
        for (const auto& sym : symbols)
293✔
217
        {
218
            // push the string, null terminated
219
            std::ranges::transform(sym, std::back_inserter(m_bytecode), [](const char i) {
2,150✔
220
                return static_cast<uint8_t>(i);
1,884✔
221
            });
222
            m_bytecode.push_back(0_u8);
266✔
223
        }
266✔
224

225
        const std::size_t value_size = values.size();
27✔
226
        if (value_size > std::numeric_limits<uint16_t>::max())
27✔
227
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of 2^16 - 1", value_size));
×
228

229
        m_bytecode.push_back(VAL_TABLE_START);
27✔
230
        m_bytecode.push_back(static_cast<uint8_t>((value_size & 0xff00) >> 8));
27✔
231
        m_bytecode.push_back(static_cast<uint8_t>(value_size & 0x00ff));
27✔
232

233
        for (const ValTableElem& val : values)
682✔
234
        {
235
            switch (val.type)
655✔
236
            {
111✔
237
                case ValTableElemType::Number:
238
                {
239
                    m_bytecode.push_back(NUMBER_TYPE);
111✔
240
                    const auto n = std::get<double>(val.value);
111✔
241
                    std::string t = std::to_string(n);
111✔
242
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
1,032✔
243
                        return static_cast<uint8_t>(i);
921✔
244
                    });
245
                    break;
246
                }
549✔
247

248
                case ValTableElemType::String:
249
                {
250
                    m_bytecode.push_back(STRING_TYPE);
438✔
251
                    auto t = std::get<std::string>(val.value);
438✔
252
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
6,539✔
253
                        return static_cast<uint8_t>(i);
6,101✔
254
                    });
255
                    break;
256
                }
544✔
257

258
                case ValTableElemType::PageAddr:
259
                {
260
                    m_bytecode.push_back(FUNC_TYPE);
106✔
261
                    const std::size_t addr = std::get<std::size_t>(val.value);
106✔
262
                    m_bytecode.push_back(static_cast<uint8_t>((addr & 0xff00) >> 8));
106✔
263
                    m_bytecode.push_back(static_cast<uint8_t>(addr & 0x00ff));
106✔
264
                    break;
265
                }
106✔
266
            }
655✔
267

268
            m_bytecode.push_back(0_u8);
655✔
269
        }
655✔
270
    }
27✔
271
}
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

© 2025 Coveralls, Inc