Skip to content

Commit 8074e7e

Browse files
committed
Implement bytecode inlining for #ifNil:, #ifNotNil: and variations
Signed-off-by: Stefan Marr <git@stefan-marr.de>
1 parent ed01dcb commit 8074e7e

File tree

11 files changed

+228
-43
lines changed

11 files changed

+228
-43
lines changed

src/compiler/BytecodeGenerator.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,21 +298,30 @@ void EmitDupSecond(MethodGenerationContext& mgenc) {
298298
Emit1(mgenc, BC_DUP_SECOND, 1);
299299
}
300300

301-
size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc,
302-
bool isIfTrue, bool needsPop) {
301+
size_t EmitJumpOnWithDummyOffset(MethodGenerationContext& mgenc, JumpCondition condition, bool needsPop) {
303302
// Remember: true and false seem flipped here.
304303
// This is because if the test passes, the block is inlined directly.
305304
// But if the test fails, we need to jump.
306305
// Thus, an `#ifTrue:` needs to generated a jump_on_false.
307306
uint8_t bc = 0;
308-
int64_t stackEffect = 0;
307+
int64_t stackEffect = needsPop ? -1 : 0;
309308

310-
if (needsPop) {
311-
bc = isIfTrue ? BC_JUMP_ON_FALSE_POP : BC_JUMP_ON_TRUE_POP;
312-
stackEffect = -1;
313-
} else {
314-
bc = isIfTrue ? BC_JUMP_ON_FALSE_TOP_NIL : BC_JUMP_ON_TRUE_TOP_NIL;
315-
stackEffect = 0;
309+
switch (condition) {
310+
case ON_TRUE:
311+
bc = needsPop ? BC_JUMP_ON_TRUE_POP : BC_JUMP_ON_TRUE_TOP_NIL;
312+
break;
313+
314+
case ON_FALSE:
315+
bc = needsPop ? BC_JUMP_ON_FALSE_POP : BC_JUMP_ON_FALSE_TOP_NIL;
316+
break;
317+
318+
case ON_NIL:
319+
bc = needsPop ? BC_JUMP_ON_NIL_POP : BC_JUMP_ON_NIL_TOP_TOP;
320+
break;
321+
322+
case ON_NOT_NIL:
323+
bc = needsPop ? BC_JUMP_ON_NOT_NIL_POP : BC_JUMP_ON_NOT_NIL_TOP_TOP;
324+
break;
316325
}
317326

318327
Emit1(mgenc, bc, stackEffect);

src/compiler/BytecodeGenerator.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ void EmitIncFieldPush(MethodGenerationContext& mgenc, uint8_t fieldIdx);
6666

6767
void EmitDupSecond(MethodGenerationContext& mgenc);
6868

69-
size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc,
70-
bool isIfTrue, bool needsPop);
69+
size_t EmitJumpOnWithDummyOffset(MethodGenerationContext& mgenc, JumpCondition condition, bool needsPop);
7170
size_t EmitJumpWithDumyOffset(MethodGenerationContext& mgenc);
7271
size_t EmitJumpIfGreaterWithDummyOffset(MethodGenerationContext& mgenc);
7372
void EmitJumpBackwardWithOffset(MethodGenerationContext& mgenc,

src/compiler/Disassembler.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,12 +567,20 @@ void Disassembler::DumpBytecode(VMFrame* frame, VMMethod* method,
567567
case BC_JUMP_ON_TRUE_POP:
568568
case BC_JUMP_ON_FALSE_TOP_NIL:
569569
case BC_JUMP_ON_TRUE_TOP_NIL:
570+
case BC_JUMP_ON_NOT_NIL_POP :
571+
case BC_JUMP_ON_NIL_POP :
572+
case BC_JUMP_ON_NOT_NIL_TOP_TOP:
573+
case BC_JUMP_ON_NIL_TOP_TOP :
570574
case BC_JUMP_BACKWARD:
571575
case BC_JUMP2:
572576
case BC_JUMP2_ON_FALSE_POP:
573577
case BC_JUMP2_ON_TRUE_POP:
574578
case BC_JUMP2_ON_FALSE_TOP_NIL:
575579
case BC_JUMP2_ON_TRUE_TOP_NIL:
580+
case BC_JUMP2_ON_NOT_NIL_POP :
581+
case BC_JUMP2_ON_NIL_POP :
582+
case BC_JUMP2_ON_NOT_NIL_TOP_TOP:
583+
case BC_JUMP2_ON_NIL_TOP_TOP :
576584
case BC_JUMP2_BACKWARD: {
577585
uint16_t const offset =
578586
ComputeOffset(method->GetBytecode(bc_idx + 1),

src/compiler/MethodGenerationContext.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ MethodGenerationContext::extractBlockMethodsAndRemoveBytecodes() {
522522
return {toBeInlined1, toBeInlined2};
523523
}
524524

525-
bool MethodGenerationContext::InlineIfTrueOrIfFalse(bool isIfTrue) {
525+
bool MethodGenerationContext::InlineThenBranch(JumpCondition condition) {
526526
// HACK: We do assume that the receiver on the stack is a boolean,
527527
// HACK: similar to the IfTrueIfFalseNode.
528528
// HACK: We don't support anything but booleans at the moment.
@@ -534,7 +534,7 @@ bool MethodGenerationContext::InlineIfTrueOrIfFalse(bool isIfTrue) {
534534
VMInvokable* toBeInlined = extractBlockMethodAndRemoveBytecode();
535535

536536
size_t const jumpOffsetIdxToSkipBody =
537-
EmitJumpOnBoolWithDummyOffset(*this, isIfTrue, false);
537+
EmitJumpOnWithDummyOffset(*this, condition, false);
538538

539539
isCurrentlyInliningABlock = true;
540540

@@ -548,7 +548,7 @@ bool MethodGenerationContext::InlineIfTrueOrIfFalse(bool isIfTrue) {
548548
return true;
549549
}
550550

551-
bool MethodGenerationContext::InlineIfTrueFalse(bool isIfTrue) {
551+
bool MethodGenerationContext::InlineThenElseBranches(JumpCondition condition) {
552552
// HACK: We do assume that the receiver on the stack is a boolean,
553553
// HACK: similar to the IfTrueIfFalseNode.
554554
// HACK: We don't support anything but booleans at the moment.
@@ -565,7 +565,7 @@ bool MethodGenerationContext::InlineIfTrueFalse(bool isIfTrue) {
565565
VMInvokable* bodyMethod = std::get<1>(methods);
566566

567567
size_t const jumpOffsetIdxToSkipTrueBranch =
568-
EmitJumpOnBoolWithDummyOffset(*this, isIfTrue, true);
568+
EmitJumpOnWithDummyOffset(*this, condition, true);
569569

570570
isCurrentlyInliningABlock = true;
571571
condMethod->InlineInto(*this);
@@ -607,7 +607,7 @@ bool MethodGenerationContext::InlineWhile(Parser& parser, bool isWhileTrue) {
607607
condMethod->InlineInto(*this);
608608

609609
size_t const jumpOffsetIdxToSkipLoopBody =
610-
EmitJumpOnBoolWithDummyOffset(*this, isWhileTrue, true);
610+
EmitJumpOnWithDummyOffset(*this, isWhileTrue ? ON_FALSE: ON_TRUE, true);
611611

612612
bodyMethod->InlineInto(*this);
613613

@@ -632,7 +632,7 @@ bool MethodGenerationContext::InlineAndOr(bool isOr) {
632632
VMInvokable* toBeInlined = extractBlockMethodAndRemoveBytecode();
633633

634634
size_t const jumpOffsetIdxToSkipBranch =
635-
EmitJumpOnBoolWithDummyOffset(*this, !isOr, true);
635+
EmitJumpOnWithDummyOffset(*this, isOr ? ON_TRUE : ON_FALSE, true);
636636

637637
isCurrentlyInliningABlock = true;
638638
toBeInlined->InlineInto(*this);

src/compiler/MethodGenerationContext.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class Parser;
4040

4141
#define NUM_LAST_BYTECODES 4
4242

43+
enum JumpCondition {
44+
ON_TRUE, ON_FALSE, ON_NIL, ON_NOT_NIL
45+
};
46+
4347
class MethodGenerationContext {
4448
public:
4549
explicit MethodGenerationContext(ClassGenerationContext& holder,
@@ -102,8 +106,8 @@ class MethodGenerationContext {
102106
std::vector<uint8_t> GetBytecodes() { return bytecode; }
103107

104108
bool InlineWhile(Parser& parser, bool isWhileTrue);
105-
bool InlineIfTrueOrIfFalse(bool isIfTrue);
106-
bool InlineIfTrueFalse(bool isIfTrue);
109+
bool InlineThenElseBranches(JumpCondition condition);
110+
bool InlineThenBranch(JumpCondition condition);
107111
bool InlineAndOr(bool isOr);
108112
bool InlineToDo();
109113

src/compiler/Parser.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,10 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) {
678678

679679
if (!super) {
680680
if (numParts == 1 &&
681-
((kw == "ifTrue:" && mgenc.InlineIfTrueOrIfFalse(true)) ||
682-
(kw == "ifFalse:" && mgenc.InlineIfTrueOrIfFalse(false)) ||
681+
((kw == "ifTrue:" && mgenc.InlineThenBranch(ON_FALSE)) ||
682+
(kw == "ifFalse:" && mgenc.InlineThenBranch(ON_TRUE)) ||
683+
(kw == "ifNil:" && mgenc.InlineThenBranch(ON_NOT_NIL)) ||
684+
(kw == "ifNotNil:" && mgenc.InlineThenBranch(ON_NIL)) ||
683685
(kw == "whileTrue:" && mgenc.InlineWhile(*this, true)) ||
684686
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)) ||
685687
(kw == "or:" && mgenc.InlineAndOr(true)) ||
@@ -688,8 +690,10 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) {
688690
}
689691

690692
if (numParts == 2 &&
691-
((kw == "ifTrue:ifFalse:" && mgenc.InlineIfTrueFalse(true)) ||
692-
(kw == "ifFalse:ifTrue:" && mgenc.InlineIfTrueFalse(false)) ||
693+
((kw == "ifTrue:ifFalse:" && mgenc.InlineThenElseBranches(ON_FALSE)) ||
694+
(kw == "ifFalse:ifTrue:" && mgenc.InlineThenElseBranches(ON_TRUE)) ||
695+
(kw == "ifNil:ifNotNil:" && mgenc.InlineThenElseBranches(ON_NOT_NIL)) ||
696+
(kw == "ifNotNil:ifNil:" && mgenc.InlineThenElseBranches(ON_NIL)) ||
693697
(kw == "to:do:" && mgenc.InlineToDo()))) {
694698
return;
695699
}

src/interpreter/InterpreterLoop.h

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,21 @@ vm_oop_t Start() {
5555
&&LABEL_BC_JUMP_ON_TRUE_POP,
5656
&&LABEL_BC_JUMP_ON_FALSE_TOP_NIL,
5757
&&LABEL_BC_JUMP_ON_TRUE_TOP_NIL,
58+
&&LABEL_BC_JUMP_ON_NOT_NIL_POP,
59+
&&LABEL_BC_JUMP_ON_NIL_POP,
60+
&&LABEL_BC_JUMP_ON_NOT_NIL_TOP_TOP,
61+
&&LABEL_BC_JUMP_ON_NIL_TOP_TOP,
5862
&&LABEL_BC_JUMP_IF_GREATER,
5963
&&LABEL_BC_JUMP_BACKWARD,
6064
&&LABEL_BC_JUMP2,
6165
&&LABEL_BC_JUMP2_ON_FALSE_POP,
6266
&&LABEL_BC_JUMP2_ON_TRUE_POP,
6367
&&LABEL_BC_JUMP2_ON_FALSE_TOP_NIL,
6468
&&LABEL_BC_JUMP2_ON_TRUE_TOP_NIL,
69+
&&LABEL_BC_JUMP2_ON_NOT_NIL_POP,
70+
&&LABEL_BC_JUMP2_ON_NIL_POP,
71+
&&LABEL_BC_JUMP2_ON_NOT_NIL_TOP_TOP,
72+
&&LABEL_BC_JUMP2_ON_NIL_TOP_TOP,
6573
&&LABEL_BC_JUMP2_IF_GREATER,
6674
&&LABEL_BC_JUMP2_BACKWARD};
6775

@@ -373,6 +381,56 @@ LABEL_BC_JUMP_ON_TRUE_TOP_NIL: {
373381
}
374382
DISPATCH_NOGC();
375383

384+
LABEL_BC_JUMP_ON_NOT_NIL_POP: {
385+
vm_oop_t val = GetFrame()->Top();
386+
if (val != load_ptr(nilObject)) {
387+
uint8_t const offset = currentBytecodes[bytecodeIndexGlobal + 1];
388+
bytecodeIndexGlobal += offset;
389+
} else {
390+
bytecodeIndexGlobal += 3;
391+
}
392+
GetFrame()->PopVoid();
393+
}
394+
DISPATCH_NOGC();
395+
396+
LABEL_BC_JUMP_ON_NIL_POP: {
397+
vm_oop_t val = GetFrame()->Top();
398+
if (val == load_ptr(nilObject)) {
399+
uint8_t const offset = currentBytecodes[bytecodeIndexGlobal + 1];
400+
bytecodeIndexGlobal += offset;
401+
} else {
402+
bytecodeIndexGlobal += 3;
403+
}
404+
GetFrame()->PopVoid();
405+
}
406+
DISPATCH_NOGC();
407+
408+
LABEL_BC_JUMP_ON_NOT_NIL_TOP_TOP: {
409+
vm_oop_t val = GetFrame()->Top();
410+
if (val != load_ptr(nilObject)) {
411+
uint8_t const offset = currentBytecodes[bytecodeIndexGlobal + 1];
412+
bytecodeIndexGlobal += offset;
413+
// GetFrame()->SetTop(val);
414+
} else {
415+
GetFrame()->PopVoid();
416+
bytecodeIndexGlobal += 3;
417+
}
418+
}
419+
DISPATCH_NOGC();
420+
421+
LABEL_BC_JUMP_ON_NIL_TOP_TOP: {
422+
vm_oop_t val = GetFrame()->Top();
423+
if (val == load_ptr(nilObject)) {
424+
uint8_t const offset = currentBytecodes[bytecodeIndexGlobal + 1];
425+
bytecodeIndexGlobal += offset;
426+
// GetFrame()->SetTop(val);
427+
} else {
428+
GetFrame()->PopVoid();
429+
bytecodeIndexGlobal += 3;
430+
}
431+
}
432+
DISPATCH_NOGC();
433+
376434
LABEL_BC_JUMP_IF_GREATER: {
377435
if (checkIsGreater()) {
378436
bytecodeIndexGlobal += currentBytecodes[bytecodeIndexGlobal + 1];
@@ -456,6 +514,64 @@ LABEL_BC_JUMP2_ON_TRUE_TOP_NIL: {
456514
}
457515
DISPATCH_NOGC();
458516

517+
LABEL_BC_JUMP2_ON_NOT_NIL_POP: {
518+
vm_oop_t val = GetFrame()->Top();
519+
if (val != load_ptr(nilObject)) {
520+
uint16_t const offset =
521+
ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1],
522+
currentBytecodes[bytecodeIndexGlobal + 2]);
523+
bytecodeIndexGlobal += offset;
524+
} else {
525+
bytecodeIndexGlobal += 3;
526+
}
527+
GetFrame()->PopVoid();
528+
}
529+
DISPATCH_NOGC();
530+
531+
LABEL_BC_JUMP2_ON_NIL_POP: {
532+
vm_oop_t val = GetFrame()->Top();
533+
if (val == load_ptr(nilObject)) {
534+
uint16_t const offset =
535+
ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1],
536+
currentBytecodes[bytecodeIndexGlobal + 2]);
537+
bytecodeIndexGlobal += offset;
538+
} else {
539+
bytecodeIndexGlobal += 3;
540+
}
541+
GetFrame()->PopVoid();
542+
}
543+
DISPATCH_NOGC();
544+
545+
LABEL_BC_JUMP2_ON_NOT_NIL_TOP_TOP: {
546+
vm_oop_t val = GetFrame()->Top();
547+
if (val != load_ptr(nilObject)) {
548+
uint16_t const offset =
549+
ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1],
550+
currentBytecodes[bytecodeIndexGlobal + 2]);
551+
bytecodeIndexGlobal += offset;
552+
// GetFrame()->SetTop(val);
553+
} else {
554+
GetFrame()->PopVoid();
555+
bytecodeIndexGlobal += 3;
556+
}
557+
}
558+
DISPATCH_NOGC();
559+
560+
LABEL_BC_JUMP2_ON_NIL_TOP_TOP: {
561+
vm_oop_t val = GetFrame()->Top();
562+
if (val == load_ptr(nilObject)) {
563+
uint16_t const offset =
564+
ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1],
565+
currentBytecodes[bytecodeIndexGlobal + 2]);
566+
bytecodeIndexGlobal += offset;
567+
// GetFrame()->SetTop(val);
568+
} else {
569+
GetFrame()->PopVoid();
570+
bytecodeIndexGlobal += 3;
571+
}
572+
}
573+
DISPATCH_NOGC();
574+
459575
LABEL_BC_JUMP2_IF_GREATER: {
460576
if (checkIsGreater()) {
461577
bytecodeIndexGlobal +=

src/interpreter/bytecodes.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ const uint8_t Bytecode::bytecodeLengths[] = {
8181
3, // BC_JUMP_ON_TRUE_POP
8282
3, // BC_JUMP_ON_FALSE_TOP_NIL
8383
3, // BC_JUMP_ON_TRUE_TOP_NIL
84+
3, // BC_JUMP_ON_NOT_NIL_POP
85+
3, // BC_JUMP_ON_NIL_POP
86+
3, // BC_JUMP_ON_NOT_NIL_TOP_TOP
87+
3, // BC_JUMP_ON_NIL_TOP_TOP
8488
3, // BC_JUMP_IF_GREATER
8589
3, // BC_JUMP_BACKWARD
8690

@@ -89,6 +93,10 @@ const uint8_t Bytecode::bytecodeLengths[] = {
8993
3, // BC_JUMP2_ON_TRUE_POP
9094
3, // BC_JUMP2_ON_FALSE_TOP_NIL
9195
3, // BC_JUMP2_ON_TRUE_TOP_NIL
96+
3, // BC_JUMP2_ON_NOT_NIL_POP
97+
3, // BC_JUMP2_ON_NIL_POP
98+
3, // BC_JUMP2_ON_NOT_NIL_TOP_TOP
99+
3, // BC_JUMP2_ON_NIL_TOP_TOP
92100
3, // BC_JUMP2_IF_GREATER
93101
3, // BC_JUMP2_BACKWARD
94102
};
@@ -144,20 +152,28 @@ const char* Bytecode::bytecodeNames[] = {
144152
"JUMP_ON_TRUE_POP", // 47
145153
"JUMP_ON_FALSE_TOP_NIL", // 48
146154
"JUMP_ON_TRUE_TOP_NIL", // 49
147-
"JUMP_IF_GREATER ", // 50
148-
"JUMP_BACKWARD ", // 51
149-
"JUMP2 ", // 52
150-
"JUMP2_ON_FALSE_POP", // 53
151-
"JUMP2_ON_TRUE_POP", // 54
152-
"JUMP2_ON_FALSE_TOP_NIL", // 55
153-
"JUMP2_ON_TRUE_TOP_NIL", // 56
154-
"JUMP2_IF_GREATER", // 57
155-
"JUMP2_BACKWARD ", // 58
155+
"JUMP_ON_NOT_NIL_POP", // 50
156+
"JUMP_ON_NIL_POP ", // 51
157+
"JUMP_ON_NOT_NIL_TOP_TOP", // 52
158+
"JUMP_ON_NIL_TOP_TOP", // 53
159+
"JUMP_IF_GREATER ", // 54
160+
"JUMP_BACKWARD ", // 55
161+
"JUMP2 ", // 56
162+
"JUMP2_ON_FALSE_POP", // 57
163+
"JUMP2_ON_TRUE_POP", // 58
164+
"JUMP2_ON_FALSE_TOP_NIL", // 59
165+
"JUMP2_ON_TRUE_TOP_NIL", // 60
166+
"JUMP2_ON_NOT_NIL_POP", // 61
167+
"JUMP2_ON_NIL_POP ", // 62
168+
"JUMP2_ON_NOT_NIL_TOP_TOP",// 63
169+
"JUMP2_ON_NIL_TOP_TOP", // 64
170+
"JUMP2_IF_GREATER", // 65
171+
"JUMP2_BACKWARD ", // 66
156172
};
157173

158174
bool IsJumpBytecode(uint8_t bc) {
159175
static_assert(BC_JUMP < BC_JUMP2_BACKWARD);
160-
static_assert((BC_JUMP2_BACKWARD - BC_JUMP) == 13);
176+
static_assert((BC_JUMP2_BACKWARD - BC_JUMP) == 21);
161177

162178
return BC_JUMP <= bc && bc <= BC_JUMP2_BACKWARD;
163179
}

0 commit comments

Comments
 (0)