From 31cf3e57f3cc82a28027948c5e2a1fc9e6139a2b Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 2 Aug 2024 11:19:16 +0100 Subject: [PATCH 1/2] Add support for inlining #to:do: loops This adds some new bytecodes: - DUP_SECOND - INC - JUMP_IF_GREATER - JUMP2_IF_GREATER Signed-off-by: Stefan Marr --- src/compiler/BytecodeGenerator.cpp | 15 +++ src/compiler/BytecodeGenerator.h | 4 + src/compiler/LexicalScope.h | 8 ++ src/compiler/MethodGenerationContext.cpp | 61 ++++++++++-- src/compiler/MethodGenerationContext.h | 3 +- src/compiler/Parser.cpp | 7 +- src/compiler/Variable.cpp | 3 +- src/compiler/Variable.h | 2 + src/interpreter/Interpreter.cpp | 34 +++++++ src/interpreter/Interpreter.h | 2 + src/interpreter/InterpreterLoop.h | 41 +++++++++ src/interpreter/bytecodes.cpp | 112 ++++++++++++----------- src/interpreter/bytecodes.h | 99 ++++++++++---------- src/unitTests/BytecodeGenerationTest.cpp | 23 +++++ src/unitTests/BytecodeGenerationTest.h | 3 + src/vmobjects/VMFrame.h | 5 + src/vmobjects/VMMethod.cpp | 30 +++++- src/vmobjects/VMMethod.h | 7 +- 18 files changed, 340 insertions(+), 119 deletions(-) diff --git a/src/compiler/BytecodeGenerator.cpp b/src/compiler/BytecodeGenerator.cpp index 4230b8e9..105c71ea 100644 --- a/src/compiler/BytecodeGenerator.cpp +++ b/src/compiler/BytecodeGenerator.cpp @@ -254,6 +254,14 @@ void EmitRETURNNONLOCAL(MethodGenerationContext& mgenc) { Emit1(mgenc, BC_RETURN_NON_LOCAL, 0); } +void EmitINC(MethodGenerationContext& mgenc) { + Emit1(mgenc, BC_INC, 0); +} + +void EmitDupSecond(MethodGenerationContext& mgenc) { + Emit1(mgenc, BC_DUP_SECOND, 1); +} + size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc, bool isIfTrue, bool needsPop) { // Remember: true and false seem flipped here. @@ -285,6 +293,13 @@ size_t EmitJumpWithDumyOffset(MethodGenerationContext& mgenc) { return idx; } +size_t EmitJumpIfGreaterWithDummyOffset(MethodGenerationContext& mgenc) { + Emit1(mgenc, BC_JUMP_IF_GREATER, 0); + size_t idx = mgenc.AddBytecodeArgumentAndGetIndex(0); + mgenc.AddBytecodeArgument(0); + return idx; +} + void EmitJumpBackwardWithOffset(MethodGenerationContext& mgenc, size_t jumpOffset) { uint8_t jumpBytecode = diff --git a/src/compiler/BytecodeGenerator.h b/src/compiler/BytecodeGenerator.h index 851cb4b3..16dbb30d 100644 --- a/src/compiler/BytecodeGenerator.h +++ b/src/compiler/BytecodeGenerator.h @@ -58,9 +58,13 @@ void EmitSUPERSEND(MethodGenerationContext& mgenc, VMSymbol* msg); void EmitRETURNLOCAL(MethodGenerationContext& mgenc); void EmitRETURNNONLOCAL(MethodGenerationContext& mgenc); +void EmitINC(MethodGenerationContext& mgenc); +void EmitDupSecond(MethodGenerationContext& mgenc); + size_t EmitJumpOnBoolWithDummyOffset(MethodGenerationContext& mgenc, bool isIfTrue, bool needsPop); size_t EmitJumpWithDumyOffset(MethodGenerationContext& mgenc); +size_t EmitJumpIfGreaterWithDummyOffset(MethodGenerationContext& mgenc); void EmitJumpBackwardWithOffset(MethodGenerationContext& mgenc, size_t jumpOffset); size_t Emit3WithDummy(MethodGenerationContext& mgenc, uint8_t bytecode, diff --git a/src/compiler/LexicalScope.h b/src/compiler/LexicalScope.h index 9e84df07..a1bcd540 100644 --- a/src/compiler/LexicalScope.h +++ b/src/compiler/LexicalScope.h @@ -20,6 +20,14 @@ class LexicalScope { locals.push_back(var); } + const Variable* GetArgument(size_t index, size_t contextLevel) { + if (contextLevel > 0) { + return outer->GetArgument(index, contextLevel - 1); + } + + return &arguments.at(index); + } + const Variable* GetLocal(size_t index, uint8_t ctxLevel) { if (ctxLevel > 0) { return outer->GetLocal(index, ctxLevel - 1); diff --git a/src/compiler/MethodGenerationContext.cpp b/src/compiler/MethodGenerationContext.cpp index 8a2e3192..093de25c 100644 --- a/src/compiler/MethodGenerationContext.cpp +++ b/src/compiler/MethodGenerationContext.cpp @@ -439,6 +439,47 @@ bool MethodGenerationContext::InlineAndOr(bool isOr) { return true; } +bool MethodGenerationContext::InlineToDo() { + // HACK: We do assume that the receiver on the stack is a integer, + // HACK: similar to the other inlined messages. + // HACK: We don't support anything but integer at the moment. + assert(Bytecode::GetBytecodeLength(BC_PUSH_BLOCK) == 2); + if (!hasOneLiteralBlockArgument()) { + return false; + } + + VMMethod* toBeInlined = + static_cast(extractBlockMethodAndRemoveBytecode()); + + toBeInlined->MergeScopeInto(*this); + + const Variable* blockArg = toBeInlined->GetArgument(1, 0); + uint8_t iVarIdx = GetInlinedLocalIdx(blockArg); + + isCurrentlyInliningABlock = true; + EmitDupSecond(*this); + + size_t loopBeginIdx = OffsetOfNextInstruction(); + size_t jumpOffsetIdxToEnd = EmitJumpIfGreaterWithDummyOffset(*this); + + EmitDUP(*this); + + EmitPOPLOCAL(*this, iVarIdx, 0); + + toBeInlined->InlineInto(*this, false); + + EmitPOP(*this); + EmitINC(*this); + + EmitBackwardsJumpOffsetToTarget(loopBeginIdx); + + PatchJumpOffsetToPointToNextInstruction(jumpOffsetIdxToEnd); + + isCurrentlyInliningABlock = false; + + return true; +} + void MethodGenerationContext::CompleteLexicalScope() { lexicalScope = new LexicalScope( outerGenc == nullptr ? nullptr : outerGenc->lexicalScope, arguments, @@ -446,19 +487,23 @@ void MethodGenerationContext::CompleteLexicalScope() { } void MethodGenerationContext::MergeIntoScope(LexicalScope& scopeToBeInlined) { - assert(scopeToBeInlined.GetNumberOfArguments() == 1); - size_t numLocals = scopeToBeInlined.GetNumberOfLocals(); - if (numLocals > 0) { - inlineLocals(scopeToBeInlined); + if (scopeToBeInlined.GetNumberOfArguments() > 1) { + inlineAsLocals(scopeToBeInlined.arguments); + } + + if (scopeToBeInlined.GetNumberOfLocals() > 0) { + inlineAsLocals(scopeToBeInlined.locals); } } -void MethodGenerationContext::inlineLocals(LexicalScope& scopeToBeInlined) { - for (const Variable& local : scopeToBeInlined.locals) { - Variable freshCopy = local.CopyForInlining(this->locals.size()); +void MethodGenerationContext::inlineAsLocals(vector& vars) { + for (const Variable& var : vars) { + Variable freshCopy = var.CopyForInlining(this->locals.size()); if (freshCopy.IsValid()) { + assert(!freshCopy.IsArgument()); + // freshCopy can be invalid, because we don't need the $blockSelf - std::string qualifiedName = local.MakeQualifiedName(); + std::string qualifiedName = var.MakeQualifiedName(); assert(!Contains(this->locals, qualifiedName)); lexicalScope->AddInlinedLocal(freshCopy); this->locals.push_back(freshCopy); diff --git a/src/compiler/MethodGenerationContext.h b/src/compiler/MethodGenerationContext.h index bff0a30b..af097c66 100644 --- a/src/compiler/MethodGenerationContext.h +++ b/src/compiler/MethodGenerationContext.h @@ -97,6 +97,7 @@ class MethodGenerationContext { bool InlineIfTrueOrIfFalse(bool isIfTrue); bool InlineIfTrueFalse(bool isIfTrue); bool InlineAndOr(bool isOr); + bool InlineToDo(); inline size_t OffsetOfNextInstruction() { return bytecode.size(); } @@ -120,7 +121,7 @@ class MethodGenerationContext { void completeJumpsAndEmitReturningNil(Parser& parser, size_t loopBeginIdx, size_t jumpOffsetIdxToSkipLoopBody); - void inlineLocals(LexicalScope& scopeToBeInlined); + void inlineAsLocals(vector& vars); void checkJumpOffset(size_t jumpOffset, uint8_t bytecode); void resetLastBytecodeBuffer(); diff --git a/src/compiler/Parser.cpp b/src/compiler/Parser.cpp index 41ae6975..ede3ac73 100644 --- a/src/compiler/Parser.cpp +++ b/src/compiler/Parser.cpp @@ -598,8 +598,8 @@ void Parser::binaryMessage(MethodGenerationContext& mgenc, bool super) { binaryOperand(mgenc); - if (!super && (msgSelector == "||" && mgenc.InlineAndOr(true)) || - (msgSelector == "&&" && mgenc.InlineAndOr(false))) { + if (!super && ((msgSelector == "||" && mgenc.InlineAndOr(true)) || + (msgSelector == "&&" && mgenc.InlineAndOr(false)))) { return; } @@ -645,7 +645,8 @@ void Parser::keywordMessage(MethodGenerationContext& mgenc, bool super) { if (numParts == 2 && ((kw == "ifTrue:ifFalse:" && mgenc.InlineIfTrueFalse(true)) || - (kw == "ifFalse:ifTrue:" && mgenc.InlineIfTrueFalse(false)))) { + (kw == "ifFalse:ifTrue:" && mgenc.InlineIfTrueFalse(false)) || + (kw == "to:do:" && mgenc.InlineToDo()))) { return; } } diff --git a/src/compiler/Variable.cpp b/src/compiler/Variable.cpp index 66cfc029..e69604c0 100644 --- a/src/compiler/Variable.cpp +++ b/src/compiler/Variable.cpp @@ -21,9 +21,10 @@ std::string Variable::MakeQualifiedName() const { Variable Variable::CopyForInlining(size_t newIndex) const { if (isArgument) { if (name == strBlockSelf) { + // that's invalid return Variable(); } - return Variable(this, newIndex, true); } + // arguments that are inlined need to turn into variables, too return Variable(this, newIndex, false); } diff --git a/src/compiler/Variable.h b/src/compiler/Variable.h index fe0531c8..8e975900 100644 --- a/src/compiler/Variable.h +++ b/src/compiler/Variable.h @@ -34,6 +34,8 @@ class Variable { Variable CopyForInlining(size_t newIndex) const; + bool IsArgument() const { return isArgument; } + protected: std::string name; uint8_t index; diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index e2880f53..1daf37ac 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -34,6 +34,7 @@ #include "../interpreter/bytecodes.h" // NOLINT(misc-include-cleaner) it's required for InterpreterLoop.h #include "../memory/Heap.h" #include "../misc/defs.h" +#include "../vm/Globals.h" #include "../vm/IsValidObject.h" #include "../vm/Universe.h" #include "../vmobjects/IntegerBox.h" @@ -42,6 +43,7 @@ #include "../vmobjects/VMArray.h" #include "../vmobjects/VMBlock.h" #include "../vmobjects/VMClass.h" +#include "../vmobjects/VMDouble.h" #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMInvokable.h" #include "../vmobjects/VMMethod.h" @@ -407,6 +409,38 @@ void Interpreter::doReturnNonLocal() { popFrameAndPushResult(result); } +void Interpreter::doInc() { + vm_oop_t val = GetFrame()->Top(); + + if (IS_TAGGED(val) || CLASS_OF(val) == load_ptr(integerClass)) { + int64_t result = (int64_t)INT_VAL(val) + 1; + val = NEW_INT(result); + } else if (CLASS_OF(val) == load_ptr(doubleClass)) { + double d = static_cast(val)->GetEmbeddedDouble(); + val = GetUniverse()->NewDouble(d + 1.0); + } else { + GetUniverse()->ErrorExit("unsupported"); + } + + GetFrame()->SetTop(store_root(val)); +} + +bool Interpreter::checkIsGreater() { + vm_oop_t top = GetFrame()->Top(); + vm_oop_t top2 = GetFrame()->Top2(); + + if ((IS_TAGGED(top) || CLASS_OF(top) == load_ptr(integerClass)) && + (IS_TAGGED(top2) || CLASS_OF(top2) == load_ptr(integerClass))) { + return INT_VAL(top) > INT_VAL(top2); + } else if ((CLASS_OF(top) == load_ptr(doubleClass)) && + (CLASS_OF(top2) == load_ptr(doubleClass))) { + return static_cast(top)->GetEmbeddedDouble() > + static_cast(top2)->GetEmbeddedDouble(); + } + + return false; +} + void Interpreter::WalkGlobals(walk_heap_fn walk) { method = load_ptr(static_cast(walk(tmp_ptr(method)))); diff --git a/src/interpreter/Interpreter.h b/src/interpreter/Interpreter.h index 2615c1dc..80264018 100644 --- a/src/interpreter/Interpreter.h +++ b/src/interpreter/Interpreter.h @@ -106,6 +106,8 @@ class Interpreter { void doSuperSend(long bytecodeIndex); void doReturnLocal(); void doReturnNonLocal(); + void doInc(); + bool checkIsGreater(); }; inline VMFrame* Interpreter::GetFrame() const { diff --git a/src/interpreter/InterpreterLoop.h b/src/interpreter/InterpreterLoop.h index 45b4494d..5fc5447f 100644 --- a/src/interpreter/InterpreterLoop.h +++ b/src/interpreter/InterpreterLoop.h @@ -7,6 +7,7 @@ vm_oop_t Start() { void* loopTargets[] = {&&LABEL_BC_HALT, &&LABEL_BC_DUP, + &&LABEL_BC_DUP_SECOND, &&LABEL_BC_PUSH_LOCAL, &&LABEL_BC_PUSH_LOCAL_0, &&LABEL_BC_PUSH_LOCAL_1, @@ -40,17 +41,20 @@ vm_oop_t Start() { &&LABEL_BC_SUPER_SEND, &&LABEL_BC_RETURN_LOCAL, &&LABEL_BC_RETURN_NON_LOCAL, + &&LABEL_BC_INC, &&LABEL_BC_JUMP, &&LABEL_BC_JUMP_ON_FALSE_POP, &&LABEL_BC_JUMP_ON_TRUE_POP, &&LABEL_BC_JUMP_ON_FALSE_TOP_NIL, &&LABEL_BC_JUMP_ON_TRUE_TOP_NIL, + &&LABEL_BC_JUMP_IF_GREATER, &&LABEL_BC_JUMP_BACKWARD, &&LABEL_BC_JUMP2, &&LABEL_BC_JUMP2_ON_FALSE_POP, &&LABEL_BC_JUMP2_ON_TRUE_POP, &&LABEL_BC_JUMP2_ON_FALSE_TOP_NIL, &&LABEL_BC_JUMP2_ON_TRUE_TOP_NIL, + &&LABEL_BC_JUMP2_IF_GREATER, &&LABEL_BC_JUMP2_BACKWARD}; goto* loopTargets[currentBytecodes[bytecodeIndexGlobal]]; @@ -66,6 +70,14 @@ vm_oop_t Start() { doDup(); DISPATCH_NOGC(); +LABEL_BC_DUP_SECOND: + PROLOGUE(1); + { + vm_oop_t elem = GetFrame()->GetStackElement(1); + GetFrame()->Push(elem); + } + DISPATCH_NOGC(); + LABEL_BC_PUSH_LOCAL: PROLOGUE(3); doPushLocal(bytecodeIndexGlobal - 3); @@ -249,6 +261,11 @@ vm_oop_t Start() { doReturnNonLocal(); DISPATCH_NOGC(); +LABEL_BC_INC: + PROLOGUE(1); + doInc(); + DISPATCH_NOGC(); + LABEL_BC_JUMP: { uint8_t offset = currentBytecodes[bytecodeIndexGlobal + 1]; bytecodeIndexGlobal += offset; @@ -305,6 +322,17 @@ LABEL_BC_JUMP_ON_TRUE_TOP_NIL: { } DISPATCH_NOGC(); +LABEL_BC_JUMP_IF_GREATER: { + if (checkIsGreater()) { + bytecodeIndexGlobal += currentBytecodes[bytecodeIndexGlobal + 1]; + GetFrame()->Pop(); + GetFrame()->Pop(); + } else { + bytecodeIndexGlobal += 3; + } +} + DISPATCH_NOGC(); + LABEL_BC_JUMP_BACKWARD: { uint8_t offset = currentBytecodes[bytecodeIndexGlobal + 1]; bytecodeIndexGlobal -= offset; @@ -376,6 +404,19 @@ LABEL_BC_JUMP2_ON_TRUE_TOP_NIL: { } DISPATCH_NOGC(); +LABEL_BC_JUMP2_IF_GREATER: { + if (checkIsGreater()) { + bytecodeIndexGlobal += + ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1], + currentBytecodes[bytecodeIndexGlobal + 2]); + GetFrame()->Pop(); + GetFrame()->Pop(); + } else { + bytecodeIndexGlobal += 3; + } +} + DISPATCH_NOGC(); + LABEL_BC_JUMP2_BACKWARD: { uint16_t offset = ComputeOffset(currentBytecodes[bytecodeIndexGlobal + 1], currentBytecodes[bytecodeIndexGlobal + 2]); diff --git a/src/interpreter/bytecodes.cpp b/src/interpreter/bytecodes.cpp index 0c25b836..7b8cb820 100644 --- a/src/interpreter/bytecodes.cpp +++ b/src/interpreter/bytecodes.cpp @@ -32,6 +32,7 @@ const uint8_t Bytecode::bytecodeLengths[] = { 1, // BC_HALT 1, // BC_DUP + 1, // BC_DUP_SECOND 3, // BC_PUSH_LOCAL 1, // BC_PUSH_LOCAL_0 1, // BC_PUSH_LOCAL_1 @@ -65,75 +66,82 @@ const uint8_t Bytecode::bytecodeLengths[] = { 2, // BC_SUPER_SEND 1, // BC_RETURN_LOCAL 1, // BC_RETURN_NON_LOCAL + 1, // BC_INC 3, // BC_JUMP - 3, // BC_JUMP_ON_TRUE_TOP_NIL - 3, // BC_JUMP_ON_FALSE_TOP_NIL - 3, // BC_JUMP_ON_TRUE_POP 3, // BC_JUMP_ON_FALSE_POP + 3, // BC_JUMP_ON_TRUE_POP + 3, // BC_JUMP_ON_FALSE_TOP_NIL + 3, // BC_JUMP_ON_TRUE_TOP_NIL + 3, // BC_JUMP_IF_GREATER 3, // BC_JUMP_BACKWARD 3, // BC_JUMP2 - 3, // BC_JUMP2_ON_TRUE_TOP_NIL - 3, // BC_JUMP2_ON_FALSE_TOP_NIL - 3, // BC_JUMP2_ON_TRUE_POP 3, // BC_JUMP2_ON_FALSE_POP + 3, // BC_JUMP2_ON_TRUE_POP + 3, // BC_JUMP2_ON_FALSE_TOP_NIL + 3, // BC_JUMP2_ON_TRUE_TOP_NIL + 3, // BC_JUMP2_IF_GREATER 3, // BC_JUMP2_BACKWARD }; const char* Bytecode::bytecodeNames[] = { "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 + "DUP_SECOND ", // 2 + "PUSH_LOCAL ", // 3 + "PUSH_LOCAL_0 ", // 4 + "PUSH_LOCAL_1 ", // 5 + "PUSH_LOCAL_2 ", // 6 + "PUSH_ARGUMENT ", // 7 + "PUSH_SELF ", // 8 + "PUSH_ARG_1 ", // 9 + "PUSH_ARG_2 ", // 10 + "PUSH_FIELD ", // 11 + "PUSH_FIELD_0 ", // 12 + "PUSH_FIELD_1 ", // 13 + "PUSH_BLOCK ", // 14 + "PUSH_CONSTANT ", // 15 + "PUSH_CONSTANT_0 ", // 16 + "PUSH_CONSTANT_1 ", // 17 + "PUSH_CONSTANT_2 ", // 18 + "PUSH_0 ", // 19 + "PUSH_1 ", // 20 + "PUSH_NIL ", // 21 + "PUSH_GLOBAL ", // 22 + "POP ", // 23 + "POP_LOCAL ", // 24 + "POP_LOCAL_0 ", // 25 + "POP_LOCAL_1 ", // 26 + "POP_LOCAL_2 ", // 27 + "POP_ARGUMENT ", // 28 + "POP_FIELD ", // 29 + "POP_FIELD_0 ", // 30 + "POP_FIELD_1 ", // 31 + "SEND ", // 32 + "SUPER_SEND ", // 33 + "RETURN_LOCAL ", // 34 + "RETURN_NON_LOCAL", // 35 + "BC_INC ", // 36 + "BC_JUMP ", // 37 + "BC_JUMP_ON_FALSE_POP", // 38 + "BC_JUMP_ON_TRUE_POP", // 39 + "BC_JUMP_ON_FALSE_TOP_NIL", // 40 + "BC_JUMP_ON_TRUE_TOP_NIL", // 41 + "BC_JUMP_IF_GREATER", // 42 + "BC_JUMP_BACKWARD", // 43 + "BC_JUMP2 ", // 44 + "BC_JUMP2_ON_FALSE_POP", // 45 + "BC_JUMP2_ON_TRUE_POP", // 46 + "BC_JUMP2_ON_FALSE_TOP_NIL", // 47 + "BC_JUMP2_ON_TRUE_TOP_NIL", // 48 + "BC_JUMP2_IF_GREATER", // 49 + "BC_JUMP2_BACKWARD", // 50 }; bool IsJumpBytecode(uint8_t bc) { assert(BC_JUMP < BC_JUMP2_BACKWARD); - assert((BC_JUMP2_BACKWARD - BC_JUMP) == 11); + assert((BC_JUMP2_BACKWARD - BC_JUMP) == 13); return BC_JUMP <= bc && bc <= BC_JUMP2_BACKWARD; } diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index ad7d8c7d..a635143d 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -33,53 +33,57 @@ // bytecode constants used by SOM++ // clang-format off -#define BC_HALT 0 -#define BC_DUP 1 -#define BC_PUSH_LOCAL 2 -#define BC_PUSH_LOCAL_0 3 -#define BC_PUSH_LOCAL_1 4 -#define BC_PUSH_LOCAL_2 5 -#define BC_PUSH_ARGUMENT 6 -#define BC_PUSH_SELF 7 -#define BC_PUSH_ARG_1 8 -#define BC_PUSH_ARG_2 9 -#define BC_PUSH_FIELD 10 -#define BC_PUSH_FIELD_0 11 -#define BC_PUSH_FIELD_1 12 -#define BC_PUSH_BLOCK 13 -#define BC_PUSH_CONSTANT 14 -#define BC_PUSH_CONSTANT_0 15 -#define BC_PUSH_CONSTANT_1 16 -#define BC_PUSH_CONSTANT_2 17 -#define BC_PUSH_0 18 -#define BC_PUSH_1 19 -#define BC_PUSH_NIL 20 -#define BC_PUSH_GLOBAL 21 -#define BC_POP 22 -#define BC_POP_LOCAL 23 -#define BC_POP_LOCAL_0 24 -#define BC_POP_LOCAL_1 25 -#define BC_POP_LOCAL_2 26 -#define BC_POP_ARGUMENT 27 -#define BC_POP_FIELD 28 -#define BC_POP_FIELD_0 29 -#define BC_POP_FIELD_1 30 -#define BC_SEND 31 -#define BC_SUPER_SEND 32 -#define BC_RETURN_LOCAL 33 -#define BC_RETURN_NON_LOCAL 34 -#define BC_JUMP 35 -#define BC_JUMP_ON_FALSE_POP 36 -#define BC_JUMP_ON_TRUE_POP 37 -#define BC_JUMP_ON_FALSE_TOP_NIL 38 -#define BC_JUMP_ON_TRUE_TOP_NIL 39 -#define BC_JUMP_BACKWARD 40 -#define BC_JUMP2 41 -#define BC_JUMP2_ON_FALSE_POP 42 -#define BC_JUMP2_ON_TRUE_POP 43 -#define BC_JUMP2_ON_FALSE_TOP_NIL 44 -#define BC_JUMP2_ON_TRUE_TOP_NIL 45 -#define BC_JUMP2_BACKWARD 46 +#define BC_HALT 0 +#define BC_DUP 1 +#define BC_DUP_SECOND 2 +#define BC_PUSH_LOCAL 3 +#define BC_PUSH_LOCAL_0 4 +#define BC_PUSH_LOCAL_1 5 +#define BC_PUSH_LOCAL_2 6 +#define BC_PUSH_ARGUMENT 7 +#define BC_PUSH_SELF 8 +#define BC_PUSH_ARG_1 9 +#define BC_PUSH_ARG_2 10 +#define BC_PUSH_FIELD 11 +#define BC_PUSH_FIELD_0 12 +#define BC_PUSH_FIELD_1 13 +#define BC_PUSH_BLOCK 14 +#define BC_PUSH_CONSTANT 15 +#define BC_PUSH_CONSTANT_0 16 +#define BC_PUSH_CONSTANT_1 17 +#define BC_PUSH_CONSTANT_2 18 +#define BC_PUSH_0 19 +#define BC_PUSH_1 20 +#define BC_PUSH_NIL 21 +#define BC_PUSH_GLOBAL 22 +#define BC_POP 23 +#define BC_POP_LOCAL 24 +#define BC_POP_LOCAL_0 25 +#define BC_POP_LOCAL_1 26 +#define BC_POP_LOCAL_2 27 +#define BC_POP_ARGUMENT 28 +#define BC_POP_FIELD 29 +#define BC_POP_FIELD_0 30 +#define BC_POP_FIELD_1 31 +#define BC_SEND 32 +#define BC_SUPER_SEND 33 +#define BC_RETURN_LOCAL 34 +#define BC_RETURN_NON_LOCAL 35 +#define BC_INC 36 +#define BC_JUMP 37 +#define BC_JUMP_ON_FALSE_POP 38 +#define BC_JUMP_ON_TRUE_POP 39 +#define BC_JUMP_ON_FALSE_TOP_NIL 40 +#define BC_JUMP_ON_TRUE_TOP_NIL 41 +#define BC_JUMP_IF_GREATER 42 +#define BC_JUMP_BACKWARD 43 +#define BC_JUMP2 44 +#define BC_JUMP2_ON_FALSE_POP 45 +#define BC_JUMP2_ON_TRUE_POP 46 +#define BC_JUMP2_ON_FALSE_TOP_NIL 47 +#define BC_JUMP2_ON_TRUE_TOP_NIL 48 +#define BC_JUMP2_IF_GREATER 49 +#define BC_JUMP2_BACKWARD 50 #define _LAST_BYTECODE BC_JUMP2_BACKWARD @@ -92,7 +96,6 @@ // clang-format off #define BC_INC_FIELD 254 #define BC_INC_FIELD_PUSH 253 -#define BC_INC 252 #define BC_DEC 251 #define BC_SEND_N 250 #define BC_SEND_3 249 diff --git a/src/unitTests/BytecodeGenerationTest.cpp b/src/unitTests/BytecodeGenerationTest.cpp index 98d754bd..ed51ee92 100644 --- a/src/unitTests/BytecodeGenerationTest.cpp +++ b/src/unitTests/BytecodeGenerationTest.cpp @@ -701,6 +701,29 @@ void BytecodeGenerationTest::inliningOfAnd(std::string selector) { tearDown(); } +void BytecodeGenerationTest::testInliningOfToDo() { + auto bytecodes = methodToBytecode("test = ( 1 to: 2 do: [:i | i ] )"); + check(bytecodes, + {BC_PUSH_1, BC_PUSH_CONSTANT_0, + BC_DUP_SECOND, // stack: Top[1, 2, 1] + + BC(BC_JUMP_IF_GREATER, 11, 0), // consume only on jump + BC_DUP, + + BC_POP_LOCAL_0, // store the i into the local (arg becomes local + // after inlining) + BC_PUSH_LOCAL_0, // push the local on the stack as part of the + // block's code + BC_POP, // cleanup after block + BC_INC, // increment top, the iteration counter + BC(BC_JUMP_BACKWARD, 8, + 0), // jump back to the jump_if_greater bytecode + BC_POP, + + // jump_if_greater target + BC_PUSH_SELF, BC_RETURN_LOCAL}); +} + /* @pytest.mark.parametrize( "operator,bytecode", diff --git a/src/unitTests/BytecodeGenerationTest.h b/src/unitTests/BytecodeGenerationTest.h index 71c53925..8282b3c2 100644 --- a/src/unitTests/BytecodeGenerationTest.h +++ b/src/unitTests/BytecodeGenerationTest.h @@ -65,6 +65,7 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase { CPPUNIT_TEST(testIfTrueIfFalseNlrArg2); CPPUNIT_TEST(testInliningOfOr); CPPUNIT_TEST(testInliningOfAnd); + CPPUNIT_TEST(testInliningOfToDo); CPPUNIT_TEST(testJumpQueuesOrdering); @@ -159,6 +160,8 @@ class BytecodeGenerationTest : public CPPUNIT_NS::TestCase { void testInliningOfAnd(); void inliningOfAnd(std::string selector); + void testInliningOfToDo(); + void testJumpQueuesOrdering(); void dump(MethodGenerationContext* mgenc); diff --git a/src/vmobjects/VMFrame.h b/src/vmobjects/VMFrame.h index 0dfb4abf..8132f9e4 100644 --- a/src/vmobjects/VMFrame.h +++ b/src/vmobjects/VMFrame.h @@ -67,6 +67,11 @@ class VMFrame : public VMObject { return result; } + inline vm_oop_t Top2() { + vm_oop_t result = load_ptr(*(stack_ptr - 1)); + return result; + } + inline void SetTop(gc_oop_t val) { *stack_ptr = val; } inline void Push(vm_oop_t obj) { diff --git a/src/vmobjects/VMMethod.cpp b/src/vmobjects/VMMethod.cpp index 488d707b..b6b96a6b 100644 --- a/src/vmobjects/VMMethod.cpp +++ b/src/vmobjects/VMMethod.cpp @@ -160,8 +160,10 @@ std::string VMMethod::AsDebugString() const { ")"; } -void VMMethod::InlineInto(MethodGenerationContext& mgenc) { - mgenc.MergeIntoScope(*lexicalScope); +void VMMethod::InlineInto(MethodGenerationContext& mgenc, bool mergeScope) { + if (mergeScope) { + mgenc.MergeIntoScope(*lexicalScope); + } inlineInto(mgenc); } @@ -194,8 +196,10 @@ void VMMethod::inlineInto(MethodGenerationContext& mgenc) { switch (bytecode) { case BC_DUP: + case BC_DUP_SECOND: { Emit1(mgenc, bytecode, 1); break; + } case BC_PUSH_FIELD: case BC_PUSH_FIELD_0: case BC_PUSH_FIELD_1: @@ -306,6 +310,17 @@ void VMMethod::inlineInto(MethodGenerationContext& mgenc) { break; } + case BC_PUSH_ARG_1: + case BC_PUSH_ARG_2: { + // this can now happen with inlining #to:do: + size_t argIdx = bytecode == BC_PUSH_ARG_1 ? 1 : 2; + + const Variable* arg = lexicalScope->GetArgument(argIdx, 0); + size_t inlinedLocalIndex = mgenc.GetInlinedLocalIdx(arg); + EmitPUSHLOCAL(mgenc, inlinedLocalIndex, 0); + break; + } + case BC_PUSH_BLOCK: { VMMethod* blockMethod = (VMMethod*)GetConstant(i); blockMethod->AdaptAfterOuterInlined(1, mgenc); @@ -380,7 +395,9 @@ void VMMethod::inlineInto(MethodGenerationContext& mgenc) { case BC_JUMP_ON_FALSE_TOP_NIL: case BC_JUMP2: case BC_JUMP2_ON_TRUE_TOP_NIL: - case BC_JUMP2_ON_FALSE_TOP_NIL: { + case BC_JUMP2_ON_FALSE_TOP_NIL: + case BC_JUMP_IF_GREATER: + case BC_JUMP2_IF_GREATER: { // emit the jump, but instead of the offset, emit a dummy const size_t idx = Emit3WithDummy(mgenc, bytecode, 0); const size_t offset = @@ -413,8 +430,6 @@ void VMMethod::inlineInto(MethodGenerationContext& mgenc) { case BC_HALT: case BC_PUSH_SELF: - case BC_PUSH_ARG_1: - case BC_PUSH_ARG_2: case BC_RETURN_SELF: case BC_RETURN_FIELD_0: case BC_RETURN_FIELD_1: @@ -624,3 +639,8 @@ bool operator<(const BackJump& a, const BackJump& b) { bool operator<(const BackJumpPatch& a, const BackJumpPatch& b) { return a.backwardsJumpIdx > b.backwardsJumpIdx; } + +void VMMethod::MergeScopeInto(MethodGenerationContext& mgenc) { + assert(lexicalScope != nullptr); + mgenc.MergeIntoScope(*lexicalScope); +} diff --git a/src/vmobjects/VMMethod.h b/src/vmobjects/VMMethod.h index f6f46039..ee1bf09a 100644 --- a/src/vmobjects/VMMethod.h +++ b/src/vmobjects/VMMethod.h @@ -159,11 +159,16 @@ class VMMethod : public VMInvokable { StdString AsDebugString() const override; - void InlineInto(MethodGenerationContext& mgenc); + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true); void AdaptAfterOuterInlined(uint8_t removedCtxLevel, MethodGenerationContext& mgencWithInlined); + void MergeScopeInto(MethodGenerationContext& mgenc); + const Variable* GetArgument(size_t index, size_t contextLevel) { + return lexicalScope->GetArgument(index, contextLevel); + } + private: void inlineInto(MethodGenerationContext& mgenc); std::priority_queue createBackJumpHeap(); From b1cfa103ed268adf752b69f985199f858d1621ee Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 2 Aug 2024 11:41:47 +0100 Subject: [PATCH 2/2] Fix bytecode names Signed-off-by: Stefan Marr --- src/interpreter/bytecodes.cpp | 102 +++++++++++++++++----------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/interpreter/bytecodes.cpp b/src/interpreter/bytecodes.cpp index 7b8cb820..a95ce8bc 100644 --- a/src/interpreter/bytecodes.cpp +++ b/src/interpreter/bytecodes.cpp @@ -86,57 +86,57 @@ const uint8_t Bytecode::bytecodeLengths[] = { }; const char* Bytecode::bytecodeNames[] = { - "HALT ", // 0 - "DUP ", // 1 - "DUP_SECOND ", // 2 - "PUSH_LOCAL ", // 3 - "PUSH_LOCAL_0 ", // 4 - "PUSH_LOCAL_1 ", // 5 - "PUSH_LOCAL_2 ", // 6 - "PUSH_ARGUMENT ", // 7 - "PUSH_SELF ", // 8 - "PUSH_ARG_1 ", // 9 - "PUSH_ARG_2 ", // 10 - "PUSH_FIELD ", // 11 - "PUSH_FIELD_0 ", // 12 - "PUSH_FIELD_1 ", // 13 - "PUSH_BLOCK ", // 14 - "PUSH_CONSTANT ", // 15 - "PUSH_CONSTANT_0 ", // 16 - "PUSH_CONSTANT_1 ", // 17 - "PUSH_CONSTANT_2 ", // 18 - "PUSH_0 ", // 19 - "PUSH_1 ", // 20 - "PUSH_NIL ", // 21 - "PUSH_GLOBAL ", // 22 - "POP ", // 23 - "POP_LOCAL ", // 24 - "POP_LOCAL_0 ", // 25 - "POP_LOCAL_1 ", // 26 - "POP_LOCAL_2 ", // 27 - "POP_ARGUMENT ", // 28 - "POP_FIELD ", // 29 - "POP_FIELD_0 ", // 30 - "POP_FIELD_1 ", // 31 - "SEND ", // 32 - "SUPER_SEND ", // 33 - "RETURN_LOCAL ", // 34 - "RETURN_NON_LOCAL", // 35 - "BC_INC ", // 36 - "BC_JUMP ", // 37 - "BC_JUMP_ON_FALSE_POP", // 38 - "BC_JUMP_ON_TRUE_POP", // 39 - "BC_JUMP_ON_FALSE_TOP_NIL", // 40 - "BC_JUMP_ON_TRUE_TOP_NIL", // 41 - "BC_JUMP_IF_GREATER", // 42 - "BC_JUMP_BACKWARD", // 43 - "BC_JUMP2 ", // 44 - "BC_JUMP2_ON_FALSE_POP", // 45 - "BC_JUMP2_ON_TRUE_POP", // 46 - "BC_JUMP2_ON_FALSE_TOP_NIL", // 47 - "BC_JUMP2_ON_TRUE_TOP_NIL", // 48 - "BC_JUMP2_IF_GREATER", // 49 - "BC_JUMP2_BACKWARD", // 50 + "HALT ", // 0 + "DUP ", // 1 + "DUP_SECOND ", // 2 + "PUSH_LOCAL ", // 3 + "PUSH_LOCAL_0 ", // 4 + "PUSH_LOCAL_1 ", // 5 + "PUSH_LOCAL_2 ", // 6 + "PUSH_ARGUMENT ", // 7 + "PUSH_SELF ", // 8 + "PUSH_ARG_1 ", // 9 + "PUSH_ARG_2 ", // 10 + "PUSH_FIELD ", // 11 + "PUSH_FIELD_0 ", // 12 + "PUSH_FIELD_1 ", // 13 + "PUSH_BLOCK ", // 14 + "PUSH_CONSTANT ", // 15 + "PUSH_CONSTANT_0 ", // 16 + "PUSH_CONSTANT_1 ", // 17 + "PUSH_CONSTANT_2 ", // 18 + "PUSH_0 ", // 19 + "PUSH_1 ", // 20 + "PUSH_NIL ", // 21 + "PUSH_GLOBAL ", // 22 + "POP ", // 23 + "POP_LOCAL ", // 24 + "POP_LOCAL_0 ", // 25 + "POP_LOCAL_1 ", // 26 + "POP_LOCAL_2 ", // 27 + "POP_ARGUMENT ", // 28 + "POP_FIELD ", // 29 + "POP_FIELD_0 ", // 30 + "POP_FIELD_1 ", // 31 + "SEND ", // 32 + "SUPER_SEND ", // 33 + "RETURN_LOCAL ", // 34 + "RETURN_NON_LOCAL", // 35 + "INC ", // 36 + "JUMP ", // 37 + "JUMP_ON_FALSE_POP", // 38 + "JUMP_ON_TRUE_POP", // 39 + "JUMP_ON_FALSE_TOP_NIL", // 40 + "JUMP_ON_TRUE_TOP_NIL", // 41 + "JUMP_IF_GREATER ", // 42 + "JUMP_BACKWARD ", // 43 + "JUMP2 ", // 44 + "JUMP2_ON_FALSE_POP", // 45 + "JUMP2_ON_TRUE_POP", // 46 + "JUMP2_ON_FALSE_TOP_NIL", // 47 + "JUMP2_ON_TRUE_TOP_NIL", // 48 + "JUMP2_IF_GREATER", // 49 + "JUMP2_BACKWARD ", // 50 }; bool IsJumpBytecode(uint8_t bc) {