ArkScript
A small, fast, functional and scripting language for video games
IROptimizer.cpp
Go to the documentation of this file.
2
3#include <utility>
5
6namespace Ark::internal
7{
9 {
11 std::size_t offset;
12 };
13
14 IROptimizer::IROptimizer(const unsigned debug) :
15 m_logger("IROptimizer", debug)
16 {}
17
18 void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
19 {
20 m_logger.traceStart("process");
21 m_symbols = symbols;
22 m_values = values;
23
24 auto map = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> decltype(std::optional(lambda(opt.value()))) {
25 if (opt.has_value())
26 return lambda(opt.value());
27 return std::nullopt;
28 };
29
30 auto or_else = []<typename T>(const std::optional<T>& opt, auto&& lambda) -> std::optional<T> {
31 if (!opt.has_value())
32 return lambda();
33 return opt;
34 };
35
36 for (const auto& block : pages)
37 {
38 m_ir.emplace_back();
39 IR::Block& current_block = m_ir.back();
40
41 std::size_t i = 0;
42 const std::size_t end = block.size();
43
44 while (i < end)
45 {
46 std::optional<EntityWithOffset> maybe_compacted = std::nullopt;
47
48 if (i + 1 < end)
49 maybe_compacted = map(
50 compactEntities(block[i], block[i + 1]),
51 [](const auto& entity) {
52 return std::make_optional<EntityWithOffset>(entity, 2);
53 });
54 if (i + 2 < end)
55 maybe_compacted = or_else(
56 maybe_compacted,
57 [&, this]() {
58 return map(
59 compactEntities(block[i], block[i + 1], block[i + 2]),
60 [](const auto& entity) {
61 return std::make_optional<EntityWithOffset>(entity, 3);
62 });
63 });
64
65 if (maybe_compacted.has_value())
66 {
67 auto [entity, offset] = maybe_compacted.value();
68 current_block.emplace_back(entity);
69 i += offset;
70 }
71 else
72 {
73 current_block.emplace_back(block[i]);
74 ++i;
75 }
76 }
77 }
78
80 }
81
82 const std::vector<IR::Block>& IROptimizer::intermediateRepresentation() const noexcept
83 {
84 return m_ir;
85 }
86
87 std::optional<IR::Entity> IROptimizer::compactEntities(const IR::Entity& first, const IR::Entity& second)
88 {
90 return std::nullopt;
91
92 // LOAD_CONST x
93 // LOAD_CONST y
94 // ---> LOAD_CONST_LOAD_CONST x y
95 if (first.inst() == LOAD_CONST && second.inst() == LOAD_CONST)
96 return IR::Entity(LOAD_CONST_LOAD_CONST, first.primaryArg(), second.primaryArg());
97 // LOAD_CONST x
98 // STORE / SET_VAL a
99 // ---> LOAD_CONST_STORE x a ; LOAD_CONST_SET_VAL x a
100 if (first.inst() == LOAD_CONST && second.inst() == STORE)
101 return IR::Entity(LOAD_CONST_STORE, first.primaryArg(), second.primaryArg());
102 if (first.inst() == LOAD_CONST && second.inst() == SET_VAL)
103 return IR::Entity(LOAD_CONST_SET_VAL, first.primaryArg(), second.primaryArg());
104 // LOAD_SYMBOL a
105 // STORE / SET_VAL b
106 // ---> STORE_FROM a b ; SET_VAL_FROM a b
107 if (first.inst() == LOAD_SYMBOL && second.inst() == STORE)
108 return IR::Entity(STORE_FROM, first.primaryArg(), second.primaryArg());
109 if (first.inst() == LOAD_SYMBOL && second.inst() == SET_VAL)
110 return IR::Entity(SET_VAL_FROM, first.primaryArg(), second.primaryArg());
111 // BUILTIN i
112 // CALL n
113 // ---> CALL_BUILTIN i n
114 if (first.inst() == BUILTIN && second.inst() == CALL && Builtins::builtins[first.primaryArg()].second.isFunction())
115 return IR::Entity(CALL_BUILTIN, first.primaryArg(), second.primaryArg());
116
117 return std::nullopt;
118 }
119
120 std::optional<IR::Entity> IROptimizer::compactEntities(const IR::Entity& first, const IR::Entity& second, const IR::Entity& third)
121 {
123 return std::nullopt;
124
125 // LOAD_SYMBOL a
126 // LOAD_CONST n (1)
127 // ADD / SUB
128 // ---> INCREMENT / DECREMENT a value
129 if (third.inst() == ADD && first.inst() == LOAD_CONST && second.inst() == LOAD_SYMBOL && isPositiveNumberInlinable(first.primaryArg()))
130 return IR::Entity(INCREMENT, second.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[first.primaryArg()].value)));
131 if (third.inst() == ADD && first.inst() == LOAD_SYMBOL && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
132 return IR::Entity(INCREMENT, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
133 if (third.inst() == SUB && first.inst() == LOAD_SYMBOL && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
134 return IR::Entity(DECREMENT, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
135 // LOAD_SYMBOL list
136 // TAIL / HEAD
137 // STORE / SET_VAL a
138 // ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
139 if (first.inst() == LOAD_SYMBOL && second.inst() == TAIL && third.inst() == STORE)
140 return IR::Entity(STORE_TAIL, first.primaryArg(), third.primaryArg());
141 if (first.inst() == LOAD_SYMBOL && second.inst() == TAIL && third.inst() == SET_VAL)
142 return IR::Entity(SET_VAL_TAIL, first.primaryArg(), third.primaryArg());
143 if (first.inst() == LOAD_SYMBOL && second.inst() == HEAD && third.inst() == STORE)
144 return IR::Entity(STORE_HEAD, first.primaryArg(), third.primaryArg());
145 if (first.inst() == LOAD_SYMBOL && second.inst() == HEAD && third.inst() == SET_VAL)
146 return IR::Entity(SET_VAL_HEAD, first.primaryArg(), third.primaryArg());
147
148 return std::nullopt;
149 }
150
151 bool IROptimizer::isPositiveNumberInlinable(const uint16_t id) const
152 {
153 if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
154 {
155 const double val = std::get<double>(m_values[id].value);
156 return val >= 0.0 &&
158 static_cast<double>(static_cast<long>(val)) == val;
159 }
160 return false;
161 }
162}
Host the declaration of all the ArkScript builtins.
Optimize IR based on IR entity grouped by 2 (or more)
std::vector< ValTableElem > m_values
std::optional< IR::Entity > compactEntities(const IR::Entity &first, const IR::Entity &second)
bool isPositiveNumberInlinable(uint16_t id) const
std::vector< IR::Block > m_ir
IROptimizer(unsigned debug)
Create a new IROptimizer.
const std::vector< IR::Block > & intermediateRepresentation() const noexcept
Return the IR blocks (one per scope)
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
Instruction inst() const
Definition Entity.hpp:59
uint16_t primaryArg() const
Definition Entity.hpp:61
void traceStart(std::string &&trace_name)
Definition Logger.hpp:75
ARK_API const std::vector< std::pair< std::string, Value > > builtins
std::vector< Entity > Block
Definition Entity.hpp:73
constexpr uint16_t MaxValueForDualArg
The maximum value an argument can have when an IR entity has two arguments.
Definition Entity.hpp:36