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

ArkScript-lang / Ark / 29851449016

21 Jul 2026 05:05PM UTC coverage: 94.39% (+0.005%) from 94.385%
29851449016

push

github

SuperFola
fix(compiler): keep provenance information of IR blocks inside the IROptimiser

9 of 9 new or added lines in 1 file covered. (100.0%)

122 existing lines in 10 files now uncovered.

10700 of 11336 relevant lines covered (94.39%)

1164644.99 hits per line

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

96.28
/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
    IR::Entity fuseMathOps3(const std::span<const IR::Entity> e)
106✔
13
    {
106✔
14
        return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), e[2].inst());
106✔
15
    }
16

17
    IR::Entity fuseMathOps2(const std::span<const IR::Entity> e)
569✔
18
    {
569✔
19
        return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), NOP);
569✔
20
    }
21

22
    IROptimizer::IROptimizer(const unsigned debug) :
1,112✔
23
        Pass("IROptimizer", debug)
556✔
24
    {
1,112✔
25
        // TODO: we could add rules to optimize (<math> <const> <const>) to have a precomputed value instead
26
        // TODO: same for (<cmp> <const> <const>)
27
        // TODO: optimize for LOAD x, STORE a, LOAD a?
28
        m_ruleset = {
32,248✔
29
            Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
556✔
30
            Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
556✔
31
            Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
556✔
32
            Rule { { LOAD_FAST, STORE }, STORE_FROM },
556✔
33
            Rule { { LOAD_FAST_BY_INDEX, STORE }, STORE_FROM_INDEX },
556✔
34
            Rule { { LOAD_FAST, SET_VAL }, SET_VAL_FROM },
556✔
35
            Rule { { LOAD_FAST_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
556✔
36
            Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
556✔
37
                   [](const Entities entities, const std::size_t start_idx) {
1,850✔
38
                       return start_idx == 0 && entities[5].inst() == RET;
1,294✔
39
                   },
40
                   [](const Entities e) {
1,848✔
41
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1);
1,292✔
42
                   } },
43
            Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
556✔
44
                   [](const Entities entities, const std::size_t start_idx) {
1,075✔
45
                       return start_idx == 0 && entities[7].inst() == RET;
519✔
46
                   },
47
                   [](const Entities e) {
1,075✔
48
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2);
519✔
49
                   } },
50
            Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
556✔
51
                   [](const Entities entities, const std::size_t start_idx) {
679✔
52
                       return start_idx == 0 && entities[9].inst() == RET;
123✔
53
                   },
54
                   [](const Entities e) {
679✔
55
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3);
123✔
56
                   } },
57
            Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
556✔
58
            Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
556✔
59
            Rule { { LIST, STORE }, STORE_LIST },
