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

ArkScript-lang / Ark / 22281229261

22 Feb 2026 04:49PM UTC coverage: 93.5% (+0.04%) from 93.464%
22281229261

push

github

SuperFola
feat(cli): ARK-336, accept '-' as a filename to mean 'read code from stdin'

9250 of 9893 relevant lines covered (93.5%)

267283.46 hits per line

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

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

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

22
    IROptimizer::IROptimizer(const unsigned debug) :
880✔
23
        m_logger("IROptimizer", debug)
440✔
24
    {
440✔
25
        m_ruleset = {
26,840✔
26
            Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
440✔
27
            Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
440✔
28
            Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
440✔
29
            Rule { { LOAD_FAST, STORE }, STORE_FROM },
440✔
30
            Rule { { LOAD_FAST_BY_INDEX, STORE }, STORE_FROM_INDEX },
440✔
31
            Rule { { LOAD_FAST, SET_VAL }, SET_VAL_FROM },
440✔
32
            Rule { { LOAD_FAST_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
440✔
33
            Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, BUILTIN, CALL },
440✔
34
                   [](const Entities entities, const std::size_t start_idx) {
1,058✔
35
                       return Builtins::builtins[entities[3].primaryArg()].second.isFunction() && start_idx == 0 && entities[6].inst() == RET;
618✔
36
                   },
37
                   [](const Entities e) {
1,057✔
38
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1);
617✔
39
                   } },
40
            Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL },
440✔
41
                   [](const Entities entities, const std::size_t start_idx) {
525✔
42
                       return Builtins::builtins[entities[5].primaryArg()].second.isFunction() && start_idx == 0 && entities[8].inst() == RET;
85✔
43
                   },
44
                   [](const Entities e) {
525✔
45
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2);
85✔
46
                   } },
47
            Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL },
440✔
48
                   [](const Entities entities, const std::size_t start_idx) {
492✔
49
                       return Builtins::builtins[entities[7].primaryArg()].second.isFunction() && start_idx == 0 && entities[10].inst() == RET;
52✔
50
                   },
51
                   [](const Entities e) {
492✔
52
                       return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3);
52✔
53
                   } },
54
            Rule { { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities, const std::size_t) {
1,404✔
55
                      return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
964✔
56
                  } },
57
            Rule { { LOAD_FAST, CALL }, CALL_SYMBOL },
440✔
58
            Rule { { GET_CURRENT_PAGE_ADDR, CALL }, CALL_CURRENT_PAGE },
440✔
59
            Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
440✔
60
            Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
440✔
61
            Rule { { LIST, STORE }, STORE_LIST },
