ArkScript
A small, lisp-inspired, functional scripting language
IROptimizer.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <utility>
5#include <ranges>
6#include <algorithm>
7
9
10namespace Ark::internal
11{
12 IR::Entity fuseMathOps3(const std::span<const IR::Entity> e)
13 {
14 return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), e[2].inst());
15 }
16
17 IR::Entity fuseMathOps2(const std::span<const IR::Entity> e)
18 {
19 return IR::Entity(FUSED_MATH, e[0].inst(), e[1].inst(), NOP);
20 }
21
22 IROptimizer::IROptimizer(const unsigned debug) :
23 Pass("IROptimizer", debug)
24 {
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 = {
29 Rule { { LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
30 Rule { { LOAD_CONST, STORE }, LOAD_CONST_STORE },
31 Rule { { LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
32 Rule { { LOAD_FAST, STORE }, STORE_FROM },
33 Rule { { LOAD_FAST_BY_INDEX, STORE }, STORE_FROM_INDEX },
34 Rule { { LOAD_FAST, SET_VAL }, SET_VAL_FROM },
35 Rule { { LOAD_FAST_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
36 Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
37 [](const Entities entities, const std::size_t start_idx) {
38 return start_idx == 0 && entities[5].inst() == RET;
39 },
40 [](const Entities e) {
41 return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1);
42 } },
43 Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
44 [](const Entities entities, const std::size_t start_idx) {
45 return start_idx == 0 && entities[7].inst() == RET;
46 },
47 [](const Entities e) {
48 return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2);
49 } },
50 Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN },
51 [](const Entities entities, const std::size_t start_idx) {
52 return start_idx == 0 && entities[9].inst() == RET;
53 },
54 [](const Entities e) {
55 return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3);
56 } },
57 Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL },
58 Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX },
59 Rule { { LIST, STORE }, STORE_LIST },
60 Rule { { LOAD_FAST, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
61 Rule { { LOAD_FAST_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
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) {
65 return isSmallerNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
66 },
67 [this](const Entities e) {
68 return IR::Entity(MUL_SET_VAL, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
69 } },
70 Rule { { LOAD_FAST, LOAD_CONST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
71 return isSmallerNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
72 },
73 [this](const Entities e) {
74 return IR::Entity(MUL_SET_VAL, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
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) {
79 return isSmallerNumberInlinable(e[0].primaryArg());
80 },
81 [this](const Entities e) {
82 return IR::Entity(MUL_BY, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
83 } },
84 Rule { { LOAD_FAST, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
85 return isSmallerNumberInlinable(e[1].primaryArg());
86 },
87 [this](const Entities e) {
88 return IR::Entity(MUL_BY, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
89 } },
90 Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, MUL }, [this](const Entities e, const std::size_t) {
91 return isSmallerNumberInlinable(e[0].primaryArg());
92 },
93 [this](const Entities e) {
94 return IR::Entity(MUL_BY_INDEX, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
95 } },
96 Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
97 return isSmallerNumberInlinable(e[1].primaryArg());
98 },
99 [this](const Entities e) {
100 return IR::Entity(MUL_BY_INDEX, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
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) {
105 return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
106 },
107 [this](const Entities e) {
108 return IR::Entity(INCREMENT_STORE, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
109 } },
110 Rule { { LOAD_FAST, LOAD_CONST, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
111 return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
112 },
113 [this](const Entities e) {
114 return IR::Entity(INCREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
115 } },
116 Rule { { LOAD_FAST, LOAD_CONST, SUB, SET_VAL }, [this](const Entities e, const std::size_t) {
117 return isPositiveNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
118 },
119 [this](const Entities e) {
120 return IR::Entity(DECREMENT_STORE, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
121 } },
122 // without the final store, just increment/decrement
123 Rule { { LOAD_CONST, LOAD_FAST, ADD }, [this](const Entities e, const std::size_t) {
124 return isPositiveNumberInlinable(e[0].primaryArg());
125 },
126 [this](const Entities e) {
127 return IR::Entity(INCREMENT, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
128 } },
129 Rule { { LOAD_FAST, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
130 return isPositiveNumberInlinable(e[1].primaryArg());
131 },
132 [this](const Entities e) {
133 return IR::Entity(INCREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
134 } },
135 Rule { { LOAD_FAST, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
136 return isPositiveNumberInlinable(e[1].primaryArg());
137 },
138 [this](const Entities e) {
139 return IR::Entity(DECREMENT, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
140 } },
141 Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, ADD }, [this](const Entities e, const std::size_t) {
142 return isPositiveNumberInlinable(e[0].primaryArg());
143 },
144 [this](const Entities e) {
145 return IR::Entity(INCREMENT_BY_INDEX, e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
146 } },
147 Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, ADD }, [this](const Entities e, const std::size_t) {
148 return isPositiveNumberInlinable(e[1].primaryArg());
149 },
150 [this](const Entities e) {
151 return IR::Entity(INCREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
152 } },
153 Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, SUB }, [this](const Entities e, const std::size_t) {
154 return isPositiveNumberInlinable(e[1].primaryArg());
155 },
156 [this](const Entities e) {
157 return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
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) {
162 return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
163 } },
164 Rule { { LOAD_FAST, TAIL, SET_VAL }, [](const Entities e) {
165 return IR::Entity(SET_VAL_TAIL, e[0].primaryArg(), e[2].primaryArg());
166 } },
167 Rule { { LOAD_FAST, HEAD, STORE }, [](const Entities e) {
168 return IR::Entity(STORE_HEAD, e[0].primaryArg(), e[2].primaryArg());
169 } },
170 Rule { { LOAD_FAST, HEAD, SET_VAL }, [](const Entities e) {
171 return IR::Entity(SET_VAL_HEAD, e[0].primaryArg(), e[2].primaryArg());
172 } },
173 Rule { { LOAD_FAST_BY_INDEX, TAIL, STORE }, [](const Entities e) {
174 return IR::Entity(STORE_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
175 } },
176 Rule { { LOAD_FAST_BY_INDEX, TAIL, SET_VAL }, [](const Entities e) {
177 return IR::Entity(SET_VAL_TAIL_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
178 } },
179 Rule { { LOAD_FAST_BY_INDEX, HEAD, STORE }, [](const Entities e) {
180 return IR::Entity(STORE_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
181 } },
182 Rule { { LOAD_FAST_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
183 return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
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) {
188 return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
189 } },
190 Rule { { LOAD_CONST, LT, POP_JUMP_IF_TRUE }, [](const Entities e) {
191 return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
192 } },
193 Rule { { LOAD_FAST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
194 return IR::Entity::GotoWithArg(e[2], LT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
195 } },
196 Rule { { LOAD_CONST, GT, POP_JUMP_IF_TRUE }, [](const Entities e) {
197 return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_TRUE, e[0].primaryArg());
198 } },
199 Rule { { LOAD_CONST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
200 return IR::Entity::GotoWithArg(e[2], GT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
201 } },
202 Rule { { LOAD_FAST, GT, POP_JUMP_IF_FALSE }, [](const Entities e) {
203 return IR::Entity::GotoWithArg(e[2], GT_SYM_JUMP_IF_FALSE, e[0].primaryArg());
204 } },
205 Rule { { LOAD_CONST, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
206 return IR::Entity::GotoWithArg(e[2], EQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
207 } },
208 Rule { { LOAD_FAST_BY_INDEX, EQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
209 return IR::Entity::GotoWithArg(e[2], EQ_SYM_INDEX_JUMP_IF_TRUE, e[0].primaryArg());
210 } },
211 Rule { { LOAD_CONST, NEQ, POP_JUMP_IF_TRUE }, [](const Entities e) {
212 return IR::Entity::GotoWithArg(e[2], NEQ_CONST_JUMP_IF_TRUE, e[0].primaryArg());
213 } },
214 Rule { { LOAD_FAST, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
215 return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
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 },
220 Rule { { LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
221 Rule { { LOAD_FAST_BY_INDEX, LOAD_CONST, AT }, AT_SYM_INDEX_CONST },
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) {
226 return IR::Entity(CHECK_TYPE_OF, e[0].primaryArg(), e[2].primaryArg());
227 } },
228 Rule { { LOAD_CONST, LOAD_FAST, TYPE, EQ }, [](const Entities e) {
229 return IR::Entity(CHECK_TYPE_OF, e[1].primaryArg(), e[0].primaryArg());
230 } },
231 Rule { { LOAD_FAST_BY_INDEX, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
232 return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
233 } },
234 Rule { { LOAD_CONST, LOAD_FAST_BY_INDEX, TYPE, EQ }, [](const Entities e) {
235 return IR::Entity(CHECK_TYPE_OF_BY_INDEX, e[1].primaryArg(), e[0].primaryArg());
236 } },
237 // ---
238 Rule { { LOAD_FAST_BY_INDEX, LEN, STORE }, [](const Entities e) {
239 return IR::Entity(STORE_LEN, e[0].primaryArg(), e[2].primaryArg());
240 } },
241 Rule { { LOAD_FAST, LEN, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
242 return IR::Entity::GotoWithArg(e[3], LT_LEN_SYM_JUMP_IF_FALSE, e[0].primaryArg());
243 } },
244 };
245
246 const auto math_ops = { ADD, SUB, MUL, DIV };
247 for (const auto& one : math_ops)
248 {
249 for (const auto& two : math_ops)
250 {
251 for (const auto& three : math_ops)
252 // cppcheck-suppress useStlAlgorithm
253 m_ruleset.emplace_back(Rule { { one, two, three }, fuseMathOps3 });
254 m_ruleset.emplace_back(Rule { { one, two }, fuseMathOps2 });
255 }
256 }
257
258 m_logger.debug("Loaded {} rules", m_ruleset.size());
259 }
260
261 void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
262 {
263 m_logger.traceStart("process");
264 m_symbols = symbols;
265 m_values = values;
266
267 for (const auto& block : pages)
268 {
269 m_ir.emplace_back(IR::Block::InitWithMetadata(block));
270 std::vector<IR::Entity>& current_block = m_ir.back().data;
271
272 std::size_t i = 0;
273 const std::size_t end = block.data.size();
274
275 while (i < end)
276 {
277 std::optional<EntityWithOffset> maybe_compacted = replaceWithRules(
278 std::span(
279 block.data.begin() + static_cast<IR::Block::vec_t::difference_type>(i),
280 block.data.size() - i),
281 i);
282
283 if (maybe_compacted.has_value())
284 {
285 auto [entity, offset] = maybe_compacted.value();
286 current_block.emplace_back(entity);
287 i += offset;
288 }
289 else
290 {
291 current_block.emplace_back(block.data[i]);
292 ++i;
293 }
294 }
295 }
296
298 }
299
300 const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
301 {
302 return m_ir;
303 }
304
305 bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const std::span<const IR::Entity> entities) const
306 {
307 if (expected_insts.size() > entities.size())
308 return false;
309
310 std::size_t i = 0;
311 while (i < expected_insts.size())
312 {
313 if (expected_insts[i] != entities[i].inst())
314 return false;
315 if (entities[i].kind() != IR::Kind::Label)
316 ++i;
317 }
318
319 return true;
320 }
321
322 bool IROptimizer::canBeOptimisedSafely(std::span<const IR::Entity> entities, const std::size_t window_size, const std::size_t position_in_block)
323 {
324 // check that we can actually safely apply the optimisation on the given instructions
325 return std::ranges::none_of(
326 entities | std::ranges::views::take(window_size),
327 [position_in_block, window_size](const IR::Entity& entity) {
328 // We need to check that we aren't optimising code using labels that could be beyond the
329 // 12 bits limits (0-4095), otherwise when translating a label to its position,
330 // we'll get the wrong value and force a wrong jump!
331 if (entity.hasLabel() && position_in_block > IR::MaxValueForDualArg + window_size)
332 return true;
333 return entity.primaryArg() > IR::MaxValueForDualArg;
334 });
335 }
336
337 std::optional<EntityWithOffset> IROptimizer::replaceWithRules(const std::span<const IR::Entity> entities, const std::size_t position_in_block)
338 {
339 for (const auto& [expected, condition, createReplacement] : m_ruleset)
340 {
341 if (match(expected, entities) && condition(entities, position_in_block))
342 {
343 const std::size_t window_size = expected.size();
344 if (!canBeOptimisedSafely(entities, window_size, position_in_block))
345 return std::nullopt; // no need to try other optimisations, they won't be applied either
346
347 auto output = createReplacement(entities);
348
349 if (const auto it = std::ranges::find_if(entities, [](const auto& entity) {
350 return entity.hasValidSourceLocation();
351 });
352 it != entities.end())
353 output.setSourceLocation(it->filename(), it->sourceLine());
354
355 return EntityWithOffset { output, window_size };
356 }
357 }
358
359 return std::nullopt;
360 }
361
362 bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
363 {
364 if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
365 {
366 const double val = std::get<double>(m_values[id].value);
367 return val >= 0.0 &&
369 static_cast<double>(static_cast<long>(val)) == val;
370 }
371 return false;
372 }
373
374 bool IROptimizer::isSmallerNumberInlinable(const uint16_t id) const
375 {
376 if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
377 {
378 const double val = std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber;
379 return val >= 0.0 &&
381 static_cast<double>(static_cast<long>(val)) == val;
382 }
383 return false;
384 }
385
386 bool IROptimizer::isNumberEqualTo(const uint16_t id, const int number) const
387 {
388 if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
389 {
390 const double val = std::get<double>(m_values[id].value);
391 return static_cast<double>(static_cast<long>(val)) == val &&
392 static_cast<int>(val) == number;
393 }
394 return false;
395 }
396
397 uint16_t IROptimizer::numberAsArg(const uint16_t id) const
398 {
399 return static_cast<uint16_t>(std::get<double>(m_values[id].value));
400 }
401
402 uint16_t IROptimizer::smallerNumberAsArg(const uint16_t id) const
403 {
404 return static_cast<uint16_t>(std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber);
405 }
406}
Host the declaration of all the ArkScript builtins.
Optimize IR based on IR entity grouped by 2 (or more)
std::vector< ValTableElem > m_values
bool isSmallerNumberInlinable(uint16_t id) const
bool isPositiveNumberInlinable(uint16_t id) const
std::vector< IR::Block > m_ir
IROptimizer(unsigned debug)
Create a new IROptimizer.
uint16_t numberAsArg(uint16_t id) const
std::optional< EntityWithOffset > replaceWithRules(std::span< const IR::Entity > entities, std::size_t position_in_block)
std::span< const IR::Entity > Entities
static bool canBeOptimisedSafely(std::span< const IR::Entity > entities, std::size_t window_size, std::size_t position_in_block)
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
std::vector< Rule > m_ruleset
void process(const std::vector< IR::Block > &pages, const std::vector< std::string > &symbols, const std::vector< ValTableElem > &values)
Turn a given IR into bytecode.
std::vector< std::string > m_symbols
bool match(const std::vector< Instruction > &expected_insts, std::span< const IR::Entity > entities) const
uint16_t smallerNumberAsArg(uint16_t id) const
bool isNumberEqualTo(uint16_t id, int number) const
bool hasLabel() const
Check if the Entity has a label attached.
Definition Entity.hpp:133
static Entity GotoWithArg(const Entity &label, Instruction inst, uint16_t primary_arg)
Create a new Goto IR Entity.
Definition Entity.cpp:52
uint16_t primaryArg() const
Return the primary argument of the IR Entity (can be 0 if the argument isn't used)
Definition Entity.hpp:181
void debug(const Logger::MessageAndLocation &data, Args &&... args)
Write a debug level log using fmtlib.
Definition Logger.hpp:96
void traceStart(std::string &&trace_name)
Definition Logger.hpp:109
An interface to describe compiler passes.
Definition Pass.hpp:24
constexpr uint16_t MaxValueForSmallNumber
Definition Entity.hpp:39
constexpr uint16_t MaxValueForDualArg
The maximum value an argument can have when an IR entity has two arguments.
Definition Entity.hpp:38
IR::Entity fuseMathOps3(const std::span< const IR::Entity > e)
IR::Entity fuseMathOps2(const std::span< const IR::Entity > e)
static Block InitWithMetadata(const Block &source)
Create a new empty IR::Block with the same metadata as the source block.
Definition Entity.hpp:262