From 51a939ac451aaac8c6daba48109d9c35f932f09d Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Wed, 11 Mar 2026 16:53:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E7=BC=96=E8=AF=91=E5=99=A8=E5=92=8C?= =?UTF-8?q?=E8=99=9A=E6=8B=9F=E6=9C=BA=E8=BF=9B=E8=A1=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E6=94=AF=E6=8C=81=E9=97=AD=E5=8C=85=E5=92=8C?= =?UTF-8?q?=E5=9E=83=E5=9C=BE=E5=9B=9E=E6=94=B6=E5=8A=9F=E8=83=BD=20-=20?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BA=86=E4=B8=8D=E5=86=8D=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E7=BB=93=E6=9E=84=EF=BC=8C=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BA=86=E7=BC=96=E8=AF=91=E5=99=A8=E4=BB=A5=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E9=97=AD=E5=8C=85=E8=AF=AD=E4=B9=89=E3=80=82?= =?UTF-8?q?=20-=20=E6=94=B9=E8=BF=9B=E4=BA=86=20Compiler=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E5=85=B6=E8=83=BD=E5=A4=9F=E7=94=9F=E6=88=90=E5=B8=A6=E6=9C=89?= =?UTF-8?q?=E6=BA=90=E4=BD=8D=E7=BD=AE=E8=B7=9F=E8=B8=AA=E7=9A=84=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E3=80=82=20-=20=E5=9C=A8=20FunctionObject=20=E5=92=8C?= =?UTF-8?q?=20VM=20=E4=B8=AD=E5=BC=95=E5=85=A5=E4=BA=86=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=E5=9F=9F=E5=8F=98=E9=87=8F=E7=AE=A1=E7=90=86=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E9=97=AD=E5=8C=85=E3=80=82?= =?UTF-8?q?=20-=20=E5=AE=9E=E7=8E=B0=E4=BA=86=E4=BD=BF=E7=94=A8=E6=A0=87?= =?UTF-8?q?=E8=AE=B0-=E6=89=AB=E6=8F=8F(Mark-And-Sweep)=20(Tri-Color=20tra?= =?UTF-8?q?cing)=20=E7=AE=97=E6=B3=95=E7=9A=84=E5=9E=83=E5=9C=BE=E5=9B=9E?= =?UTF-8?q?=E6=94=B6=E6=9C=BA=E5=88=B6=EF=BC=8C=E5=8C=85=E6=8B=AC=E5=AF=B9?= =?UTF-8?q?=E4=BD=9C=E7=94=A8=E5=9F=9F=E5=8F=98=E9=87=8F=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E3=80=82=20-=20=E5=9C=A8=20VM=20=E4=B8=AD=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E5=87=BD=E6=95=B0=E5=8A=A0=E8=BD=BD=E5=92=8C?= =?UTF-8?q?=E4=BD=9C=E7=94=A8=E5=9F=9F=E5=8F=98=E9=87=8F=E6=A3=80=E7=B4=A2?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=E3=80=82=20-=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BA=86=E5=AF=B9=E8=B1=A1=E6=A8=A1=E5=9E=8B=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=E5=BC=95=E5=85=A5=20InstanceObject=20=E5=B9=B6?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=86=85=E5=AD=98=E7=AE=A1=E7=90=86=E3=80=82?= =?UTF-8?q?=20-=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E7=94=A8=E4=BA=8E=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E7=9A=84=E5=85=A8=E5=B1=80=E5=8F=98=E9=87=8F=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ExampleCodes/SpeedTest/fib.lua | 11 ++ src/Ast/Stmt/FnDefStmt.hpp | 4 + src/Bytecode/Bytecode.hpp | 28 +++ src/Compiler/Compiler.cpp | 47 ++++-- src/Compiler/Compiler.hpp | 19 +-- src/Compiler/ExprCompiler.cpp | 63 ++++--- src/Compiler/StmtCompiler.cpp | 86 ++++++++-- src/Object/FunctionObject.hpp | 32 +++- src/Object/InstanceObject.hpp | 16 ++ src/Object/Object.cpp | 32 +++- src/Object/Object.hpp | 3 +- src/Object/ObjectBase.hpp | 26 +-- src/Object/StringObject.hpp | 5 - src/Object/StructObject.hpp | 3 - src/Sema/Analyzer.cpp | 46 ++++- src/Sema/Type.cpp | 2 +- src/Sema/Type.hpp | 2 +- src/VM/VM.cpp | 110 +++++++++++- src/VM/VM.hpp | 300 ++++++++++++++++++++++++++++++++- src/main.cpp | 3 +- 20 files changed, 712 insertions(+), 126 deletions(-) create mode 100644 ExampleCodes/SpeedTest/fib.lua create mode 100644 src/Object/InstanceObject.hpp diff --git a/ExampleCodes/SpeedTest/fib.lua b/ExampleCodes/SpeedTest/fib.lua new file mode 100644 index 0000000..7183087 --- /dev/null +++ b/ExampleCodes/SpeedTest/fib.lua @@ -0,0 +1,11 @@ +function fib(n) + if (n <= 1) then + return n + else + return fib(n - 1) + fib(n - 2) end +end + +local start = os.clock() +local result = fib(30) +local endt = os.clock() +print(result, " cost: ", (endt - start) * 1000, "ms") \ No newline at end of file diff --git a/src/Ast/Stmt/FnDefStmt.hpp b/src/Ast/Stmt/FnDefStmt.hpp index c6662b3..d5ab75f 100644 --- a/src/Ast/Stmt/FnDefStmt.hpp +++ b/src/Ast/Stmt/FnDefStmt.hpp @@ -6,6 +6,7 @@ #pragma once #include #include +#include namespace Fig { @@ -33,6 +34,9 @@ namespace Fig Type resolvedReturnType; Symbol *resolvedSymbol = nullptr; // 连接物理符号 + int protoIndex = -1; // 在CompiledModule扁平化protos的下标 + DynArray upvalues; + FnDefStmt() { type = AstType::FnDefStmt; } FnDefStmt(bool _p, String _n, DynArray _pa, TypeExpr *_rt, BlockStmt *_b, SourceLocation _loc) : name(std::move(_n)), params(std::move(_pa)), returnTypeSpecifier(_rt), body(_b) diff --git a/src/Bytecode/Bytecode.hpp b/src/Bytecode/Bytecode.hpp index 0909518..5df9a5d 100644 --- a/src/Bytecode/Bytecode.hpp +++ b/src/Bytecode/Bytecode.hpp @@ -5,8 +5,13 @@ #pragma once +#include +#include +#include + #include + namespace Fig { using Instruction = std::uint32_t; @@ -80,4 +85,27 @@ namespace Fig | (static_cast(static_cast(sbx)) << 16); } } // namespace Op + + struct UpvalueInfo + { + uint8_t index; + bool isLocal; + }; + + struct Proto + { + String name; + DynArray code; + DynArray locations; + DynArray constants; + DynArray upvalues; + uint8_t maxRegisters = 0; + uint8_t numParams = 0; + }; + + struct CompiledModule + { + DynArray protos; + }; + } // namespace Fig diff --git a/src/Compiler/Compiler.cpp b/src/Compiler/Compiler.cpp index 3b41ee6..bcfe9fa 100644 --- a/src/Compiler/Compiler.cpp +++ b/src/Compiler/Compiler.cpp @@ -3,16 +3,20 @@ @brief 编译器主逻辑实现:物理 Bootstrapper 与双步扫描 */ -#include #include +#include namespace Fig { Result Compiler::Compile(Program *program) { - module = new CompiledModule(); + module = new CompiledModule(); + if (program->nodes.empty()) + { + return module; + } - // 1. 预留 Protos[0] 给 Bootstrapper + // 预留 Protos[0] 给 Bootstrapper Proto *bootProto = new Proto(); bootProto->name = "[bootstrapper]"; module->protos.push_back(bootProto); @@ -20,17 +24,22 @@ namespace Fig int initIdx = -1; int mainIdx = -1; - // 2. 第一步:预扫描顶层函数,锁定物理索引 + SourceLocation *mainFnLoc = nullptr; + SourceLocation *initFnLoc = nullptr; + + // 预扫描顶层函数 for (auto *stmt : program->nodes) { if (stmt->type == AstType::FnDefStmt) { auto *f = static_cast(stmt); int idx = (int) module->protos.size(); + Proto *p = new Proto(); p->name = f->name; p->numParams = (uint8_t) f->params.size(); p->maxRegisters = p->numParams; + f->protoIndex = idx; module->protos.push_back(p); @@ -41,13 +50,19 @@ namespace Fig } if (f->name == "init") - initIdx = idx; + { + initIdx = idx; + initFnLoc = &stmt->location; + } if (f->name == "main") - mainIdx = idx; + { + mainIdx = idx; + mainFnLoc = &stmt->location; + } } } - // 3. 第二步:在 Bootstrapper 环境中编译所有语句 + // Bootstrapper 中编译所有语句 FuncState bootState(bootProto, nullptr); current = &bootState; @@ -60,18 +75,18 @@ namespace Fig } } - // 4. 发射 Bootstrapper 引导指令 + // 发射 Bootstrapper 引导指令 if (initIdx != -1) { - emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0)); + emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0), initFnLoc); } if (mainIdx != -1) { - emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0)); + emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0), mainFnLoc); } - emit(Op::iAsBx(OpCode::Exit, 0, 0)); + emit(Op::iAsBx(OpCode::Exit, 0, 0), &program->nodes.back()->location); return module; } @@ -89,7 +104,8 @@ namespace Fig { if (current->freereg >= MAX_REGISTERS) { - return std::unexpected(Error(ErrorType::RegisterOverflow, "too many registers", "", loc)); + return std::unexpected( + Error(ErrorType::RegisterOverflow, "too many registers", "", loc)); } Register reg = current->freereg++; @@ -112,14 +128,15 @@ namespace Fig { if (current->constantMap.contains(val)) return current->constantMap[val]; - int idx = (int) current->proto->constants.size(); + int idx = (int) current->proto->constants.size(); current->proto->constants.push_back(val); current->constantMap[val] = idx; return idx; } - void Compiler::emit(Instruction instr) + void Compiler::emit(Instruction inst, SourceLocation *loc) { - current->proto->code.push_back(instr); + current->proto->code.push_back(inst); + current->proto->locations.push_back(loc); } } // namespace Fig diff --git a/src/Compiler/Compiler.hpp b/src/Compiler/Compiler.hpp index e9a5959..237e167 100644 --- a/src/Compiler/Compiler.hpp +++ b/src/Compiler/Compiler.hpp @@ -14,23 +14,6 @@ namespace Fig { using Register = std::uint8_t; - struct UpvalueInfo { uint8_t index; bool isLocal; }; - - struct Proto - { - String name; - DynArray code; - DynArray constants; - DynArray upvalues; - uint8_t maxRegisters = 0; - uint8_t numParams = 0; - }; - - struct CompiledModule - { - DynArray protos; - }; - class Compiler { private: @@ -60,7 +43,7 @@ namespace Fig void freeReg(Register count = 1); int addConstant(Value val); - void emit(Instruction instr); + void emit(Instruction inst, SourceLocation *loc); Result compileStmt(Stmt *stmt); Result compileExpr(Expr *expr, Register target = NO_REG); diff --git a/src/Compiler/ExprCompiler.cpp b/src/Compiler/ExprCompiler.cpp index 90ecac3..090e84f 100644 --- a/src/Compiler/ExprCompiler.cpp +++ b/src/Compiler/ExprCompiler.cpp @@ -94,24 +94,24 @@ namespace Fig parsePhysicalNumber(manager.GetSub(tok.index, tok.length), l->location); if (!vRes) return std::unexpected(vRes.error()); - emit(Op::iABx(OpCode::LoadK, r, static_cast(addConstant(*vRes)))); + emit(Op::iABx(OpCode::LoadK, r, static_cast(addConstant(*vRes))), &l->location); } else if (tok.type == TokenType::LiteralString) { int kIdx = addConstant(Value::GetNullInstance()); // TODO: String 支持 - emit(Op::iABx(OpCode::LoadK, r, static_cast(kIdx))); + emit(Op::iABx(OpCode::LoadK, r, static_cast(kIdx)), &l->location); } else if (tok.type == TokenType::LiteralNull) { - emit(Op::iABC(OpCode::LoadNull, r, 0, 0)); + emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->location); } else if (tok.type == TokenType::LiteralTrue) { - emit(Op::iABC(OpCode::LoadTrue, r, 0, 0)); + emit(Op::iABC(OpCode::LoadTrue, r, 0, 0), &l->location); } else if (tok.type == TokenType::LiteralFalse) { - emit(Op::iABC(OpCode::LoadFalse, r, 0, 0)); + emit(Op::iABC(OpCode::LoadFalse, r, 0, 0), &l->location); } return r; } @@ -129,7 +129,7 @@ namespace Fig // 仅在被强制指定目标(如参数装填)时发射搬运指令 if (target != sym->index) { - emit(Op::iABx(OpCode::Mov, target, static_cast(sym->index))); + emit(Op::iABx(OpCode::Mov, target, static_cast(sym->index)), &i->location); } return target; } @@ -137,12 +137,12 @@ namespace Fig Register r = (target == NO_REG) ? *allocateReg(i->location) : target; if (sym->location == SymbolLocation::Upvalue) { - emit(Op::iABC(OpCode::GetUpval, r, static_cast(sym->index), 0)); + emit(Op::iABC(OpCode::GetUpval, r, static_cast(sym->index), 0), &i->location); } else if (sym->location == SymbolLocation::Global) { int gId = getGlobalID(i->name); - emit(Op::iABx(OpCode::GetGlobal, r, static_cast(gId))); + emit(Op::iABx(OpCode::GetGlobal, r, static_cast(gId)), &i->location); } return r; } @@ -167,30 +167,43 @@ namespace Fig return std::unexpected(res.error()); } + bool isGlobalFastCall = false; if (c->callee->type == AstType::IdentiExpr) { - // 静态去虚化:编译期直接跳板 - auto *id = static_cast(c->callee); - int protoIdx = id->resolvedSymbol->index; - emit(Op::iABC(OpCode::FastCall, - static_cast(protoIdx), - baseReg, - static_cast(c->args.args.size()))); + auto *id = static_cast(c->callee); + // 只有在全局区的函数,才能使用 FastCall + if (id->resolvedSymbol->location == SymbolLocation::Global) + { + isGlobalFastCall = true; + int protoIdx = id->resolvedSymbol->index; + emit(Op::iABC(OpCode::FastCall, + static_cast(protoIdx), + baseReg, + static_cast(c->args.args.size())), + &c->location); + } } - else + + if (!isGlobalFastCall) { // 动态闭包调用 + // 先获取闭包对象所在的物理寄存器 auto r_fn = compileExpr(c->callee); if (!r_fn) return std::unexpected(r_fn.error()); - emit(Op::iABC( - OpCode::Call, *r_fn, baseReg, static_cast(c->args.args.size()))); + + // 使用动态 Call 指令,RA 是指向堆闭包的寄存器 + emit(Op::iABC(OpCode::Call, + *r_fn, + baseReg, + static_cast(c->args.args.size())), + &c->location); } - // 回滚水位线:彻底释放传参时的临时占用 + // 回滚水位线, 释放传参时的临时占用 current->freereg = mark; - // 目标对齐:若 target 未指定,allocateReg 将自然复用 baseReg,实现零开销回写 + // 目若 target 未指定,allocateReg 将复用 baseReg,实现零开销回写 Register r_dest; if (target == NO_REG) @@ -207,7 +220,7 @@ namespace Fig if (r_dest != baseReg) { - emit(Op::iABx(OpCode::Mov, r_dest, baseReg)); + emit(Op::iABx(OpCode::Mov, r_dest, baseReg), &c->location); } return r_dest; @@ -227,18 +240,18 @@ namespace Fig Symbol *sym = lid->resolvedSymbol; if (sym->location == SymbolLocation::Local) { - emit(Op::iABx(OpCode::Mov, static_cast(sym->index), *r_val)); + emit(Op::iABx(OpCode::Mov, static_cast(sym->index), *r_val), &lid->location); } else if (sym->location == SymbolLocation::Upvalue) { emit(Op::iABC( - OpCode::SetUpval, *r_val, static_cast(sym->index), 0)); + OpCode::SetUpval, *r_val, static_cast(sym->index), 0), &lid->location); } else { emit(Op::iABx(OpCode::SetGlobal, *r_val, - static_cast(getGlobalID(lid->name)))); + static_cast(getGlobalID(lid->name))), &lid->location); } } return r_val; @@ -300,7 +313,7 @@ namespace Fig r_d = target; } - emit(Op::iABC(op, r_d, *r_l, *r_r)); + emit(Op::iABC(op, r_d, *r_l, *r_r), &in->location); return r_d; } diff --git a/src/Compiler/StmtCompiler.cpp b/src/Compiler/StmtCompiler.cpp index 0b82d78..6e1ace7 100644 --- a/src/Compiler/StmtCompiler.cpp +++ b/src/Compiler/StmtCompiler.cpp @@ -9,7 +9,6 @@ #include #include - namespace Fig { Result Compiler::compileStmt(Stmt *stmt) @@ -41,8 +40,10 @@ namespace Fig if (!regRes) return std::unexpected(regRes.error()); - emit(Op::iABx( - OpCode::SetGlobal, *regRes, static_cast(getGlobalID(v->name)))); + emit(Op::iABx(OpCode::SetGlobal, + *regRes, + static_cast(getGlobalID(v->name))), + &v->location); current->freereg = mark; // 释放初始化表达式的临时占用 } else @@ -68,8 +69,21 @@ namespace Fig case AstType::FnDefStmt: { auto *f = static_cast(stmt); - // 物理连线:对接 Compile() 第一阶段预分配的 Proto - Proto *p = module->protos[f->resolvedSymbol->index]; + if (f->protoIndex == -1) // 闭包环境没有被扫到 + { + Proto *newProto = new Proto(); + newProto->name = f->name; + newProto->numParams = static_cast(f->params.size()); + newProto->maxRegisters = newProto->numParams; // 同步水位线 + + f->protoIndex = static_cast(module->protos.size()); + module->protos.push_back(newProto); + } + + // 获取静态原型 (flat protos) + Proto *p = module->protos[f->protoIndex]; + + p->upvalues = f->upvalues; FuncState fs(p, current); FuncState *old = current; @@ -79,13 +93,47 @@ namespace Fig if (!res) return res; - // 窥孔拦截:防死代码污染 if (p->code.empty() || static_cast(p->code.back() & 0xFF) != OpCode::Return) { - emit(Op::iABC(OpCode::Return, 0, 0, 0)); + emit(Op::iABC(OpCode::Return, 0, 0, 0), &f->location); } current = old; + + // 如果是局部闭包,在当前栈帧分配寄存器并生成 LoadFn + if (f->resolvedSymbol->location == SymbolLocation::Local) + { + Register targetReg = static_cast(f->resolvedSymbol->index); + + while (current->freereg <= targetReg) + { + auto allocRes = allocateReg(f->location); + if (!allocRes) + return std::unexpected(allocRes.error()); + } + + // 生成 LoadFn: RA = 目标寄存器, Bx = Proto 在 module->protos 中的绝对索引 + emit(Op::iABx(OpCode::LoadFn, targetReg, static_cast(f->protoIndex)), + &f->location); + } + else if (f->resolvedSymbol->location == SymbolLocation::Global) + { + auto result = allocateReg(f->location); + if (!result) + { + return std::unexpected(result.error()); + } + + Register r = *result; + emit(Op::iABx(OpCode::LoadFn, r, static_cast(f->protoIndex)), + &f->location); + + int gId = getGlobalID(f->name); + emit(Op::iABx(OpCode::SetGlobal, r, static_cast(gId)), + &f->location); + + freeReg(); + } break; } @@ -99,13 +147,13 @@ namespace Fig return std::unexpected(r_cond.error()); int jmpToNext = static_cast(current->proto->code.size()); - emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0)); + emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &i->location); current->freereg = mark; // 回收条件表达式临时槽位 if (auto r = compileStmt(i->consequent); !r) return r; exitJumps.push_back(static_cast(current->proto->code.size())); - emit(Op::iAsBx(OpCode::Jmp, 0, 0)); + emit(Op::iAsBx(OpCode::Jmp, 0, 0), &i->location); int targetIdx = static_cast(current->proto->code.size()); current->proto->code[jmpToNext] = Op::iAsBx( @@ -119,17 +167,22 @@ namespace Fig return std::unexpected(ec.error()); int nextElif = static_cast(current->proto->code.size()); - emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0)); + emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0), &elif->location); current->freereg = elifMark; // 回收 elif 临时槽位 if (auto r = compileStmt(elif->consequent); !r) return r; exitJumps.push_back(static_cast(current->proto->code.size())); - emit(Op::iAsBx(OpCode::Jmp, 0, 0)); + emit(Op::iAsBx(OpCode::Jmp, 0, 0), &elif->location); - int target = static_cast(current->proto->code.size()); + int target = static_cast(current->proto->code.size()); + + current->proto->code.resize(nextElif); current->proto->code[nextElif] = Op::iAsBx( OpCode::JmpIfFalse, *ec, static_cast(target - nextElif - 1)); + + current->proto->locations.resize(nextElif); + current->proto->locations[nextElif] = &elif->location; } if (i->alternate) @@ -157,14 +210,15 @@ namespace Fig return std::unexpected(r_cond.error()); int exitJmpIdx = static_cast(current->proto->code.size()); - emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0)); + emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &w->location); current->freereg = mark; // 回收循环条件临时槽位 if (auto r = compileStmt(w->body); !r) return r; int backJmpIdx = static_cast(current->proto->code.size()); - emit(Op::iAsBx(OpCode::Jmp, 0, static_cast(startIdx - backJmpIdx - 1))); + emit(Op::iAsBx(OpCode::Jmp, 0, static_cast(startIdx - backJmpIdx - 1)), + &w->location); int endIdx = static_cast(current->proto->code.size()); current->proto->code[exitJmpIdx] = Op::iAsBx( @@ -189,11 +243,11 @@ namespace Fig auto r = allocateReg(rs->location); if (!r) return std::unexpected(r.error()); - emit(Op::iABC(OpCode::LoadNull, *r, 0, 0)); + emit(Op::iABC(OpCode::LoadNull, *r, 0, 0), &rs->location); retReg = *r; } - emit(Op::iABC(OpCode::Return, retReg, 0, 0)); + emit(Op::iABC(OpCode::Return, retReg, 0, 0), &rs->location); current->freereg = mark; // 回收返回值计算的占用 break; } diff --git a/src/Object/FunctionObject.hpp b/src/Object/FunctionObject.hpp index c5dd56e..e2a997c 100644 --- a/src/Object/FunctionObject.hpp +++ b/src/Object/FunctionObject.hpp @@ -1,26 +1,40 @@ /*! @file src/Object/FunctionObject.hpp @brief 函数对象定义 - @author PuqiAR (im@puqiar.top) - @date 2026-02-28 */ #pragma once #include +#include namespace Fig { - // 运行时闭包对象 (24字节 Base + 8字节 Proto指针 = 32 bytes) + // Upvalue (Stack Open / Heap Closed) + struct Upvalue + { + Value *location; // Open 状态指向 VM 的 registerBase[x],Closed 状态指向下面的 closedValue + Value closedValue; // 栈帧销毁时,数据物理迁移至此 + Upvalue *next; // 侵入式链表,供 VM 追踪当前 Open 的 Upvalue + std::uint32_t refCount = 0; // 多少个闭包正在使用 + }; - struct Proto; struct FunctionObject final : public Object { - String name; // 调试使用 - Proto *proto; // 指向编译器生成的只读字节码与常量池 - std::uint8_t paraCount; + String name; + Proto *proto; // 静态只读字节码 + std::uint8_t paraCount; + std::uint32_t upvalueCount; // 捕获数量 - // TODO: 实现闭包时 加一个 Upvalue 指针数组 - // Value* upvalues; + // 柔性数组 + Upvalue *upvalues[]; + + FunctionObject(const String &_name, Proto *_proto, std::uint32_t _upvalueCount) : + name(_name), proto(_proto), paraCount(_proto->numParams), upvalueCount(_upvalueCount) + { + type = ObjectType::Function; + } + + ~FunctionObject() = default; }; } // namespace Fig \ No newline at end of file diff --git a/src/Object/InstanceObject.hpp b/src/Object/InstanceObject.hpp new file mode 100644 index 0000000..2659491 --- /dev/null +++ b/src/Object/InstanceObject.hpp @@ -0,0 +1,16 @@ +/*! + @file src/Object/InstanceObject.hpp + @brief 实例(InstanceObject)定义 + @author PuqiAR (im@puqiar.top) + @date 2026-03-10 +*/ + +#include + +namespace Fig +{ + struct InstanceObject final : public Object + { + Value fields[]; + }; +} diff --git a/src/Object/Object.cpp b/src/Object/Object.cpp index c6031e5..fb1fb89 100644 --- a/src/Object/Object.cpp +++ b/src/Object/Object.cpp @@ -5,7 +5,7 @@ @date 2026-02-19 */ -#include +#include namespace Fig { @@ -29,7 +29,35 @@ namespace Fig } else if (IsObject()) { - return "Object"; // TODO: 分派 + Object *obj = AsObject(); + if (!obj) + return ""; + + // 物理分发, 扔掉虚函数!awa + switch (obj->type) + { + case ObjectType::String: { + auto *strObj = static_cast(obj); + return strObj->data; + } + case ObjectType::Function: { + auto *fnObj = static_cast(obj); + return std::format("", fnObj->name); + } + case ObjectType::Struct: { + auto *structObj = static_cast(obj); + return std::format("", structObj->name); + } + case ObjectType::Instance: { + // 利用你预留的神级指针 klass 溯源 + if (obj->klass) + { + return std::format("", obj->klass->name); + } + return ""; + } + default: return ""; + } } else { diff --git a/src/Object/Object.hpp b/src/Object/Object.hpp index 687f913..ac08955 100644 --- a/src/Object/Object.hpp +++ b/src/Object/Object.hpp @@ -7,7 +7,8 @@ #pragma once +#include +#include #include #include #include -#include \ No newline at end of file diff --git a/src/Object/ObjectBase.hpp b/src/Object/ObjectBase.hpp index 4cd1b0a..c468d55 100644 --- a/src/Object/ObjectBase.hpp +++ b/src/Object/ObjectBase.hpp @@ -125,7 +125,7 @@ namespace Fig { return (v_ & (SIGN_BIT | QNAN_MASK)) == (SIGN_BIT | QNAN_MASK); } - + // 提取数据 (Unbox / As) [[nodiscard]] constexpr double AsDouble() const { @@ -165,7 +165,8 @@ namespace Fig // 让 VM 的 OP_EQ 指令极简:`if (RA == RB)` [[nodiscard]] constexpr bool operator==(const Value &other) const { - // IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 double进行C++比对 + // IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 + // double进行C++比对 if (IsDouble() && other.IsDouble()) { return AsDouble() == other.AsDouble(); @@ -199,13 +200,20 @@ namespace Fig struct StructObject /* : public Object */; // 结构体基类的定义,前向声明 + enum class GCColor : std::uint8_t + { + White = 0, // 垃圾(或新对象)! + Gray = 1, // 已发现,子节点待扫描 + Black = 2, // 存活 + }; + // Total 24 bytes size struct Object { - Object *next; // 8 bytes: gc链表 - StructObject *klass; // 8 bytes: 一切皆对象,父类指针 - ObjectType type; // 1 byte : 类型 - bool isMarked = false; // 1 byte : gc标记 + Object *next; // 8 bytes: gc链表 + StructObject *klass; // 8 bytes: 一切皆对象,父类指针 + ObjectType type; // 1 byte : 类型 + GCColor color = GCColor::White; // 1 byte : gc标记 // + 6 bytes padding constexpr bool isString() const @@ -227,12 +235,6 @@ namespace Fig { return type == ObjectType::Instance; } - - // 调试输出 - virtual String toString() const - { - return "Object"; - } }; } // namespace Fig diff --git a/src/Object/StringObject.hpp b/src/Object/StringObject.hpp index a5cef2a..b4d698a 100644 --- a/src/Object/StringObject.hpp +++ b/src/Object/StringObject.hpp @@ -25,10 +25,5 @@ namespace Fig struct StringObject final : public Object { String data; // 40 bytes - - virtual String toString() const override - { - return data; - } }; }; \ No newline at end of file diff --git a/src/Object/StructObject.hpp b/src/Object/StructObject.hpp index 4bcd7c0..7383e16 100644 --- a/src/Object/StructObject.hpp +++ b/src/Object/StructObject.hpp @@ -39,9 +39,6 @@ namespace Fig 0 - UnaryOperators::Count BinaryOperators::Count */ - Value fields[]; - - Object *GetUnaryOperator(UnaryOperator _op) { diff --git a/src/Sema/Analyzer.cpp b/src/Sema/Analyzer.cpp index 6356b05..46c76cc 100644 --- a/src/Sema/Analyzer.cpp +++ b/src/Sema/Analyzer.cpp @@ -206,8 +206,39 @@ namespace Fig v->localId = idx; break; } + case AstType::FnDefStmt: { - auto *f = static_cast(stmt); + auto *f = static_cast(stmt); + + // 3.10: 局部闭包延迟类型推导 + + if (!f->resolvedSymbol) // 闭包? + { + SymbolLocation loc = + env.current->parent ? SymbolLocation::Local : SymbolLocation::Global; + int idx = (loc == SymbolLocation::Local) ? env.current->nextLocalId++ : 0; + + Symbol *sym = arena.Allocate(f->name, Type{}, loc, idx, true); + f->resolvedSymbol = sym; + env.current->locals[f->name] = sym; + + auto res = resolveTypeExpr(f->returnTypeSpecifier); + if (!res) + return std::unexpected(res.error()); + f->resolvedReturnType = *res; + + DynArray paramTypes; + for (auto *p : f->params) + { + auto pres = resolveTypeExpr(p->typeSpecifier); + if (!pres) + return std::unexpected(pres.error()); + p->resolvedType = *pres; + paramTypes.push_back(*pres); + } + f->resolvedSymbol->type = typeCtx.CreateFuncType(std::move(paramTypes), *res); + } + FnStateGuard fnGuard(state.currentFn, f); ScopeGuard scopeGuard(env, true); for (auto *p : f->params) @@ -220,8 +251,15 @@ namespace Fig } if (auto r = analyzeStmt(f->body); !r) return r; + + for (const auto &upval : env.current->upvalues) + { + f->upvalues.push_back({static_cast(upval.index), upval.isLocal}); + } + break; } + case AstType::IfStmt: { auto *i = static_cast(stmt); @@ -394,7 +432,7 @@ namespace Fig auto r = analyzeExpr(arg.value); if (!r) return std::unexpected(r.error()); - // 顺手做字段赋值类型检查 + // 字段赋值类型检查 if (!arg.name.empty() && !r->isAssignableTo(st->fields[st->fieldMap[arg.name]].type)) { @@ -436,7 +474,7 @@ namespace Fig if (l.is(TypeTag::Any) || r.is(TypeTag::Any)) return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any); - // 🔥 算术操作强检查 + // 算术操作强检查 if (in->op == BinaryOperator::Add && l.is(TypeTag::String) && r.is(TypeTag::String)) return expr->resolvedType = typeCtx.GetBasic(TypeTag::String); if (l.is(TypeTag::Int) && r.is(TypeTag::Int)) @@ -467,7 +505,7 @@ namespace Fig if (calleeType.is(TypeTag::Any)) return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any); - // 🔥 终极函数签名校验 + // 函数签名校验 if (!calleeType.is(TypeTag::Function)) return std::unexpected( Error(ErrorType::TypeError, "callee is not a function", "", c->location)); diff --git a/src/Sema/Type.cpp b/src/Sema/Type.cpp index 770cff6..78e6f30 100644 --- a/src/Sema/Type.cpp +++ b/src/Sema/Type.cpp @@ -42,7 +42,7 @@ namespace Fig return true; // Any 逃逸通道 if (this->is(TypeTag::Null) && target.isNullable) return true; // Null 安全赋值 - return this->base == target.base && (!this->isNullable || target.isNullable); // 严格匹配 + return this->base == target.base && (!this->isNullable || target.isNullable); } TypeContext::TypeContext() diff --git a/src/Sema/Type.hpp b/src/Sema/Type.hpp index a14547a..8d7a481 100644 --- a/src/Sema/Type.hpp +++ b/src/Sema/Type.hpp @@ -76,7 +76,7 @@ namespace Fig }; DynArray fields; HashMap fieldMap; - HashMap methods; + HashMap methods; StructType(String n) : BaseType(TypeTag::Struct, std::move(n)) {} void AddField(String name, Type type, bool isPublic) diff --git a/src/VM/VM.cpp b/src/VM/VM.cpp index c825be8..db33585 100644 --- a/src/VM/VM.cpp +++ b/src/VM/VM.cpp @@ -150,7 +150,8 @@ namespace Fig CoreIO::GetStdErr() << std::format( "Oops! max recursion depth limit {} exceeded in Fn `{}` , exiting...\n", MAX_RECURSION_DEPTH, - (currentFrame - 1)->proto->name); // pushFrame失败了,但currentFrame仍然移动,所以 (currentFrame - 1)是 lastFrame + (currentFrame - 1)->proto->name); // pushFrame失败了,但currentFrame仍然移动,所以 + // (currentFrame - 1)是 lastFrame std::exit(static_cast(MAX_RECURSION_DEPTH)); } @@ -191,6 +192,40 @@ namespace Fig do_Call: { // TODO: FunctionObject 动态解包 + + std::uint8_t a = decodeA(inst); + std::uint8_t baseReg = decodeB(inst); + + Value callee = currentFrame->registerBase[a]; + + FunctionObject *closure = nullptr; + + if (!callee.IsObject()) + { + size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data(); + + return std::unexpected(Error(ErrorType::TypeError, + std::format("Object `{}` is not callable", callee.ToString()), + "none", + *currentFrame->proto->locations[ipIdx])); + } + else + { + Object *obj = callee.AsObject(); + if (!obj->isFunction()) + { + size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data(); + + return std::unexpected(Error(ErrorType::TypeError, + std::format("Object `{}` is not callable", callee.ToString()), + "none", + *currentFrame->proto->locations[ipIdx])); + } + closure = static_cast(obj); + } + + currentFrame->ip = pushFrame(closure, currentFrame->registerBase + baseReg); + DISPATCH(); } @@ -198,8 +233,9 @@ namespace Fig std::uint8_t a = decodeA(inst); Value retVal = currentFrame->registerBase[a]; - // 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位 + closeUpvalues(currentFrame->registerBase); + // 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位 currentFrame->registerBase[0] = retVal; popFrame(); @@ -207,6 +243,67 @@ namespace Fig } do_LoadFn: { + std::uint8_t a = decodeA(inst); + std::uint16_t bx = decodeBx(inst); + + Proto *p = compiledModule->protos[bx]; + + size_t upValSize = p->upvalues.size(); + size_t extraSize = upValSize * sizeof(Upvalue *); + + FunctionObject *closure = + (FunctionObject *) allocateObject(ObjectType::Function, extraSize); + + // CoreIO::GetStdErr() << "DEBUG: p->name = " << p->name << '\n'; + new (&closure->name) String(p->name); // String非平凡类型,有自己的构造函数 + closure->proto = p; + closure->paraCount = p->numParams; + closure->upvalueCount = static_cast(upValSize); + + for (size_t i = 0; i < closure->upvalueCount; ++i) + { + auto &info = p->upvalues[i]; + if (info.isLocal) + { + Value *targetSlot = ¤tFrame->registerBase[info.index]; + Upvalue *prev = nullptr; + Upvalue *curr = openUpvalues; + + while (curr != nullptr && curr->location > targetSlot) + { + prev = curr; + curr = curr->next; + } + + if (curr != nullptr && curr->location == targetSlot) + { + // 如果别的闭包已经捕获了这个槽位,共享物理指针 + closure->upvalues[i] = curr; + ++curr->refCount; + } + else + { + // 首次捕获 + Upvalue *uv = new Upvalue; + + uv->location = targetSlot; + uv->next = curr; + ++uv->refCount; + + if (prev == nullptr) + openUpvalues = uv; + else + prev->next = uv; + closure->upvalues[i] = uv; + } + } + else + { + closure->upvalues[i] = currentFrame->closure->upvalues[info.index]; + } + } + + currentFrame->registerBase[a] = Value::FromObject(closure); DISPATCH(); } @@ -315,12 +412,17 @@ namespace Fig } do_GetUpval: { - assert(false && "VM: GetUpval requires FunctionObject (Closure) implementation"); + std::uint8_t a = decodeA(inst); + std::uint8_t b = decodeB(inst); + + currentFrame->registerBase[a] = *(currentFrame->closure->upvalues[b]->location); DISPATCH(); } do_SetUpval: { - assert(false && "VM: SetUpval requires FunctionObject (Closure) implementation"); + std::uint8_t a = decodeA(inst); + std::uint8_t b = decodeB(inst); + *(currentFrame->closure->upvalues[b]->location) = currentFrame->registerBase[a]; // copy DISPATCH(); } diff --git a/src/VM/VM.hpp b/src/VM/VM.hpp index b23a5ae..a699bf1 100644 --- a/src/VM/VM.hpp +++ b/src/VM/VM.hpp @@ -9,8 +9,9 @@ #include #include -#include +#include +#include #include // debug #include @@ -18,9 +19,10 @@ namespace Fig { struct CallFrame { - Proto *proto; // 当前执行的原型 - Instruction *ip; // 当前指令指针 - Value *registerBase; // 寄存器起点 + FunctionObject *closure; // 动态闭包Context (FastCall时 null) + Proto *proto; // 当前执行的原型 + Instruction *ip; // 当前指令指针 + Value *registerBase; // 寄存器起点 inline Value getConstant(std::uint16_t idx) { @@ -28,6 +30,14 @@ namespace Fig } }; + enum class GCPhase : std::uint8_t + { + Idle, + MarkRoots, + Marking, + Sweeping + }; + class VM { private: @@ -45,6 +55,251 @@ namespace Fig CallFrame *currentFrame; CallFrame *frameLimit; + Upvalue *openUpvalues = nullptr; + + // GC + Object *objects = nullptr; // 链表头 + DynArray grayStack; + + size_t allocatedBytes = 0; + size_t nextGC = 1024 * 1024; // byte, 1MB初始阈值 + GCPhase gcPhase = GCPhase::Idle; + + private: + inline void closeUpvalues(Value *level) + { + // 函数销毁时,逃逸即将销毁的变量 + while (openUpvalues && openUpvalues->location >= level) + { + Upvalue *upval = openUpvalues; + upval->closedValue = *upval->location; + upval->location = &upval->closedValue; + openUpvalues = upval->next; + } + } + + template + [[nodiscard]] T *allocateObject(ObjectType type, size_t extraBytes) + { + + if (allocatedBytes > nextGC) // 超出阈值 + { + switch (gcPhase) + { + case GCPhase::Idle: markRoots(); break; + case GCPhase::Marking: stepMarking(); break; + case GCPhase::Sweeping: sweep(); break; + } + } + + size_t totalSize = sizeof(T) + extraBytes; + + // 物理分配 + T *obj = static_cast(std::malloc(totalSize)); + if (!obj) [[unlikely]] + { + // 分配失败 + CoreIO::GetStdErr() << "Oops! Object allocating failed! Exiting...\n"; + std::exit(1); + } + + // 构造 Header + obj->type = type; + obj->color = GCColor::Black; + obj->klass = nullptr; + obj->next = objects; // 插入全局追踪链表 + objects = obj; + + allocatedBytes += totalSize; + return obj; + } + + inline void writeBarrier(Object *parent, Value childVal) + { + if (!childVal.IsObject()) + return; // 栈对象无需 + + Object *child = childVal.AsObject(); + // 三色不变式, 黑色对象绝对不能指向白色对象 + if (parent->color == GCColor::Black && child->color == GCColor::White) + { + child->color = GCColor::Gray; + grayStack.push_back(child); + } + } + + inline void markValue(Value value) + { + if (!value.IsObject()) + return; + + Object *obj = value.AsObject(); + if (obj && obj->color == GCColor::White) + { + obj->color = GCColor::Gray; + grayStack.push_back(obj); + } + } + + void markRoots() + { + // 扫描全局变量 + for (std::uint32_t i = 0; i < MAX_GLOBALS; ++i) + { + markValue(globals[i]); + } + + // 扫描vm全部栈 [registers[0], currentFrame] + if (currentFrame && currentFrame->proto) + { + Value *stackTop = currentFrame->registerBase + currentFrame->proto->maxRegisters; + for (Value *slot = registers; slot < stackTop; ++slot) + { + markValue(*slot); + } + } + + // 扫描逃逸链表 (Open Upvalues) + for (Upvalue *uv = openUpvalues; uv != nullptr; uv = uv->next) + { + markValue(uv->closedValue); + } + + gcPhase = GCPhase::Marking; + } + + void stepMarking() + { + // 每次步进处理的对象数量 + constexpr int WORK_LIMIT = 64; + int workCount = 0; + + while (!grayStack.empty() && workCount++ < WORK_LIMIT) + { + Object *obj = grayStack.back(); + grayStack.pop_back(); + + // 标记为黑色:表示该对象及其子引用已处理完毕 + obj->color = GCColor::Black; + + switch (obj->type) + { + case ObjectType::Function: { + auto *fn = static_cast(obj); + for (std::uint32_t i = 0; i < fn->upvalueCount; ++i) + { + if (fn->upvalues[i]) + markValue(*(fn->upvalues[i]->location)); + } + break; + } + case ObjectType::Instance: { + auto *inst = static_cast(obj); + if (inst->klass) + markValue(Value::FromObject(inst->klass)); + // 扫描所有实例字段 + std::uint8_t fieldCount = inst->klass ? inst->klass->fieldCount : 0; + for (std::uint8_t i = 0; i < fieldCount; ++i) + { + markValue(inst->fields[i]); + } + break; + } + case ObjectType::Struct: { + auto *st = static_cast(obj); + for (int i = 0; i < GetOperatorsSize(); ++i) + { + if (st->operators[i]) + markValue(Value::FromObject(st->operators[i])); + } + break; + } + case ObjectType::String: break; // 叶子节点 + } + } + + if (grayStack.empty()) + gcPhase = GCPhase::Sweeping; + } + + void sweep() + { + Object **curr = &objects; + size_t liveBytes = 0; + + while (*curr != nullptr) + { + Object *obj = *curr; + if (obj->color == GCColor::White) + { + *curr = obj->next; + + // 函数 upvalue需要手动析构 + if (obj->type == ObjectType::Function) + { + auto *fn = static_cast(obj); + fn->name.~String(); + for (std::uint32_t i = 0; i < fn->upvalueCount; ++i) + { + Upvalue *uv = fn->upvalues[i]; + if (uv) + { + uv->refCount--; // 减引用 + if (uv->refCount == 0) + { + // 只有当所有闭包都死后,才 free 这个 Upvalue 结构体 + + std::free(uv); + } + } + } + } + + // 持有 C++ 堆资源的成员手动析构! + else if (obj->type == ObjectType::String) + { + static_cast(obj)->data.~String(); + } + + std::free(obj); + } + else + { + // 洗白,仍是一条好汉 + // 以备下次 GC,统计存活大小 + obj->color = GCColor::White; + + // 计算存活对象的真实物理大小 (Header + 柔性数组/额外数据) + size_t objectSize = 0; + switch (obj->type) + { + case ObjectType::String: objectSize = sizeof(StringObject); break; + case ObjectType::Instance: + objectSize = + sizeof(InstanceObject) + + (obj->klass ? obj->klass->fieldCount * sizeof(Value) : 0); + break; + case ObjectType::Function: + objectSize = sizeof(FunctionObject) + + (static_cast(obj)->upvalueCount + * sizeof(Upvalue *)); + break; + case ObjectType::Struct: objectSize = sizeof(StructObject); break; + } + + liveBytes += objectSize; + + curr = &obj->next; + } + } + + allocatedBytes = liveBytes; + // 阈值调整, 当下一次分配超过存活内存的 2 倍时触发 GC + nextGC = (liveBytes < 512 * 1024) ? 1024 * 1024 : liveBytes * 2; + + gcPhase = GCPhase::Idle; + } + public: VM() { @@ -66,7 +321,7 @@ namespace Fig private: [[nodiscard]] - inline Instruction *pushFrame(Proto *proto, Value *base) + inline Instruction *pushFrame(Proto *proto, Value *base) // fastcall { if (++currentFrame >= frameLimit) [[unlikely]] // 达到最大递归层数 { @@ -75,7 +330,21 @@ namespace Fig return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST; } - *currentFrame = CallFrame{proto, proto->code.data(), base}; + *currentFrame = CallFrame{nullptr, proto, proto->code.data(), base}; + return currentFrame->ip; + } + + [[nodiscard]] + inline Instruction *pushFrame(FunctionObject *closure, Value *base) // 普通调用 + { + if (++currentFrame >= frameLimit) [[unlikely]] + { + POISON_MAX_RECURSION_DEPTH_EXCEED_INST = + Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0); + return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST; + } + + *currentFrame = CallFrame{closure, closure->proto, closure->proto->code.data(), base}; return currentFrame->ip; } @@ -113,15 +382,28 @@ namespace Fig // 执行入口:接收 Proto Result Execute(CompiledModule *); - inline void PrintRegisters() + void PrintRegisters(std::ostream &ostream = CoreIO::GetStdOut()) { - std::cout << "=== Registers ===" << '\n'; + ostream << "=== Registers ===\n"; for (unsigned int i = 0; i < MAX_REGISTERS; ++i) { Value &v = registers[i]; if (!v.IsNull()) { - std::println("[{}] {}", i, v.ToString()); + ostream << std::format("[{}] {}\n", i, v.ToString()); + } + } + } + + void PrintGlobals(std::ostream &ostream = CoreIO::GetStdOut()) + { + ostream << "== Globals ===\n"; + for (unsigned int i = 0; i < MAX_GLOBALS; ++i) + { + Value &v = globals[i]; + if (!v.IsNull()) + { + ostream << std::format("[{}] {}\n", i, v.ToString()); } } } diff --git a/src/main.cpp b/src/main.cpp index 8b6d744..0fa0c9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,6 +81,7 @@ int main() std::cout << "Result: " << (*result_).ToString() << "\n"; std::cout << "Execution Cost: " << duration.count() << "ms\n"; - vm.PrintRegisters(); + vm.PrintRegisters(); + vm.PrintGlobals(); return 0; }