From bbca581837ce2a53bc5c4bce80619134de2040e0 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 10 Aug 2024 12:13:42 +0100 Subject: [PATCH 1/5] Added SEND_1 bytecode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn’t do any further specialization yet, it only avoids reading the number of arguments from the symbol. Signed-off-by: Stefan Marr --- src/compiler/BytecodeGenerator.cpp | 2 +- src/interpreter/Interpreter.cpp | 23 +++++++++++ src/interpreter/Interpreter.h | 1 + src/interpreter/InterpreterLoop.h | 6 +++ src/interpreter/bytecodes.cpp | 52 ++++++++++++------------ src/interpreter/bytecodes.h | 52 ++++++++++++------------ src/unitTests/BytecodeGenerationTest.cpp | 30 +++++++------- 7 files changed, 99 insertions(+), 67 deletions(-) diff --git a/src/compiler/BytecodeGenerator.cpp b/src/compiler/BytecodeGenerator.cpp index 92c74fcd..28772441 100644 --- a/src/compiler/BytecodeGenerator.cpp +++ b/src/compiler/BytecodeGenerator.cpp @@ -234,7 +234,7 @@ void EmitSEND(MethodGenerationContext& mgenc, VMSymbol* msg) { const int numArgs = Signature::GetNumberOfArguments(msg); const size_t stackEffect = -numArgs + 1; // +1 for the result - Emit2(mgenc, BC_SEND, idx, stackEffect); + Emit2(mgenc, numArgs == 1 ? BC_SEND_1 : BC_SEND, idx, stackEffect); } void EmitSUPERSEND(MethodGenerationContext& mgenc, VMSymbol* msg) { diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index e40b3c06..b6366e55 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -366,6 +366,29 @@ void Interpreter::doSend(long bytecodeIndex) { send(signature, receiverClass); } +void Interpreter::doUnarySend(long bytecodeIndex) { + VMSymbol* signature = + static_cast(method->GetConstant(bytecodeIndex)); + + const int numOfArgs = 1; + + vm_oop_t receiver = GetFrame()->GetStackElement(numOfArgs - 1); + + assert(IsValidObject(receiver)); + // make sure it is really a class + assert(dynamic_cast(CLASS_OF(receiver)) != nullptr); + + VMClass* receiverClass = CLASS_OF(receiver); + + assert(IsValidObject(receiverClass)); + +#ifdef LOG_RECEIVER_TYPES + Universe::receiverTypes[receiverClass->GetName()->GetStdString()]++; +#endif + + send(signature, receiverClass); +} + void Interpreter::doSuperSend(long bytecodeIndex) { VMSymbol* signature = static_cast(method->GetConstant(bytecodeIndex)); diff --git a/src/interpreter/Interpreter.h b/src/interpreter/Interpreter.h index f60f7ee7..879683aa 100644 --- a/src/interpreter/Interpreter.h +++ b/src/interpreter/Interpreter.h @@ -105,6 +105,7 @@ class Interpreter { static void doPopField(long bytecodeIndex); static void doPopFieldWithIndex(uint8_t fieldIndex); static void doSend(long bytecodeIndex); + static void doUnarySend(long bytecodeIndex); static void doSuperSend(long bytecodeIndex); static void doReturnLocal(); static void doReturnNonLocal(); diff --git a/src/interpreter/InterpreterLoop.h b/src/interpreter/InterpreterLoop.h index 2835cf4e..2fe3f03f 100644 --- a/src/interpreter/InterpreterLoop.h +++ b/src/interpreter/InterpreterLoop.h @@ -38,6 +38,7 @@ vm_oop_t Start() { &&LABEL_BC_POP_FIELD_0, &&LABEL_BC_POP_FIELD_1, &&LABEL_BC_SEND, + &&LABEL_BC_SEND_1, &&LABEL_BC_SUPER_SEND, &&LABEL_BC_RETURN_LOCAL, &&LABEL_BC_RETURN_NON_LOCAL, @@ -253,6 +254,11 @@ vm_oop_t Start() { doSend(bytecodeIndexGlobal - 2); DISPATCH_GC(); +LABEL_BC_SEND_1: + PROLOGUE(2); + doUnarySend(bytecodeIndexGlobal - 2); + DISPATCH_GC(); + LABEL_BC_SUPER_SEND: PROLOGUE(2); doSuperSend(bytecodeIndexGlobal - 2); diff --git a/src/interpreter/bytecodes.cpp b/src/interpreter/bytecodes.cpp index dc3f42e0..f05dfd41 100644 --- a/src/interpreter/bytecodes.cpp +++ b/src/interpreter/bytecodes.cpp @@ -63,6 +63,7 @@ const uint8_t Bytecode::bytecodeLengths[] = { 1, // BC_POP_FIELD_0 1, // BC_POP_FIELD_1 2, // BC_SEND + 2, // BC_SEND_1 2, // BC_SUPER_SEND 1, // BC_RETURN_LOCAL 1, // BC_RETURN_NON_LOCAL @@ -126,31 +127,32 @@ const char* Bytecode::bytecodeNames[] = { "POP_FIELD_0 ", // 30 "POP_FIELD_1 ", // 31 "SEND ", // 32 - "SUPER_SEND ", // 33 - "RETURN_LOCAL ", // 34 - "RETURN_NON_LOCAL", // 35 - "RETURN_SELF ", // 36 - "RETURN_FIELD_0 ", // 37 - "RETURN_FIELD_1 ", // 38 - "RETURN_FIELD_2 ", // 39 - "INC ", // 40 - "DEC ", // 41 - "INC_FIELD ", // 42 - "INC_FIELD_PUSH ", // 43 - "JUMP ", // 44 - "JUMP_ON_FALSE_POP", // 45 - "JUMP_ON_TRUE_POP", // 46 - "JUMP_ON_FALSE_TOP_NIL", // 47 - "JUMP_ON_TRUE_TOP_NIL", // 48 - "JUMP_IF_GREATER ", // 49 - "JUMP_BACKWARD ", // 50 - "JUMP2 ", // 51 - "JUMP2_ON_FALSE_POP", // 52 - "JUMP2_ON_TRUE_POP", // 53 - "JUMP2_ON_FALSE_TOP_NIL", // 54 - "JUMP2_ON_TRUE_TOP_NIL", // 55 - "JUMP2_IF_GREATER", // 56 - "JUMP2_BACKWARD ", // 57 + "SEND_1 ", // 33 + "SUPER_SEND ", // 34 + "RETURN_LOCAL ", // 35 + "RETURN_NON_LOCAL", // 36 + "RETURN_SELF ", // 37 + "RETURN_FIELD_0 ", // 38 + "RETURN_FIELD_1 ", // 39 + "RETURN_FIELD_2 ", // 40 + "INC ", // 41 + "DEC ", // 42 + "INC_FIELD ", // 43 + "INC_FIELD_PUSH ", // 44 + "JUMP ", // 45 + "JUMP_ON_FALSE_POP", // 46 + "JUMP_ON_TRUE_POP", // 47 + "JUMP_ON_FALSE_TOP_NIL", // 48 + "JUMP_ON_TRUE_TOP_NIL", // 49 + "JUMP_IF_GREATER ", // 50 + "JUMP_BACKWARD ", // 51 + "JUMP2 ", // 52 + "JUMP2_ON_FALSE_POP", // 53 + "JUMP2_ON_TRUE_POP", // 54 + "JUMP2_ON_FALSE_TOP_NIL", // 55 + "JUMP2_ON_TRUE_TOP_NIL", // 56 + "JUMP2_IF_GREATER", // 57 + "JUMP2_BACKWARD ", // 58 }; bool IsJumpBytecode(uint8_t bc) { diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index cce9fc34..ea3916ba 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -66,31 +66,32 @@ #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_RETURN_SELF 36 -#define BC_RETURN_FIELD_0 37 -#define BC_RETURN_FIELD_1 38 -#define BC_RETURN_FIELD_2 39 -#define BC_INC 40 -#define BC_DEC 41 -#define BC_INC_FIELD 42 -#define BC_INC_FIELD_PUSH 43 -#define BC_JUMP 44 -#define BC_JUMP_ON_FALSE_POP 45 -#define BC_JUMP_ON_TRUE_POP 46 -#define BC_JUMP_ON_FALSE_TOP_NIL 47 -#define BC_JUMP_ON_TRUE_TOP_NIL 48 -#define BC_JUMP_IF_GREATER 49 -#define BC_JUMP_BACKWARD 50 -#define BC_JUMP2 51 -#define BC_JUMP2_ON_FALSE_POP 52 -#define BC_JUMP2_ON_TRUE_POP 53 -#define BC_JUMP2_ON_FALSE_TOP_NIL 54 -#define BC_JUMP2_ON_TRUE_TOP_NIL 55 -#define BC_JUMP2_IF_GREATER 56 -#define BC_JUMP2_BACKWARD 57 +#define BC_SEND_1 33 +#define BC_SUPER_SEND 34 +#define BC_RETURN_LOCAL 35 +#define BC_RETURN_NON_LOCAL 36 +#define BC_RETURN_SELF 37 +#define BC_RETURN_FIELD_0 38 +#define BC_RETURN_FIELD_1 39 +#define BC_RETURN_FIELD_2 40 +#define BC_INC 41 +#define BC_DEC 42 +#define BC_INC_FIELD 43 +#define BC_INC_FIELD_PUSH 44 +#define BC_JUMP 45 +#define BC_JUMP_ON_FALSE_POP 46 +#define BC_JUMP_ON_TRUE_POP 47 +#define BC_JUMP_ON_FALSE_TOP_NIL 48 +#define BC_JUMP_ON_TRUE_TOP_NIL 49 +#define BC_JUMP_IF_GREATER 50 +#define BC_JUMP_BACKWARD 51 +#define BC_JUMP2 52 +#define BC_JUMP2_ON_FALSE_POP 53 +#define BC_JUMP2_ON_TRUE_POP 54 +#define BC_JUMP2_ON_FALSE_TOP_NIL 55 +#define BC_JUMP2_ON_TRUE_TOP_NIL 56 +#define BC_JUMP2_IF_GREATER 57 +#define BC_JUMP2_BACKWARD 58 #define _LAST_BYTECODE BC_JUMP2_BACKWARD @@ -104,7 +105,6 @@ #define BC_SEND_N 250 #define BC_SEND_3 249 #define BC_SEND_2 248 -#define BC_SEND_1 247 // clang-format on // properties of the bytecodes diff --git a/src/unitTests/BytecodeGenerationTest.cpp b/src/unitTests/BytecodeGenerationTest.cpp index 18eec87f..3320afec 100644 --- a/src/unitTests/BytecodeGenerationTest.cpp +++ b/src/unitTests/BytecodeGenerationTest.cpp @@ -127,7 +127,7 @@ void BytecodeGenerationTest::testSendDupPopFieldReturnLocal() { auto bytecodes = methodToBytecode("test = ( ^ field := self method )"); check(bytecodes, - {BC_PUSH_SELF, BC(BC_SEND, 0), BC_DUP, BC_POP_FIELD_0, + {BC_PUSH_SELF, BC(BC_SEND_1, 0), BC_DUP, BC_POP_FIELD_0, BC_RETURN_LOCAL}); } @@ -136,7 +136,7 @@ void BytecodeGenerationTest::testSendDupPopFieldReturnLocalPeriod() { auto bytecodes = methodToBytecode("test = ( ^ field := self method. )"); check(bytecodes, - {BC_PUSH_SELF, BC(BC_SEND, 0), BC_DUP, BC_POP_FIELD_0, + {BC_PUSH_SELF, BC(BC_SEND_1, 0), BC_DUP, BC_POP_FIELD_0, BC_RETURN_LOCAL}); } @@ -377,7 +377,7 @@ void BytecodeGenerationTest::ifTrueWithLiteralReturn(std::string literal, bytecode.bytecode == BC_PUSH_BLOCK; check(bytecodes, - {BC_PUSH_SELF, BC(BC_SEND, 0), + {BC_PUSH_SELF, BC(BC_SEND_1, 0), BC(BC_JUMP_ON_FALSE_TOP_NIL, twoByte2 ? 5 : 4, 0), bytecode, BC_POP, BC_RETURN_SELF}); @@ -418,7 +418,7 @@ void BytecodeGenerationTest::ifTrueWithSomethingAndLiteralReturn( bytecode.bytecode == BC_PUSH_BLOCK; check(bytecodes, - {BC_PUSH_SELF, BC(BC_SEND, 0), + {BC_PUSH_SELF, BC(BC_SEND_1, 0), BC(BC_JUMP_ON_FALSE_TOP_NIL, twoByte2 ? 7 : 6, 0), BC_PUSH_CONSTANT_1, BC_POP, bytecode, BC_POP, BC_RETURN_SELF}); @@ -433,7 +433,7 @@ void BytecodeGenerationTest::testIfTrueIfFalseArg() { ) )"""); check(bytecodes, - {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), + {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND_1, 1), BC(BC_JUMP_ON_FALSE_POP, 7, 0), BC_PUSH_ARG_1, BC(BC_JUMP, 4, 0), BC_PUSH_ARG_2, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_SELF}); } @@ -446,7 +446,7 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg1() { ) )"""); check(bytecodes, - {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), + {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND_1, 1), BC(BC_JUMP_ON_FALSE_POP, 8, 0), BC_PUSH_ARG_1, BC_RETURN_LOCAL, BC(BC_JUMP, 4, 0), BC_PUSH_ARG_2, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_SELF}); @@ -460,7 +460,7 @@ void BytecodeGenerationTest::testIfTrueIfFalseNlrArg2() { ) )"""); check(bytecodes, - {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), + {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND_1, 1), BC(BC_JUMP_ON_FALSE_POP, 7, 0), BC_PUSH_ARG_1, BC(BC_JUMP, 5, 0), BC_PUSH_ARG_2, BC_RETURN_LOCAL, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_SELF}); @@ -552,7 +552,7 @@ void BytecodeGenerationTest::ifArg(std::string selector, int8_t jumpBytecode) { auto bytecodes = methodToBytecode(source.data()); check(bytecodes, - {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), + {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND_1, 1), BC(jumpBytecode, 4, 0), BC_PUSH_ARG_1, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_SELF}); @@ -588,7 +588,7 @@ void BytecodeGenerationTest::ifReturnNonLocal(std::string selector, auto bytecodes = methodToBytecode(source.data()); check(bytecodes, - {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), + {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND_1, 1), BC(jumpBytecode, 5, 0), BC_PUSH_ARG_1, BC_RETURN_LOCAL, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_SELF}); @@ -813,7 +813,7 @@ void BytecodeGenerationTest::testBlockIfTrueArg() { check(bytecodes, {BC_PUSH_CONSTANT_0, BC_POP, BC(BC_PUSH_ARGUMENT, 0, 1), - BC(BC_SEND, 1), BC(BC_JUMP_ON_FALSE_TOP_NIL, 4, 0), BC_PUSH_ARG_1, + BC(BC_SEND_1, 1), BC(BC_JUMP_ON_FALSE_TOP_NIL, 4, 0), BC_PUSH_ARG_1, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_LOCAL}); } @@ -829,7 +829,7 @@ void BytecodeGenerationTest::testBlockIfTrueMethodArg() { ] )"""); check(bytecodes, {BC_PUSH_CONSTANT_0, BC_POP, BC(BC_PUSH_ARGUMENT, 0, 1), - BC(BC_SEND, 1), BC(BC_JUMP_ON_FALSE_TOP_NIL, 6, 0), + BC(BC_SEND_1, 1), BC(BC_JUMP_ON_FALSE_TOP_NIL, 6, 0), BC(BC_PUSH_ARGUMENT, 1, 1), BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_LOCAL}); } @@ -844,9 +844,9 @@ void BytecodeGenerationTest::ifTrueIfFalseReturn(std::string sel1, sel1 + " [ ^ arg1 ] " + sel2 + " [ arg2 ] )"; auto bytecodes = methodToBytecode(source.data()); - check(bytecodes, {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, BC(BC_SEND, 1), - bc, BC_PUSH_ARG_1, BC_RETURN_LOCAL, BC(BC_JUMP, 4, 0), - BC_PUSH_ARG_2, BC_RETURN_LOCAL}); + check(bytecodes, {BC_PUSH_CONSTANT_0, BC_POP, BC_PUSH_SELF, + BC(BC_SEND_1, 1), bc, BC_PUSH_ARG_1, BC_RETURN_LOCAL, + BC(BC_JUMP, 4, 0), BC_PUSH_ARG_2, BC_RETURN_LOCAL}); tearDown(); } @@ -867,7 +867,7 @@ void BytecodeGenerationTest::blockIfReturnNonLocal(std::string sel, BC bc) { auto bytecodes = blockToBytecode(source.data()); check(bytecodes, {BC_PUSH_CONSTANT_0, BC_POP, BC(BC_PUSH_ARGUMENT, 0, 1), - BC(BC_SEND, 1), bc, BC_PUSH_ARG_1, BC_RETURN_NON_LOCAL, BC_POP, + BC(BC_SEND_1, 1), bc, BC_PUSH_ARG_1, BC_RETURN_NON_LOCAL, BC_POP, BC_PUSH_CONSTANT_2, BC_RETURN_LOCAL}); tearDown(); From 79e17c036a40bf85ac6dd504bd6e7a942d5320e7 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 10 Aug 2024 14:07:17 +0100 Subject: [PATCH 2/5] Add specialization for 1 argument call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What’s a bit odd at the moment, the return bytecodes handle the stack management of the caller. This works because only bytecode methods would trigger these bytecodes. But, it seems unituitive and confusing. Signed-off-by: Stefan Marr --- src/interpreter/Interpreter.cpp | 22 +++++++++++- src/vmobjects/VMEvaluationPrimitive.cpp | 17 +++++++++ src/vmobjects/VMEvaluationPrimitive.h | 1 + src/vmobjects/VMFrame.h | 11 +++--- src/vmobjects/VMInvokable.h | 1 + src/vmobjects/VMMethod.cpp | 7 ++++ src/vmobjects/VMMethod.h | 1 + src/vmobjects/VMPrimitive.h | 5 +++ src/vmobjects/VMSafePrimitive.cpp | 15 ++++++++ src/vmobjects/VMSafePrimitive.h | 3 ++ src/vmobjects/VMTrivialMethod.cpp | 46 ++++++++++++++++++++++++- src/vmobjects/VMTrivialMethod.h | 8 +++++ 12 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index b6366e55..5134686b 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -386,7 +386,27 @@ void Interpreter::doUnarySend(long bytecodeIndex) { Universe::receiverTypes[receiverClass->GetName()->GetStdString()]++; #endif - send(signature, receiverClass); + VMInvokable* invokable = receiverClass->LookupInvokable(signature); + + if (invokable != nullptr) { +#ifdef LOG_RECEIVER_TYPES + std::string name = receiverClass->GetName()->GetStdString(); + if (Universe::callStats.find(name) == Universe::callStats.end()) { + Universe::callStats[name] = {0, 0}; + } + Universe::callStats[name].noCalls++; + if (invokable->IsPrimitive()) { + Universe::callStats[name].noPrimitiveCalls++; + } +#endif + // since an invokable is able to change/use the frame, we have to write + // cached values before, and read cached values after calling + GetFrame()->SetBytecodeIndex(bytecodeIndexGlobal); + invokable->Invoke1(GetFrame()); + bytecodeIndexGlobal = GetFrame()->GetBytecodeIndex(); + } else { + triggerDoesNotUnderstand(signature); + } } void Interpreter::doSuperSend(long bytecodeIndex) { diff --git a/src/vmobjects/VMEvaluationPrimitive.cpp b/src/vmobjects/VMEvaluationPrimitive.cpp index a7d700a3..3d80069d 100644 --- a/src/vmobjects/VMEvaluationPrimitive.cpp +++ b/src/vmobjects/VMEvaluationPrimitive.cpp @@ -102,6 +102,23 @@ VMFrame* VMEvaluationPrimitive::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMEvaluationPrimitive::Invoke1(VMFrame* frame) { + assert(numberOfArguments == 1); + // Get the block (the receiver) from the stack + VMBlock* block = static_cast(frame->GetStackElement(0)); + + // Get the context of the block... + VMFrame* context = block->GetContext(); + VMInvokable* method = block->GetMethod(); + VMFrame* newFrame = method->Invoke1(frame); + + // Push set its context to be the one specified in the block + if (newFrame != nullptr) { + newFrame->SetContext(context); + } + return nullptr; +} + std::string VMEvaluationPrimitive::AsDebugString() const { return "VMEvaluationPrimitive(" + to_string(numberOfArguments) + ")"; } diff --git a/src/vmobjects/VMEvaluationPrimitive.h b/src/vmobjects/VMEvaluationPrimitive.h index f7d60f0f..d3778839 100644 --- a/src/vmobjects/VMEvaluationPrimitive.h +++ b/src/vmobjects/VMEvaluationPrimitive.h @@ -48,6 +48,7 @@ class VMEvaluationPrimitive : public VMInvokable { bool IsMarkedInvalid() const override; VMFrame* Invoke(VMFrame* frm) override; + VMFrame* Invoke1(VMFrame* frm) override; void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; diff --git a/src/vmobjects/VMFrame.h b/src/vmobjects/VMFrame.h index 312d8e1c..e5daeb6e 100644 --- a/src/vmobjects/VMFrame.h +++ b/src/vmobjects/VMFrame.h @@ -124,6 +124,11 @@ class VMFrame : public VMObject { void PrintStackTrace() const; long ArgumentStackIndex(long index) const; void CopyArgumentsFrom(VMFrame* frame); + + inline void SetArgument(size_t argIdx, vm_oop_t value) { + store_ptr(arguments[argIdx], value); + } + void WalkObjects(walk_heap_fn) override; VMFrame* CloneForMovingGC() const override; @@ -152,8 +157,6 @@ class VMFrame : public VMObject { gc_oop_t* locals; gc_oop_t* stack_ptr; - inline void SetArgument(long index, vm_oop_t value); - static const long VMFrameNumberOfFields; }; @@ -200,7 +203,3 @@ void VMFrame::ClearPreviousFrame() { VMMethod* VMFrame::GetMethod() const { return load_ptr(method); } - -void VMFrame::SetArgument(long index, vm_oop_t value) { - store_ptr(arguments[index], value); -} diff --git a/src/vmobjects/VMInvokable.h b/src/vmobjects/VMInvokable.h index 3a112a7f..2c5880be 100644 --- a/src/vmobjects/VMInvokable.h +++ b/src/vmobjects/VMInvokable.h @@ -44,6 +44,7 @@ class VMInvokable : public AbstractVMObject { int64_t GetHash() const override { return hash; } virtual VMFrame* Invoke(VMFrame*) = 0; + virtual VMFrame* Invoke1(VMFrame*) = 0; virtual void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) = 0; virtual void MergeScopeInto( diff --git a/src/vmobjects/VMMethod.cpp b/src/vmobjects/VMMethod.cpp index 2fe74fc7..1d005ed1 100644 --- a/src/vmobjects/VMMethod.cpp +++ b/src/vmobjects/VMMethod.cpp @@ -132,6 +132,13 @@ VMFrame* VMMethod::Invoke(VMFrame* frame) { return frm; } +VMFrame* VMMethod::Invoke1(VMFrame* frame) { + VMFrame* frm = Interpreter::PushNewFrame(this); + frm->SetArgument(0, frame->Top()); + return frm; +} + + void VMMethod::SetHolder(VMClass* hld) { VMInvokable::SetHolder(hld); SetHolderAll(hld); diff --git a/src/vmobjects/VMMethod.h b/src/vmobjects/VMMethod.h index c4e96f4b..d43a502e 100644 --- a/src/vmobjects/VMMethod.h +++ b/src/vmobjects/VMMethod.h @@ -150,6 +150,7 @@ class VMMethod : public VMInvokable { } VMFrame* Invoke(VMFrame* frame) override; + VMFrame* Invoke1(VMFrame* frame) override; void MarkObjectAsInvalid() override { VMInvokable::MarkObjectAsInvalid(); diff --git a/src/vmobjects/VMPrimitive.h b/src/vmobjects/VMPrimitive.h index 4abd9dfe..5d35b565 100644 --- a/src/vmobjects/VMPrimitive.h +++ b/src/vmobjects/VMPrimitive.h @@ -58,6 +58,11 @@ class VMPrimitive : public VMInvokable { return nullptr; }; + VMFrame* Invoke1(VMFrame* frm) override { + prim.pointer(frm); + return nullptr; + }; + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; diff --git a/src/vmobjects/VMSafePrimitive.cpp b/src/vmobjects/VMSafePrimitive.cpp index 192bbb09..8457e2de 100644 --- a/src/vmobjects/VMSafePrimitive.cpp +++ b/src/vmobjects/VMSafePrimitive.cpp @@ -26,6 +26,13 @@ VMFrame* VMSafeUnaryPrimitive::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMSafeUnaryPrimitive::Invoke1(VMFrame* frame) { + vm_oop_t receiverObj = frame->Pop(); + + frame->Push(prim.pointer(receiverObj)); + return nullptr; +} + VMSafePrimitive* VMSafePrimitive::GetSafeBinary(VMSymbol* sig, BinaryPrim prim) { VMSafeBinaryPrimitive* p = @@ -41,6 +48,10 @@ VMFrame* VMSafeBinaryPrimitive::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMSafeBinaryPrimitive::Invoke1(VMFrame* frame) { + ErrorExit("Unary invoke on binary primitive"); +} + VMSafePrimitive* VMSafePrimitive::GetSafeTernary(VMSymbol* sig, TernaryPrim prim) { VMSafeTernaryPrimitive* p = @@ -57,6 +68,10 @@ VMFrame* VMSafeTernaryPrimitive::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMSafeTernaryPrimitive::Invoke1(VMFrame* frame) { + ErrorExit("Unary invoke on binary primitive"); +} + std::string VMSafePrimitive::AsDebugString() const { return "SafePrim(" + GetClass()->GetName()->GetStdString() + ">>#" + GetSignature()->GetStdString() + ")"; diff --git a/src/vmobjects/VMSafePrimitive.h b/src/vmobjects/VMSafePrimitive.h index e5f7a1da..f4493330 100644 --- a/src/vmobjects/VMSafePrimitive.h +++ b/src/vmobjects/VMSafePrimitive.h @@ -41,6 +41,7 @@ class VMSafeUnaryPrimitive : public VMSafePrimitive { } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; AbstractVMObject* CloneForMovingGC() const final; @@ -69,6 +70,7 @@ class VMSafeBinaryPrimitive : public VMSafePrimitive { } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; AbstractVMObject* CloneForMovingGC() const final; @@ -97,6 +99,7 @@ class VMSafeTernaryPrimitive : public VMSafePrimitive { } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; AbstractVMObject* CloneForMovingGC() const final; diff --git a/src/vmobjects/VMTrivialMethod.cpp b/src/vmobjects/VMTrivialMethod.cpp index 7013e7e6..d92f6e11 100644 --- a/src/vmobjects/VMTrivialMethod.cpp +++ b/src/vmobjects/VMTrivialMethod.cpp @@ -58,6 +58,13 @@ VMFrame* VMLiteralReturn::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMLiteralReturn::Invoke1(VMFrame* frame) { + assert(numberOfArguments == 1); + frame->Pop(); + frame->Push(load_ptr(literal)); + return nullptr; +} + AbstractVMObject* VMLiteralReturn::CloneForMovingGC() const { VMLiteralReturn* prim = new (GetHeap(), 0 ALLOC_MATURE) VMLiteralReturn(*this); @@ -93,6 +100,20 @@ VMFrame* VMGlobalReturn::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMGlobalReturn::Invoke1(VMFrame* frame) { + assert(numberOfArguments == 1); + frame->Pop(); + + vm_oop_t value = Universe::GetGlobal(load_ptr(globalName)); + if (value != nullptr) { + frame->Push(value); + } else { + Interpreter::SendUnknownGlobal(load_ptr(globalName)); + } + + return nullptr; +} + void VMGlobalReturn::InlineInto(MethodGenerationContext& mgenc, bool) { EmitPUSHGLOBAL(mgenc, load_ptr(globalName)); } @@ -134,6 +155,25 @@ VMFrame* VMGetter::Invoke(VMFrame* frame) { return nullptr; } +VMFrame* VMGetter::Invoke1(VMFrame* frame) { + assert(numberOfArguments == 1); + vm_oop_t self = frame->Pop(); + + assert(self != nullptr); + + vm_oop_t result; + if (unlikely(IS_TAGGED(self))) { + result = nullptr; + ErrorExit("Integers do not have fields!"); + } else { + result = ((VMObject*)self)->GetField(fieldIndex); + } + + frame->Push(result); + + return nullptr; +} + void VMGetter::InlineInto(MethodGenerationContext& mgenc, bool) { EmitPushFieldWithIndex(mgenc, fieldIndex); } @@ -176,7 +216,11 @@ VMFrame* VMSetter::Invoke(VMFrame* frame) { return nullptr; } -void VMSetter::InlineInto(MethodGenerationContext& mgenc, bool) { +VMFrame* VMSetter::Invoke1(VMFrame*) { + ErrorExit("VMSetter::Invoke1 should not be reachable"); +} + +void VMSetter::InlineInto(MethodGenerationContext&, bool) { ErrorExit("We don't currently support blocks for trivial setters"); } diff --git a/src/vmobjects/VMTrivialMethod.h b/src/vmobjects/VMTrivialMethod.h index ad2c9df1..49251eba 100644 --- a/src/vmobjects/VMTrivialMethod.h +++ b/src/vmobjects/VMTrivialMethod.h @@ -67,6 +67,8 @@ class VMLiteralReturn : public VMTrivialMethod { } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; @@ -106,6 +108,8 @@ class VMGlobalReturn : public VMTrivialMethod { } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; @@ -142,6 +146,8 @@ class VMGetter : public VMTrivialMethod { inline size_t GetObjectSize() const override { return sizeof(VMGetter); } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; @@ -177,6 +183,8 @@ class VMSetter : public VMTrivialMethod { inline size_t GetObjectSize() const override { return sizeof(VMSetter); } VMFrame* Invoke(VMFrame*) override; + VMFrame* Invoke1(VMFrame*) override; + void InlineInto(MethodGenerationContext& mgenc, bool mergeScope = true) final; From 96a152da3a0366ab9c9fe072d90fd667baa0d405 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 11 Aug 2024 16:34:12 +0100 Subject: [PATCH 3/5] Only update from and set global bytecode index for invoking bytecode methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only need to set the global BCI into the previous frame when calling into another method. And we don’t need to read it from the new frame, because it’s already set when setting the frame. Signed-off-by: Stefan Marr --- src/interpreter/Interpreter.cpp | 10 ++-------- src/interpreter/Interpreter.h | 2 ++ src/vmobjects/VMFrame.h | 12 ++++++------ src/vmobjects/VMMethod.cpp | 9 ++++++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 5134686b..9a5d31ee 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -147,11 +147,8 @@ void Interpreter::send(VMSymbol* signature, VMClass* receiverClass) { Universe::callStats[name].noPrimitiveCalls++; } #endif - // since an invokable is able to change/use the frame, we have to write - // cached values before, and read cached values after calling - GetFrame()->SetBytecodeIndex(bytecodeIndexGlobal); + invokable->Invoke(GetFrame()); - bytecodeIndexGlobal = GetFrame()->GetBytecodeIndex(); } else { triggerDoesNotUnderstand(signature); } @@ -372,7 +369,7 @@ void Interpreter::doUnarySend(long bytecodeIndex) { const int numOfArgs = 1; - vm_oop_t receiver = GetFrame()->GetStackElement(numOfArgs - 1); + vm_oop_t receiver = frame->GetStackElement(numOfArgs - 1); assert(IsValidObject(receiver)); // make sure it is really a class @@ -399,9 +396,6 @@ void Interpreter::doUnarySend(long bytecodeIndex) { Universe::callStats[name].noPrimitiveCalls++; } #endif - // since an invokable is able to change/use the frame, we have to write - // cached values before, and read cached values after calling - GetFrame()->SetBytecodeIndex(bytecodeIndexGlobal); invokable->Invoke1(GetFrame()); bytecodeIndexGlobal = GetFrame()->GetBytecodeIndex(); } else { diff --git a/src/interpreter/Interpreter.h b/src/interpreter/Interpreter.h index 879683aa..60052dd8 100644 --- a/src/interpreter/Interpreter.h +++ b/src/interpreter/Interpreter.h @@ -58,6 +58,8 @@ class Interpreter { static void SendUnknownGlobal(VMSymbol* globalName); + static inline long GetBytecodeIndex() { return bytecodeIndexGlobal; } + private: static vm_oop_t GetSelf(); diff --git a/src/vmobjects/VMFrame.h b/src/vmobjects/VMFrame.h index e5daeb6e..eaa3dc4f 100644 --- a/src/vmobjects/VMFrame.h +++ b/src/vmobjects/VMFrame.h @@ -33,6 +33,9 @@ class Universe; class VMFrame : public VMObject { friend class Universe; + friend class Interpreter; + friend class Shell; + friend class VMMethod; public: typedef GCFrame Stored; @@ -89,7 +92,6 @@ class VMFrame : public VMObject { } inline long GetBytecodeIndex() const; - inline void SetBytecodeIndex(long); inline vm_oop_t GetStackElement(long index) const { return load_ptr(stack_ptr[-index]); @@ -124,7 +126,7 @@ class VMFrame : public VMObject { void PrintStackTrace() const; long ArgumentStackIndex(long index) const; void CopyArgumentsFrom(VMFrame* frame); - + inline void SetArgument(size_t argIdx, vm_oop_t value) { store_ptr(arguments[argIdx], value); } @@ -158,6 +160,8 @@ class VMFrame : public VMObject { gc_oop_t* stack_ptr; static const long VMFrameNumberOfFields; + + inline void SetBytecodeIndex(long index) { bytecodeIndex = index; } }; bool VMFrame::HasContext() const { @@ -172,10 +176,6 @@ long VMFrame::GetBytecodeIndex() const { return bytecodeIndex; } -void VMFrame::SetBytecodeIndex(long index) { - bytecodeIndex = index; -} - bool VMFrame::IsBootstrapFrame() const { return !HasPreviousFrame(); } diff --git a/src/vmobjects/VMMethod.cpp b/src/vmobjects/VMMethod.cpp index 1d005ed1..0998034d 100644 --- a/src/vmobjects/VMMethod.cpp +++ b/src/vmobjects/VMMethod.cpp @@ -127,18 +127,25 @@ void VMMethod::SetCachedFrame(VMFrame* frame) { #endif VMFrame* VMMethod::Invoke(VMFrame* frame) { + // since an invokable is able to change/use the frame, we have to write + // cached values before, and read cached values after calling + frame->SetBytecodeIndex(Interpreter::GetBytecodeIndex()); + VMFrame* frm = Interpreter::PushNewFrame(this); frm->CopyArgumentsFrom(frame); return frm; } VMFrame* VMMethod::Invoke1(VMFrame* frame) { + // since an invokable is able to change/use the frame, we have to write + // cached values before, and read cached values after calling + frame->SetBytecodeIndex(Interpreter::GetBytecodeIndex()); + VMFrame* frm = Interpreter::PushNewFrame(this); frm->SetArgument(0, frame->Top()); return frm; } - void VMMethod::SetHolder(VMClass* hld) { VMInvokable::SetHolder(hld); SetHolderAll(hld); From 83f8f17307b3bb8a936ff230ed0a472122034a0a Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 11 Aug 2024 16:34:45 +0100 Subject: [PATCH 4/5] Add IsSafeUnaryPrim Signed-off-by: Stefan Marr --- src/vm/IsValidObject.cpp | 5 +++++ src/vm/IsValidObject.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/vm/IsValidObject.cpp b/src/vm/IsValidObject.cpp index 47e98d61..592160bc 100644 --- a/src/vm/IsValidObject.cpp +++ b/src/vm/IsValidObject.cpp @@ -159,6 +159,11 @@ bool IsSetter(vm_oop_t obj) { return get_vtable(AS_OBJ(obj)) == vt_setter; } +bool IsSafeUnaryPrim(vm_oop_t obj) { + assert(vt_safe_un_primitive != nullptr); + return get_vtable(AS_OBJ(obj)) == vt_safe_un_primitive; +} + void obtain_vtables_of_known_classes(VMSymbol* someValidSymbol) { // These objects are allocated on the heap. So, they will get GC'ed soon // enough. diff --git a/src/vm/IsValidObject.h b/src/vm/IsValidObject.h index 31da1cc7..6c42b0cf 100644 --- a/src/vm/IsValidObject.h +++ b/src/vm/IsValidObject.h @@ -12,6 +12,7 @@ bool IsLiteralReturn(vm_oop_t obj); bool IsGlobalReturn(vm_oop_t obj); bool IsGetter(vm_oop_t obj); bool IsSetter(vm_oop_t obj); +bool IsSafeUnaryPrim(vm_oop_t obj); void set_vt_to_null(); From 0d94d1ac74a82492bdc547ed0d47e3264ef13670 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 11 Aug 2024 16:51:43 +0100 Subject: [PATCH 5/5] =?UTF-8?q?For=20unary=20sends,=20don=E2=80=99t=20need?= =?UTF-8?q?=20to=20updated=20global=20bytecode=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because there’s nothing that will change it anymore. Because of inlining while loops. Signed-off-by: Stefan Marr --- src/interpreter/Interpreter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 9a5d31ee..8ec5b9c0 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -397,7 +397,6 @@ void Interpreter::doUnarySend(long bytecodeIndex) { } #endif invokable->Invoke1(GetFrame()); - bytecodeIndexGlobal = GetFrame()->GetBytecodeIndex(); } else { triggerDoesNotUnderstand(signature); }