Skip to content

Commit 74a670f

Browse files
committed
feat(vm): improved shortcircuit implementation for 'and', 'or', to use a single instruction instead of (DUP, POP JUMP IF FALSE/TRUE, POP)
1 parent 1aa02f6 commit 74a670f

File tree

5 files changed

+113
-91
lines changed

5 files changed

+113
-91
lines changed

include/Ark/Compiler/Instructions.hpp

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -137,231 +137,234 @@ namespace Ark::internal
137137
// @role Remove the top of the stack
138138
POP = 0x1b,
139139

140-
// @role Duplicate the top of the stack
141-
DUP = 0x1c,
140+
// @role Pop the top of the stack, if it's false, jump to an address
141+
SHORTCIRCUIT_AND = 0x1c,
142+
143+
// @role Pop the top of the stack, if it's true, jump to an address
144+
SHORTCIRCUIT_OR = 0x1d,
142145

143146
// @role Create a new local scope
144-
CREATE_SCOPE = 0x1d,
147+
CREATE_SCOPE = 0x1e,
145148

146149
// @role Reset the current scope so that it is empty, and jump to a given location
147-
RESET_SCOPE_JUMP = 0x1e,
150+
RESET_SCOPE_JUMP = 0x1f,
148151

149152
// @role Destroy the last local scope
150-
POP_SCOPE = 0x1f,
153+
POP_SCOPE = 0x20,
151154

152-
FIRST_OPERATOR = 0x20,
155+
FIRST_OPERATOR = 0x21,
153156

154157
// @role Push #[code TS1 + TS]
155-
ADD = 0x20,
158+
ADD = 0x21,
156159

157160
// @role Push #[code TS1 - TS]
158-
SUB = 0x21,
161+
SUB = 0x22,
159162

160163
// @role Push #[code TS1 * TS]
161-
MUL = 0x22,
164+
MUL = 0x23,
162165

163166
// @role Push #[code TS1 / TS]
164-
DIV = 0x23,
167+
DIV = 0x24,
165168

166169
// @role Push #[code TS1 > TS]
167-
GT = 0x24,
170+
GT = 0x25,
168171

169172
// @role Push #[code TS1 < TS]
170-
LT = 0x25,
173+
LT = 0x26,
171174

172175
// @role Push #[code TS1 <= TS]
173-
LE = 0x26,
176+
LE = 0x27,
174177

175178
// @role Push #[code TS1 >= TS]
176-
GE = 0x27,
179+
GE = 0x28,
177180

178181
// @role Push #[code TS1 != TS]
179-
NEQ = 0x28,
182+
NEQ = 0x29,
180183

181184
// @role Push #[code TS1 == TS]
182-
EQ = 0x29,
185+
EQ = 0x2a,
183186

184187
// @role Push #[code len(TS)], TS must be a list
185-
LEN = 0x2a,
188+
LEN = 0x2b,
186189

187190
// @role Push #[code empty?(TS)], TS must be a list or string
188-
EMPTY = 0x2b,
191+
EMPTY = 0x2c,
189192

190193
// @role Push #[code tail(TS)], all the elements of TS except the first one. TS must be a list or string
191-
TAIL = 0x2c,
194+
TAIL = 0x2d,
192195

193196
// @role Push #[code head(TS)], the first element of TS or nil if empty. TS must be a list or string
194-
HEAD = 0x2d,
197+
HEAD = 0x2e,
195198

196199
// @role Push true if TS is nil, false otherwise
197-
ISNIL = 0x2e,
200+
ISNIL = 0x2f,
198201

199202
// @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack
200-
ASSERT = 0x2f,
203+
ASSERT = 0x30,
201204

202205
// @role Convert TS to number (must be a string)
203-
TO_NUM = 0x30,
206+
TO_NUM = 0x31,
204207

205208
// @role Convert TS to string
206-
TO_STR = 0x31,
209+
TO_STR = 0x32,
207210

208211
// @role Push the value at index TS (must be a number) in TS1, which must be a list or string
209-
AT = 0x32,
212+
AT = 0x33,
210213

211214
// @role Push the value at index TS (must be a number), inside the list or string at index TS1 (must be a number) in the list at TS2
212-
AT_AT = 0x33,
215+
AT_AT = 0x34,
213216

214217
// @role Push #[code TS1 % TS]
215-
MOD = 0x34,
218+
MOD = 0x35,
216219

