Skip to content

Commit 0c17986

Browse files
committed
feat(ir optimizer): improve instructions merging to use a list of rules, easier to maintain with less code repetition
1 parent cffc640 commit 0c17986

File tree

2 files changed

+134
-71
lines changed

2 files changed

+134
-71
lines changed

include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <Ark/Compiler/IntermediateRepresentation/Entity.hpp>
1818

1919
#include <optional>
20+
#include <functional>
2021

2122
namespace Ark::internal
2223
{
@@ -47,15 +48,35 @@ namespace Ark::internal
4748
[[nodiscard]] const std::vector<IR::Block>& intermediateRepresentation() const noexcept;
4849

4950
private:
51+
using Entities = std::vector<IR::Entity>;
52+
using DualArgs = std::pair<uint16_t, uint16_t>;
53+
54+
struct Rule
55+
{
56+
std::vector<Instruction> expected;
57+
Instruction replacement;
58+
std::function<bool(const Entities&)> condition = [](const Entities&) {
59+
return true;
60+
}; ///< Additional condition to match
61+
std::function<DualArgs(const Entities&)> createReplacement =
62+
[](const Entities& entities) {
63+
return std::make_pair(entities[0].primaryArg(), entities[1].primaryArg());
64+
}; ///< Create the replacement instructions from given context
65+
};
66+
67+
std::vector<Rule> m_ruleset_two;
68+
std::vector<Rule> m_ruleset_three;
69+
5070
Logger m_logger;
5171
std::vector<IR::Block> m_ir;
5272
std::vector<std::string> m_symbols;
5373
std::vector<ValTableElem> m_values;
5474

55-
[[nodiscard]] std::optional<IR::Entity> compactEntities(const IR::Entity& first, const IR::Entity& second);
56-
[[nodiscard]] std::optional<IR::Entity> compactEntities(const IR::Entity& first, const IR::Entity& second, const IR::Entity& third);
75+
[[nodiscard]] bool match(const std::vector<Instruction>& expected_insts, const Entities& entities) const;
76+
std::optional<IR::Entity> replaceWithRules(const std::vector<Rule>& rules, const Entities& entities);
5777

5878
[[nodiscard]] bool isPositiveNumberInlinable(uint16_t id) const;
79+
[[nodiscard]] uint16_t numberAsArg(uint16_t id) const;
5980
};
6081
}
6182

src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp

Lines changed: 111 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,88 @@ namespace Ark::internal
1313