556✔
60
            Rule { { LOAD_FAST, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
556✔
61
            Rule { { LOAD_FAST_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
556✔
62
            // LOAD_CONST, LOAD_FAST a, MUL, SET_VAL / LOAD_FAST a, LOAD_CONST, MUL, SET_VAL
63
            // ---> MUL_SET_VAL a value
64
            Rule { { LOAD_CONST, LOAD_FAST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
558✔
65
                      return isSmallerNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
2✔
66
                  },
67
                   [this](const Entities e) {
558✔
68
                       return IR::Entity(MUL_SET_VAL, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
2✔
69
                   } },
70
            Rule { { LOAD_FAST, LOAD_CONST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
559✔
71
                      return isSmallerNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
3✔
72
                  },
73
                   [this](const Entities e) {
559✔
74
                       return IR::Entity(MUL_SET_VAL, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
3✔
75
                   } },
76
            // LOAD_CONST, LOAD_FAST a, MUL / LOAD_FAST a, LOAD_CONST, MUL
77
            // ---> MUL_(BY|BY_INDEX) a value
78
            Rule { { LOAD_CONST, LOAD_FAST, MUL }, [this](const Entities e, const std::size_t) {
870✔
79
                      return isSmallerNumberInlinable(e[0].primaryArg());
314✔
80
                  },
81
                   [this](const Entities e) {
819✔
82
                       return IR::Entity(MUL_BY, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
263✔
83
                   } },
84
            Rule { { LOAD_FAST, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
561✔
85
                      return isSmallerNumberInlinable(e[1].primaryArg());
5✔
86
                  },
87
                   [this](const Entities e) {
561✔
88
                       return IR::Entity(MUL_BY, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
5✔
89
                   } },
90
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, MUL }, [this](const Entities e, const std::size_t) {
971✔
91
                      return isSmallerNumberInlinable(e[0].primaryArg());
415✔
92
                  },
93
                   [this](const Entities e) {
911✔
94
                       return IR::Entity(MUL_BY_INDEX, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
355✔
95
                   } },
96
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
560✔
97
                      return isSmallerNumberInlinable(e[1].primaryArg());
4✔
98
                  },
99
                   [this](const Entities e) {
560✔
100
                       return IR::Entity(MUL_BY_INDEX, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
4✔
101
                   } },
102
            // (LOAD_FAST a | LOAD_FAST_BY_INDEX index), LOAD_CONST n (=1), (ADD | SUB), STORE
103
            // ---> INCREMENT_STORE / DECREMENT_STORE a value
104
            Rule { { LOAD_CONST, LOAD_FAST, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
2,899✔
105
                      return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
2,343✔
106
                  },
107
                   [this](const Entities e) {
2,895✔
108
                       return IR::Entity(INCREMENT_STORE, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
2,339✔
109
                   } },
110
            Rule { { LOAD_FAST, LOAD_CONST, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
870✔
111
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
314✔
112
                  },
113
                   [this](const Entities e) {
849✔
114
                       return IR::Entity(INCREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
293✔
115
                   } },
116
            Rule { { LOAD_FAST, LOAD_CONST, SUB, SET_VAL }, [this](const Entities e, const std::size_t) {
711✔
117
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
155✔
118
                  },
119
                   [this](const Entities e) {
711✔
120
                       return IR::Entity(DECREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
155✔
121
                   } },
122
            // without the final store, just increment/decrement
123
            Rule { { LOAD_CONST, LOAD_FAST, ADD }, [this](const Entities e, const std::size_t) {
689✔
124
                      return isPositiveNumberInlinable(e[0].primaryArg());
133✔
125
                  },
126
                   [this](const Entities e) {
689✔
127
                       return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
133✔
128
                   } },
129
            Rule { { LOAD_FAST, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
593✔
130
                      return isPositiveNumberInlinable(e[1].primaryArg());
37✔
131
                  },
132
                   [this](const Entities e) {
561✔
133
                       return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
5✔
134
                   } },
135
            Rule { { LOAD_FAST, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
776✔
136
                      return isPositiveNumberInlinable(e[1].primaryArg());
220✔
137
                  },
138
                   [this](const Entities e) {
776✔
139
                       return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
220✔
140
                   } },
141
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, ADD }, [this](const Entities e, const std::size_t) {
754✔
142
                      return isPositiveNumberInlinable(e[0].primaryArg());
198✔
143
                  },
144
                   [this](const Entities e) {
738✔
145
                       return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
182✔
146
                   } },
147
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
611✔
148
                      return isPositiveNumberInlinable(e[1].primaryArg());
55✔
149
                  },
150
                   [this](const Entities e) {
587✔
151
                       return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
31✔
152
                   } },
153
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
928✔
154
                      return isPositiveNumberInlinable(e[1].primaryArg());
372✔
155
                  },
156
                   [this](const Entities e) {
927✔
157
                       return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
371✔
158
                   } },
159
            // LOAD_FAST list, (TAIL | HEAD), (STORE | SET_VAL a)
160
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
161
            Rule { { LOAD_FAST, TAIL, STORE }, [](const Entities e) {
559✔
162
                      return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
3✔
163
                  } },
164
            Rule { { LOAD_FAST, TAIL, SET_VAL }, [](const Entities e) {
565✔
165
                      return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
9✔
166
                  } },
167
            Rule { { LOAD_FAST, HEAD, STORE }, [](const Entities e) {
560✔
168
                      return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
4✔
169
                  } },
170
            Rule { { LOAD_FAST, HEAD, SET_VAL }, [](const Entities e) {
559✔
171
                      return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
3✔
172
                  } },
173
            Rule { { LOAD_FAST_BY_INDEX, TAIL, STORE }, [](const Entities e) {
596✔
174
                      return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
40✔
175
                  } },
176
            Rule { { LOAD_FAST_BY_INDEX, TAIL, SET_VAL }, [](const Entities e) {
559✔
177
                      return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
178
                  } },
179
            Rule { { LOAD_FAST_BY_INDEX, HEAD, STORE }, [](const Entities e) {
597✔
180
                      return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
41✔
181
                  } },
182
            Rule { { LOAD_FAST_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
559✔
183
                      return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
184
                  } },
185
            // (LOAD_CONST id | LOAD_FAST id), <comparison operator>, POP_JUMP_IF_(FALSE|TRUE)
186
            // ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
187
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
573✔
188
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
17✔
189
                  } },
190
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_TRUE }, [](const Entities e) {
818✔
191
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
262✔
192
                  } },
193
            Rule { { LOAD_FAST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
1,232✔
194
                      return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
676✔
195
                  } },
196
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_TRUE }, [](const Entities e) {
694✔
197
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
138✔
198
                  } },
199
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
735✔
200
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
179✔
201
                  } },
202
            Rule { { LOAD_FAST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
600✔
203
                      return IR::Entity::GotoWithArg(e[2], GT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
44✔
204
                  } },
205
            Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
711✔
206
                      return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
155✔
207
                  } },
