Skip to content

Commit 7a105bf

Browse files
committed
Add inlining of #&&, #||, #and:, #or:
Signed-off-by: Stefan Marr <[email protected]>
1 parent 46a1ccb commit 7a105bf

File tree

5 files changed

+98
-45
lines changed

5 files changed

+98
-45
lines changed

src/compiler/MethodGenerationContext.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,39 @@ bool MethodGenerationContext::InlineWhile(Parser& parser, bool isWhileTrue) {
406406
return true;
407407
}
408408

409+
bool MethodGenerationContext::InlineAndOr(bool isOr) {
410+
// HACK: We do assume that the receiver on the stack is a boolean,
411+
// HACK: similar to the IfTrueIfFalseNode.
412+
// HACK: We don't support anything but booleans at the moment.
413+
414+
assert(Bytecode::GetBytecodeLength(BC_PUSH_BLOCK) == 2);
415+
if (!hasOneLiteralBlockArgument()) {
416+
return false;
417+
}
418+
419+
VMMethod* toBeInlined =
420+
static_cast<VMMethod*>(extractBlockMethodAndRemoveBytecode());
421+
422+
size_t jumpOffsetIdxToSkipBranch =
423+
EmitJumpOnBoolWithDummyOffset(*this, !isOr, true);
424+
425+
isCurrentlyInliningABlock = true;
426+
toBeInlined->InlineInto(*this);
427+
isCurrentlyInliningABlock = false;
428+
429+
size_t jumpOffsetIdxToSkipPushTrue = EmitJumpWithDumyOffset(*this);
430+
431+
PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipBranch);
432+
EmitPUSHCONSTANT(*this,
433+
isOr ? load_ptr(trueObject) : load_ptr(falseObject));
434+
435+
PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipPushTrue);
436+
437+
resetLastBytecodeBuffer();
438+
439+
return true;
440+
}
441+
409442
void MethodGenerationContext::CompleteLexicalScope() {
410443
lexicalScope = new LexicalScope(
411444
outerGenc == nullptr ? nullptr : outerGenc->lexicalScope, arguments,

src/compiler/MethodGenerationContext.h

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class MethodGenerationContext {
9696
bool InlineWhile(Parser& parser, bool isWhileTrue);
9797
bool InlineIfTrueOrIfFalse(bool isIfTrue);
9898
bool InlineIfTrueFalse(bool isIfTrue);
99+
bool InlineAndOr(bool isOr);
99100

100101
inline size_t OffsetOfNextInstruction() { return bytecode.size(); }
101102

src/compiler/Parser.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -593,10 +593,16 @@ void Parser::unaryMessage(MethodGenerationContext& mgenc, bool super) {
593593
}
594594

595595
void Parser::binaryMessage(MethodGenerationContext& mgenc, bool super) {
596+
std::string msgSelector(text);
596597
VMSymbol* msg = binarySelector();
597598

598599
binaryOperand(mgenc);
599600

601+
if (!super && (msgSelector == "||" && mgenc.InlineAndOr(true)) ||
602+
(msgSelector == "&&" && mgenc.InlineAndOr(false))) {
603+
return;
604+
}
605+
600606
if (super) {
601607
EmitSUPERSEND(mgenc, msg);
602608
} else {
@@ -631,7 +637,9 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) {
631637
((kw == "ifTrue:" && mgenc.InlineIfTrueOrIfFalse(true)) ||
632638
(kw == "ifFalse:" && mgenc.InlineIfTrueOrIfFalse(false)) ||
633639
(kw == "whileTrue:" && mgenc.InlineWhile(*this, true)) ||
634-
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)))) {
640+
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)) ||
641+
(kw == "or:" && mgenc.InlineAndOr(true)) ||
642+
(kw == "and:" && mgenc.InlineAndOr(false)))) {
635643
return;
636644
}
637645

src/unitTests/BytecodeGenerationTest.cpp

+47-44
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include "../compiler/Parser.h"
1717
#include "../interpreter/bytecodes.h"
1818
#include "../misc/StringUtil.h"
19-
#include "../misc/debug.h"
2019
#include "../vm/Symbols.h"
2120
#include "../vmobjects/VMMethod.h"
2221

