Skip to content

Commit

Permalink
Add inlining of #&&, #||, #and:, #or:
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Marr <[email protected]>
  • Loading branch information
smarr committed Aug 2, 2024
1 parent 46a1ccb commit 7a105bf
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 45 deletions.
33 changes: 33 additions & 0 deletions src/compiler/MethodGenerationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,39 @@ bool MethodGenerationContext::InlineWhile(Parser& parser, bool isWhileTrue) {
return true;
}

bool MethodGenerationContext::InlineAndOr(bool isOr) {
// HACK: We do assume that the receiver on the stack is a boolean,
// HACK: similar to the IfTrueIfFalseNode.
// HACK: We don't support anything but booleans at the moment.

assert(Bytecode::GetBytecodeLength(BC_PUSH_BLOCK) == 2);
if (!hasOneLiteralBlockArgument()) {
return false;
}

VMMethod* toBeInlined =
static_cast<VMMethod*>(extractBlockMethodAndRemoveBytecode());

size_t jumpOffsetIdxToSkipBranch =
EmitJumpOnBoolWithDummyOffset(*this, !isOr, true);

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

size_t jumpOffsetIdxToSkipPushTrue = EmitJumpWithDumyOffset(*this);

PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipBranch);
EmitPUSHCONSTANT(*this,
isOr ? load_ptr(trueObject) : load_ptr(falseObject));

PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipPushTrue);

resetLastBytecodeBuffer();

return true;
}

void MethodGenerationContext::CompleteLexicalScope() {
lexicalScope = new LexicalScope(
outerGenc == nullptr ? nullptr : outerGenc->lexicalScope, arguments,
Expand Down
1 change: 1 addition & 0 deletions src/compiler/MethodGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class MethodGenerationContext {
bool InlineWhile(Parser& parser, bool isWhileTrue);
bool InlineIfTrueOrIfFalse(bool isIfTrue);
bool InlineIfTrueFalse(bool isIfTrue);
bool InlineAndOr(bool isOr);

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

Expand Down
10 changes: 9 additions & 1 deletion src/compiler/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,16 @@ void Parser::unaryMessage(MethodGenerationContext& mgenc, bool super) {
}

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

binaryOperand(mgenc);

if (!super && (msgSelector == "||" && mgenc.InlineAndOr(true)) ||
(msgSelector == "&&" && mgenc.InlineAndOr(false))) {
return;
}

if (super) {
EmitSUPERSEND(mgenc, msg);
} else {
Expand Down Expand Up @@ -631,7 +637,9 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) {
((kw == "ifTrue:" && mgenc.InlineIfTrueOrIfFalse(true)) ||
(kw == "ifFalse:" && mgenc.InlineIfTrueOrIfFalse(false)) ||
(kw == "whileTrue:" && mgenc.InlineWhile(*this, true)) ||
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)))) {
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)) ||
(kw == "or:" && mgenc.InlineAndOr(true)) ||
(kw == "and:" && mgenc.InlineAndOr(false)))) {
return;
}

Expand Down
91 changes: 47 additions & 44 deletions src/unitTests/BytecodeGenerationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include "../compiler/Parser.h"
#include "../interpreter/bytecodes.h"
#include "../misc/StringUtil.h"
#include "../misc/debug.h"
#include "../vm/Symbols.h"
#include "../vmobjects/VMMethod.h"

Expand Down Expand Up @@ -654,6 +653,53 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg2() {
BC_PUSH_ARG_2, BC_RETURN_LOCAL, BC_POP, BC_PUSH_CONSTANT_2, BC_POP,
BC_PUSH_SELF, BC_RETURN_LOCAL});
}
void BytecodeGenerationTest::testInliningOfOr() {
inliningOfOr("or:");
inliningOfOr("||");
}