1414
IROptimizer::IROptimizer(const unsigned debug) :
1515
m_logger("IROptimizer", debug)
16-
{}
16+
{
17+
m_ruleset_two = {
18+
Rule {
19+
{ LOAD_CONST, LOAD_CONST }, LOAD_CONST_LOAD_CONST },
20+
Rule {
21+
{ LOAD_CONST, STORE }, LOAD_CONST_STORE },
22+
Rule {
23+
{ LOAD_CONST, SET_VAL }, LOAD_CONST_SET_VAL },
24+
Rule {
25+
{ LOAD_SYMBOL, STORE }, STORE_FROM },
26+
Rule {
27+
{ LOAD_SYMBOL_BY_INDEX, STORE }, STORE_FROM_INDEX },
28+
Rule {
29+
{ LOAD_SYMBOL, SET_VAL }, SET_VAL_FROM },
30+
Rule {
31+
{ LOAD_SYMBOL_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX },
32+
Rule {
33+
{ BUILTIN, CALL }, CALL_BUILTIN, [](const Entities& entities) {
34+
return Builtins::builtins[entities[0].primaryArg()].second.isFunction();
35+
} }
36+
};
37+
38+
m_ruleset_three = {
39+
// LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
40+
// LOAD_CONST n (1)
41+
// ADD / SUB
42+
// ---> INCREMENT / DECREMENT a value
43+
Rule {
44+
{ LOAD_CONST, LOAD_SYMBOL, ADD }, INCREMENT, [this](const Entities& e) {
45+
return isPositiveNumberInlinable(e[0].primaryArg());
46+
},
47+
[this](const Entities& e) {
48+
return std::make_pair(e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
49+
} },
50+
Rule { { LOAD_SYMBOL, LOAD_CONST, ADD }, INCREMENT, [this](const Entities& e) {
51+
return isPositiveNumberInlinable(e[1].primaryArg());
52+
},
53+
[this](const Entities& e) {
54+
return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
55+
} },
56+
Rule { { LOAD_SYMBOL, LOAD_CONST, SUB }, DECREMENT, [this](const Entities& e) {
57+
return isPositiveNumberInlinable(e[1].primaryArg());
58+
},
59+
[this](const Entities& e) {
60+
return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
61+
} },
62+
Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, ADD }, INCREMENT_BY_INDEX, [this](const Entities& e) {
63+
return isPositiveNumberInlinable(e[0].primaryArg());
64+
},
65+
[this](const Entities& e) {
66+
return std::make_pair(e[1].primaryArg(), numberAsArg(e[0].primaryArg()));
67+
} },
68+
Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, ADD }, INCREMENT_BY_INDEX, [this](const Entities& e) {
69+
return isPositiveNumberInlinable(e[1].primaryArg());
70+
},
71+
[this](const Entities& e) {
72+
return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
73+
} },
74+
Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, SUB }, DECREMENT_BY_INDEX, [this](const Entities& e) {
75+
return isPositiveNumberInlinable(e[1].primaryArg());
76+
},
77+
[this](const Entities& e) {
78+
return std::make_pair(e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
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 { { LOAD_SYMBOL, TAIL, STORE }, STORE_TAIL, .createReplacement = [](const Entities& e) {
85+
return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
86+
} },
87+
Rule { { LOAD_SYMBOL, TAIL, SET_VAL }, SET_VAL_TAIL, .createReplacement = [](const Entities& e) {
88+
return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
89+
} },
90+
Rule { { LOAD_SYMBOL, HEAD, STORE }, STORE_HEAD, .createReplacement = [](const Entities& e) {
91+
return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
92+
} },
93+
Rule { { LOAD_SYMBOL, HEAD, SET_VAL }, SET_VAL_HEAD, .createReplacement = [](const Entities& e) {
94+
return std::make_pair(e[0].primaryArg(), e[1].primaryArg());
95+
} }
96+
};
97+
}
1798

1899
void IROptimizer::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
19100
{
@@ -47,7 +128,7 @@ namespace Ark::internal
47128

48129
if (i + 1 < end)
49130
maybe_compacted = map(
50-
compactEntities(block[i], block[i + 1]),
131+
replaceWithRules(m_ruleset_two, { block[i], block[i + 1] }),
51132
[](const auto& entity) {
52133
return std::make_optional<EntityWithOffset>(entity, 2);
53134
});
@@ -56,7 +137,7 @@ namespace Ark::internal
56137
maybe_compacted,
57138
[&, this]() {
58139
return map(
59-
compactEntities(block[i], block[i + 1], block[i + 2]),
140+
replaceWithRules(m_ruleset_three, { block[i], block[i + 1], block[i + 2] }),
60141
[](const auto& entity) {
61142
return std::make_optional<EntityWithOffset>(entity, 3);
62143
});
@@ -84,79 +165,35 @@ namespace Ark::internal
84165
return m_ir;
85166
}
86167

87-
std::optional<IR::Entity> IROptimizer::compactEntities(const IR::Entity& first, const IR::Entity& second)
168+
bool IROptimizer::match(const std::vector<Instruction>& expected_insts, const Entities& entities) const
88169
{
89-
if (first.primaryArg() > IR::MaxValueForDualArg || second.primaryArg() > IR::MaxValueForDualArg)
90-
return std::nullopt;
170+
assert(expected_insts.size() == entities.size() && "Mismatching size between expected instructions and given entities");
91171

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 / LOAD_SYMBOL_BY_INDEX 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_BY_INDEX && second.inst() == STORE)
110-
return IR::Entity(STORE_FROM_INDEX, first.primaryArg(), second.primaryArg());
111-
if (first.inst() == LOAD_SYMBOL && second.inst() == SET_VAL)
112-
return IR::Entity(SET_VAL_FROM, first.primaryArg(), second.primaryArg());
113-
if (first.inst() == LOAD_SYMBOL_BY_INDEX && second.inst() == SET_VAL)
114-
return IR::Entity(SET_VAL_FROM_INDEX, first.primaryArg(), second.primaryArg());
115-
// BUILTIN i
116-
// CALL n
117-
// ---> CALL_BUILTIN i n
118-
if (first.inst() == BUILTIN && second.inst() == CALL && Builtins::builtins[first.primaryArg()].second.isFunction())
119-
return IR::Entity(CALL_BUILTIN, first.primaryArg(), second.primaryArg());
172+
for (std::size_t i = 0; i < expected_insts.size(); ++i)
173+
{
174+
if (expected_insts[i] != entities[i].inst())
175+
return false;
176+
}
120177

121-
return std::nullopt;
178+
return true;
122179
}
123180