@@ -654,6 +653,53 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg2() {
654653
BC_PUSH_ARG_2, BC_RETURN_LOCAL, BC_POP, BC_PUSH_CONSTANT_2, BC_POP,
655654
BC_PUSH_SELF, BC_RETURN_LOCAL});
656655
}
656+
void BytecodeGenerationTest::testInliningOfOr() {
657+
inliningOfOr("or:");
658+
inliningOfOr("||");
659+
}
660+
661+
void BytecodeGenerationTest::inliningOfOr(std::string selector) {
662+
std::string source = "test = ( true OR_SEL [ #val ] )";
663+
bool wasReplaced = ReplacePattern(source, "OR_SEL", selector);
664+
assert(wasReplaced);
665+
666+
auto bytecodes = methodToBytecode(source.data());
667+
check(bytecodes,
668+
{BC_PUSH_CONSTANT_0, BC(BC_JUMP_ON_TRUE_POP, 7, 0),
669+
// true branch
670+
BC_PUSH_CONSTANT_1, // push the `#val`
671+
BC(BC_JUMP, 4, 0),
672+
// false branch, jump_on_true target, push true
673+
BC_PUSH_CONSTANT_0, BC_POP,
674+
// target of the jump in the true branch
675+
BC_PUSH_SELF, BC_RETURN_LOCAL});
676+
677+
tearDown();
678+
}
679+
680+
void BytecodeGenerationTest::testInliningOfAnd() {
681+
inliningOfAnd("and:");
682+
inliningOfAnd("&&");
683+
}
684+
685+
void BytecodeGenerationTest::inliningOfAnd(std::string selector) {
686+
std::string source = "test = ( true AND_SEL [ #val ] )";
687+
bool wasReplaced = ReplacePattern(source, "AND_SEL", selector);
688+
assert(wasReplaced);
689+
690+
auto bytecodes = methodToBytecode(source.data());
691+
check(bytecodes,
692+
{BC_PUSH_CONSTANT_0, BC(BC_JUMP_ON_FALSE_POP, 7, 0),
693+
// true branch
694+
BC_PUSH_CONSTANT_1, // push the `#val`
695+
BC(BC_JUMP, 4, 0),
696+
// false branch, jump_on_false target, push false
697+
BC_PUSH_CONSTANT_2, BC_POP,
698+
// target of the jump in the true branch
699+
BC_PUSH_SELF, BC_RETURN_LOCAL});
700+
701+
tearDown();
702+
}
657703

658704
/*
659705
@pytest.mark.parametrize(
@@ -1265,50 +1311,7 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg2() {
12651311
)
12661312
12671313
1268-
@pytest.mark.parametrize("and_sel", ["and:", "&&"])
1269-
def test_inlining_of_and(mgenc, and_sel):
1270-
bytecodes = method_to_bytecodes(
1271-
mgenc, "test = ( true AND_SEL [ #val ] )".replace("AND_SEL", and_sel)
1272-
)
1273-
1274-
assert len(bytecodes) == 11
1275-
check(
1276-
bytecodes,
1277-
[
1278-
Bytecodes.push_constant_0,
1279-
BC(Bytecodes.jump_on_false_pop, 7),
1280-
# true branch
1281-
BC_PUSH_CONSTANT_2, # push the `#val`
1282-
BC(Bytecodes.jump, 5),
1283-
# false branch, jump_on_false target, push false
1284-
Bytecodes.push_constant,
1285-
# target of the jump in the true branch
1286-
Bytecodes.return_self,
1287-
],
1288-
)
1289-
1290-
1291-
@pytest.mark.parametrize("or_sel", ["or:", "||"])
1292-
def test_inlining_of_or(mgenc, or_sel):
1293-
bytecodes = method_to_bytecodes(
1294-
mgenc, "test = ( true OR_SEL [ #val ] )".replace("OR_SEL", or_sel)
1295-
)
12961314
1297-
assert len(bytecodes) == 10
1298-
check(
1299-
bytecodes,
1300-
[
1301-
Bytecodes.push_constant_0,
1302-
BC(Bytecodes.jump_on_true_pop, 7),
1303-
# true branch
1304-
BC_PUSH_CONSTANT_2, # push the `#val`
1305-
BC(Bytecodes.jump, 4),
1306-
# false branch, jump_on_true target, push true
1307-
Bytecodes.push_constant_0,
1308-
# target of the jump in the true branch
1309-
Bytecodes.return_self,
1310-
],
1311-
)
13121315
13131316
13141317
def test_field_read_inlining(cgenc, mgenc):

src/unitTests/BytecodeGenerationTest.h

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase {
6363
CPPUNIT_TEST(testIfTrueIfFalseArg);
6464
CPPUNIT_TEST(testIfTrueIfFalseNlrArg1);
6565
CPPUNIT_TEST(testIfTrueIfFalseNlrArg2);
66+
CPPUNIT_TEST(testInliningOfOr);
67+
CPPUNIT_TEST(testInliningOfAnd);
6668

6769
CPPUNIT_TEST(testJumpQueuesOrdering);
6870

@@ -151,6 +153,12 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase {
151153
void testIfTrueIfFalseArg();
152154
void testIfTrueIfFalseNlrArg1();
153155
void testIfTrueIfFalseNlrArg2();
156+
157+
void testInliningOfOr();
158+
void inliningOfOr(std::string selector);
159+
void testInliningOfAnd();
160+
void inliningOfAnd(std::string selector);
161+
154162
void testJumpQueuesOrdering();
155163

156164
void dump(MethodGenerationContext* mgenc);

0 commit comments

Comments
 (0)