Skip to content

Commit

Permalink
Add support for trivial methods that read globals
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Marr <[email protected]>
  • Loading branch information
smarr committed Aug 5, 2024
1 parent 1186232 commit f88ec45
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 33 deletions.
16 changes: 16 additions & 0 deletions src/compiler/MethodGenerationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,22 @@ VMTrivialMethod* MethodGenerationContext::assembleLiteralReturn(
"returns a literal");
}

VMTrivialMethod* MethodGenerationContext::assembleGlobalReturn() {
if (bytecode.size() != (Bytecode::GetBytecodeLength(BC_PUSH_GLOBAL) +
Bytecode::GetBytecodeLength(BC_RETURN_LOCAL))) {
return nullptr;
}

if (literals.size() != 1) {
GetUniverse()->ErrorExit(
"Unexpected situation when trying to create trivial method that "
"reads a global. New Bytecode?");
}

VMSymbol* globalName = (VMSymbol*)literals.at(0);
return MakeGlobalReturn(signature, arguments, globalName);
}

VMPrimitive* MethodGenerationContext::AssemblePrimitive(bool classSide) {
return VMPrimitive::GetEmptyPrimitive(signature, classSide);
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/MethodGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class MethodGenerationContext {
private:
VMTrivialMethod* assembleTrivialMethod();
VMTrivialMethod* assembleLiteralReturn(uint8_t pushCandidate);
VMTrivialMethod* assembleGlobalReturn() { return nullptr; }
VMTrivialMethod* assembleGlobalReturn();
VMTrivialMethod* assembleFieldGetter(uint8_t pushCandidate) {
return nullptr;
}
Expand Down
29 changes: 16 additions & 13 deletions src/interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,22 +260,25 @@ void Interpreter::doPushGlobal(long bytecodeIndex) {
if (global != nullptr) {
GetFrame()->Push(global);
} else {
vm_oop_t arguments[] = {globalName};
vm_oop_t self = GetSelf();
SendUnknownGlobal(globalName);
}
}

// check if there is enough space on the stack for this unplanned Send
// unknowGlobal: needs 2 slots, one for "this" and one for the argument
long additionalStackSlots = 2 - GetFrame()->RemainingStackSize();
if (additionalStackSlots > 0) {
GetFrame()->SetBytecodeIndex(bytecodeIndexGlobal);
// copy current frame into a bigger one and replace the current
// frame
SetFrame(
VMFrame::EmergencyFrameFrom(GetFrame(), additionalStackSlots));
}
void Interpreter::SendUnknownGlobal(VMSymbol* globalName) {
vm_oop_t arguments[] = {globalName};
vm_oop_t self = GetSelf();

AS_OBJ(self)->Send(this, unknownGlobal, arguments, 1);
// check if there is enough space on the stack for this unplanned Send
// unknowGlobal: needs 2 slots, one for "this" and one for the argument
long additionalStackSlots = 2 - GetFrame()->RemainingStackSize();
if (additionalStackSlots > 0) {
GetFrame()->SetBytecodeIndex(bytecodeIndexGlobal);
// copy current frame into a bigger one and replace the current
// frame
SetFrame(VMFrame::EmergencyFrameFrom(GetFrame(), additionalStackSlots));
}

AS_OBJ(self)->Send(this, unknownGlobal, arguments, 1);
}

void Interpreter::doPop() {
Expand Down
2 changes: 2 additions & 0 deletions src/interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class Interpreter {
uint8_t* GetBytecodes() const;
void WalkGlobals(walk_heap_fn);

void SendUnknownGlobal(VMSymbol* globalName);

private:
vm_oop_t GetSelf() const;

Expand Down
51 changes: 33 additions & 18 deletions src/unitTests/TrivialMethodTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,29 +111,44 @@ void TrivialMethodTest::testNonTrivialLiteralReturn() {
nonTrivialLiteralReturn("nil");
}

/*
void TrivialMethodTest::globalReturn(std::string source) {
std::string s = "test = ( ^ " + source + " )";
methodToBytecode(s.data());
VMInvokable* m = _mgenc->Assemble();

@pytest.mark.parametrize("source", ["Nil", "system", "MyClassFooBar"])
def test_global_return(mgenc, source):
body_or_none = parse_method(mgenc, "test = ( ^ " + source + " )")
m = mgenc.assemble(body_or_none)
assert isinstance(m, GlobalRead)
std::string expected = "Expected to be trivial: " + s;
bool result = IsGlobalReturn(m);
CPPUNIT_ASSERT_MESSAGE(expected.data(), result);

tearDown();
}

def test_non_trivial_global_return(mgenc):
body_or_none = parse_method(mgenc, "test = ( #foo. ^ system )")
m = mgenc.assemble(body_or_none)
assert isinstance(m, AstMethod) or isinstance(m, BcMethod)
void TrivialMethodTest::testGlobalReturn() {
globalReturn("Nil");
globalReturn("system");
globalReturn("MyClassFooBar");
}

void TrivialMethodTest::testNonTrivialGlobalReturn() {
methodToBytecode("test = ( #foo. ^ system )");
VMInvokable* m = _mgenc->Assemble();

def test_unknown_global_in_block(bgenc):
"""
In PySOM we can actually support this, in TruffleSOM we can't
because of the difference in frame format.
"""
body_or_none = parse_block(bgenc, "[ UnknownGlobalSSSS ]")
m = bgenc.assemble(body_or_none)
assert isinstance(m, GlobalRead)
std::string expected =
"Expected to be non-trivial: test = ( #foo. ^ system )";
bool result = !IsGlobalReturn(m);
CPPUNIT_ASSERT_MESSAGE(expected.data(), result);
}

void TrivialMethodTest::testUnknownGlobalInBlock() {
blockToBytecode("[ UnknownGlobalSSSS ]");
VMInvokable* m = _bgenc->Assemble();

std::string expected = "Expected to be trivial: [ UnknownGlobalSSSS ]";
bool result = IsGlobalReturn(m);
CPPUNIT_ASSERT_MESSAGE(expected.data(), result);
}

/*
def test_field_getter_0(cgenc, mgenc):
add_field(cgenc, "field")
body_or_none = parse_method(mgenc, "test = ( ^ field )")
Expand Down
9 changes: 9 additions & 0 deletions src/unitTests/TrivialMethodTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class TrivialMethodTest : public TestWithParsing {
CPPUNIT_TEST(testLiteralNoReturn);
CPPUNIT_TEST(testBlockLiteralReturn);
CPPUNIT_TEST(testNonTrivialLiteralReturn);
CPPUNIT_TEST(testGlobalReturn);
CPPUNIT_TEST(testNonTrivialGlobalReturn);
CPPUNIT_TEST(testUnknownGlobalInBlock);
CPPUNIT_TEST_SUITE_END();

private:
Expand All @@ -24,4 +27,10 @@ class TrivialMethodTest : public TestWithParsing {

void testNonTrivialLiteralReturn();
void nonTrivialLiteralReturn(std::string source);

void testGlobalReturn();
void globalReturn(std::string source);

void testNonTrivialGlobalReturn();
void testUnknownGlobalInBlock();
};
14 changes: 13 additions & 1 deletion src/vm/IsValidObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ void* vt_safe_un_primitive;
void* vt_safe_bin_primitive;
void* vt_safe_ter_primitive;
void* vt_literal_return;
void* vt_global_return;
void* vt_string;
void* vt_symbol;

Expand Down Expand Up @@ -69,7 +70,8 @@ bool IsValidObject(vm_oop_t obj) {
vt == vt_integer || vt == vt_method || vt == vt_object ||
vt == vt_primitive || vt == vt_safe_un_primitive ||
vt == vt_safe_bin_primitive || vt == vt_safe_ter_primitive ||
vt == vt_string || vt == vt_symbol || vt == vt_literal_return;
vt == vt_string || vt == vt_symbol || vt == vt_literal_return ||
vt == vt_global_return;
if (!b) {
assert(b && "Expected vtable to be one of the known ones.");
return false;
Expand Down Expand Up @@ -109,6 +111,7 @@ void set_vt_to_null() {
vt_safe_bin_primitive = nullptr;
vt_safe_ter_primitive = nullptr;
vt_literal_return = nullptr;
vt_global_return = nullptr;
vt_string = nullptr;
vt_symbol = nullptr;
}
Expand Down Expand Up @@ -137,6 +140,11 @@ bool IsLiteralReturn(vm_oop_t obj) {
return get_vtable(AS_OBJ(obj)) == vt_literal_return;
}

bool IsGlobalReturn(vm_oop_t obj) {
assert(vt_global_return != nullptr);
return get_vtable(AS_OBJ(obj)) == vt_global_return;
}

void obtain_vtables_of_known_classes(VMSymbol* someValidSymbol) {
// These objects are allocated on the heap. So, they will get GC'ed soon
// enough.
Expand Down Expand Up @@ -187,6 +195,10 @@ void obtain_vtables_of_known_classes(VMSymbol* someValidSymbol) {
VMLiteralReturn(someValidSymbol, v, someValidSymbol);
vt_literal_return = get_vtable(lr);

VMGlobalReturn* gr = new (GetHeap<HEAP_CLS>(), 0)
VMGlobalReturn(someValidSymbol, v, someValidSymbol);
vt_global_return = get_vtable(gr);

VMString* str =
new (GetHeap<HEAP_CLS>(), PADDED_SIZE(1)) VMString(0, nullptr);
vt_string = get_vtable(str);
Expand Down
1 change: 1 addition & 0 deletions src/vm/IsValidObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bool IsVMInteger(vm_oop_t obj);
bool IsVMMethod(vm_oop_t obj);
bool IsVMSymbol(vm_oop_t obj);
bool IsLiteralReturn(vm_oop_t obj);
bool IsGlobalReturn(vm_oop_t obj);

void set_vt_to_null();

Expand Down
2 changes: 2 additions & 0 deletions src/vmobjects/ObjectFormats.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class VMSafeBinaryPrimitive;
class VMSafeTernaryPrimitive;
class VMTrivialMethod;
class VMLiteralReturn;
class VMGlobalReturn;
class VMString;
class VMSymbol;

Expand Down Expand Up @@ -154,6 +155,7 @@ class GCSafeBinaryPrimitive : public GCSafePrimitive { public: typedef VMSafeBi
class GCSafeTernaryPrimitive : public GCSafePrimitive { public: typedef VMSafeTernaryPrimitive Loaded; };
class GCTrivialMethod : public GCInvokable { public: typedef VMTrivialMethod Loaded; };
class GCLiteralReturn : public GCTrivialMethod { public: typedef VMLiteralReturn Loaded; };
class GCGlobalReturn : public GCTrivialMethod { public: typedef VMGlobalReturn Loaded; };
class GCString : public GCAbstractObject { public: typedef VMString Loaded; };
class GCSymbol : public GCString { public: typedef VMSymbol Loaded; };
// clang-format on
Expand Down
44 changes: 44 additions & 0 deletions src/vmobjects/VMTrivialMethod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "../memory/Heap.h"
#include "../misc/defs.h"
#include "../vm/LogAllocation.h"
#include "../vm/Universe.h"
#include "AbstractObject.h"
#include "ObjectFormats.h"
#include "VMFrame.h"
Expand All @@ -21,6 +22,14 @@ VMTrivialMethod* MakeLiteralReturn(VMSymbol* sig, vector<Variable>& arguments,
return result;
}

VMTrivialMethod* MakeGlobalReturn(VMSymbol* sig, vector<Variable>& arguments,
VMSymbol* globalName) {
VMGlobalReturn* result =
new (GetHeap<HEAP_CLS>(), 0) VMGlobalReturn(sig, arguments, globalName);
LOG_ALLOCATION("VMGlobalReturn", result->GetObjectSize());
return result;
}

VMFrame* VMLiteralReturn::Invoke(Interpreter*, VMFrame* frame) {
for (int i = 0; i < numberOfArguments; i += 1) {
frame->Pop();
Expand Down Expand Up @@ -48,3 +57,38 @@ void VMLiteralReturn::WalkObjects(walk_heap_fn walk) {
void VMLiteralReturn::InlineInto(MethodGenerationContext& mgenc, bool) {
EmitPUSHCONSTANT(mgenc, load_ptr(literal));
}

VMFrame* VMGlobalReturn::Invoke(Interpreter* interpreter, VMFrame* frame) {
for (int i = 0; i < numberOfArguments; i += 1) {
frame->Pop();
}

vm_oop_t value = GetUniverse()->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));
}

void VMGlobalReturn::WalkObjects(walk_heap_fn walk) {
VMInvokable::WalkObjects(walk);
globalName = (GCSymbol*)walk(globalName);
}

std::string VMGlobalReturn::AsDebugString() const {
return "VMGlobalReturn(" + AS_OBJ(load_ptr(globalName))->AsDebugString() +
")";
}

AbstractVMObject* VMGlobalReturn::CloneForMovingGC() const {
VMGlobalReturn* prim =
new (GetHeap<HEAP_CLS>(), 0 ALLOC_MATURE) VMGlobalReturn(*this);
return prim;
}
45 changes: 45 additions & 0 deletions src/vmobjects/VMTrivialMethod.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

#include "../compiler/MethodGenerationContext.h"
#include "../vm/Globals.h"
#include "ObjectFormats.h"
#include "Signature.h"
#include "VMInvokable.h"
#include "VMSymbol.h"

class VMTrivialMethod : public VMInvokable {
public:
Expand Down Expand Up @@ -40,6 +42,8 @@ class VMTrivialMethod : public VMInvokable {

VMTrivialMethod* MakeLiteralReturn(VMSymbol* sig, vector<Variable>& arguments,
vm_oop_t literal);
VMTrivialMethod* MakeGlobalReturn(VMSymbol* sig, vector<Variable>& arguments,
VMSymbol* globalName);

class VMLiteralReturn : public VMTrivialMethod {
public:
Expand Down Expand Up @@ -79,3 +83,44 @@ class VMLiteralReturn : public VMTrivialMethod {
gc_oop_t literal;
int numberOfArguments;
};

class VMGlobalReturn : public VMTrivialMethod {
public:
typedef GCGlobalReturn Stored;

VMGlobalReturn(VMSymbol* sig, vector<Variable>& arguments,
VMSymbol* globalName)
: VMTrivialMethod(sig, arguments),
globalName(store_with_separate_barrier(globalName)),
numberOfArguments(Signature::GetNumberOfArguments(sig)) {
write_barrier(this, sig);
write_barrier(this, globalName);
}

inline size_t GetObjectSize() const override {
return sizeof(VMLiteralReturn);
}

VMFrame* Invoke(Interpreter*, VMFrame*) override;
void InlineInto(MethodGenerationContext& mgenc,
bool mergeScope = true) final;

AbstractVMObject* CloneForMovingGC() const final;

void MarkObjectAsInvalid() final {
VMTrivialMethod::MarkObjectAsInvalid();
globalName = (GCSymbol*)INVALID_GC_POINTER;
}

void WalkObjects(walk_heap_fn) override;

bool IsMarkedInvalid() const final {
return globalName == (GCSymbol*)INVALID_GC_POINTER;
}

std::string AsDebugString() const final;

private:
GCSymbol* globalName;
int numberOfArguments;
};

0 comments on commit f88ec45

Please sign in to comment.