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

ArkScript-lang / Ark / 15214772429

23 May 2025 04:19PM UTC coverage: 86.895% (+0.2%) from 86.726%
15214772429

push

github

SuperFola
feat(compiler, vm): adding new AT_SYM_SYM and AT_SYM_INDEX_SYM_INDEX super instructions to get elements from list in a single instruction

40 of 44 new or added lines in 2 files covered. (90.91%)

178 existing lines in 8 files now uncovered.

7095 of 8165 relevant lines covered (86.9%)

86688.13 hits per line

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

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

3
#include <cassert>
4
#include <utility>
5
#include <Ark/Builtins/Builtins.hpp>
6

7
namespace Ark::internal
8
{
9
    struct EntityWithOffset
63,435✔
10
    {
11
        IR::Entity entity;
12
        std::size_t offset;
13
    };
14

15
    IROptimizer::IROptimizer(const unsigned debug) :
546✔
16
        m_logger("IROptimizer", debug)
273✔
17
    {
273✔
18
        m_ruleset_two = {
3,276✔
19
            Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
273✔
20
            Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
273✔
21
            Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
273✔
22
            Rule { { LOAD_SYMBOL, STORE }, STORE_FROM },
273✔
23
            Rule { { LOAD_SYMBOL_BY_INDEX, STORE }, STORE_FROM_INDEX },
273✔
24
            Rule { { LOAD_SYMBOL, SET_VAL }, SET_VAL_FROM },
273✔
25
            Rule { { LOAD_SYMBOL_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
273✔
26
            Rule {
273✔
27
                { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities& entities) {
848✔
28
                    return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
575✔
29
                } },
30
            Rule { { LOAD_SYMBOL, CALL }, CALL_SYMBOL },
273✔
31
            Rule { { LOAD_SYMBOL, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
273✔
32
            Rule { { LOAD_SYMBOL_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
273✔
33
        };
34

35
        m_ruleset_three = {
6,006✔
36
            // LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
37
            // LOAD_CONST n (1)
38
            // ADD / SUB
39
            // ---> INCREMENT / DECREMENT a value
40
            Rule {
273✔
41
                { LOAD_CONST, LOAD_SYMBOL, ADD }, [this](const Entities& e) {
716✔
42
                    return isPositiveNumberInlinable(e[0].primaryArg());
443✔
43
                },
44
                [this](const Entities& e) {
716✔
45
                    return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
443✔
46
                } },
47
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD }, [this](const Entities& e) {
323✔
48
                      return isPositiveNumberInlinable(e[1].primaryArg());
50✔
49
                  },
50
                   [this](const Entities& e) {
317✔
51
                       return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
44✔
52
                   } },
53
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB }, [this](const Entities& e) {
288✔
54
                      return isPositiveNumberInlinable(e[1].primaryArg());
15✔
55
                  },
56
                   [this](const Entities& e) {
288✔
57
                       return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
15✔
58
                   } },
59
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, ADD }, [this](const Entities& e) {
327✔
60
                      return isPositiveNumberInlinable(e[0].primaryArg());
54✔
61
                  },
62
                   [this](const Entities& e) {
327✔
63
                       return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
54✔
64
                   } },
65
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities& e) {
292✔
66
                      return isPositiveNumberInlinable(e[1].primaryArg());
19✔
67
                  },
68
                   [this](const Entities& e) {
279✔
69
                       return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
6✔
70
                   } },
71
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities& e) {
333✔
72
                      return isPositiveNumberInlinable(e[1].primaryArg());
60✔
73
                  },
74
                   [this](const Entities& e) {
332✔
75
                       return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
59✔
76
                   } },
77
            // LOAD_SYMBOL list
78
            // TAIL / HEAD
79
            // STORE / SET_VAL a
80
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
81
            Rule { { LOAD_SYMBOL, TAIL, STORE }, [](const Entities& e) {
274✔
82
                      return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
83
                  } },
84
            Rule { { LOAD_SYMBOL, TAIL, SET_VAL }, [](const Entities& e) {
274✔
85
                      return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
1✔
86
                  } },
87
            Rule { { LOAD_SYMBOL, HEAD, STORE }, [](const Entities& e) {
274✔
88
                      return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
89
                  } },
90
            Rule { { LOAD_SYMBOL, HEAD, SET_VAL }, [](const Entities& e) {
274✔
91
                      return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
1✔
92
                  } },
93
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, STORE }, [](const Entities& e) {
275✔
94
                      return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
95
                  } },
96
            Rule { { LOAD_SYMBOL_BY_INDEX, TAIL, SET_VAL }, [](const Entities& e) {
275✔
97
                      return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
98
                  } },
99
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, STORE }, [](const Entities& e) {
276✔
100
                      return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
101
                  } },
102
            Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, SET_VAL }, [](const Entities& e) {
275✔
103
                      return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
104
                  } },
105
            // LOAD_CONST id / LOAD_SYMBOL id
106
            // <comparison operator>
107
            // POP_JUMP_IF_(FALSE|TRUE)
108
            // ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
109
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities& e) {
282✔
110
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
9✔
111
                  } },
112
            Rule { { LOAD_SYMBOL, LT, POP_JUMP_IF_FALSE }, [](const Entities& e) {
412✔
113
                      return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
139✔
114
                  } },
115
            Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities& e) {
482✔
116
                      return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
209✔
117
                  } },
