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

ArkScript-lang / Ark / 13990508998

21 Mar 2025 11:14AM UTC coverage: 78.752% (-0.1%) from 78.852%
13990508998

Pull #519

github

web-flow
Merge 78bb993e7 into 4aa4303da
Pull Request #519: Feat/better locals

227 of 255 new or added lines in 10 files covered. (89.02%)

28 existing lines in 2 files now uncovered.

5982 of 7596 relevant lines covered (78.75%)

77962.89 hits per line

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

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

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

6
namespace Ark::internal
7
{
8
    struct EntityWithOffset
9
    {
10
        IR::Entity entity;
11
        std::size_t offset;
12
    };
13

14
    IROptimizer::IROptimizer(const unsigned debug) :
392✔
15
        m_logger("IROptimizer", debug)
196✔
16
    {
196✔
17
        m_ruleset_two = {
1,764✔
18
            Rule {
196✔
19
                { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
196✔
20
            Rule {
392✔
21
                { LOAD_CONST, STORE }, LOAD_CONST_STORE },
196✔
22
            Rule {
392✔
23
                { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
196✔
24
            Rule {
392✔
25
                { LOAD_SYMBOL, STORE }, STORE_FROM },
196✔
26
            Rule {
392✔
27
                { LOAD_SYMBOL_BY_INDEX, STORE }, STORE_FROM_INDEX },
196✔
28
            Rule {
392✔
29
                { LOAD_SYMBOL, SET_VAL }, SET_VAL_FROM },
196✔
30
            Rule {
392✔
31
                { LOAD_SYMBOL_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
196✔
32
            Rule {
392✔
33
                { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities& entities) {
697✔
34
                    return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
501✔
35
                } }
36
        };
37

38
        m_ruleset_three = {
2,156✔
39
            // LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
40
            // LOAD_CONST n (1)
41
            // ADD / SUB
42
            // ---> INCREMENT / DECREMENT a value
43
            Rule {
196✔
44
                { LOAD_CONST, LOAD_SYMBOL, ADD }, INCREMENT, [this](const Entities& e) {
591✔
45
                    return isPositiveNumberInlinable(e[0].primaryArg());
395✔
46
                },
47
                [this](const Entities& e) {
591✔
48
                    return std::make_pair(e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
395✔
49
                } },
50
            Rule { { LOAD_SYMBOL, LOAD_CONST, ADD }, INCREMENT, [this](const Entities& e) {
239✔
51
                      return isPositiveNumberInlinable(e[1].primaryArg());
43✔
52
                  },
53
                   [this](const Entities& e) {
239✔
54
                       return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
43✔
55
                   } },
56
            Rule { { LOAD_SYMBOL, LOAD_CONST, SUB }, DECREMENT, [this](const Entities& e) {
211✔
57
                      return isPositiveNumberInlinable(e[1].primaryArg());
15✔
58
                  },
2,156✔
59
                   [this](const Entities& e) {
211✔
60
                       return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
15✔
61
                   } },
62
            Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, ADD }, INCREMENT_BY_INDEX, [this](const Entities& e) {
1,810✔
63
                      return isPositiveNumberInlinable(e[0].primaryArg());
46✔
64
                  },
65
                   [this](const Entities& e) {
242✔
66
                       return std::make_pair(e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
46✔
67
                   } },
68
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, ADD }, INCREMENT_BY_INDEX, [this](const Entities& e) {
214✔
69
                      return isPositiveNumberInlinable(e[1].primaryArg());
18✔
70
                  },
71
                   [this](const Entities& e) {
201✔
72
                       return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
5✔
73
                   } },
74
            Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, SUB }, DECREMENT_BY_INDEX, [this](const Entities& e) {
252✔
75
                      return isPositiveNumberInlinable(e[1].primaryArg());
56✔
76
                  },
77
                   [this](const Entities& e) {
251✔
78
                       return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
55✔
79
                   } },
80
            // LOAD_SYMBOL list
81
            // TAIL / HEAD
82
            // STORE / SET_VAL a
83
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
84
            Rule { .expected = { LOAD_SYMBOL, TAIL, STORE }, .replacement = STORE_TAIL, .createReplacement = [](const Entities& e) {
196✔
NEW
85
                      return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
×
86
                  } },
87
            Rule { .expected = { LOAD_SYMBOL, TAIL, SET_VAL }, .replacement = SET_VAL_TAIL, .createReplacement = [](const Entities& e) {
196✔
NEW
88
                      return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
×
89
                  } },
90
            Rule { .expected = { LOAD_SYMBOL, HEAD, STORE }, .replacement = STORE_HEAD, .createReplacement = [](const Entities& e) {
196✔
NEW
91
                      return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
×
92
                  } },
93
            Rule { .expected = { LOAD_SYMBOL, HEAD, SET_VAL }, .replacement = SET_VAL_HEAD, .createReplacement = [](const Entities& e) {
196✔
NEW
94
                      return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
×
95
                  } }
96
        };
97
    }
196✔
98

99
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
88✔
100
    {
88✔
101
        m_logger.traceStart("process");
88✔
102
        m_symbols = symbols;
88✔
103
        m_values = values;
88✔
104

105
        auto map = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> decltype(std::optional(lambda(opt.value()))) {
69,464✔
106
            if (opt.has_value())
69,376✔
107
                return lambda(opt.value());
4,155✔
108
            return std::nullopt;
65,221✔
109
        };
69,376✔
110

111
        auto or_else = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> std::optional<T> {
35,967✔
112
            if (!opt.has_value())
35,879✔
113
                return lambda();
32,310✔
114
            return opt;
3,569✔
115
        };
35,879✔
116

117
        for (const auto& block : pages)
1,394✔
118
        {
119
            m_ir.emplace_back();
1,306✔
120
            IR::Block& current_block = m_ir.back();
1,306✔
121

122
            std::size_t i = 0;
1,306✔
123
            const std::size_t end = block.size();
1,306✔
124

125
            while (i < end)
39,651✔
126
            {
127
                std::optional<EntityWithOffset> maybe_compacted = std::nullopt;
38,345✔
128

129
                if (i + 1 < end)
38,345✔
130
                    maybe_compacted = map(
74,132✔
131
                        replaceWithRules(m_ruleset_two, { block[i], block[i + 1] }),
37,066✔
132
                        [](const auto& entity) {
40,662✔
133
                            return std::make_optional<EntityWithOffset>(entity, 2);
3,596✔
134
                        });
135
                if (i + 2 < end)
38,345✔
136
                    maybe_compacted = or_else(
35,879✔
137
                        maybe_compacted,
138
                        [&, this]() {
68,189✔
139
                            return map(
64,620✔
140
                                replaceWithRules(m_ruleset_three, { block[i], block[i + 1], block[i + 2] }),
32,310✔
141
                                [](const auto& entity) {
32,869✔
142
                                    return std::make_optional<EntityWithOffset>(entity, 3);
559✔
143
                                });
UNCOV
144
                        });
×
145

146
                if (maybe_compacted.has_value())
38,345✔
147
                {
148
                    auto [entity, offset] = maybe_compacted.value();
4,155✔
149
                    current_block.emplace_back(entity);
4,155✔
150
                    i += offset;
4,155✔
151
                }
4,155✔
152
                else
153
                {
154
                    current_block.emplace_back(block[i]);
34,190✔
155
                    ++i;
34,190✔
156
                }
157
            }
38,345✔
158
        }
1,306✔
159

160
        m_logger.traceEnd();
88✔
161
    }
88✔
162

163
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
88✔
164
    {
88✔
165
        return m_ir;
88✔
166
    }
167

168
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const Entities& entities) const
595,354✔
169
    {
595,354✔
170
        assert(expected_insts.size() == entities.size() && "Mismatching size between expected instructions and given entities");
595,354✔
171

172
        for (std::size_t i = 0; i < expected_insts.size(); ++i)
1,267,101✔
173
        {
174
            if (expected_insts[i] != entities[i].inst())
671,747✔
175
                return false;
591,183✔
176
        }
80,564✔
177

178
        return true;
4,171✔
179
    }
595,354✔
180

181
    std::optional<IR::Entity> IROptimizer::replaceWithRules(const std::vector<Rule>& rules, const Entities& entities)
69,376✔
182
    {
69,376✔
183
        for (auto&& entity : entities)
240,438✔
184
        {
185
            if (entity.primaryArg() > IR::MaxValueForDualArg)
171,062✔
NEW
186
                return std::nullopt;
×
187
        }
171,062✔
188

189
        for (const auto& [expected, replacement, condition, createReplacement] : rules)
677,211✔
190
        {
191
            if (match(expected, entities) && condition(entities))
595,354✔
192
            {
193
                auto [first, second] = createReplacement(entities);
8,310✔
194
                return IR::Entity(replacement, first, second);
16,620✔
195
            }
4,155✔
196
        }
595,354✔
197

198
        return std::nullopt;
65,221✔
199
    }
69,376✔
200

201
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
573✔
202
    {
573✔
203
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
573✔
204
        {
205
            const double val = std::get<double>(m_values[id].value);
562✔
206
            return val >= 0.0 &&
1,124✔
207
                val < IR::MaxValueForDualArg &&
562✔
208
                static_cast<double>(static_cast<long>(val)) == val;
561✔
209
        }
562✔
210
        return false;
11✔
211
    }
573✔
212

213
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
559✔
214
    {
559✔
215
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
559✔
216
    }
217
}
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