Skip to content

Commit 2f9b2e8

Browse files
committed
feat(ir optimizer, vm): new super instructions MUL_BY, MUL_BY_INDEX and MUL_SET_VAL
1 parent 0186418 commit 2f9b2e8

File tree

15 files changed

+262
-20
lines changed

15 files changed

+262
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased changes] - 20XX-XX-XX
44
### Added
55
- the repl prints the output of the last expression it ran
6+
- new super instructions: `MUL_BY`, `MUL_BY_INDEX`, `MUL_SET_VAL` that can do multiplications (and optional storing in vars) in place
67

78
### Fixed
89
- the REPL doesn't color `import` in two colors (red for `imp__t` and blue for `___or_`), it keeps the first color that matched (red for import here)

include/Ark/Compiler/Instructions.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ namespace Ark::internal
429429
// @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false
430430
LT_LEN_SYM_JUMP_IF_FALSE = 0x6b,
431431

432+
MUL_BY = 0x6c,
433+
MUL_BY_INDEX = 0x6d,
434+
MUL_SET_VAL = 0x6e,
435+
432436
InstructionsCount
433437
};
434438

@@ -542,7 +546,10 @@ namespace Ark::internal
542546
"APPEND_IN_PLACE_SYM",
543547
"APPEND_IN_PLACE_SYM_INDEX",
544548
"STORE_LEN",
545-
"LT_LEN_SYM_JUMP_IF_FALSE"
549+
"LT_LEN_SYM_JUMP_IF_FALSE",
550+
"MUL_BY",
551+
"MUL_BY_INDEX",
552+
"MUL_SET_VAL"
546553
};
547554

548555
static_assert(InstructionNames.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instruction names appear to be missing");

include/Ark/Compiler/IntermediateRepresentation/Entity.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace Ark::internal::IR
3333

3434
/// The maximum value an argument can have when an IR entity has two arguments
3535
constexpr uint16_t MaxValueForDualArg = 0x0fff;
36+
constexpr uint16_t MaxValueForSmallNumber = 0x0800;
37+
static_assert(MaxValueForSmallNumber + MaxValueForSmallNumber - 1 == MaxValueForDualArg);
3638

3739
class Entity
3840
{

include/Ark/Compiler/IntermediateRepresentation/IROptimizer.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ namespace Ark::internal
9797
std::optional<EntityWithOffset> replaceWithRules(std::span<const IR::Entity> entities, const std::size_t position_in_block);
9898

9999
[[nodiscard]] bool isPositiveNumberInlinable(uint16_t id) const;
100+
[[nodiscard]] bool isSmallerNumberInlinable(uint16_t id) const;
101+
[[nodiscard]] bool isNumberEqualTo(uint16_t id, int number) const;
100102
[[nodiscard]] uint16_t numberAsArg(uint16_t id) const;
103+
[[nodiscard]] uint16_t smallerNumberAsArg(uint16_t id) const;
101104
};
102105
}
103106

src/arkreactor/Compiler/BytecodeReader.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,10 @@ namespace Ark
559559
{ APPEND_IN_PLACE_SYM, ArgKind::SymRaw },
560560
{ APPEND_IN_PLACE_SYM_INDEX, ArgKind::RawRaw },
561561
{ STORE_LEN, ArgKind::RawSym },
562-
{ LT_LEN_SYM_JUMP_IF_FALSE, ArgKind::SymRaw }
562+
{ LT_LEN_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
563+
{ MUL_BY, ArgKind::RawRaw },
564+
{ MUL_BY_INDEX, ArgKind::RawRaw },
565+
{ MUL_SET_VAL, ArgKind::RawRaw }
563566
};
564567

