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

ArkScript-lang / Ark / 15453345670

04 Jun 2025 09:40PM UTC coverage: 86.735% (+0.1%) from 86.608%
15453345670

push

github

SuperFola
chore: updating fuzzing corpus

2 of 3 new or added lines in 2 files covered. (66.67%)

140 existing lines in 7 files now uncovered.

7258 of 8368 relevant lines covered (86.74%)

120482.26 hits per line

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

98.95
/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) :
578✔
13
        m_logger("IROptimizer", debug)
289✔
14
    {
289✔
15
        m_ruleset = {
14,739✔
16
            Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
289✔
17
            Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
289✔
18
            Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
289✔
19
            Rule { { LOAD_SYMBOL, STORE }, STORE_FROM },
289✔
20
            Rule { { LOAD_SYMBOL_BY_INDEX, STORE }, STORE_FROM_INDEX },
289✔
21
            Rule { { LOAD_SYMBOL, SET_VAL }, SET_VAL_FROM },
289✔
22
            Rule { { LOAD_SYMBOL_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
289✔
23
            Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_SYMBOL_BY_INDEX, BUILTIN, CALL }, [](const Entities entities) {
797✔
24
                      return Builtins::builtins[entities[3].primaryArg()].second.isFunction();
508✔
25
                  },
26
                   [](const Entities e) {
797✔
27
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1);
508✔
28
                   } },
29
            Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, BUILTIN, CALL }, [](const Entities entities) {
350✔
30
                      return Builtins::builtins[entities[5].primaryArg()].second.isFunction();
61✔
31
                  },
32
                   [](const Entities e) {
350✔
33
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2);
61✔
34
                   } },
35
            Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, BUILTIN, CALL }, [](const Entities entities) {
327✔
36
                      return Builtins::builtins[entities[7].primaryArg()].second.isFunction();
38✔
37
                  },
38
                   [](const Entities e) {
327✔
39
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3);
38✔
40
                   } },
41
            Rule { { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities) {
704✔
42
                      return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
415✔
43
                  } },
44
            Rule { { LOAD_SYMBOL, CALL }, CALL_SYMBOL },
289✔
45
            Rule { { LOAD_SYMBOL, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
289✔
46
            Rule { { LOAD_SYMBOL_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
289✔
47
            Rule { { LIST, STORE }, STORE_LIST },
289✔
48
            Rule { { LOAD_SYMBOL, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
289✔
49
            Rule { { LOAD_SYMBOL_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
289✔
50
            // LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
51
            // LOAD_CONST n (1)
52
            // ADD / SUB
53
            // STORE
54
            // ---> INCREMENT_STORE / DECREMENT_STORE a value
55
            Rule { { LOAD_CONST, LOAD_SYMBOL, ADD, SET_VAL }, [this](const Entities e) {
787✔
56
                      return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
498✔
57
                  },
58
                   [this](const Entities e) {
787✔
59
                       return IR::Entity(INCREMENT_STORE, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
498✔
60
                   } },
61
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD, SET_VAL }, [this](const Entities e) {
356✔
62
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
67✔
63
                  },
64
                   [this](const Entities e) {
347✔
65
                       return IR::Entity(INCREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
58✔
66
                   } },
67
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB, SET_VAL }, [this](const Entities e) {
303✔
68
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
14✔
69
                  },
70
                   [this](const Entities e) {
290✔
71
                       return IR::Entity(DECREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
1✔
72
                   } },
73
            // without the final store, just increment/decrement
74
            Rule { { LOAD_CONST, LOAD_SYMBOL, ADD }, [this](const Entities e) {
290✔
75
                      return isPositiveNumberInlinable(e[0].primaryArg());
1✔
76
                  },
77
                   [this](const Entities e) {
290✔
78
                       return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
1✔
79
                   } },
80
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD }, [this](const Entities e) {
298✔
81
                      return isPositiveNumberInlinable(e[1].primaryArg());
9✔
82
                  },
83
                   [this](const Entities e) {
289✔
UNCOV
84
                       return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
×
85
                   } },
86
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB }, [this](const Entities e) {
308✔
87
                      return isPositiveNumberInlinable(e[1].primaryArg());
19✔
88
                  },
89
                   [this](const Entities e) {
308✔
90
                       return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
19✔
91
                   } },
92
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, ADD }, [this](const Entities e) {
348✔
93
                      return isPositiveNumberInlinable(e[0].primaryArg());
59✔
94
                  },
95
                   [this](const Entities e) {
348✔
96
                       return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
59✔
97
                   } },
98
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities e) {
308✔
99
                      return isPositiveNumberInlinable(e[1].primaryArg());
19✔
100
                  },