440✔
62
            Rule { { LOAD_FAST, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
440✔
63
            Rule { { LOAD_FAST_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
440✔
64
            // LOAD_CONST, LOAD_FAST a, MUL, SET_VAL / LOAD_FAST a, LOAD_CONST, MUL, SET_VAL
65
            // ---> MUL_SET_VAL a value
66
            Rule { { LOAD_CONST, LOAD_FAST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
442✔
67
                      return isSmallerNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
2✔
68
                  },
69
                   [this](const Entities e) {
442✔
70
                       return IR::Entity(MUL_SET_VAL, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
2✔
71
                   } },
72
            Rule { { LOAD_FAST, LOAD_CONST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
442✔
73
                      return isSmallerNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
2✔
74
                  },
75
                   [this](const Entities e) {
442✔
76
                       return IR::Entity(MUL_SET_VAL, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
2✔
77
                   } },
78
            // LOAD_CONST, LOAD_FAST a, MUL / LOAD_FAST a, LOAD_CONST, MUL
79
            // ---> MUL_(BY|BY_INDEX) a value
80
            Rule { { LOAD_CONST, LOAD_FAST, MUL }, [this](const Entities e, const std::size_t) {
465✔
81
                      return isSmallerNumberInlinable(e[0].primaryArg());
25✔
82
                  },
83
                   [this](const Entities e) {
465✔
84
                       return IR::Entity(MUL_BY, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
25✔
85
                   } },
86
            Rule { { LOAD_FAST, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
442✔
87
                      return isSmallerNumberInlinable(e[1].primaryArg());
2✔
88
                  },
89
                   [this](const Entities e) {
442✔
90
                       return IR::Entity(MUL_BY, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
2✔
91
                   } },
92
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, MUL }, [this](const Entities e, const std::size_t) {
520✔
93
                      return isSmallerNumberInlinable(e[0].primaryArg());
80✔
94
                  },
95
                   [this](const Entities e) {
520✔
96
                       return IR::Entity(MUL_BY_INDEX, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
80✔
97
                   } },
98
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
443✔
99
                      return isSmallerNumberInlinable(e[1].primaryArg());
3✔
100
                  },
101
                   [this](const Entities e) {
443✔
102
                       return IR::Entity(MUL_BY_INDEX, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
3✔
103
                   } },
104
            // (LOAD_FAST a | LOAD_FAST_BY_INDEX index), LOAD_CONST n (=1), (ADD | SUB), STORE
105
            // ---> INCREMENT_STORE / DECREMENT_STORE a value
106
            Rule { { LOAD_CONST, LOAD_FAST, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
1,410✔
107
                      return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
970✔
108
                  },
109
                   [this](const Entities e) {
1,409✔
110
                       return IR::Entity(INCREMENT_STORE, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
969✔
111
                   } },
112
            Rule { { LOAD_FAST, LOAD_CONST, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
581✔
113
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
141✔
114
                  },
115
                   [this](const Entities e) {
568✔
116
                       return IR::Entity(INCREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
128✔
117
                   } },
118
            Rule { { LOAD_FAST, LOAD_CONST, SUB, SET_VAL }, [this](const Entities e, const std::size_t) {
489✔
119
                      return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
49✔
120
                  },
121
                   [this](const Entities e) {
489✔
122
                       return IR::Entity(DECREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
49✔
123
                   } },
124
            // without the final store, just increment/decrement
125
            Rule { { LOAD_CONST, LOAD_FAST, ADD }, [this](const Entities e, const std::size_t) {
446✔
126
                      return isPositiveNumberInlinable(e[0].primaryArg());
6✔
127
                  },
128
                   [this](const Entities e) {
446✔
129
                       return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
6✔
130
                   } },
131
            Rule { { LOAD_FAST, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
456✔
132
                      return isPositiveNumberInlinable(e[1].primaryArg());
16✔
133
                  },
134
                   [this](const Entities e) {
443✔
135
                       return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
3✔
136
                   } },
137
            Rule { { LOAD_FAST, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
497✔
138
                      return isPositiveNumberInlinable(e[1].primaryArg());
57✔
139
                  },
140
                   [this](const Entities e) {
497✔
141
                       return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
57✔
142
                   } },
143
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, ADD }, [this](const Entities e, const std::size_t) {
589✔
144
                      return isPositiveNumberInlinable(e[0].primaryArg());
149✔
145
                  },
146
                   [this](const Entities e) {
585✔
147
                       return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
145✔
148
                   } },
149
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
473✔
150
                      return isPositiveNumberInlinable(e[1].primaryArg());
33✔
151
                  },
152
                   [this](const Entities e) {
459✔
153
                       return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
19✔
154
                   } },
155
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
598✔
156
                      return isPositiveNumberInlinable(e[1].primaryArg());
158✔
157
                  },
158
                   [this](const Entities e) {
597✔
159
                       return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
157✔
160
                   } },
161
            // LOAD_FAST list, (TAIL | HEAD), (STORE | SET_VAL a)
162
            // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
163
            Rule { { LOAD_FAST, TAIL, STORE }, [](const Entities e) {
442✔
164
                      return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
2✔
165
                  } },
166
            Rule { { LOAD_FAST, TAIL, SET_VAL }, [](const Entities e) {
445✔
167
                      return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
5✔
168
                  } },
169
            Rule { { LOAD_FAST, HEAD, STORE }, [](const Entities e) {
443✔
170
                      return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
3✔
171
                  } },
172
            Rule { { LOAD_FAST, HEAD, SET_VAL }, [](const Entities e) {
442✔
173
                      return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
2✔
174
                  } },
175
            Rule { { LOAD_FAST_BY_INDEX, TAIL, STORE }, [](const Entities e) {
465✔
176
                      return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
25✔
177
                  } },
178
            Rule { { LOAD_FAST_BY_INDEX, TAIL, SET_VAL }, [](const Entities e) {
442✔
179
                      return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
180
                  } },
181
            Rule { { LOAD_FAST_BY_INDEX, HEAD, STORE }, [](const Entities e) {
466✔
182
                      return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
26✔
183
                  } },
184
            Rule { { LOAD_FAST_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
442✔
185
                      return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
2✔
186
                  } },
187
            // (LOAD_CONST id | LOAD_FAST id), <comparison operator>, POP_JUMP_IF_(FALSE|TRUE)
188
            // ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
189
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
452✔
190
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
12✔
191
                  } },
192
            Rule { { LOAD_CONST, LT, POP_JUMP_IF_TRUE }, [](const Entities e) {
520✔
193
                      return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
80✔
194
                  } },
195
            Rule { { LOAD_FAST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
797✔
196
                      return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
357✔
197
                  } },
198
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_TRUE }, [](const Entities e) {
477✔
199
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
37✔
200
                  } },
