Skip to content

Commit

Permalink
Inline #ifTrue:, #ifFalse:, #ifTrue:ifFalse:, #ifFalse:ifTrue:, #||, …
Browse files Browse the repository at this point in the history
…#or:, #&&, #and: (#35)

This PR adds the inlining for the various messages.

It reduces the median run time by 23%
https://rebench.dev/SOMpp/compare/2dc14379c566f40d51791d0e6eca95d743952799..692cc6c47727a7fa76923daf92e0bb8c7d4be3b1
  • Loading branch information
smarr authored Aug 2, 2024
2 parents 2dc1437 + 7a105bf commit 14ffdfd
Show file tree
Hide file tree
Showing 9 changed files with 472 additions and 343 deletions.
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

0 comments on commit 14ffdfd

Please sign in to comment.