217220
// @role Push the type of TS as a string
218-
TYPE = 0x35,
221+
TYPE = 0x36,
219222

220223
// @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String
221-
HASFIELD = 0x36,
224+
HASFIELD = 0x37,
222225

223226
// @role Push #[code !TS]
224-
NOT = 0x37,
227+
NOT = 0x38,
225228

226229
// @args constant id, constant id
227230
// @role Load two consts (#[code primary] then #[code secondary]) on the stack in one instruction
228-
LOAD_CONST_LOAD_CONST = 0x38,
231+
LOAD_CONST_LOAD_CONST = 0x39,
229232

230233
// @args constant id, symbol id
231234
// @role Load const #[code primary] into the symbol #[code secondary] (create a variable)
232-
LOAD_CONST_STORE = 0x39,
235+
LOAD_CONST_STORE = 0x3a,
233236

234237
// @args constant id, symbol id
235238
// @role Load const #[code primary] into the symbol #[code secondary] (search for the variable with the given symbol id)
236-
LOAD_CONST_SET_VAL = 0x3a,
239+
LOAD_CONST_SET_VAL = 0x3b,
237240

238241
// @args symbol id, symbol id
239242
// @role Store the value of the symbol #[code primary] into a new variable #[code secondary]
240-
STORE_FROM = 0x3b,
243+
STORE_FROM = 0x3c,
241244

242245
// @args symbol index, symbol id
243246
// @role Store the value of the symbol #[code primary] into a new variable #[code secondary]
244-
STORE_FROM_INDEX = 0x3c,
247+
STORE_FROM_INDEX = 0x3d,
245248

246249
// @args symbol id, symbol id
247250
// @role Store the value of the symbol #[code primary] into an existing variable #[code secondary]
248-
SET_VAL_FROM = 0x3d,
251+
SET_VAL_FROM = 0x3e,
249252

250253
// @args symbol index, symbol id
251254
// @role Store the value of the symbol #[code primary] into an existing variable #[code secondary]
252-
SET_VAL_FROM_INDEX = 0x3e,
255+
SET_VAL_FROM_INDEX = 0x3f,
253256

254257
// @args symbol id, count
255258
// @role Increment the variable #[code primary] by #[code count] and push its value on the stack
256-
INCREMENT = 0x3f,
259+
INCREMENT = 0x40,
257260

258261
// @args symbol index, count
259262
// @role Increment the variable #[code primary] by #[code count] and push its value on the stack
260-
INCREMENT_BY_INDEX = 0x40,
263+
INCREMENT_BY_INDEX = 0x41,
261264

262265
// @args symbol id, count
263266
// @role Decrement the variable #[code primary] by #[code count] and push its value on the stack
264-
DECREMENT = 0x41,
267+
DECREMENT = 0x42,
265268

266269
// @args symbol index, count
267270
// @role Decrement the variable #[code primary] by #[code count] and push its value on the stack
268-
DECREMENT_BY_INDEX = 0x42,
271+
DECREMENT_BY_INDEX = 0x43,
269272

270273
// @args symbol id, symbol id
271274
// @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary]
272-
STORE_TAIL = 0x43,
275+
STORE_TAIL = 0x44,
273276

274277
// @args symbol index, symbol id
275278
// @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary]
276-
STORE_TAIL_BY_INDEX = 0x44,
279+
STORE_TAIL_BY_INDEX = 0x45,
277280

278281
// @args symbol id, symbol id
279282
// @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary]
280-
STORE_HEAD = 0x45,
283+
STORE_HEAD = 0x46,
281284

282285
// @args symbol index, symbol id
283286
// @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary]
284-
STORE_HEAD_BY_INDEX = 0x46,
287+
STORE_HEAD_BY_INDEX = 0x47,
285288

286289
// @args symbol id, symbol id
287290
// @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary]
288-
SET_VAL_TAIL = 0x47,
291+
SET_VAL_TAIL = 0x48,
289292

290293
// @args symbol index, symbol id
291294
// @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary]
292-
SET_VAL_TAIL_BY_INDEX = 0x48,
295+
SET_VAL_TAIL_BY_INDEX = 0x49,
293296

294297
// @args symbol id, symbol id
295298
// @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary]
296-
SET_VAL_HEAD = 0x49,
299+
SET_VAL_HEAD = 0x4a,
297300