201
            Rule { { LOAD_CONST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
482✔
202
                      return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
42✔
203
                  } },
204
            Rule { { LOAD_FAST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
442✔
205
                      return IR::Entity::GotoWithArg(e[2], GT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
2✔
206
                  } },
207
            Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
519✔
208
                      return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
79✔
209
                  } },
210
            Rule { { LOAD_FAST_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
644✔
211
                      return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
204✔
212
                  } },
213
            Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
443✔
214
                      return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
3✔
215
                  } },
216
            Rule { { LOAD_FAST, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
469✔
217
                      return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
29✔
218
                  } },
219
            // LOAD_FAST id, LOAD_FAST id2, AT
220
            // ---> AT_SYM_SYM id id2
221
            Rule { { LOAD_FAST, LOAD_FAST, AT }, AT_SYM_SYM },
440✔
222
            Rule { { LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
440✔
223
            Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, AT }, AT_SYM_INDEX_CONST },
440✔
224
            // LOAD_FAST sym, TYPE, LOAD_CONST cst, EQ
225
            // ---> CHECK_TYPE_OF sym, cst
226
            // also works with LOAD_CONST cst, LOAD_FAST sym, TYPE, EQ, but args will be flipped
227
            Rule { { LOAD_FAST, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
443✔
228
                      return IR::Entity(CHECK_TYPE_OF, e[0].primaryArg(), e[2].primaryArg());
3✔
229
                  } },
230
            Rule { { LOAD_CONST, LOAD_FAST, TYPE, EQ }, [](const Entities e) {
441✔
231
                      return IR::Entity(CHECK_TYPE_OF, e[1].primaryArg(), e[0].primaryArg());
1✔
232
                  } },
233
            Rule { { LOAD_FAST_BY_INDEX, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
443✔
234
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
3✔
235
                  } },
236
            Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, TYPE, EQ }, [](const Entities e) {
499✔
237
                      return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[1].primaryArg(), e[0].primaryArg());
59✔
238
                  } },
239
            // ---
240
            Rule { { LOAD_FAST_BY_INDEX, LEN, STORE }, [](const Entities e) {
697✔
241
                      return IR::Entity(STORE_LEN, e[0].primaryArg(), e[2].primaryArg());
257✔
242
                  } },
243
            Rule { { LOAD_FAST, LEN, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
996✔
244
                      return IR::Entity::GotoWithArg(e[3], LT_LEN_SYM_JUMP_IF_FALSE, e[0].primaryArg());
556✔
245
                  } },
246
        };
247

248
        const auto math_ops = { ADD, SUB, MUL, DIV };
440✔
249
        for (const auto& one : math_ops)
2,200✔
250
        {
251
            for (const auto& two : math_ops)
8,800✔
252
            {
253
                for (const auto& three : math_ops)
35,200✔
254
                    m_ruleset.emplace_back(Rule { { one, two, three }, fuseMathOps3 });
28,160✔
255
            }
7,040✔
256
        }
1,760✔
257

258
        for (const auto& one : math_ops)
2,200✔
259
        {
260
            for (const auto& two : math_ops)
8,800✔
261
                m_ruleset.emplace_back(Rule { { one, two }, fuseMathOps2 });
7,040✔
262
        }
1,760✔
263

264
        m_logger.debug("Loaded {} rules", m_ruleset.size());
440✔
265
    }
440✔
266

267
    void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
293✔
268
    {
293✔
269
        m_logger.traceStart("process");
293✔
270
        m_symbols = symbols;
293✔
271
        m_values = values;
293✔
272

273
        for (const auto& block : pages)
4,362✔
274
        {
275
            m_ir.emplace_back();
4,069✔
276
            IR::Block& current_block = m_ir.back();
4,069✔
277

278
            std::size_t i = 0;
4,069✔
279
            const std::size_t end = block.size();
4,069✔
280

281
            while (i < end)
116,975✔
282
            {
283
                std::optional<EntityWithOffset> maybe_compacted = replaceWithRules(
225,812✔
284
                    std::span(
112,906✔
285
                        block.begin() + static_cast<IR::Block::difference_type>(i),
112,906✔
286
                        block.size() - i),
112,906✔
287
                    i);
112,906✔
288

289
                if (maybe_compacted.has_value())
112,906✔
290
                {
291
                    auto [entity, offset] = maybe_compacted.value();
51,438✔
292
                    current_block.emplace_back(entity);
25,719✔
293
                    i += offset;
25,719✔
294
                }
25,719✔
295
                else
296
                {
297
                    current_block.emplace_back(block[i]);
87,187✔
298
                    ++i;
87,187✔
299
                }
300
            }
112,906✔
301
        }
4,069✔
302

303
        m_logger.traceEnd();
293✔
304
    }