101
                   [this](const Entities e) {
295✔
102
                       return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
6✔
103
                   } },
104
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities e) {
353✔
105
                      return isPositiveNumberInlinable(e[1].primaryArg());
64✔
106
                  },
107
                   [this](const Entities e) {
352✔
108
                       return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
63✔
109
                   } },
110
            // LOAD_SYMBOL list
111
            // TAIL / HEAD
112
            // STORE / SET_VAL a
113
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
114
            Rule { { LOAD_SYMBOL, TAIL, STORE }, [](const Entities e) {
290✔
115
                      return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
116
                  } },
117
            Rule { { LOAD_SYMBOL, TAIL, SET_VAL }, [](const Entities e) {
290✔
118
                      return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
119
                  } },
120
            Rule { { LOAD_SYMBOL, HEAD, STORE }, [](const Entities e) {
290✔
121
                      return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
122
                  } },
123
            Rule { { LOAD_SYMBOL, HEAD, SET_VAL }, [](const Entities e) {
290✔
124
                      return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
125
                  } },
126
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, STORE }, [](const Entities e) {
291✔
127
                      return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
128
                  } },
129
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, SET_VAL }, [](const Entities e) {
291✔
130
                      return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
131
                  } },
132
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, STORE }, [](const Entities e) {
292✔
133
                      return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
134
                  } },
135
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
291✔
136
                      return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
137
                  } },
138
            // LOAD_CONST id / LOAD_SYMBOL id
139
            // <comparison operator>
140
            // POP_JUMP_IF_(FALSE|TRUE)
141
            // ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
142
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
299✔
143
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
10✔
144
                  } },
145
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_TRUE }, [](const Entities e) {
313✔
146
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
24✔
147
                  } },
148
            Rule { { LOAD_SYMBOL, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
444✔
149
                      return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
155✔
150
                  } },
151
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_TRUE }, [](const Entities e) {
322✔
152
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
33✔
153
                  } },
154
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
300✔
155
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
11✔
156
                  } },
157
            Rule { { LOAD_SYMBOL, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
290✔
158
                      return IR::Entity::GotoWithArg(e[2], GT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
1✔
159
                  } },
160
            Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
504✔
161
                      return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
215✔
162
                  } },
163
            Rule { { LOAD_SYMBOL_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
376✔
164
                      return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
87✔
165
                  } },
166
            Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
291✔
167
                      return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
2✔
168
                  } },
169
            Rule { { LOAD_SYMBOL, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
300✔
170
                      return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
11✔
171
                  } },
172
            // LOAD_SYMBOL id
173
            // LOAD_SYMBOL id2
174
            // AT
175
            // ---> AT_SYM_SYM id id2
176
            Rule { { LOAD_SYMBOL, LOAD_SYMBOL, AT }, AT_SYM_SYM },
289✔
177
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
289✔
178
            // LOAD_SYMBOL sym
179
            // TYPE
180
            // LOAD_CONST cst
181
            // EQ
182
            // ---> CHECK_TYPE_OF sym, cst
183
            // also works with LOAD_CONST cst, LOAD_SYMBOL sym, TYPE, EQ, but args will be flipped
184
            Rule { { LOAD_SYMBOL, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
296✔
185
                      return IR::Entity(CHECK_TYPE_OF, e[0].primaryArg(), e[2].primaryArg());
7✔
186
                  } },
187
            Rule { { LOAD_CONST, LOAD_SYMBOL, TYPE, EQ }, [](const Entities e) {
290✔
188
                      return IR::Entity(CHECK_TYPE_OF, e[1].primaryArg(), e[0].primaryArg());
1✔
189
                  } },
190
            Rule { { LOAD_SYMBOL_BY_INDEX, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
292✔
191
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
192
                  } },
193
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, TYPE, EQ }, [](const Entities e) {
334✔
194
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[1].primaryArg(), e[0].primaryArg());
45✔
195
                  } },
196
        };
197
    }
289✔
198