565568
const auto builtin_name = [](const uint16_t idx) {

src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,47 @@ namespace Ark::internal
5151
Rule { { LIST, STORE }, STORE_LIST },
5252
Rule { { LOAD_SYMBOL, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM },
5353
Rule { { LOAD_SYMBOL_BY_INDEX, APPEND_IN_PLACE }, APPEND_IN_PLACE_SYM_INDEX },
54-
// LOAD_SYMBOL a / LOAD_SYMBOL_BY_INDEX index
55-
// LOAD_CONST n (1)
56-
// ADD / SUB
57-
// STORE
54+
// LOAD_CONST, LOAD_SYMBOL a, MUL, SET_VAL / LOAD_SYMBOL a, LOAD_CONST, MUL, SET_VAL
55+
// ---> MUL_SET_VAL a value
56+
Rule { { LOAD_CONST, LOAD_SYMBOL, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
57+
return isSmallerNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
58+
},
59+
[this](const Entities e) {
60+
return IR::Entity(MUL_SET_VAL, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
61+
} },
62+
Rule { { LOAD_SYMBOL, LOAD_CONST, MUL, SET_VAL }, [this](const Entities e, const std::size_t) {
63+
return isSmallerNumberInlinable(e[1].primaryArg()) && e[0].primaryArg() == e[3].primaryArg();
64+
},
65+
[this](const Entities e) {
66+
return IR::Entity(MUL_SET_VAL, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
67+
} },
68+
// LOAD_CONST, LOAD_SYMBOL a, MUL / LOAD_SYMBOL a, LOAD_CONST, MUL
69+
// ---> MUL_(BY|BY_INDEX) a value
70+
Rule { { LOAD_CONST, LOAD_SYMBOL, MUL }, [this](const Entities e, const std::size_t) {
71+
return isSmallerNumberInlinable(e[0].primaryArg());
72+
},
73+
[this](const Entities e) {
74+
return IR::Entity(MUL_BY, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
75+
} },
76+
Rule { { LOAD_SYMBOL, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
77+
return isSmallerNumberInlinable(e[1].primaryArg());
78+
},
79+
[this](const Entities e) {
80+
return IR::Entity(MUL_BY, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
81+
} },
82+
Rule { { LOAD_CONST, LOAD_SYMBOL_BY_INDEX, MUL }, [this](const Entities e, const std::size_t) {
83+
return isSmallerNumberInlinable(e[0].primaryArg());
84+
},
85+
[this](const Entities e) {
86+
return IR::Entity(MUL_BY_INDEX, e[1].primaryArg(), smallerNumberAsArg(e[0].primaryArg()));
87+
} },
88+
Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, MUL }, [this](const Entities e, const std::size_t) {
89+
return isSmallerNumberInlinable(e[1].primaryArg());
90+
},
91+
[this](const Entities e) {
92+
return IR::Entity(MUL_BY_INDEX, e[0].primaryArg(), smallerNumberAsArg(e[1].primaryArg()));
93+
} },
94+
// (LOAD_SYMBOL a | LOAD_SYMBOL_BY_INDEX index), LOAD_CONST n (=1), (ADD | SUB), STORE
5895
// ---> INCREMENT_STORE / DECREMENT_STORE a value
5996
Rule { { LOAD_CONST, LOAD_SYMBOL, ADD, SET_VAL }, [this](const Entities e, const std::size_t) {
6097
return isPositiveNumberInlinable(e[0].primaryArg()) && e[1].primaryArg() == e[3].primaryArg();
@@ -111,9 +148,7 @@ namespace Ark::internal
111148
[this](const Entities e) {
112149
return IR::Entity(DECREMENT_BY_INDEX, e[0].primaryArg(), numberAsArg(e[1].primaryArg()));
113150
} },
114-
// LOAD_SYMBOL list
115-
// TAIL / HEAD
116-
// STORE / SET_VAL a
151+
// LOAD_SYMBOL list, (TAIL | HEAD), (STORE | SET_VAL a)
117152
// ---> STORE_TAIL list a ; STORE_HEAD ; SET_VAL_TAIL ; SET_VAL_HEAD
118153
Rule { { LOAD_SYMBOL, TAIL, STORE }, [](const Entities e) {
119154
return IR::Entity(STORE_TAIL, e[0].primaryArg(), e[2].primaryArg());
@@ -139,9 +174,7 @@ namespace Ark::internal
139174
Rule { { LOAD_SYMBOL_BY_INDEX, HEAD, SET_VAL }, [](const Entities e) {
140175
return IR::Entity(SET_VAL_HEAD_BY_INDEX, e[0].primaryArg(), e[2].primaryArg());
141176
} },
142-
// LOAD_CONST id / LOAD_SYMBOL id
143-
// <comparison operator>
144-
// POP_JUMP_IF_(FALSE|TRUE)
177+
// (LOAD_CONST id | LOAD_SYMBOL id), <comparison operator>, POP_JUMP_IF_(FALSE|TRUE)
145178
// ---> <OP>_(CONST|SYM)_JUMP_IF_(FALSE|TRUE)
146179
Rule { { LOAD_CONST, LT, POP_JUMP_IF_FALSE }, [](const Entities e) {
147180
return IR::Entity::GotoWithArg(e[2], LT_CONST_JUMP_IF_FALSE, e[0].primaryArg());
@@ -173,17 +206,12 @@ namespace Ark::internal
173206
Rule { { LOAD_SYMBOL, NEQ, POP_JUMP_IF_FALSE }, [](const Entities e) {
174207
return IR::Entity::GotoWithArg(e[2], NEQ_SYM_JUMP_IF_FALSE, e[0].primaryArg());
175208
} },
176-
// LOAD_SYMBOL id
177-
// LOAD_SYMBOL id2
178-
// AT
209+
// LOAD_SYMBOL id, LOAD_SYMBOL id2, AT
179210
// ---> AT_SYM_SYM id id2
180211
Rule { { LOAD_SYMBOL, LOAD_SYMBOL, AT }, AT_SYM_SYM },
181212
Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_SYMBOL_BY_INDEX, AT }, AT_SYM_INDEX_SYM_INDEX },
182213
Rule { { LOAD_SYMBOL_BY_INDEX, LOAD_CONST, AT }, AT_SYM_INDEX_CONST },
183-
// LOAD_SYMBOL sym
184-
// TYPE
185-
// LOAD_CONST cst
186-
// EQ
214+
// LOAD_SYMBOL sym, TYPE, LOAD_CONST cst, EQ
187215
// ---> CHECK_TYPE_OF sym, cst
188216
// also works with LOAD_CONST cst, LOAD_SYMBOL sym, TYPE, EQ, but args will be flipped
189217
Rule { { LOAD_SYMBOL, TYPE, LOAD_CONST, EQ }, [](const Entities e) {
@@ -313,8 +341,36 @@ namespace Ark::internal
313341
return false;
314342
}
315343

344+
bool IROptimizer::isSmallerNumberInlinable(const uint16_t id) const
345+
{
346+
if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
347+
{
348+
const double val = std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber;
349+
return val >= 0.0 &&
350+
val < IR::MaxValueForDualArg &&
351+
static_cast<double>(static_cast<long>(val)) == val;
352+
}
353+
return false;
354+
}
355+
356+
bool IROptimizer::isNumberEqualTo(const uint16_t id, const int number) const
357+
{
358+
if (std::cmp_less(id, m_values.size()) && m_values[id].type == ValTableElemType::Number)
359+
{
360+
const double val = std::get<double>(m_values[id].value);
361+
return static_cast<double>(static_cast<long>(val)) == val &&
362+
static_cast<int>(val) == number;
363+
}
364+
return false;
365+
}
366+
316367
uint16_t IROptimizer::numberAsArg(const uint16_t id) const
317368
{
318369
return static_cast<uint16_t>(std::get<double>(m_values[id].value));
319370
}
371+
372+
uint16_t IROptimizer::smallerNumberAsArg(const uint16_t id) const
373+
{
374+
return static_cast<uint16_t>(std::get<double>(m_values[id].value) + IR::MaxValueForSmallNumber);
375+
}
320376
}

src/arkreactor/VM/VM.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,10 @@ namespace Ark
644644
&&TARGET_APPEND_IN_PLACE_SYM,
645645
&&TARGET_APPEND_IN_PLACE_SYM_INDEX,
646646
&&TARGET_STORE_LEN,
647-
&&TARGET_LT_LEN_SYM_JUMP_IF_FALSE
647+
&&TARGET_LT_LEN_SYM_JUMP_IF_FALSE,
648+
&&TARGET_MUL_BY,
649+
&&TARGET_MUL_BY_INDEX,
650+
&&TARGET_MUL_SET_VAL
648651
};
649652

650653
static_assert(opcode_targets.size() == static_cast<std::size_t>(Instruction::InstructionsCount) && "Some instructions are not implemented in the VM");
@@ -1979,6 +1982,75 @@ namespace Ark
19791982
}
19801983
DISPATCH();
19811984
}
1985+
1986+
TARGET(MUL_BY)
1987+
{
1988+
UNPACK_ARGS();
1989+
{
1990+
Value* var = loadSymbol(primary_arg, context);
1991+
const int other = static_cast<int>(secondary_arg) - 2048;
1992+
1993+
// use internal reference, shouldn't break anything so far, unless it's already a ref
1994+
if (var->valueType() == ValueType::Reference)
1995+
var = var->reference();
1996+
1997+
if (var->valueType() == ValueType::Number)
1998+
push(Value(var->number() * other), context);
1999+
else
2000+
throw types::TypeCheckingError(
2001+
"*",
2002+
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
2003+
{ *var, Value(other) });
2004+
}
2005+
DISPATCH();
2006+
}
2007+
2008+
TARGET(MUL_BY_INDEX)
2009+
{
2010+
UNPACK_ARGS();
2011+
{
2012+
Value* var = loadSymbolFromIndex(primary_arg, context);
2013+
const int other = static_cast<int>(secondary_arg) - 2048;
2014+
2015+
// use internal reference, shouldn't break anything so far, unless it's already a ref
2016+
if (var->valueType() == ValueType::Reference)
2017+
var = var->reference();
2018+
2019+
if (var->valueType() == ValueType::Number)
2020+
push(Value(var->number() * other), context);
2021+
else
2022+
throw types::TypeCheckingError(
2023+
"*",
2024+
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
2025+
{ *var, Value(other) });
2026+
}
2027+
DISPATCH();
2028+
}
2029+
2030+
TARGET(MUL_SET_VAL)
2031+
{
2032+
UNPACK_ARGS();
2033+
{
2034+
Value* var = loadSymbol(primary_arg, context);
2035+
const int other = static_cast<int>(secondary_arg) - 2048;
2036+
2037+
// use internal reference, shouldn't break anything so far, unless it's already a ref
2038+
if (var->valueType() == ValueType::Reference)
2039+
var = var->reference();
2040+
2041+
if (var->valueType() == ValueType::Number)
2042+
{
2043+
auto val = Value(var->number() * other);
2044+
setVal(primary_arg, &val, context);
2045+
}
2046+
else
2047+
throw types::TypeCheckingError(
2048+
"*",
2049+
{ { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } },
2050+
{ *var, Value(other) });
2051+
}
2052+
DISPATCH();
2053+
}
19822054
#pragma endregion
19832055
}
19842056
#if ARK_USE_COMPUTED_GOTOS
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
(mut a 5)
2+
# MUL_BY_INDEX
3+
(set a (* a 6))
4+
(set a (* 6 a))
5+
6+
(let foo (fun () {
7+
# MUL_BY
8+
(mut b (* a 7))
9+
(mut b (* 7 a))
10+
# MUL_SET_VAL
11+
(set a (* a 8))
12+
(set a (* 8 a)) }))
13+
14+
(foo)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
page_0
2+
LOAD_CONST_STORE 0, 0
3+
MUL_BY_INDEX 0, 2054
4+
SET_VAL 0
5+
MUL_BY_INDEX 0, 2054
6+
SET_VAL 0
7+
LOAD_CONST_STORE 2, 1
8+
PUSH_RETURN_ADDRESS L0
9+
LOAD_SYMBOL_BY_INDEX 0
10+
CALL 0
11+
.L0:
12+
HALT 0
13+
14+
page_1
15+
MUL_BY 0, 2055
16+
STORE 2
17+
MUL_BY 0, 2055
18+
STORE 2
19+
MUL_SET_VAL 0, 2056
20+
MUL_SET_VAL 0, 2056
21+
RET 0
22+
HALT 0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(mut a "5")
2+
(set a (* a 6))

0 commit comments

Comments
 (0)