208
            Rule { { LOAD_FAST_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
1,074✔
209
                      return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
518✔
210
                  } },
211
            Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
561✔
212
                      return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
5✔
213
                  } },
214
            Rule { { LOAD_FAST, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
601✔
215
                      return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
45✔
216
                  } },
217
            // LOAD_FAST id, LOAD_FAST id2, AT
218
            // ---> AT_SYM_SYM id id2
219
            Rule { { LOAD_FAST, LOAD_FAST, AT }, AT_SYM_SYM },
556✔
220
            Rule { { LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
556✔
221
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, AT }, AT_SYM_INDEX_CONST },
556✔
222
            // LOAD_FAST sym, TYPE, LOAD_CONST cst, EQ
223
            // ---> CHECK_TYPE_OF sym, cst
224
            // also works with LOAD_CONST cst, LOAD_FAST sym, TYPE, EQ, but args will be flipped
225
            Rule { { LOAD_FAST, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
561✔
226
                      return IR::Entity(CHECK_TYPE_OF, e[0].primaryArg(), e[2].primaryArg());
5✔
227
                  } },
228
            Rule { { LOAD_CONST, LOAD_FAST, TYPE, EQ }, [](const Entities e) {
559✔
229
                      return IR::Entity(CHECK_TYPE_OF, e[1].primaryArg(), e[0].primaryArg());
3✔
230
                  } },
231
            Rule { { LOAD_FAST_BY_INDEX, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
561✔
232
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
5✔
233
                  } },
234
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, TYPE, EQ }, [](const Entities e) {
662✔
235
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[1].primaryArg(), e[0].primaryArg());
106✔
236
                  } },
237
            // ---
238
            Rule { { LOAD_FAST_BY_INDEX, LEN, STORE }, [](const Entities e) {
1,081✔
239
                      return IR::Entity(STORE_LEN, e[0].primaryArg(), e[2].primaryArg());
525✔
240
                  } },
241
            Rule { { LOAD_FAST, LEN, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
1,839✔
242
                      return IR::Entity::GotoWithArg(e[3], LT_LEN_SYM_JUMP_IF_FALSE, e[0].primaryArg());
1,283✔
243
                  } },
244
        };
245

246
        const auto math_ops = { ADD, SUB, MUL, DIV };
556✔
247
        for (const auto& one : math_ops)
2,780✔
248
        {
249
            for (const auto& two : math_ops)
11,120✔
250
            {
251
                for (const auto& three : math_ops)
44,480✔
252
                    // cppcheck-suppress useStlAlgorithm
253
                    m_ruleset.emplace_back(Rule { { one, two, three }, fuseMathOps3 });
35,584✔
254
                m_ruleset.emplace_back(Rule { { one, two }, fuseMathOps2 });
8,896✔
255
            }
8,896✔
256
        }
2,224✔
257

258
        m_logger.debug("Loaded {} rules", m_ruleset.size());
556✔
259
    }
556✔
260

261
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
390✔
262
    {
390✔
263
        m_logger.traceStart("process");
390✔
264
        m_symbols = symbols;
390✔
265
        m_values = values;
390✔
266

267
        for (const auto& block : pages)
9,141✔
268
        {
269
            m_ir.emplace_back(IR::Block {
17,502✔
270
                .metadata = {
61,257✔
271
                    .name = block.metadata.name,
8,751✔
272
                    .argument_count = block.metadata.argument_count,
8,751✔
273
                    .addr = block.metadata.addr,
8,751✔
274
                    .is_closure = block.metadata.is_closure,
8,751✔
275
                    .is_recursive = block.metadata.is_recursive,
8,751✔
276
                    .is_simple = block.metadata.is_simple },
8,751✔
277
                .data = {} });
8,751✔
278
            std::vector<IR::Entity>& current_block = m_ir.back().data;
8,751✔
279

280
            std::size_t i = 0;
8,751✔
281
            const std::size_t end = block.data.size();
8,751✔
282

283
            while (i < end)
317,956✔
284
            {
285
                std::optional<EntityWithOffset> maybe_compacted = replaceWithRules(
618,410✔
286
                    std::span(
309,205✔
287
                        block.data.begin() + static_cast<IR::Block::vec_t::difference_type>(i),
309,205✔
288
                        block.data.size() - i),
309,205✔
289
                    i);
309,205✔
290

291
                if (maybe_compacted.has_value())
309,205✔
292
                {
293
                    auto [entity, offset] = maybe_compacted.value();
89,698✔
294
                    current_block.emplace_back(entity);
44,849✔
295
                    i += offset;
44,849✔
296
                }
44,849✔
297
                else
298
                {
299
                    current_block.emplace_back(block.data[i]);
264,356✔
300
                    ++i;
264,356✔
301
                }
302
            }
309,205✔
303
        }
8,751✔
304

305
        m_logger.traceEnd();
390✔
306
    }
