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

ArkScript-lang / Ark / 15237873748

25 May 2025 12:18PM UTC coverage: 86.737% (-0.03%) from 86.765%
15237873748

push

github

SuperFola
feat(compiler, vm): adding new APPEND_IN_PLACE_SYM and APPEND_IN_PLACE_SYM_INDEX super instructions to append to a list faster

22 of 23 new or added lines in 2 files covered. (95.65%)

6 existing lines in 2 files now uncovered.

7194 of 8294 relevant lines covered (86.74%)

84293.17 hits per line

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

98.88
/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp
1
#include <Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp>
2

3
#include <cassert>
4
#include <utility>
5
#include <ranges>
6
#include <algorithm>
7

8
#include <Ark/Builtins/Builtins.hpp>
9

10
namespace Ark::internal
11
{
12
    IROptimizer::IROptimizer(const unsigned debug) :
548✔
13
        m_logger("IROptimizer", debug)
274✔
14
    {
274✔
15
        m_ruleset = {
13,152✔
16
            Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
274✔
17
            Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
274✔
18
            Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
274✔
19
            Rule { { LOAD_SYMBOL, STORE }, STORE_FROM },
274✔
20
            Rule { { LOAD_SYMBOL_BY_INDEX, STORE }, STORE_FROM_INDEX },
274✔
21
            Rule { { LOAD_SYMBOL, SET_VAL }, SET_VAL_FROM },
274✔
22
            Rule { { LOAD_SYMBOL_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
274✔
23
            Rule {
274✔
24
                { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities) {
859✔
25
                    return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
585✔
26
                } },
27
            Rule { { LOAD_SYMBOL, CALL }, CALL_SYMBOL },
274✔
28
            Rule { { LOAD_SYMBOL, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
274✔
29
            Rule { { LOAD_SYMBOL_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
274✔
30
            Rule { { LIST, STORE }, STORE_LIST },
274✔
31
            Rule { { LOAD_SYMBOL, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
274✔
32
            Rule { { LOAD_SYMBOL_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
274✔
33
            // LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
34
            // LOAD_CONST n (1)
35
            // ADD / SUB
36
            // STORE
37
            // ---> INCREMENT_STORE / DECREMENT_STORE a value
38
            Rule { { LOAD_CONST, LOAD_SYMBOL, ADD, SET_VAL }, [this](const Entities e) {
717✔
39
                      return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
443✔
40
                  },
41
                   [this](const Entities e) {
717✔
42
                       return IR::Entity(INCREMENT_STORE, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
443✔
43
                   } },
44
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD, SET_VAL }, [this](const Entities e) {
325✔
45
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
51✔
46
                  },
47
                   [this](const Entities e) {
319✔
48
                       return IR::Entity(INCREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
45✔
49
                   } },
50
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB, SET_VAL }, [this](const Entities e) {
285✔
51
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
11✔
52
                  },
53
                   [this](const Entities e) {
275✔
54
                       return IR::Entity(DECREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
1✔
55
                   } },
56
            // without the final store, just increment/decrement
57
            Rule { { LOAD_CONST, LOAD_SYMBOL, ADD }, [this](const Entities e) {
275✔
58
                      return isPositiveNumberInlinable(e[0].primaryArg());
1✔
59
                  },
60
                   [this](const Entities e) {
275✔
61
                       return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
1✔
62
                   } },
63
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD }, [this](const Entities e) {
280✔
64
                      return isPositiveNumberInlinable(e[1].primaryArg());
6✔
65
                  },
66
                   [this](const Entities e) {
274✔
UNCOV
67
                       return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
×
68
                   } },
69
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB }, [this](const Entities e) {
289✔
70
                      return isPositiveNumberInlinable(e[1].primaryArg());
15✔
71
                  },
72
                   [this](const Entities e) {
289✔
73
                       return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
15✔
74
                   } },
75
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, ADD }, [this](const Entities e) {
328✔
76
                      return isPositiveNumberInlinable(e[0].primaryArg());
54✔
77
                  },
78
                   [this](const Entities e) {
328✔
79
                       return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
54✔
80
                   } },
81
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities e) {
293✔
82
                      return isPositiveNumberInlinable(e[1].primaryArg());
19✔
83
                  },
84
                   [this](const Entities e) {
280✔
85
                       return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
6✔
86
                   } },
87
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities e) {
334✔
88
                      return isPositiveNumberInlinable(e[1].primaryArg());
60✔
89
                  },
90
                   [this](const Entities e) {
333✔
91
                       return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
59✔
92
                   } },
93
            // LOAD_SYMBOL list
94
            // TAIL / HEAD
95
            // STORE / SET_VAL a
96
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
97
            Rule { { LOAD_SYMBOL, TAIL, STORE }, [](const Entities e) {
275✔
98
                      return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
99
                  } },
100
            Rule { { LOAD_SYMBOL, TAIL, SET_VAL }, [](const Entities e) {
275✔
101
                      return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
102
                  } },
103
            Rule { { LOAD_SYMBOL, HEAD, STORE }, [](const Entities e) {
275✔
104
                      return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
105
                  } },
106
            Rule { { LOAD_SYMBOL, HEAD, SET_VAL }, [](const Entities e) {
275✔
107
                      return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
108
                  } },
109
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, STORE }, [](const Entities e) {
276✔
110
                      return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
111
                  } },