298301
// @args symbol index, symbol id
299302
// @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary]
300-
SET_VAL_HEAD_BY_INDEX = 0x4a,
303+
SET_VAL_HEAD_BY_INDEX = 0x4b,
301304

302305
// @args builtin id, argument count
303306
// @role Call a builtin by its id in #[code primary], with #[code secondary] arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack
304-
CALL_BUILTIN = 0x4b,
307+
CALL_BUILTIN = 0x4c,
305308

306309
// @args constant id, absolute address to jump to
307310
// @role Compare #[code TS < constant], if the comparison fails, jump to the given address. Otherwise, does nothing
308-
LT_CONST_JUMP_IF_FALSE = 0x4c,
311+
LT_CONST_JUMP_IF_FALSE = 0x4d,
309312

310313
// @args constant id, absolute address to jump to
311314
// @role Compare #[code TS < constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing
312-
LT_CONST_JUMP_IF_TRUE = 0x4d,
315+
LT_CONST_JUMP_IF_TRUE = 0x4e,
313316

314317
// @args symbol id, absolute address to jump to
315318
// @role Compare #[code TS < symbol], if the comparison fails, jump to the given address. Otherwise, does nothing
316-
LT_SYM_JUMP_IF_FALSE = 0x4e,
319+
LT_SYM_JUMP_IF_FALSE = 0x4f,
317320

318321
// @args constant id, absolute address to jump to
319322
// @role Compare #[code TS > constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing
320-
GT_CONST_JUMP_IF_TRUE = 0x4f,
323+
GT_CONST_JUMP_IF_TRUE = 0x50,
321324

322325
// @args constant id, absolute address to jump to
323326
// @role Compare #[code TS > constant], if the comparison fails, jump to the given address. Otherwise, does nothing
324-
GT_CONST_JUMP_IF_FALSE = 0x50,
327+
GT_CONST_JUMP_IF_FALSE = 0x51,
325328

326329
// @args symbol id, absolute address to jump to
327330
// @role Compare #[code TS > symbol], if the comparison fails, jump to the given address. Otherwise, does nothing
328-
GT_SYM_JUMP_IF_FALSE = 0x51,
331+
GT_SYM_JUMP_IF_FALSE = 0x52,
329332

330333
// @args constant id, absolute address to jump to
331334
// @role Compare #[code TS == constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing
332-
EQ_CONST_JUMP_IF_TRUE = 0x52,
335+
EQ_CONST_JUMP_IF_TRUE = 0x53,
333336

334337
// @args symbol index, absolute address to jump to
335338
// @role Compare #[code TS == symbol], if the comparison succeeds, jump to the given address. Otherwise, does nothing
336-
EQ_SYM_INDEX_JUMP_IF_TRUE = 0x53,
339+
EQ_SYM_INDEX_JUMP_IF_TRUE = 0x54,
337340

338341
// @args constant id, absolute address to jump to
339342
// @role Compare #[code TS != constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing
340-
NEQ_CONST_JUMP_IF_TRUE = 0x54,
343+
NEQ_CONST_JUMP_IF_TRUE = 0x55,
341344

342345
// @args symbol id, absolute address to jump to
343346
// @role Compare #[code TS != symbol], if the comparison fails, jump to the given address. Otherwise, does nothing
344-
NEQ_SYM_JUMP_IF_FALSE = 0x55,
347+
NEQ_SYM_JUMP_IF_FALSE = 0x56,
345348

346349
// @args symbol id, argument count
347350
// @role Call a symbol by its id in #[code primary], with #[code secondary] arguments
348-
CALL_SYMBOL = 0x56,
351+
CALL_SYMBOL = 0x57,
349352

350353
// @args symbol id, field id in symbols table
351354
// @role Push the field of a given symbol (which has to be a closure) on the stack
352-
GET_FIELD_FROM_SYMBOL = 0x57,
355+
GET_FIELD_FROM_SYMBOL = 0x58,
353356

354357
// @args symbol index, field id in symbols table
355358
// @role Push the field of a given symbol (which has to be a closure) on the stack
356-
GET_FIELD_FROM_SYMBOL_INDEX = 0x58,
359+
GET_FIELD_FROM_SYMBOL_INDEX = 0x59,
357360