293✔
305

306
    const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
293✔
307
    {
293✔
308
        return m_ir;
293✔
309
    }
310

311
    bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const std::span<const IR::Entity> entities) const
12,603,703✔
312
    {
12,603,703✔
313
        if (expected_insts.size() > entities.size())
12,603,703✔
314
            return false;
1,008,126✔
315

316
        std::size_t i = 0;
11,595,577✔
317
        while (i < expected_insts.size())
12,220,667✔
318
        {
319
            if (expected_insts[i] != entities[i].inst())
12,194,898✔
320
                return false;
11,569,808✔
321
            if (entities[i].kind() != IR::Kind::Label)
625,090✔
322
                ++i;
625,090✔
323
        }
324

325
        return true;
25,769✔
326
    }
12,603,703✔
327

328
    bool IROptimizer::canBeOptimizedSafely(std::span<const IR::Entity> entities, std::size_t window_size) const
25,719✔
329
    {
25,719✔
330
        // check that we can actually safely apply the optimization on the given instructions
331
        return std::ranges::none_of(
25,719✔
332
            entities | std::ranges::views::take(window_size),
25,719✔
333
            [](const IR::Entity& entity) {
60,648✔
334
                return entity.primaryArg() > IR::MaxValueForDualArg;
60,648✔
335
            });
336
    }
337

338
    std::optional<EntityWithOffset> IROptimizer::replaceWithRules(const std::span<const IR::Entity> entities, const std::size_t position_in_block)
112,906✔
339
    {
112,906✔
340
        for (const auto& [expected, condition, createReplacement] : m_ruleset)
12,793,816✔
341
        {
342
            if (match(expected, entities) && condition(entities, position_in_block))
12,603,703✔
343
            {
344
                const std::size_t window_size = expected.size();
51,438✔
345
                if (!canBeOptimizedSafely(entities, window_size))
25,719✔
346
                    return std::nullopt;  // no need to try other optimizations, they won't be applied either
×
347

348
                auto output = createReplacement(entities);
51,438✔
349

350
                if (const auto it = std::ranges::find_if(entities, [](const auto& entity) {
4,268,476✔
351
                        return entity.hasValidSourceLocation();
4,242,757✔
352
                    });
353
                    it != entities.end())
51,438✔
354
                    output.setSourceLocation(it->filename(), it->sourceLine());
25,719✔
355

356
                return EntityWithOffset { output, window_size };
25,719✔
357
            }
25,719✔
358
        }
12,603,703✔
359

360
        return std::nullopt;
87,187✔
361
    }
112,906✔
362

363
    bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
1,579✔
364
    {
1,579✔
365
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
1,579✔
366
        {
367
            const double val = std::get<double>(m_values[id].value);
1,537✔
368
            return val >= 0.0 &&
3,074✔
369
                val < IR::MaxValueForDualArg &&
1,537✔
370
                static_cast<double>(static_cast<long>(val)) == val;
1,536✔
371
        }
1,537✔
372
        return false;
42✔
373
    }
1,579✔
374

375
    bool IROptimizer::isSmallerNumberInlinable(const uint16_t id) const
114✔
376
    {
114✔
377
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
114✔
378
        {
379
            const double val = std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber;
114✔
380
            return val >= 0.0 &&
228✔
381
                val < IR::MaxValueForDualArg &&
114✔
382
                static_cast<double>(static_cast<long>(val)) == val;
114✔
383
        }
114✔
384
        return false;
×
385
    }
114✔
386

387
    bool IROptimizer::isNumberEqualTo(const uint16_t id, const int number) const
×
388
    {
×
389
        if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
×
390
        {
391
            const double val = std::get<double>(m_values[id].value);
×
392
            return static_cast<double>(static_cast<long>(val)) == val &&
×
393
                static_cast<int>(val) == number;
×
394
        }
×
395
        return false;
×
396
    }
×
397

398
    uint16_t IROptimizer::numberAsArg(const uint16_t id) const
1,533✔
399
    {
1,533✔
400
        return static_cast<uint16_t>(std::get<double>(m_values[id].value));
1,533✔
401
    }
402

403
    uint16_t IROptimizer::smallerNumberAsArg(const uint16_t id) const
114✔
404
    {
114✔
405
        return static_cast<uint16_t>(std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber);
114✔
406
    }
407
}
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