118
            Rule { { LOAD_SYMBOL_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities& e) {
329✔
119
                      return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
56✔
120
                  } },
121
            Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities& e) {
275✔
122
                      return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
2✔
123
                  } },
124
            // LOAD_SYMBOL id
125
            // LOAD_SYMBOL id2
126
            // AT
127
            // ---> AT_SYM_SYM id id2
128
            Rule { { LOAD_SYMBOL, LOAD_SYMBOL, AT }, AT_SYM_SYM }, Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX }
273✔
129
        };
130
    }
273✔
131

132
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
163✔
133
    {
163✔
134
        m_logger.traceStart("process");
163✔
135
        m_symbols = symbols;
163✔
136
        m_values = values;
163✔
137

138
        auto map = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> decltype(std::optional(lambda(opt.value()))) {
66,017✔
139
            if (opt.has_value())
65,854✔
140
                return lambda(opt.value());
8,532✔
141
            return std::nullopt;
57,322✔
142
        };
65,854✔
143

144
        auto or_else = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> std::optional<T> {
35,975✔
145
            if (!opt.has_value())
35,812✔
146
                return lambda();
28,887✔
147
            return opt;
6,925✔
148
        };
35,812✔
149

150
        for (const auto& block : pages)
1,663✔
151
        {
152
            m_ir.emplace_back();
1,500✔
153
            IR::Block& current_block = m_ir.back();
1,500✔
154

155
            std::size_t i = 0;
1,500✔
156
            const std::size_t end = block.size();
1,500✔
157

158
            while (i < end)
39,895✔
159
            {
160
                std::optional<EntityWithOffset> maybe_compacted = std::nullopt;
38,395✔
161

162
                if (i + 1 < end)
38,395✔
163
                    maybe_compacted = map(
73,934✔
164
                        replaceWithRules(m_ruleset_two, { block[i], block[i + 1] }),
36,967✔
165
                        [](const auto& entity) {
43,963✔
166
                            return std::make_optional<EntityWithOffset>(entity, 2);
6,996✔
167
                        });
168
                if (i + 2 < end)
38,395✔
169
                    maybe_compacted = or_else(
35,812✔
170
                        maybe_compacted,
171
                        [&, this]() {
64,699✔
172
                            return map(
57,774✔
173
                                replaceWithRules(m_ruleset_three, { block[i], block[i + 1], block[i + 2] }),
28,887✔
174
                                [](const auto& entity) {
30,423✔
175
                                    return std::make_optional<EntityWithOffset>(entity, 3);
1,536✔
176
                                });
UNCOV
177
                        });
×
178

179
                if (maybe_compacted.has_value())
38,395✔
180
                {
181
                    auto [entity, offset] = maybe_compacted.value();
17,064✔
182
                    current_block.emplace_back(entity);
8,532✔
183
                    i += offset;
8,532✔
184
                }
8,532✔
185
                else
186
                {
187
                    current_block.emplace_back(block[i]);
29,863✔
188
                    ++i;
29,863✔
189
                }
190
            }
38,395✔
191
        }
1,500✔
192

193
        m_logger.traceEnd();
163✔
194
    }
163✔
195

196
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
163✔
197
    {
163✔
198
        return m_ir;
163✔
199
    }
200

201
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const Entities& entities) const
960,830✔
202
    {
960,830✔
203
        assert(expected_insts.size() == entities.size() && "Mismatching size between expected instructions and given entities");
960,830✔
204

205
        for (std::size_t i = 0; i < expected_insts.size(); ++i)
2,035,807✔
206
        {
207
            if (expected_insts[i] != entities[i].inst())
1,074,977✔
208
                return false;
952,275✔
209
        }
122,702✔
210

211
        return true;
8,555✔
212
    }
960,830✔
213

214
    std::optional<IR::Entity> IROptimizer::replaceWithRules(const std::vector<Rule>& rules, const Entities& entities)
65,854✔
215
    {
65,854✔
216
        for (auto&& entity : entities)
226,449✔
217
        {
218
            if (entity.primaryArg() > IR::MaxValueForDualArg)
160,595✔
UNCOV
219
                return std::nullopt;
×
220
        }
160,595✔
221

222
        for (const auto& [expected, condition, createReplacement] : rules)
1,043,771✔
223
        {
224
            if (match(expected, entities) && condition(entities))
960,830✔
225
            {
226
                auto output = createReplacement(entities);
17,064✔
227

228
                if (auto it = std::ranges::find_if(entities, [](const auto& entity) {
26,704✔
229
                        return entity.hasValidSourceLocation();
18,172✔
230
                    });
231
                    it != entities.end())
15,424✔
232
                    output.setSourceLocation(it->filename(), it->sourceLine());
6,892✔
233

234
                return output;
8,532✔
235
            }
8,532✔
236
        }
960,830✔
237

238
        return std::nullopt;
57,322✔
239
    }
65,854✔
240

241
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
641✔
242
    {
641✔
243
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
641✔
244
        {
245
            const double val = std::get<double>(m_values[id].value);
624✔
246
            return val >= 0.0 &&
1,248✔
247
                val < IR::MaxValueForDualArg &&
624✔
248
                static_cast<double>(static_cast<long>(val)) == val;
623✔
249
        }
624✔
250
        return false;
17✔
251
    }
641✔
252

253
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
621✔
254
    {
621✔
255
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
621✔
256
    }
257
}
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