void BytecodeGenerationTest::inliningOfOr(std::string selector) {
std::string source = "test = ( true OR_SEL [ #val ] )";
bool wasReplaced = ReplacePattern(source, "OR_SEL", selector);
assert(wasReplaced);

auto bytecodes = methodToBytecode(source.data());
check(bytecodes,
{BC_PUSH_CONSTANT_0, BC(BC_JUMP_ON_TRUE_POP, 7, 0),
// true branch
BC_PUSH_CONSTANT_1, // push the `#val`
BC(BC_JUMP, 4, 0),
// false branch, jump_on_true target, push true
BC_PUSH_CONSTANT_0, BC_POP,
// target of the jump in the true branch
BC_PUSH_SELF, BC_RETURN_LOCAL});

tearDown();
}

void BytecodeGenerationTest::testInliningOfAnd() {
inliningOfAnd("and:");
inliningOfAnd("&&");
}

void BytecodeGenerationTest::inliningOfAnd(std::string selector) {
std::string source = "test = ( true AND_SEL [ #val ] )";
bool wasReplaced = ReplacePattern(source, "AND_SEL", selector);
assert(wasReplaced);

auto bytecodes = methodToBytecode(source.data());
check(bytecodes,
{BC_PUSH_CONSTANT_0, BC(BC_JUMP_ON_FALSE_POP, 7, 0),
// true branch
BC_PUSH_CONSTANT_1, // push the `#val`
BC(BC_JUMP, 4, 0),
// false branch, jump_on_false target, push false
BC_PUSH_CONSTANT_2, BC_POP,
// target of the jump in the true branch
BC_PUSH_SELF, BC_RETURN_LOCAL});

tearDown();
}

/*
@pytest.mark.parametrize(
Expand Down Expand Up @@ -1265,50 +1311,7 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg2() {
)
@pytest.mark.parametrize("and_sel", ["and:", "&&"])
def test_inlining_of_and(mgenc, and_sel):
bytecodes = method_to_bytecodes(
mgenc, "test = ( true AND_SEL [ #val ] )".replace("AND_SEL", and_sel)
)
assert len(bytecodes) == 11
check(
bytecodes,
[
Bytecodes.push_constant_0,
BC(Bytecodes.jump_on_false_pop, 7),
# true branch
BC_PUSH_CONSTANT_2, # push the `#val`
BC(Bytecodes.jump, 5),
# false branch, jump_on_false target, push false
Bytecodes.push_constant,
# target of the jump in the true branch
Bytecodes.return_self,
],
)
@pytest.mark.parametrize("or_sel", ["or:", "||"])
def test_inlining_of_or(mgenc, or_sel):
bytecodes = method_to_bytecodes(
mgenc, "test = ( true OR_SEL [ #val ] )".replace("OR_SEL", or_sel)
)
assert len(bytecodes) == 10
check(
bytecodes,
[
Bytecodes.push_constant_0,
BC(Bytecodes.jump_on_true_pop, 7),
# true branch
BC_PUSH_CONSTANT_2, # push the `#val`
BC(Bytecodes.jump, 4),
# false branch, jump_on_true target, push true
Bytecodes.push_constant_0,
# target of the jump in the true branch
Bytecodes.return_self,
],
)
def test_field_read_inlining(cgenc, mgenc):
Expand Down
8 changes: 8 additions & 0 deletions src/unitTests/BytecodeGenerationTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase {
CPPUNIT_TEST(testIfTrueIfFalseArg);
CPPUNIT_TEST(testIfTrueIfFalseNlrArg1);
CPPUNIT_TEST(testIfTrueIfFalseNlrArg2);
CPPUNIT_TEST(testInliningOfOr);
CPPUNIT_TEST(testInliningOfAnd);

CPPUNIT_TEST(testJumpQueuesOrdering);

Expand Down Expand Up @@ -151,6 +153,12 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase {
void testIfTrueIfFalseArg();
void testIfTrueIfFalseNlrArg1();
void testIfTrueIfFalseNlrArg2();

void testInliningOfOr();
void inliningOfOr(std::string selector);
void testInliningOfAnd();
void inliningOfAnd(std::string selector);

void testJumpQueuesOrdering();

void dump(MethodGenerationContext* mgenc);
Expand Down

0 comments on commit 7a105bf

Please sign in to comment.