112
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, SET_VAL }, [](const Entities e) {
276✔
113
                      return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
114
                  } },
115
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, STORE }, [](const Entities e) {
277✔
116
                      return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
117
                  } },
118
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
276✔
119
                      return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
120
                  } },
121
            // LOAD_CONST id / LOAD_SYMBOL id
122
            // <comparison operator>
123
            // POP_JUMP_IF_(FALSE|TRUE)
124
            // ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
125
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
284✔
126
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
10✔
127
                  } },
128
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_TRUE }, [](const Entities e) {
295✔
129
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
21✔
130
                  } },
131
            Rule { { LOAD_SYMBOL, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
413✔
132
                      return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
139✔
133
                  } },
134
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_TRUE }, [](const Entities e) {
306✔
135
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
32✔
136
                  } },
137
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
282✔
138
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
8✔
139
                  } },
140
            Rule { { LOAD_SYMBOL, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
275✔
141
                      return IR::Entity::GotoWithArg(e[2], GT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
1✔
142
                  } },
143
            Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
476✔
144
                      return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
202✔
145
                  } },
146
            Rule { { LOAD_SYMBOL_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
331✔
147
                      return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
57✔
148
                  } },
149
            Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
276✔
150
                      return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
2✔
151
                  } },
152
            Rule { { LOAD_SYMBOL, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
282✔
153
                      return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
8✔
154
                  } },
155
            // LOAD_SYMBOL id
156
            // LOAD_SYMBOL id2
157
            // AT
158
            // ---> AT_SYM_SYM id id2
159
            Rule { { LOAD_SYMBOL, LOAD_SYMBOL, AT }, AT_SYM_SYM },
274✔
160
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
274✔
161
            // LOAD_SYMBOL sym
162
            // TYPE
163
            // LOAD_CONST cst
164
            // EQ
165
            // ---> CHECK_TYPE_OF sym, cst
166
            // also works with LOAD_CONST cst, LOAD_SYMBOL sym, TYPE, EQ, but args will be flipped
167
            Rule { { LOAD_SYMBOL, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
280✔
168
                      return IR::Entity(CHECK_TYPE_OF, e[0].primaryArg(), e[2].primaryArg());
6✔
169
                  } },
170
            Rule { { LOAD_CONST, LOAD_SYMBOL, TYPE, EQ }, [](const Entities e) {
275✔
171
                      return IR::Entity(CHECK_TYPE_OF, e[1].primaryArg(), e[0].primaryArg());
1✔
172
                  } },
173
            Rule { { LOAD_SYMBOL_BY_INDEX, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
277✔
174
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
175
                  } },
176
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, TYPE, EQ }, [](const Entities e) {
317✔
177
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[1].primaryArg(), e[0].primaryArg());
43✔
178
                  } },
179
        };
180
    }
274✔
181