358361
// @args symbol id, symbol id2
359362
// @role Push symbol[symbol2]
360-
AT_SYM_SYM = 0x59,
363+
AT_SYM_SYM = 0x5a,
361364

362365
// @args symbol index, symbol index2
363366
// @role Push symbol[symbol2]
364-
AT_SYM_INDEX_SYM_INDEX = 0x5a,
367+
AT_SYM_INDEX_SYM_INDEX = 0x5b,
365368

366369
InstructionsCount
367370
};
@@ -395,7 +398,8 @@ namespace Ark::internal
395398
"SET_AT_INDEX",
396399
"SET_AT_2_INDEX",
397400
"POP",
398-
"DUP",
401+
"SHORTCIRCUIT_AND",
402+
"SHORTCIRCUIT_OR",
399403
"CREATE_SCOPE",
400404
"RESET_SCOPE_JUMP",
401405
"POP_SCOPE",

include/Ark/VM/VM.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,14 @@ namespace Ark
251251
*/
252252
inline Value* pop(internal::ExecutionContext& context);
253253

254+
/**
255+
* @brief Return a pointer to the top of the stack without consuming it, and resolve it if possible
256+
*
257+
* @param context
258+
* @return Value*
259+
*/
260+
inline Value* peekAndResolveAsPtr(internal::ExecutionContext& context);
261+
254262
/**
255263
* @brief Push a value on the stack
256264
*

include/Ark/VM/VM.inl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ inline Value* VM::pop(internal::ExecutionContext& context)
156156
return &m_undefined_value;
157157
}
158158

159+
inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context)
160+
{
161+
if (context.sp > 0)
162+
{
163+
Value* tmp = &context.stack[context.sp - 1];
164+
if (tmp->valueType() == ValueType::Reference)
165+
return tmp->reference();
166+
return tmp;
167+
}
168+
return &m_undefined_value;
169+
}
170+
159171
inline void VM::push(const Value& value, internal::ExecutionContext& context)
160172
{
161173
context.stack[context.sp] = value;

src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -488,17 +488,12 @@ namespace Ark::internal
488488
const auto node = x.constList()[0];
489489
const std::optional<Instruction> maybe_operator = node.nodeType() == NodeType::Symbol ? getOperator(node.string()) : std::nullopt;
490490

491-
enum class ShortcircuitOp
492-
{
493-
And,
494-
Or
495-
};
496-
const std::optional<ShortcircuitOp> maybe_shortcircuit =
491+
const std::optional<Instruction> maybe_shortcircuit =
497492
node.nodeType() == NodeType::Symbol
498493
? (node.string() == Language::And
499-
? std::make_optional(ShortcircuitOp::And)
494+
? std::make_optional(Instruction::SHORTCIRCUIT_AND)
500495
: (node.string() == Language::Or
501-
? std::make_optional(ShortcircuitOp::Or)
496+
? std::make_optional(Instruction::SHORTCIRCUIT_OR)
502497
: std::nullopt))
503498
: std::nullopt;
504499

@@ -514,25 +509,16 @@ namespace Ark::internal
514509
x);
515510

516511
compileExpression(x.constList()[1], p, false, false);
517-
page(p).emplace_back(DUP);
518512

519513
const auto label_shortcircuit = IR::Entity::Label(m_current_label++);
514+
auto shortcircuit_entity = IR::Entity::Goto(label_shortcircuit, maybe_shortcircuit.value());
515+
page(p).emplace_back(shortcircuit_entity);
516+
520517
for (std::size_t i = 2, end = x.constList().size(); i < end; ++i)
521518
{
522-
switch (maybe_shortcircuit.value())
523-
{
524-
case ShortcircuitOp::And:
525-
page(p).emplace_back(IR::Entity::GotoIf(label_shortcircuit, false));
526-
break;
527-
case ShortcircuitOp::Or:
528-
page(p).emplace_back(IR::Entity::GotoIf(label_shortcircuit, true));
529-
break;
530-
}
531-
page(p).emplace_back(POP);
532-
533519
compileExpression(x.constList()[i], p, false, false);
534520
if (i + 1 != end)
535-
page(p).emplace_back(DUP);
521+
page(p).emplace_back(shortcircuit_entity);
536522
}
537523

538524
page(p).emplace_back(label_shortcircuit);

0 commit comments

Comments
 (0)