124-
std::optional<IR::Entity> IROptimizer::compactEntities(const IR::Entity& first, const IR::Entity& second, const IR::Entity& third)
181+
std::optional<IR::Entity> IROptimizer::replaceWithRules(const std::vector<Rule>& rules, const Entities& entities)
125182
{
126-
if (first.primaryArg() > IR::MaxValueForDualArg || second.primaryArg() > IR::MaxValueForDualArg || third.primaryArg() > IR::MaxValueForDualArg)
127-
return std::nullopt;
183+
for (auto&& entity : entities)
184+
{
185+
if (entity.primaryArg() > IR::MaxValueForDualArg)
186+
return std::nullopt;
187+
}
128188

129-
// LOAD_SYMBOL a
130-
// LOAD_CONST n (1)
131-
// ADD / SUB
132-
// ---> INCREMENT / DECREMENT a value
133-
if (third.inst() == ADD && first.inst() == LOAD_CONST && second.inst() == LOAD_SYMBOL && isPositiveNumberInlinable(first.primaryArg()))
134-
return IR::Entity(INCREMENT, second.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[first.primaryArg()].value)));
135-
if (third.inst() == ADD && first.inst() == LOAD_SYMBOL && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
136-
return IR::Entity(INCREMENT, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
137-
if (third.inst() == SUB && first.inst() == LOAD_SYMBOL && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
138-
return IR::Entity(DECREMENT, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
139-
140-
// todo: refactor
141-
if (third.inst() == ADD && first.inst() == LOAD_CONST && second.inst() == LOAD_SYMBOL_BY_INDEX && isPositiveNumberInlinable(first.primaryArg()))
142-
return IR::Entity(INCREMENT_BY_INDEX, second.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[first.primaryArg()].value)));
143-
if (third.inst() == ADD && first.inst() == LOAD_SYMBOL_BY_INDEX && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
144-
return IR::Entity(INCREMENT_BY_INDEX, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
145-
if (third.inst() == SUB && first.inst() == LOAD_SYMBOL_BY_INDEX && second.inst() == LOAD_CONST && isPositiveNumberInlinable(second.primaryArg()))
146-
return IR::Entity(DECREMENT_BY_INDEX, first.primaryArg(), static_cast<uint16_t>(std::get<double>(m_values[second.primaryArg()].value)));
147-
148-
// LOAD_SYMBOL list
149-
// TAIL / HEAD
150-
// STORE / SET_VAL a
151-
// ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
152-
if (first.inst() == LOAD_SYMBOL && second.inst() == TAIL && third.inst() == STORE)
153-
return IR::Entity(STORE_TAIL, first.primaryArg(), third.primaryArg());
154-
if (first.inst() == LOAD_SYMBOL && second.inst() == TAIL && third.inst() == SET_VAL)
155-
return IR::Entity(SET_VAL_TAIL, first.primaryArg(), third.primaryArg());
156-
if (first.inst() == LOAD_SYMBOL && second.inst() == HEAD && third.inst() == STORE)
157-
return IR::Entity(STORE_HEAD, first.primaryArg(), third.primaryArg());
158-
if (first.inst() == LOAD_SYMBOL && second.inst() == HEAD && third.inst() == SET_VAL)
159-
return IR::Entity(SET_VAL_HEAD, first.primaryArg(), third.primaryArg());
189+
for (const auto& [expected, replacement, condition, createReplacement] : rules)
190+
{
191+
if (match(expected, entities) && condition(entities))
192+
{
193+
auto [first, second] = createReplacement(entities);
194+
return IR::Entity(replacement, first, second);
195+
}
196+
}
160197

161198
return std::nullopt;
162199
}
@@ -172,4 +209,9 @@ namespace Ark::internal
172209
}
173210
return false;
174211
}
212+
213+
uint16_t IROptimizer::numberAsArg(const uint16_t id) const
214+
{
215+
return static_cast<uint16_t>(std::get<double>(m_values[id].value));
216+
}
175217
}

0 commit comments

Comments
 (0)