182
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
164✔
183
    {
164✔
184
        m_logger.traceStart("process");
164✔
185
        m_symbols = symbols;
164✔
186
        m_values = values;
164✔
187

188
        for (const auto& block : pages)
1,666✔
189
        {
190
            m_ir.emplace_back();
1,502✔
191
            IR::Block& current_block = m_ir.back();
1,502✔
192

193
            std::size_t i = 0;
1,502✔
194
            const std::size_t end = block.size();
1,502✔
195

196
            while (i < end)
38,243✔
197
            {
198
                std::optional<EntityWithOffset> maybe_compacted = replaceWithRules(
110,223✔
199
                    m_ruleset,
36,741✔
200
                    std::span(
36,741✔
201
                        block.begin() + static_cast<IR::Block::difference_type>(i),
36,741✔
202
                        block.size() - i));
36,741✔
203

204
                if (maybe_compacted.has_value())
36,741✔
205
                {
206
                    auto [entity, offset] = maybe_compacted.value();
18,808✔
207
                    current_block.emplace_back(entity);
9,404✔
208
                    i += offset;
9,404✔
209
                }
9,404✔
210
                else
211
                {
212
                    current_block.emplace_back(block[i]);
27,337✔
213
                    ++i;
27,337✔
214
                }
215
            }
36,741✔
216
        }
1,502✔
217

218
        m_logger.traceEnd();
164✔
219
    }
164✔
220

221
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
164✔
222
    {
164✔
223
        return m_ir;
164✔
224
    }
225

226
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const std::span<const IR::Entity> entities) const
1,384,030✔
227
    {
1,384,030✔
228
        if (expected_insts.size() > entities.size())
1,384,030✔
229
            return false;
109,706✔
230

231
        for (std::size_t i = 0; i < expected_insts.size(); ++i)
2,698,636✔
232
        {
233
            if (expected_insts[i] != entities[i].inst())
1,424,312✔
234
                return false;
1,264,881✔
235
        }
159,431✔
236

237
        return true;
9,443✔
238
    }
1,384,030✔
239

240
    bool IROptimizer::canBeOptimizedSafely(std::span<const IR::Entity> entities, std::size_t window_size) const
9,404✔
241
    {
9,404✔
242
        // check that we can actually safely apply the optimization on the given instructions
243
        return std::ranges::none_of(
9,404✔
244
            entities | std::ranges::views::take(window_size),
9,404✔
245
            [](const IR::Entity& entity) {
21,007✔
246
                return entity.primaryArg() > IR::MaxValueForDualArg;
21,007✔
247
            });
248
    }
249

250
    std::optional<EntityWithOffset> IROptimizer::replaceWithRules(const std::vector<Rule>& rules, const std::span<const IR::Entity> entities)
36,741✔
251
    {
36,741✔
252
        for (const auto& [expected, condition, createReplacement] : rules)
1,449,022✔
253
        {
254
            if (match(expected, entities) && condition(entities))
1,384,030✔
255
            {
256
                const std::size_t window_size = expected.size();
18,808✔
257
                if (!canBeOptimizedSafely(entities, window_size))
9,404✔
258
                    return std::nullopt;  // no need to try other optimizations, they won't be applied either
×
259

260
                auto output = createReplacement(entities);
18,808✔
261

262
                if (const auto it = std::ranges::find_if(entities, [](const auto& entity) {
35,808✔
263
                        return entity.hasValidSourceLocation();
26,404✔
264
                    });
265
                    it != entities.end())
18,808✔
266
                    output.setSourceLocation(it->filename(), it->sourceLine());
9,404✔
267

268
                return EntityWithOffset { output, window_size };
9,404✔
269
            }
9,404✔
270
        }
1,384,030✔
271

272
        return std::nullopt;
27,337✔
273
    }
36,741✔
274

275
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
660✔
276
    {
660✔
277
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
660✔
278
        {
279
            const double val = std::get<double>(m_values[id].value);
637✔
280
            return val >= 0.0 &&
1,274✔
281
                val < IR::MaxValueForDualArg &&
637✔
282
                static_cast<double>(static_cast<long>(val)) == val;
636✔
283
        }
637✔
284
        return false;
23✔
285
    }
660✔
286

287
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
624✔
288
    {
624✔
289
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
624✔
290
    }
291
}
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