diff --git a/src/checker/vm2.cpp b/src/checker/vm2.cpp index 64221af..05a56fc 100644 --- a/src/checker/vm2.cpp +++ b/src/checker/vm2.cpp @@ -42,12 +42,19 @@ namespace ts::vm2 { gcQueueRef[gcQueueRefIdx++] = typeRef; } - void gc(Type *type) { + inline void gcWithoutChildren(Type *type) { if (gcQueueIdx>=maxGcSize) { //garbage collect now gcFlush(); } // debug("gc ({}) {}", type->users, stringify(type)); + if (type->users > 0) return; + gcQueue[gcQueueIdx++] = type; + } + + void gc(Type *type) { + gcWithoutChildren(type); + switch (type->kind) { case TypeKind::Union: case TypeKind::Tuple: @@ -69,7 +76,6 @@ namespace ts::vm2 { break; } } - gcQueue[gcQueueIdx++] = type; } inline Type *use(Type *type) { @@ -105,17 +111,23 @@ namespace ts::vm2 { void drop(Type *type) { if (type == nullptr) return; +// debug("drop({}) {}", stringify(type), type->users); type->users--; -// debug("drop ({}) {}", type->users, stringify(type)); if (!type->users) { gc(type); } } + void gcStackAndFlush() { + gcStack(); + gcFlush(); + } + void gcStack() { for (unsigned int i = 0; i &module) { @@ -166,6 +178,10 @@ namespace ts::vm2 { report(DiagnosticMessage(message, activeSubroutine->ip)); } + inline void report(const string &message, unsigned int ip) { + report(DiagnosticMessage(message, ip)); + } + inline void pushFrame() { auto next = frames.push(); ///&frames[frameIdx++]; //important to reset necessary stuff, since we reuse @@ -427,6 +443,7 @@ namespace ts::vm2 { case OP::Halt: { // activeSubroutine = activeSubroutines.reset(); // frame = frames.reset(); +// gcStack(); // gcFlush(); return; } @@ -545,7 +562,7 @@ namespace ts::vm2 { push(types[0]); } else { auto result = allocate(TypeKind::Union); - TypeRef *current = useAsRef(types[0]); + TypeRef *current = (TypeRef *)(result->type = useAsRef(types[0])); for_each(++types.begin(), types.end(), [¤t](auto v) { current->next = useAsRef(v); current = current->next; @@ -715,6 +732,7 @@ namespace ts::vm2 { auto types = popFrame(); if (types.empty()) { item->type = nullptr; + push(item); break; } @@ -723,8 +741,9 @@ namespace ts::vm2 { //if type has no owner, we can steal its children if (type->users == 0) { item->type = type->type; - item->type = nullptr; - //set current to the end of the list + type->type = nullptr; + //since we stole its children, we want it to GC but without its children. their 'users' count belongs now to us. + gcWithoutChildren(type); } else { throw std::runtime_error("Can not merge used union"); } @@ -742,6 +761,8 @@ namespace ts::vm2 { if (v->users == 0) { current->next = (TypeRef *) v->type; v->type = nullptr; + //since we stole its children, we want it to GC but without its children. their 'users' count belongs now to us. + gcWithoutChildren(v); //set current to the end of the list while (current->next) current = current->next; } else { @@ -753,8 +774,7 @@ namespace ts::vm2 { } }); current->next = nullptr; - - stack[sp++] = item; + push(item); break; } case OP::TupleMember: { diff --git a/src/checker/vm2.h b/src/checker/vm2.h index 8f64bce..9e32347 100644 --- a/src/checker/vm2.h +++ b/src/checker/vm2.h @@ -93,6 +93,7 @@ namespace ts::vm2 { } Type *next() { + if (!current) return nullptr; auto t = current->type; current = current->next; return t; @@ -157,6 +158,7 @@ namespace ts::vm2 { void gc(Type *type); // Garbage collect whatever is left on the stack void gcStack(); + void gcStackAndFlush(); std::span popFrame(); diff --git a/src/tests/test_vm2.cpp b/src/tests/test_vm2.cpp index 3a2e896..c31657b 100644 --- a/src/tests/test_vm2.cpp +++ b/src/tests/test_vm2.cpp @@ -15,78 +15,6 @@ using namespace ts::vm2; using std::string; using std::string_view; -//struct StringLiteral { -// string_view text; -//}; - -//struct Type; -// -//struct PropertySignature { -// string_view name; -//// std::reference_wrapper type; -//}; -// -//struct ObjectLiteral { -// string_view name; -// std::array, 4> types; -//// std::vector types; -//}; -// -//struct Type { -// unsigned int ip; -// TypeKind type = TypeKind::Unknown; -// -// union T { -// struct StringLiteral stringLiteral; -// struct PropertySignature propertySignature; -//// struct ObjectLiteral objectLiteral; -// }; -// -// T v; -//}; - -template -struct Pool { - std::array pool; - unsigned int i{}; - - void clear() { - i = 0; - } - - T *allocate() { - return &pool[i++]; - } - -// std::vector pool; -// Pool () { -// pool.reserve(300); -// } -// T *make() { -// return &pool.emplace_back(); -// } - -// shared make() { -// return std::make_shared(); -// } -}; -// -//struct TypeMemoryPool { -// Pool never; -// Pool any; -// Pool literal; -// Pool objectLiteral; -// Pool propertySignature; -// -// void clear() { -// never.clear(); -// any.clear(); -// literal.clear(); -// objectLiteral.clear(); -// propertySignature.clear(); -// } -//}; - TEST(bench, size) { std::array a1; std::array a2; @@ -101,15 +29,6 @@ TEST(bench, size) { debug("std::array = {}", sizeof(std::array)); } -//TEST(vm2, reinterpret) { -// auto base = std::make_shared(); -//// printKind(base); -// printKind(std::reinterpret_pointer_cast(base)); -// auto never = std::make_shared(); -// printKind(never); -//// printKind(std::reinterpret_pointer_cast(never)); -//} - TEST(vm2, vm2Base1) { string code = R"( const v1: string = "abc"; @@ -118,6 +37,9 @@ const v2: number = 123; auto module = std::make_shared(ts::compile(code), "app.ts", ""); run(module); EXPECT_EQ(module->errors.size(), 0); + ts::vm2::gcStackAndFlush(); + //only v1, v2 + EXPECT_EQ(ts::vm2::pool.active, 2); ts::bench("first", 1000, [&] { module->clear(); @@ -141,9 +63,9 @@ const v3: a = false; module->printErrors(); EXPECT_EQ(module->errors.size(), 1); - ts::vm2::gcFlush(); - //only v1, v2, v3 cached value should live - EXPECT_EQ(ts::vm2::pool.active, 3); + ts::vm2::gcStackAndFlush(); + //only v1, v2, v3 plus for each 4 union (true | string | number) + EXPECT_EQ(ts::vm2::pool.active, 3 * 4); ts::bench("first", 1000, [&] { ts::vm2::clear(module); @@ -164,8 +86,8 @@ const v3: a = 'nope'; module->printErrors(); EXPECT_EQ(module->errors.size(), 1); - ts::vm2::gcFlush(); //only v1, v2, v3, plus 'yes' : 'no' subroutine cached value should live + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 5); ts::bench("first", 1000, [&] { @@ -222,7 +144,7 @@ const var1: a = false; EXPECT_EQ(ts::vm2::pool.active, 1); ts::vm2::clear(module); - ts::vm2::gcFlush(); + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 0); ts::bench("first", 1000, [&] { @@ -246,8 +168,7 @@ TEST(vm2, gcUnion) { auto module = std::make_shared(program.build(), "app.ts", ""); run(module); EXPECT_EQ(module->errors.size(), 0); - ts::vm2::gcStack(); - ts::vm2::gcFlush(); + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 0); } @@ -264,8 +185,7 @@ TEST(vm2, gcTuple) { auto module = std::make_shared(program.build(), "app.ts", ""); run(module); EXPECT_EQ(module->errors.size(), 0); - ts::vm2::gcStack(); - ts::vm2::gcFlush(); + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 0); } @@ -285,8 +205,7 @@ TEST(vm2, gcObject) { auto module = std::make_shared(program.build(), "app.ts", ""); run(module); EXPECT_EQ(module->errors.size(), 0); - ts::vm2::gcStack(); - ts::vm2::gcFlush(); + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 0); }