199
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
177✔
200
    {
177✔
201
        m_logger.traceStart("process");
177✔
202
        m_symbols = symbols;
177✔
203
        m_values = values;
177✔
204

205
        for (const auto& block : pages)
2,475✔
206
        {
207
            m_ir.emplace_back();
2,298✔
208
            IR::Block& current_block = m_ir.back();
2,298✔
209

210
            std::size_t i = 0;
2,298✔
211
            const std::size_t end = block.size();
2,298✔
212

213
            while (i < end)
54,881✔
214
            {
215
                std::optional<EntityWithOffset> maybe_compacted = replaceWithRules(
157,749✔
216
                    m_ruleset,
52,583✔
217
                    std::span(
52,583✔
218
                        block.begin() + static_cast<IR::Block::difference_type>(i),
52,583✔
219
                        block.size() - i));
52,583✔
220

221
                if (maybe_compacted.has_value())
52,583✔
222
                {
223
                    auto [entity, offset] = maybe_compacted.value();
23,668✔
224
                    current_block.emplace_back(entity);
11,834✔
225
                    i += offset;
11,834✔
226
                }
11,834✔
227
                else
228
                {
229
                    current_block.emplace_back(block[i]);
40,749✔
230
                    ++i;
40,749✔
231
                }
232
            }
52,583✔
233
        }
2,298✔
234

235
        m_logger.traceEnd();
177✔
236
    }
177✔
237

238
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
177✔
239
    {
177✔
240
        return m_ir;
177✔
241
    }
242

243
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const std::span<const IR::Entity> entities) const
2,176,354✔
244
    {
2,176,354✔
245
        if (expected_insts.size() > entities.size())
2,176,354✔
246
            return false;
213,872✔
247

248
        for (std::size_t i = 0; i < expected_insts.size(); ++i)
4,119,802✔
249
        {
250
            if (expected_insts[i] != entities[i].inst())
2,157,320✔
251
                return false;
1,950,600✔
252
        }
206,720✔
253

254
        return true;
11,882✔
255
    }
2,176,354✔
256

257
    bool IROptimizer::canBeOptimizedSafely(std::span<const IR::Entity> entities, std::size_t window_size) const
11,834✔
258
    {
11,834✔
259
        // check that we can actually safely apply the optimization on the given instructions
260
        return std::ranges::none_of(
11,834✔
261
            entities | std::ranges::views::take(window_size),
11,834✔
262
            [](const IR::Entity& entity) {
28,232✔
263
                return entity.primaryArg() > IR::MaxValueForDualArg;
28,232✔
264
            });
265
    }
266

267
    std::optional<EntityWithOffset> IROptimizer::replaceWithRules(const std::vector<Rule>& rules, const std::span<const IR::Entity> entities)
52,583✔
268
    {
52,583✔
269
        for (const auto& [expected, condition, createReplacement] : rules)
2,264,487✔
270
        {
271
            if (match(expected, entities) && condition(entities))
2,176,354✔
272
            {
273
                const std::size_t window_size = expected.size();
23,668✔
274
                if (!canBeOptimizedSafely(entities, window_size))
11,834✔
UNCOV
275
                    return std::nullopt;  // no need to try other optimizations, they won't be applied either
×
276

277
                auto output = createReplacement(entities);
23,668✔
278

279
                if (const auto it = std::ranges::find_if(entities, [](const auto& entity) {
46,597✔
280
                        return entity.hasValidSourceLocation();
34,763✔
281
                    });
282
                    it != entities.end())
23,668✔
283
                    output.setSourceLocation(it->filename(), it->sourceLine());
11,834✔
284

285
                return EntityWithOffset { output, window_size };
11,834✔
286
            }
11,834✔
287
        }
2,176,354✔
288

289
        return std::nullopt;
40,749✔
290
    }
52,583✔
291

292
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
750✔
293
    {
750✔
294
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
750✔
295
        {
296
            const double val = std::get<double>(m_values[id].value);
721✔
297
            return val >= 0.0 &&
1,442✔
298
                val < IR::MaxValueForDualArg &&
721✔
299
                static_cast<double>(static_cast<long>(val)) == val;
720✔
300
        }
721✔
301
        return false;
29✔
302
    }
750✔
303

304
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
705✔
305
    {
705✔
306
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
705✔
307
    }
308
}
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