390✔
307

308
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
390✔
309
    {
390✔
310
        return m_ir;
390✔
311
    }
312

313
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const std::span<const IR::Entity> entities) const
36,851,187✔
314
    {
36,851,187✔
315
        if (expected_insts.size() > entities.size())
36,851,187✔
316
            return false;
2,212,343✔
317

318
        std::size_t i = 0;
34,638,844✔
319
        while (i < expected_insts.size())
36,302,870✔
320
        {
321
            if (expected_insts[i] != entities[i].inst())
36,257,802✔
322
                return false;
34,593,776✔
323
            if (entities[i].kind() != IR::Kind::Label)
1,664,026✔
324
                ++i;
1,664,026✔
325
        }
326

327
        return true;
45,068✔
328
    }
36,851,187✔
329

330
    bool IROptimizer::canBeOptimisedSafely(std::span<const IR::Entity> entities, const std::size_t window_size, const std::size_t position_in_block)
44,857✔
331
    {
44,857✔
332
        // check that we can actually safely apply the optimisation on the given instructions
333
        return std::ranges::none_of(
44,857✔
334
            entities | std::ranges::views::take(window_size),
44,857✔
335
            [position_in_block, window_size](const IR::Entity& entity) {
155,623✔
336
                // We need to check that we aren't optimising code using labels that could be beyond the
337
                // 12 bits limits (0-4095), otherwise when translating a label to its position,
338
                // we'll get the wrong value and force a wrong jump!
339
                if (entity.hasLabel() && position_in_block > IR::MaxValueForDualArg + window_size)
110,766✔
340
                    return true;
8✔
341
                return entity.primaryArg() > IR::MaxValueForDualArg;
110,758✔
342
            });
110,766✔
343
    }
344

345
    std::optional<EntityWithOffset> IROptimizer::replaceWithRules(const std::span<const IR::Entity> entities, const std::size_t position_in_block)
309,205✔
346
    {
309,205✔
347
        for (const auto& [expected, condition, createReplacement] : m_ruleset)
37,295,166✔
348
        {
349
            if (match(expected, entities) && condition(entities, position_in_block))
36,851,187✔
350
            {
351
                const std::size_t window_size = expected.size();
89,714✔
352
                if (!canBeOptimisedSafely(entities, window_size, position_in_block))
44,857✔
353
                    return std::nullopt;  // no need to try other optimisations, they won't be applied either
8✔
354

355
                auto output = createReplacement(entities);
89,698✔
356

357
                if (const auto it = std::ranges::find_if(entities, [](const auto& entity) {
5,076,082✔
358
                        return entity.hasValidSourceLocation();
5,031,233✔
359
                    });
360
                    it != entities.end())
89,691✔
361
                    output.setSourceLocation(it->filename(), it->sourceLine());
44,842✔
362

363
                return EntityWithOffset { output, window_size };
44,849✔
364
            }
44,857✔
365
        }
36,851,187✔
366

367
        return std::nullopt;
264,348✔
368
    }
309,205✔
369

370
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
3,827✔
371
    {
3,827✔
372
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
3,827✔
373
        {
374
            const double val = std::get<double>(m_values[id].value);
3,738✔
375
            return val >= 0.0 &&
7,476✔
376
                val < IR::MaxValueForDualArg &&
3,738✔
377
                static_cast<double>(static_cast<long>(val)) == val;
3,735✔
378
        }
3,738✔
379
        return false;
89✔
380
    }
3,827✔
381

382
    bool IROptimizer::isSmallerNumberInlinable(const uint16_t id) const
743✔
383
    {
743✔
384
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
743✔
385
        {
386
            const double val = std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber;
743✔
387
            return val >= 0.0 &&
1,486✔
388
                val < IR::MaxValueForDualArg &&
743✔
389
                static_cast<double>(static_cast<long>(val)) == val;
632✔
390
        }
743✔
391
        return false;
×
392
    }
743✔
393

UNCOV
394
    bool IROptimizer::isNumberEqualTo(const uint16_t id, const int number) const
×
395
    {
×
396
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
×
397
        {
398
            const double val = std::get<double>(m_values[id].value);
×
399
            return static_cast<double>(static_cast<long>(val)) == val &&
×
400
                static_cast<int>(val) == number;
×
UNCOV
401
        }
×
UNCOV
402
        return false;
×
UNCOV
403
    }
×
404

405
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
3,729✔
406
    {
3,729✔
407
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
3,729✔
408
    }
409

410
    uint16_t IROptimizer::smallerNumberAsArg(const uint16_t id) const
632✔
411
    {
632✔
412
        return static_cast<uint16_t>(std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber);
632✔
413
    }
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc