Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline #ifTrue:, #ifFalse:, #ifTrue:ifFalse:, #ifFalse:ifTrue:, #||, #or:, #&&, #and: #35

Merged
merged 6 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/compiler/BytecodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@ size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc,
return idx;
}

size_t EmitJumpWithDumyOffset(MethodGenerationContext& mgenc) {
Emit1(mgenc, BC_JUMP, 0);
size_t idx = mgenc.AddBytecodeArgumentAndGetIndex(0);
mgenc.AddBytecodeArgument(0);
return idx;
}

void EmitJumpBackwardWithOffset(MethodGenerationContext& mgenc,
size_t jumpOffset) {
uint8_t jumpBytecode =
Expand Down
1 change: 1 addition & 0 deletions src/compiler/BytecodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ void EmitRETURNNONLOCAL(MethodGenerationContext& mgenc);

size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc,
bool isIfTrue, bool needsPop);
size_t EmitJumpWithDumyOffset(MethodGenerationContext& mgenc);
void EmitJumpBackwardWithOffset(MethodGenerationContext& mgenc,
size_t jumpOffset);
size_t Emit3WithDummy(MethodGenerationContext& mgenc, uint8_t bytecode,
Expand Down
115 changes: 115 additions & 0 deletions src/compiler/MethodGenerationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ void MethodGenerationContext::removeLastBytecodes(size_t numBytecodes) {
bytecode.erase(bytecode.end() - bytesToRemove, bytecode.end());
}

bool MethodGenerationContext::hasOneLiteralBlockArgument() {
return lastBytecodeIs(0, BC_PUSH_BLOCK);
}

bool MethodGenerationContext::hasTwoLiteralBlockArguments() {
if (!lastBytecodeIs(0, BC_PUSH_BLOCK)) {
return false;
Expand All @@ -280,6 +284,16 @@ vm_oop_t MethodGenerationContext::getLastBlockMethodAndFreeLiteral(
return block;
}

vm_oop_t MethodGenerationContext::extractBlockMethodAndRemoveBytecode() {
uint8_t blockLitIdx = bytecode.at(bytecode.size() - 1);

vm_oop_t toBeInlined = getLastBlockMethodAndFreeLiteral(blockLitIdx);

removeLastBytecodes(1);

return toBeInlined;
}

std::tuple<vm_oop_t, vm_oop_t>
MethodGenerationContext::extractBlockMethodsAndRemoveBytecodes() {
uint8_t block1LitIdx = bytecode.at(bytecode.size() - 3);
Expand All @@ -294,6 +308,74 @@ MethodGenerationContext::extractBlockMethodsAndRemoveBytecodes() {
return {toBeInlined1, toBeInlined2};
}

bool MethodGenerationContext::InlineIfTrueOrIfFalse(bool isIfTrue) {
// 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 jumpOffsetIdxToSkipBody =
EmitJumpOnBoolWithDummyOffset(*this, isIfTrue, false);

isCurrentlyInliningABlock = true;

toBeInlined->InlineInto(*this);
PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipBody);

// with the jumping, it's best to prevent any subsequent optimizations here
// otherwise we may not have the correct jump target
resetLastBytecodeBuffer();

return true;
}

bool MethodGenerationContext::InlineIfTrueFalse(bool isIfTrue) {
// 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.

if (!hasTwoLiteralBlockArguments()) {
return false;
}

assert(Bytecode::GetBytecodeLength(BC_PUSH_BLOCK) == 2);

std::tuple<vm_oop_t, vm_oop_t> methods =
extractBlockMethodsAndRemoveBytecodes();
VMMethod* condMethod = static_cast<VMMethod*>(std::get<0>(methods));
VMMethod* bodyMethod = static_cast<VMMethod*>(std::get<1>(methods));

size_t jumpOffsetIdxToSkipTrueBranch =
EmitJumpOnBoolWithDummyOffset(*this, isIfTrue, true);

isCurrentlyInliningABlock = true;
condMethod->InlineInto(*this);

size_t jumpOffsetIdxToSkipFalseBranch = EmitJumpWithDumyOffset(*this);

PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipTrueBranch);

// prevent optimizations between blocks to avoid issues with jump targets
resetLastBytecodeBuffer();

bodyMethod->InlineInto(*this);

isCurrentlyInliningABlock = false;

PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToSkipFalseBranch);

// prevent optimizations messing with the final jump target
resetLastBytecodeBuffer();

return true;
}

bool MethodGenerationContext::InlineWhile(Parser& parser, bool isWhileTrue) {
if (!hasTwoLiteralBlockArguments()) {
return false;
Expand Down Expand Up @@ -324,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
6 changes: 6 additions & 0 deletions src/compiler/MethodGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ class MethodGenerationContext {
std::vector<uint8_t> GetBytecodes() { return bytecode; }

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 All @@ -107,9 +110,12 @@ class MethodGenerationContext {

private:
void removeLastBytecodes(size_t numBytecodes);
bool hasOneLiteralBlockArgument();
bool hasTwoLiteralBlockArguments();
bool lastBytecodeIs(size_t indexFromEnd, uint8_t bytecode);
std::tuple<vm_oop_t, vm_oop_t> extractBlockMethodsAndRemoveBytecodes();
vm_oop_t extractBlockMethodAndRemoveBytecode();

vm_oop_t getLastBlockMethodAndFreeLiteral(uint8_t blockLiteralIdx);

void completeJumpsAndEmitReturningNil(Parser& parser, size_t loopBeginIdx,
Expand Down
20 changes: 18 additions & 2 deletions 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 @@ -628,8 +634,18 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) {

if (!super) {
if (numParts == 1 &&
((kw == "whileTrue:" && mgenc.InlineWhile(*this, true)) ||
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)))) {
((kw == "ifTrue:" && mgenc.InlineIfTrueOrIfFalse(true)) ||
(kw == "ifFalse:" && mgenc.InlineIfTrueOrIfFalse(false)) ||
(kw == "whileTrue:" && mgenc.InlineWhile(*this, true)) ||
(kw == "whileFalse:" && mgenc.InlineWhile(*this, false)) ||
(kw == "or:" && mgenc.InlineAndOr(true)) ||
(kw == "and:" && mgenc.InlineAndOr(false)))) {
return;
}

if (numParts == 2 &&
((kw == "ifTrue:ifFalse:" && mgenc.InlineIfTrueFalse(true)) ||
(kw == "ifFalse:ifTrue:" && mgenc.InlineIfTrueFalse(false)))) {
return;
}
}
Expand Down
72 changes: 47 additions & 25 deletions src/interpreter/bytecodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,53 @@ const uint8_t Bytecode::bytecodeLengths[] = {
};

const char* Bytecode::bytecodeNames[] = {
"HALT ", "DUP ",
"PUSH_LOCAL ", "PUSH_LOCAL_0 ",
"PUSH_LOCAL_1 ", "PUSH_LOCAL_2 ",
"PUSH_ARGUMENT ", "PUSH_SELF ",
"PUSH_ARG_1 ", "PUSH_ARG_2 ",
"PUSH_FIELD ", "PUSH_FIELD_0 ",
"PUSH_FIELD_1 ", "PUSH_BLOCK ",
"PUSH_CONSTANT ", "PUSH_CONSTANT_0 ",
"PUSH_CONSTANT_1 ", "PUSH_CONSTANT_2 ",
"PUSH_0 ", "PUSH_1 ",
"PUSH_NIL ", "PUSH_GLOBAL ",
"POP ", "POP_LOCAL ",
"POP_LOCAL_0 ", "POP_LOCAL_1 ",
"POP_LOCAL_2 ", "POP_ARGUMENT ",
"POP_FIELD ", "POP_FIELD_0 ",
"POP_FIELD_1 ", "SEND ",
"SUPER_SEND ", "RETURN_LOCAL ",
"RETURN_NON_LOCAL", "BC_JUMP ",
"BC_JUMP_ON_TRUE_TOP_NIL", "BC_JUMP_ON_FALSE_TOP_NIL",
"BC_JUMP_ON_TRUE_POP", "BC_JUMP_ON_FALSE_POP",
"BC_JUMP_BACKWARD",

"BC_JUMP2 ", "BC_JUMP2_ON_TRUE_TOP_NIL",
"BC_JUMP2_ON_FALSE_TOP_NIL", "BC_JUMP2_ON_TRUE_POP",
"BC_JUMP2_ON_FALSE_POP", "BC_JUMP2_BACKWARD",
"HALT ", // 0
"DUP ", // 1
"PUSH_LOCAL ", // 2
"PUSH_LOCAL_0 ", // 3
"PUSH_LOCAL_1 ", // 4
"PUSH_LOCAL_2 ", // 5
"PUSH_ARGUMENT ", // 6
"PUSH_SELF ", // 7
"PUSH_ARG_1 ", // 8
"PUSH_ARG_2 ", // 9
"PUSH_FIELD ", // 10
"PUSH_FIELD_0 ", // 11
"PUSH_FIELD_1 ", // 12
"PUSH_BLOCK ", // 13
"PUSH_CONSTANT ", // 14
"PUSH_CONSTANT_0 ", // 15
"PUSH_CONSTANT_1 ", // 16
"PUSH_CONSTANT_2 ", // 17
"PUSH_0 ", // 18
"PUSH_1 ", // 19
"PUSH_NIL ", // 20
"PUSH_GLOBAL ", // 21
"POP ", // 22
"POP_LOCAL ", // 23
"POP_LOCAL_0 ", // 24
"POP_LOCAL_1 ", // 25
"POP_LOCAL_2 ", // 26
"POP_ARGUMENT ", // 27
"POP_FIELD ", // 28
"POP_FIELD_0 ", // 29
"POP_FIELD_1 ", // 30
"SEND ", // 31
"SUPER_SEND ", // 32
"RETURN_LOCAL ", // 33
"RETURN_NON_LOCAL", // 34
"BC_JUMP ", // 35
"BC_JUMP_ON_FALSE_POP", // 36
"BC_JUMP_ON_TRUE_POP", // 37
"BC_JUMP_ON_FALSE_TOP_NIL", // 38
"BC_JUMP_ON_TRUE_TOP_NIL", // 39
"BC_JUMP_BACKWARD", // 40
"BC_JUMP2 ", // 41
"BC_JUMP2_ON_FALSE_POP", // 42
"BC_JUMP2_ON_TRUE_POP", // 43
"BC_JUMP2_ON_FALSE_TOP_NIL", // 44
"BC_JUMP2_ON_TRUE_TOP_NIL", // 45
"BC_JUMP2_BACKWARD", // 46
};

bool IsJumpBytecode(uint8_t bc) {
Expand Down
Loading