diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 4b306cccb2659231a1f06fa6f9dd783fe9a40d51..4f9f580473b0751c90192bd7f5687b3ed076ad4d 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -23,6 +23,7 @@ #include "checker/ETSchecker.h" #include "ir/expressions/identifier.h" #include "util/helpers.h" +#include "generated/isa.h" #include namespace ark::es2panda::compiler { diff --git a/ets2panda/compiler/core/codeGen.cpp b/ets2panda/compiler/core/codeGen.cpp index fd01a1c9f22233b98eef8ca278a813014eea0587..dbd93430fff920f10ada9fb62757f450fbbc6628 100644 --- a/ets2panda/compiler/core/codeGen.cpp +++ b/ets2panda/compiler/core/codeGen.cpp @@ -32,9 +32,9 @@ ArenaAllocator *CodeGen::Allocator() const noexcept return allocator_; } -const ArenaVector &CodeGen::CatchList() const noexcept +const std::vector &CodeGen::CatchList() const noexcept { - return catchList_; + return programElement_->CatchList(); } const varbinder::FunctionScope *CodeGen::TopScope() const noexcept @@ -52,14 +52,14 @@ const ir::AstNode *CodeGen::RootNode() const noexcept return rootNode_; } -ArenaVector &CodeGen::Insns() noexcept +std::vector &CodeGen::Insns() noexcept { - return insns_; + return programElement_->Insns(); } -const ArenaVector &CodeGen::Insns() const noexcept +const std::vector &CodeGen::Insns() const noexcept { - return insns_; + return programElement_->Insns(); } VReg CodeGen::NextReg() const noexcept @@ -272,20 +272,21 @@ std::uint32_t CodeGen::TryDepth() const CatchTable *CodeGen::CreateCatchTable(const util::StringView exceptionType) { auto *catchTable = allocator_->New(this, TryDepth(), exceptionType); - catchList_.push_back(catchTable); + programElement_->CatchList().push_back(catchTable); return catchTable; } CatchTable *CodeGen::CreateCatchTable(const LabelPair tryLabelPair, const util::StringView exceptionType) { auto *catchTable = allocator_->New(this, TryDepth(), tryLabelPair, exceptionType); - catchList_.push_back(catchTable); + programElement_->CatchList().push_back(catchTable); return catchTable; } void CodeGen::SortCatchTables() { - std::stable_sort(catchList_.begin(), catchList_.end(), + auto &catchList = programElement_->CatchList(); + std::stable_sort(catchList.begin(), catchList.end(), [](const CatchTable *a, const CatchTable *b) { return b->Depth() < a->Depth(); }); } diff --git a/ets2panda/compiler/core/codeGen.h b/ets2panda/compiler/core/codeGen.h index 8fbeeb010606d820b488a2ddc2cbb144342c4f79..6265c53a17b6a6fa7456ab70dd5a027ff3f802b8 100644 --- a/ets2panda/compiler/core/codeGen.h +++ b/ets2panda/compiler/core/codeGen.h @@ -78,8 +78,6 @@ public: topScope_(std::get(toCompile)), scope_(topScope_), rootNode_(scope_->Node()), - insns_(allocator_->Adapter()), - catchList_(allocator_->Adapter()), typeMap_(allocator_->Adapter()), programElement_(std::get(toCompile)), sa_(this), @@ -96,13 +94,13 @@ public: [[nodiscard]] virtual IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) = 0; [[nodiscard]] ArenaAllocator *Allocator() const noexcept; - [[nodiscard]] const ArenaVector &CatchList() const noexcept; + [[nodiscard]] const std::vector &CatchList() const noexcept; [[nodiscard]] const varbinder::FunctionScope *TopScope() const noexcept; [[nodiscard]] const varbinder::Scope *Scope() const noexcept; [[nodiscard]] const ir::AstNode *RootNode() const noexcept; - [[nodiscard]] ArenaVector &Insns() noexcept; - [[nodiscard]] const ArenaVector &Insns() const noexcept; + [[nodiscard]] std::vector &Insns() noexcept; + [[nodiscard]] const std::vector &Insns() const noexcept; [[nodiscard]] VReg AllocReg(); [[nodiscard]] VReg AllocRegWithType(const checker::Type *type); @@ -173,8 +171,7 @@ private: varbinder::FunctionScope *topScope_ {}; varbinder::Scope *scope_ {}; const ir::AstNode *rootNode_ {}; - ArenaVector insns_; - ArenaVector catchList_; + // insns_ and catchList_ moved to ProgramElement TypeMap typeMap_; ProgramElement *programElement_ {}; DynamicContext *dynamicContext_ {}; diff --git a/ets2panda/compiler/core/compilerImpl.cpp b/ets2panda/compiler/core/compilerImpl.cpp index 539573f68cef867f68908e67f72726a508a3e1dd..15d2e29faab2a36877fa80b23996d5d75a796260 100644 --- a/ets2panda/compiler/core/compilerImpl.cpp +++ b/ets2panda/compiler/core/compilerImpl.cpp @@ -28,6 +28,11 @@ #include "compiler/core/pandagen.h" #include "compiler/core/ETSCompiler.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/programElement.h" +#include "compiler/core/emitter.h" +#include +#include +#include #include "compiler/core/JSCompiler.h" #include "compiler/core/JSemitter.h" #include "compiler/core/ETSemitter.h" @@ -44,6 +49,13 @@ #include "public/public.h" #include "util/ustring.h" #include "util/perfMetrics.h" +#include "assembler/annotation.h" +#include "assembler/assembly-function.h" +#include "assembler/assembly-program.h" +#include "compiler/base/catchTable.h" + +// Forward declarations + #include "varbinder/JSBinder.h" #include "varbinder/ASBinder.h" #include "varbinder/TSBinder.h" @@ -51,6 +63,11 @@ namespace ark::es2panda::compiler { + + +// Use full namespace paths to avoid conflicts + + void CompilerImpl::HandleContextLiterals(public_lib::Context *context) { auto *emitter = context->emitter; @@ -63,18 +80,236 @@ void CompilerImpl::HandleContextLiterals(public_lib::Context *context) emitter->LiteralBufferIndex() += context->contextLiterals.size(); } +void CompilerImpl::EmitStage1(public_lib::Context *context) +{ + // 设置Stage1回调 + context->codeGenCb = context->stage1Cb; + + // 调度和执行Stage1任务 + queue_.Schedule(context); + queue_.Consume(); + //queue_.Wait([](CompileJob *) {}); +} + +void CompilerImpl::EmitStage2(public_lib::Context *context) +{ + (void)context; + queue_.Wait([](CompileJob *) {}); +} + +void CompilerImpl::ProcessCollectedProgramElements(public_lib::Context *context) +{ + // 获取在Stage1中收集到的所有ProgramElement + const auto& collectedElements = context->emitter->GetCollectedProgramElements(); + + // 遍历所有收集到的ProgramElement,实现类似Gen*函数的功能 + for (ProgramElement* element : collectedElements) { + if (element == nullptr || element->Function() == nullptr) { + continue; + } + + auto *func = element->Function(); + + // 1. 处理指令生成 - 类似GenFunctionInstructions + ProcessProgramElementInstructions(element, func); + + // 2. 处理Catch表生成 - 类似GenFunctionCatchTables + //ProcessProgramElementCatchTables(element, func); + + // 3. 处理函数注解生成 - 类似GenFunctionAnnotations + //ProcessProgramElementAnnotations(element, func); + + // 4. 处理调试信息 - 类似GenVariablesDebugInfo和GenSourceFileDebugInfo + ProcessProgramElementDebugInfo(element, func); + + // 5. 添加ProgramElement到emitter - 类似emitter->AddProgramElement(job->GetProgramElement()) + context->emitter->AddProgramElement(element); + } + + // 处理完成后清理收集的ProgramElement列表 + //context->emitter->ClearCollectedProgramElements(); +} + +void CompilerImpl::ProcessProgramElementInstructions(ProgramElement *element, pandasm::Function *func) +{ + // 类似GenFunctionInstructions的功能 + // 检查debugsnapshot中是否有totalregs信息,如果没有则设置默认值 + uint32_t totalRegs = 16; // 默认值 + + if (element->HasDebugSnapshot()) { + const auto *debugSnapshot = element->GetDebugSnapshot(); + totalRegs = debugSnapshot->totalRegs; + } + + // 获取IRNode指令集合 + const auto &insns = element->Insns(); + + // 预分配指令空间 + func->ins.reserve(insns.size()); + + // 遍历IRNode并生成panda指令,参考GenFunctionInstructions实现 + for (const auto *ins : insns) { + if (ins == nullptr) { + continue; + } + + // 创建新的panda指令 + auto &pandaIns = func->ins.emplace_back(); + + // 调用IRNode的Transform方法生成panda指令 + // 这里传入element作为ProgramElement参数,totalRegs作为寄存器总数 + ins->Transform(&pandaIns, element, totalRegs); + + // TODO: 生成指令调试信息 + // 这里可以添加类似GenInstructionDebugInfo的功能 + // 但由于我们在Stage2阶段,可能需要从debugSnapshot中恢复调试信息 + } +} + +void CompilerImpl::ProcessProgramElementCatchTables(ProgramElement *element, pandasm::Function *func) +{ + // 类似GenFunctionCatchTables的功能 + // 遍历ProgramElement的CatchList并转换为pandasm::Function的catchBlocks + + if (element == nullptr || func == nullptr) { + return; + } + + const auto &catchList = element->CatchList(); + if (catchList.empty()) { + return; + } + + // 清空现有的catch blocks + func->catchBlocks.clear(); + + // 遍历每个CatchTable并转换为pandasm::CatchBlock + for (const auto *catchTable : catchList) { + if (catchTable == nullptr) { + continue; + } + + pandasm::Function::CatchBlock catchBlock; + + // 设置异常类型 + catchBlock.exceptionRecord = catchTable->Exception(); + + // 设置try块的开始和结束标签 + const auto &labelSet = catchTable->LabelSet(); + if (labelSet.TryBegin() != nullptr) { + catchBlock.tryBeginLabel = labelSet.TryBegin()->Id(); + } + if (labelSet.TryEnd() != nullptr) { + catchBlock.tryEndLabel = labelSet.TryEnd()->Id(); + } + + // 设置catch块的开始标签 + if (labelSet.CatchBegin() != nullptr) { + catchBlock.catchBeginLabel = labelSet.CatchBegin()->Id(); + } + + // 设置catch块的结束标签(可选) + if (labelSet.CatchEnd() != nullptr) { + catchBlock.catchEndLabel = labelSet.CatchEnd()->Id(); + } + + // 添加到函数的catch blocks中 + func->catchBlocks.emplace_back(std::move(catchBlock)); + } +} + +void CompilerImpl::ProcessProgramElementAnnotations(ProgramElement *element, pandasm::Function *func) +{ + (void)element; + (void)func; +} + +void CompilerImpl::ProcessProgramElementDebugInfo(ProgramElement *element, pandasm::Function *func) +{ + // 类似GenVariablesDebugInfo和GenSourceFileDebugInfo的功能 + // 应用从debugsnapshot中收集的调试信息 + + if (element == nullptr || func == nullptr) { + return; + } + + // 获取调试快照 + const auto *debugSnapshot = element->GetDebugSnapshot(); + if (debugSnapshot == nullptr) { + return; + } + + // 1. 应用变量调试信息 (类似GenVariablesDebugInfo) + // 清空现有的本地变量调试信息 + func->localVariableDebug.clear(); + + // 从调试快照中恢复变量调试信息 + for (const auto &scopeSnapshot : debugSnapshot->scopeDebugInfo) { + for (const auto &varSnapshot : scopeSnapshot.variables) { + pandasm::debuginfo::LocalVariable localVar; + localVar.name = varSnapshot.name; + localVar.signature = std::string(varSnapshot.signature); + localVar.signatureType = std::string(varSnapshot.signatureType); + localVar.reg = static_cast(varSnapshot.reg); + localVar.start = varSnapshot.start; + localVar.length = varSnapshot.length; + + func->localVariableDebug.emplace_back(std::move(localVar)); + } + } + + // 2. 应用源文件调试信息 (类似GenSourceFileDebugInfo) + // 设置源文件路径 + if (element->HasFileDebugSnapshot()) { + const auto *fileSnapshot = element->GetFileDebugSnapshot(); + if (fileSnapshot != nullptr && !fileSnapshot->sourceFile.Empty()) { + func->sourceFile = std::string(fileSnapshot->sourceFile); + + // 如果是初始化方法,设置源代码内容 + if (func->name.find("") != std::string::npos || + func->name.find("_$init$_") != std::string::npos) { + if (!fileSnapshot->sourceCode.Empty()) { + func->sourceCode = std::string(fileSnapshot->sourceCode); + } + } + } + } + + // 3. 应用指令调试信息 + // 确保指令数量匹配 + if (func->ins.size() == debugSnapshot->instructionDebugInfo.size()) { + for (size_t i = 0; i < func->ins.size(); ++i) { + const auto &insSnapshot = debugSnapshot->instructionDebugInfo[i]; + auto &insDebug = func->ins[i].insDebug; + + insDebug.SetLineNumber(insSnapshot.lineNumber + 1); // 行号从1开始 + insDebug.SetColumnNumber(insSnapshot.columnNumber); + if (!insSnapshot.wholeLine.empty()) { + insDebug.SetWholeLine(insSnapshot.wholeLine); + } + } + } +} + ark::pandasm::Program *CompilerImpl::Emit(public_lib::Context *context) { HandleContextLiterals(context); - queue_.Schedule(context); + // 执行Stage1 + EmitStage1(context); + + // 在两个阶段之间释放AST分配器(若存在),避免释放通用allocator导致悬空引用 - /* Main thread can also be used instead of idling */ - queue_.Consume(); - auto *emitter = context->emitter; - queue_.Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); }); + context->astAllocator->Resize(0); + + // 在Stage1和Stage2之间处理收集到的ProgramElement + ProcessCollectedProgramElements(context); + + // 执行Stage2 + EmitStage2(context); - return emitter->Finalize(context->config->options->IsDumpDebugInfo(), Signatures::ETS_GLOBAL); + // 最终生成程序 + return context->emitter->Finalize(context->config->options->IsDumpDebugInfo(), Signatures::ETS_GLOBAL); } template @@ -92,6 +327,45 @@ static public_lib::Context::CodeGenCb MakeCompileJob() }; } +// Two-stage compilation: Stage1 job - only execute GenerateStage1 +template +static public_lib::Context::CodeGenCb MakeStage1Job() +{ + return [](public_lib::Context *context, varbinder::FunctionScope *scope, + compiler::ProgramElement *programElement) -> void { + RegSpiller regSpiller; + AstCompiler astcompiler; + compiler::SetPhaseManager(context->phaseManager); + CodeGen cg(context->IRNodeAllocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.GenerateStage1(); // Only execute Stage1 + + // Collect ProgramElement in Emitter for later processing + context->emitter->CollectProgramElement(programElement); + }; +} + +// Two-stage compilation: Stage2 job - only execute GenerateStage2 +template +static public_lib::Context::CodeGenCb MakeStage2Job() +{ + + return [](public_lib::Context *context, varbinder::FunctionScope *scope, + compiler::ProgramElement *programElement) -> void { + (void)context; + (void)scope; + (void)programElement; + /* RegSpiller regSpiller; + ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + AstCompiler astcompiler; // Create AstCompiler instance for Stage2 + compiler::SetPhaseManager(context->phaseManager); + CodeGen cg(&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.GenerateStage2(); // Only execute Stage2 */ + }; + +} + static bool CheckOptionsBeforePhase(const util::Options &options, const parser::Program &program, const std::string &name) { @@ -415,6 +689,10 @@ static pandasm::Program *Compile(const CompilationUnit &unit, CompilerImpl *comp context->sourceFile = &unit.input; context->queue = compilerImpl->Queue(); context->plugins = &compilerImpl->Plugins(); + // Ensure dedicated AST allocator exists for parsing/AST building + if (context->astAllocator == nullptr) { + context->astAllocator = new ThreadSafeArenaAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + } auto program = parser::Program::NewProgram( context->allocator, context->compilingState == public_lib::CompilingState::MULTI_COMPILING_FOLLOW ? context->transitionMemory->VarBinder() @@ -429,7 +707,10 @@ static pandasm::Program *Compile(const CompilationUnit &unit, CompilerImpl *comp auto analyzer = Analyzer(&checker); checker.SetAnalyzer(&analyzer); context->PushAnalyzer(checker.GetAnalyzer()); - context->codeGenCb = MakeCompileJob(); + // Setup two-stage compilation callbacks + context->stage1Cb = MakeStage1Job(); + context->stage2Cb = MakeStage2Job(); + context->codeGenCb = context->stage1Cb; // Start with Stage1 context->diagnosticEngine = &unit.diagnosticEngine; context->phaseManager = &phaseManager; diff --git a/ets2panda/compiler/core/compilerImpl.h b/ets2panda/compiler/core/compilerImpl.h index 02ac0514b20271e37b69e44adabbb145fdc86da4..e74d9cb76ab46f42b5e5a586cda7ba2dd104578d 100644 --- a/ets2panda/compiler/core/compilerImpl.h +++ b/ets2panda/compiler/core/compilerImpl.h @@ -66,6 +66,15 @@ public: static std::string GetPhasesList(ScriptExtension ext); ark::pandasm::Program *Emit(public_lib::Context *context); + void EmitStage1(public_lib::Context *context); + void EmitStage2(public_lib::Context *context); + void ProcessCollectedProgramElements(public_lib::Context *context); + + // Helper methods for processing ProgramElement components + void ProcessProgramElementInstructions(ProgramElement *element, pandasm::Function *func); + void ProcessProgramElementCatchTables(ProgramElement *element, pandasm::Function *func); + void ProcessProgramElementAnnotations(ProgramElement *element, pandasm::Function *func); + void ProcessProgramElementDebugInfo(ProgramElement *element, pandasm::Function *func); CompileQueue *Queue() { diff --git a/ets2panda/compiler/core/emitter.cpp b/ets2panda/compiler/core/emitter.cpp index f6027fc99ed63e7e3626e81912872d79f1a09e3d..022d81c8195f46110369569a09c1f4ed5779c8fb 100644 --- a/ets2panda/compiler/core/emitter.cpp +++ b/ets2panda/compiler/core/emitter.cpp @@ -132,6 +132,46 @@ void FunctionEmitter::Generate() GenFunctionAnnotations(func); } +void FunctionEmitter::GenerateStage1() +{ + // Stage1: Create function signature and collect frontend-dependent debug snapshots + auto *func = GenFunctionSignature(); + GenFunctionAnnotations(func); + + GenFunctionCatchTables(func); + + // Only collect debug information that depends on frontend components + auto debugSnapshot = std::make_unique(); + CollectInstructionDebugSnapshot(*debugSnapshot); + CollectVariableDebugSnapshot(*debugSnapshot); + + // Store the snapshot in ProgramElement + programElement_->SetDebugSnapshot(std::move(debugSnapshot)); +} + +/* void FunctionEmitter::GenerateStage2() +{ + // Stage2: Generate Panda instructions and all backend metadata + auto *func = programElement_->Function(); + if (func == nullptr) { + return; + } + + // Generate Panda instructions from IRNodes + GenFunctionInstructions(func); + + // Apply frontend-dependent debug snapshots + auto *debugSnapshot = programElement_->GetDebugSnapshot(); + if (debugSnapshot != nullptr) { + ApplyInstructionDebugFromSnapshot(func, *debugSnapshot); + ApplyVariableDebugFromSnapshot(func, *debugSnapshot); + } + + // Generate backend metadata directly (no snapshots needed) + GenFunctionCatchTables(func); + GenFunctionAnnotations(func); +} + */ util::StringView FunctionEmitter::SourceCode() const { return cg_->VarBinder()->Program()->SourceCode(); @@ -455,6 +495,14 @@ void Emitter::AddProgramElement(ProgramElement *programElement) literalBufferIndex_ = newLiteralBufferIndex; auto *function = programElement->Function(); + + // Apply debug information from snapshot if available + if (programElement->HasDebugSnapshot()) { + const auto *debugSnapshot = programElement->GetDebugSnapshot(); + FunctionEmitter::ApplyInstructionDebugFromSnapshot(function, *debugSnapshot); + FunctionEmitter::ApplyVariableDebugFromSnapshot(function, *debugSnapshot); + } + prog_->AddToFunctionTable(std::move(*function)); } @@ -549,4 +597,156 @@ pandasm::Program *Emitter::Finalize(bool dumpDebugInfo, std::string_view globalC prog_ = nullptr; return prog; } + +// Debug snapshot collection methods for Stage1 +void FunctionEmitter::CollectInstructionDebugSnapshot(FunctionDebugSnapshot &snapshot) +{ + // Collect totalRegs information for instruction generation + snapshot.totalRegs = Cg()->TotalRegsNum(); + + // Collect icSize information for function annotation generation + snapshot.icSize = Cg()->IcSize(); + + // Collect instruction debug information from IRNodes + for (const auto &irNode : Cg()->Insns()) { + const ir::AstNode *astNode = irNode->Node(); + if (astNode != nullptr && astNode != FIRST_NODE_OF_FUNCTION) { + InstructionDebugSnapshot insDebug; + insDebug.lineNumber = astNode->Range().start.line; + insDebug.columnNumber = astNode->Range().start.index; + insDebug.wholeLine = WholeLine(astNode->Range()); + snapshot.instructionDebugInfo.push_back(insDebug); + } else if (astNode == FIRST_NODE_OF_FUNCTION) { + // Handle special case for FIRST_NODE_OF_FUNCTION + const ir::AstNode *firstStatement = Cg()->Debuginfo().FirstStatement(); + if (firstStatement != nullptr) { + InstructionDebugSnapshot insDebug; + insDebug.lineNumber = firstStatement->Range().start.line; + insDebug.columnNumber = firstStatement->Range().start.index; + insDebug.wholeLine = WholeLine(firstStatement->Range()); + snapshot.instructionDebugInfo.push_back(insDebug); + } + } + } +} + +void FunctionEmitter::CollectVariableDebugSnapshot(FunctionDebugSnapshot &snapshot) +{ + // Collect variable debug information from varbinder + auto *topScope = Cg()->TopScope(); + if (topScope == nullptr) { + return; + } + + std::function collectScopeVars = [&](const varbinder::Scope *scope) { + ScopeDebugSnapshot scopeSnapshot; + scopeSnapshot.scopeStart = 0; // Will be set during Stage2 + scopeSnapshot.scopeEnd = 0; // Will be set during Stage2 + + for (const auto &[name, var] : scope->Bindings()) { + if (var->HasFlag(varbinder::VariableFlags::LOCAL)) { + VariableDebugSnapshot varSnapshot; + varSnapshot.name = std::string(name); + varSnapshot.signature = util::StringView("any"); + varSnapshot.signatureType = util::StringView("any"); + varSnapshot.reg = var->AsLocalVariable()->Vreg().GetIndex(); + // start and length will be set during Stage2 + scopeSnapshot.variables.push_back(varSnapshot); + } + } + + if (!scopeSnapshot.variables.empty()) { + snapshot.scopeDebugInfo.push_back(scopeSnapshot); + } + + // Note: Child scope traversal would require AST node iteration + // For now, we collect only the current scope variables + // TODO: Implement child scope traversal through AST nodes if needed + }; + + collectScopeVars(topScope); +} + + + +// Debug snapshot application methods for Stage2 +void FunctionEmitter::ApplyInstructionDebugFromSnapshot(pandasm::Function *func, const FunctionDebugSnapshot &snapshot) +{ + // Apply instruction debug info from snapshot to pandasm function + if (func->ins.size() != snapshot.instructionDebugInfo.size()) { + return; // Size mismatch, cannot apply debug info + } + + for (size_t i = 0; i < func->ins.size(); ++i) { + const auto &insSnapshot = snapshot.instructionDebugInfo[i]; + auto &insDebug = func->ins[i].insDebug; + + insDebug.SetLineNumber(insSnapshot.lineNumber); + insDebug.SetColumnNumber(insSnapshot.columnNumber); + if (!insSnapshot.wholeLine.empty()) { + insDebug.SetWholeLine(insSnapshot.wholeLine); + } + } +} + +void FunctionEmitter::ApplyVariableDebugFromSnapshot(pandasm::Function *func, const FunctionDebugSnapshot &snapshot) +{ + // Apply variable debug info from snapshot to pandasm function + func->localVariableDebug.clear(); + + for (const auto &scopeSnapshot : snapshot.scopeDebugInfo) { + for (const auto &varSnapshot : scopeSnapshot.variables) { + pandasm::debuginfo::LocalVariable localVar; + localVar.name = std::string(varSnapshot.name); + localVar.signature = std::string(varSnapshot.signature); + localVar.signatureType = std::string(varSnapshot.signatureType); + localVar.reg = static_cast(varSnapshot.reg); + localVar.start = varSnapshot.start; + localVar.length = varSnapshot.length; + + func->localVariableDebug.emplace_back(std::move(localVar)); + } + } +} + +// Emitter file-level debug information methods +void Emitter::CollectFileDebugSnapshot(FileDebugSnapshot &snapshot) +{ + // Get source file information from the context or program + if (context_ != nullptr && context_->parserProgram != nullptr) { + auto *program = context_->parserProgram; + snapshot.sourceFile = program->SourceFile().GetPath(); + snapshot.sourceCode = program->SourceCode(); + } +} + +void Emitter::ApplyFileDebugToAllFunctions(const FileDebugSnapshot &snapshot) +{ + // Apply file-level debug information to all functions in the program + for (auto &[name, func] : prog_->functionStaticTable) { + func.sourceFile = std::string(snapshot.sourceFile); + } + for (auto &[name, func] : prog_->functionInstanceTable) { + func.sourceFile = std::string(snapshot.sourceFile); + } +} + +// ProgramElement collection functionality implementation +void Emitter::CollectProgramElement(ProgramElement *programElement) +{ + std::lock_guard lock(collectionMutex_); + collectedProgramElements_.push_back(programElement); +} + +const std::vector& Emitter::GetCollectedProgramElements() const +{ + return collectedProgramElements_; +} + +void Emitter::ClearCollectedProgramElements() +{ + std::lock_guard lock(collectionMutex_); + collectedProgramElements_.clear(); +} + } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/emitter.h b/ets2panda/compiler/core/emitter.h index 7e2623cc38f6a6f1c37ff01b0aa8caa488a4c1db..67374d4516bad748b76a999d309e343618e1af2f 100644 --- a/ets2panda/compiler/core/emitter.h +++ b/ets2panda/compiler/core/emitter.h @@ -53,6 +53,41 @@ class IRNode; class ProgramElement; class RegSpiller; +// Debug information snapshot structures for two-stage compilation +struct InstructionDebugSnapshot { + uint32_t lineNumber; + uint32_t columnNumber; + std::string wholeLine; // 保持std::string,因为需要转义处理 +}; + +struct VariableDebugSnapshot { + std::string name; + util::StringView signature; + util::StringView signatureType; + uint32_t reg; + uint32_t start; + uint32_t length; +}; + +struct ScopeDebugSnapshot { + uint32_t scopeStart; + uint32_t scopeEnd; + std::vector variables; +}; + +struct FunctionDebugSnapshot { + std::vector instructionDebugInfo; + std::vector scopeDebugInfo; + uint32_t totalRegs {0}; // Total register count for instruction generation + uint32_t icSize {0}; // IC size for function annotation generation +}; + +// File-level debug information snapshot for two-stage compilation +struct FileDebugSnapshot { + util::StringView sourceFile; + util::StringView sourceCode; +}; + class FunctionEmitter { public: explicit FunctionEmitter(const CodeGen *cg, ProgramElement *programElement) @@ -65,6 +100,10 @@ public: NO_MOVE_SEMANTIC(FunctionEmitter); void Generate(); + + // Two-stage compilation methods + void GenerateStage1(); // Generate IRNodes and collect debug snapshots + void GenerateStage2(); // Generate Panda instructions using snapshots protected: using VariablesStartsMap = std::unordered_map; @@ -84,6 +123,19 @@ protected: void GenFunctionCatchTables(ark::pandasm::Function *func); void GenVariablesDebugInfo(pandasm::Function *func); util::StringView SourceCode() const; + + // Debug snapshot collection methods for Stage1 + void CollectInstructionDebugSnapshot(FunctionDebugSnapshot &snapshot); + void CollectVariableDebugSnapshot(FunctionDebugSnapshot &snapshot); + + +public: + // Debug snapshot application methods for Stage2 + static void ApplyInstructionDebugFromSnapshot(pandasm::Function *func, const FunctionDebugSnapshot &snapshot); + static void ApplyVariableDebugFromSnapshot(pandasm::Function *func, const FunctionDebugSnapshot &snapshot); + + +protected: const CodeGen *Cg() const { @@ -111,6 +163,15 @@ public: void AddProgramElement(ProgramElement *programElement); static void DumpAsm(const pandasm::Program *prog); pandasm::Program *Finalize(bool dumpDebugInfo, std::string_view globalClass = ""); + + // ProgramElement collection functionality for stage1 + void CollectProgramElement(ProgramElement *programElement); + const std::vector& GetCollectedProgramElements() const; + void ClearCollectedProgramElements(); + + // File-level debug information management + void CollectFileDebugSnapshot(FileDebugSnapshot &snapshot); + void ApplyFileDebugToAllFunctions(const FileDebugSnapshot &snapshot); uint32_t &LiteralBufferIndex() { @@ -136,6 +197,10 @@ private: pandasm::Program *prog_; const public_lib::Context *context_; uint32_t literalBufferIndex_ {}; + + // Collection for ProgramElements from stage1 + std::vector collectedProgramElements_; + std::mutex collectionMutex_; }; } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/programElement.cpp b/ets2panda/compiler/core/programElement.cpp index 0ba4b63cc3a9fd13792059075bd06828a97edba4..1a9b36af0310b34070756bd0c8216f202616e78c 100644 --- a/ets2panda/compiler/core/programElement.cpp +++ b/ets2panda/compiler/core/programElement.cpp @@ -14,10 +14,31 @@ */ #include "programElement.h" +#include "emitter.h" // For FunctionDebugSnapshot +#include "compiler/base/catchTable.h" +#include "ir/irnode.h" #include +#include +#include +#include + +// Forward declarations +namespace ark::pandasm { +class Ins; +class Function; +} namespace ark::es2panda::compiler { +class IRNode; +class CatchTable; +struct FunctionDebugSnapshot; +struct FileDebugSnapshot; + +ProgramElement::ProgramElement() +{ +} + std::set &ProgramElement::Strings() { return strings_; @@ -43,6 +64,56 @@ void ProgramElement::SetFunction(pandasm::Function *func) func_ = func; } +std::vector &ProgramElement::Insns() +{ + return insns_; +} + +const std::vector &ProgramElement::Insns() const +{ + return insns_; +} + +std::vector &ProgramElement::CatchList() +{ + return catchList_; +} + +const std::vector &ProgramElement::CatchList() const +{ + return catchList_; +} + +FunctionDebugSnapshot *ProgramElement::GetDebugSnapshot() +{ + return debugSnapshot_.get(); +} + +void ProgramElement::SetDebugSnapshot(std::unique_ptr snapshot) +{ + debugSnapshot_ = std::move(snapshot); +} + +bool ProgramElement::HasDebugSnapshot() const +{ + return debugSnapshot_ != nullptr; +} + +FileDebugSnapshot *ProgramElement::GetFileDebugSnapshot() +{ + return fileDebugSnapshot_.get(); +} + +void ProgramElement::SetFileDebugSnapshot(std::unique_ptr snapshot) +{ + fileDebugSnapshot_ = std::move(snapshot); +} + +bool ProgramElement::HasFileDebugSnapshot() const +{ + return fileDebugSnapshot_ != nullptr; +} + ProgramElement::~ProgramElement() { delete func_; diff --git a/ets2panda/compiler/core/programElement.h b/ets2panda/compiler/core/programElement.h index 481f69375731a0c4506758b5d9a3b1ab92b7932a..24cc5a25f11a5549337ffcdc891e30ec36b8e4c8 100644 --- a/ets2panda/compiler/core/programElement.h +++ b/ets2panda/compiler/core/programElement.h @@ -16,8 +16,13 @@ #ifndef ES2PANDA_COMPILER_CORE_PROGRAM_ELEMENT_H #define ES2PANDA_COMPILER_CORE_PROGRAM_ELEMENT_H -#include "util/es2pandaMacros.h" #include "compiler/base/literals.h" +#include "util/es2pandaMacros.h" + +#include +#include +#include +#include namespace ark::pandasm { class Ins; @@ -25,9 +30,15 @@ class Function; } // namespace ark::pandasm namespace ark::es2panda::compiler { +class IRNode; +class CatchTable; +struct FunctionDebugSnapshot; +struct FileDebugSnapshot; + + class ProgramElement { public: - explicit ProgramElement() = default; + explicit ProgramElement(); ~ProgramElement(); NO_COPY_SEMANTIC(ProgramElement); NO_MOVE_SEMANTIC(ProgramElement); @@ -37,12 +48,36 @@ public: std::vector &BuffStorage(); pandasm::Function *Function(); void SetFunction(pandasm::Function *func); + + // Instruction and catch table access methods + std::vector &Insns(); + const std::vector &Insns() const; + std::vector &CatchList(); + const std::vector &CatchList() const; + + + + // Debug snapshot methods for two-stage compilation + FunctionDebugSnapshot *GetDebugSnapshot(); + void SetDebugSnapshot(std::unique_ptr snapshot); + bool HasDebugSnapshot() const; + + // File-level debug snapshot methods + FileDebugSnapshot *GetFileDebugSnapshot(); + void SetFileDebugSnapshot(std::unique_ptr snapshot); + bool HasFileDebugSnapshot() const; private: std::set strings_; std::vector literalBufferIns_; std::vector buffStorage_; pandasm::Function *func_ {}; + std::unique_ptr debugSnapshot_; + std::unique_ptr fileDebugSnapshot_; + + // Moved from CodeGen + std::vector insns_; + std::vector catchList_; }; } // namespace ark::es2panda::compiler #endif diff --git a/ets2panda/compiler/core/regScope.cpp b/ets2panda/compiler/core/regScope.cpp index f0ef27d3b8f76cce4480d69f41aa5c050eee71ff..9a77d6045325e919da20277bcdb2ff5247b76e69 100644 --- a/ets2panda/compiler/core/regScope.cpp +++ b/ets2panda/compiler/core/regScope.cpp @@ -40,7 +40,7 @@ void RegScope::DebuggerCloseScope() return; } - cg_->scope_->SetScopeEnd(cg_->insns_.back()); + cg_->scope_->SetScopeEnd(cg_->Insns().back()); } // LocalRegScope @@ -59,8 +59,8 @@ LocalRegScope::LocalRegScope(CodeGen *cg, varbinder::Scope *scope) : RegScope(cg } } - if (cg_->IsDebug() && !cg_->insns_.empty()) { - cg_->scope_->SetScopeStart(cg_->insns_.back()); + if (cg_->IsDebug() && !cg_->Insns().empty()) { + cg_->scope_->SetScopeStart(cg_->Insns().back()); cg_->debugInfo_.VariableDebugInfo().push_back(cg_->scope_); } } @@ -155,8 +155,8 @@ FunctionRegScope::FunctionRegScope(PandaGen *pg) : RegScope(pg), envScope_(pg->A FunctionRegScope::~FunctionRegScope() { - if (cg_->IsDebug() && !cg_->insns_.empty()) { - cg_->topScope_->SetScopeStart(cg_->insns_.front()); + if (cg_->IsDebug() && !cg_->Insns().empty()) { + cg_->topScope_->SetScopeStart(cg_->Insns().front()); DebuggerCloseScope(); } diff --git a/ets2panda/es2panda.cpp b/ets2panda/es2panda.cpp index 52aaeb6f0c901462b64538b1d56a372a02b36ffc..d58950bb49dea91bf5a34d0b0feaa141daed45ab 100644 --- a/ets2panda/es2panda.cpp +++ b/ets2panda/es2panda.cpp @@ -88,7 +88,9 @@ pandasm::Program *Compiler::Compile(const SourceFile &input, const util::Options { public_lib::Context context; ThreadSafeArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + ThreadSafeArenaAllocator IRNodeAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); context.allocator = &allocator; + context.IRNodeAllocator = &IRNodeAllocator; context.compilingState = public_lib::CompilingState::SINGLE_COMPILING; try { @@ -106,7 +108,9 @@ unsigned int Compiler::CompileM(std::vector &inputs, util::Options & public_lib::Context context; context.transitionMemory = new public_lib::TransitionMemory(new ThreadSafeArenaAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true)); + ThreadSafeArenaAllocator IRNodeAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); context.allocator = context.transitionMemory->PermanentAllocator(); + context.IRNodeAllocator = &IRNodeAllocator; context.compilingState = public_lib::CompilingState::MULTI_COMPILING_INIT; unsigned int overallRes = 0; diff --git a/ets2panda/lexer/lexer.cpp b/ets2panda/lexer/lexer.cpp index 326dcaed3cb78d33e00515d55c68119fcf6676c0..37862e4b9639cc93404b0f2d32f41b1c51caa802 100644 --- a/ets2panda/lexer/lexer.cpp +++ b/ets2panda/lexer/lexer.cpp @@ -16,12 +16,13 @@ #include "lexer.h" #include "generated/keywords.h" +#include "public/public.h" namespace ark::es2panda::lexer { LexerPosition::LexerPosition(const util::StringView &source) : iterator_(source) {} Lexer::Lexer(const parser::ParserContext *parserContext, util::DiagnosticEngine &diagnosticEngine, bool startLexer) - : allocator_(parserContext->GetProgram()->Allocator()), + : allocator_(parserContext->GetProgram()->VarBinder()->GetContext()->Allocator()), parserContext_(parserContext), source_(parserContext->GetProgram()->SourceCode()), pos_(source_), diff --git a/ets2panda/parser/parserImpl.cpp b/ets2panda/parser/parserImpl.cpp index 7b7c10dc59d1b23b59c6b4f55799422cd20c5903..fd2fdcd7218440a54246bb552022afe63e88444b 100644 --- a/ets2panda/parser/parserImpl.cpp +++ b/ets2panda/parser/parserImpl.cpp @@ -16,6 +16,7 @@ #include "parserImpl.h" #include "forwardDeclForParserImpl.h" #include "parserStatusContext.h" +#include "public/public.h" #include "generated/diagnostic.h" #include "varbinder/privateBinding.h" @@ -50,6 +51,14 @@ ParserImpl::ParserImpl(Program *program, const util::Options *options, util::Dia { } +ArenaAllocator *ParserImpl::Allocator() const +{ + if (ctx_ != nullptr) { + return ctx_->AstAllocator(); + } + return program_->Allocator(); +} + std::unique_ptr ParserImpl::InitLexer(const SourceFile &sourceFile) { program_->SetSource(sourceFile); diff --git a/ets2panda/parser/parserImpl.h b/ets2panda/parser/parserImpl.h index cd39352b1d1015be72c794a3ccb5842142086e0e..c765b2b136a76b150085c92f19cc9967cc584790 100644 --- a/ets2panda/parser/parserImpl.h +++ b/ets2panda/parser/parserImpl.h @@ -238,10 +238,7 @@ protected: ir::TypeNode *AllocBrokenType(const lexer::SourcePosition &pos); ir::TypeNode *AllocBrokenType(const lexer::SourceRange &range); - ArenaAllocator *Allocator() const - { - return program_->Allocator(); - } + ArenaAllocator *Allocator() const; bool CheckModuleAsModifier(); diff --git a/ets2panda/public/public.h b/ets2panda/public/public.h index 72c7553a18454f2d47da38885160feb121da5090..7bbb2a051861b50b514b9a41116fa181082552a3 100644 --- a/ets2panda/public/public.h +++ b/ets2panda/public/public.h @@ -161,11 +161,17 @@ struct Context { return allocator; } + // Return allocator used specifically for AST nodes + ArenaAllocator *AstAllocator() const + { + return astAllocator; + } + template T *AllocNode(Args &&...args) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return util::NodeAllocator::ForceSetParent(Allocator(), std::forward(args)...); + return util::NodeAllocator::ForceSetParent(AstAllocator(), std::forward(args)...); } checker::Checker *GetChecker() const; @@ -211,11 +217,28 @@ struct Context { std::string input; SourceFile const *sourceFile = nullptr; ThreadSafeArenaAllocator *allocator = nullptr; + ThreadSafeArenaAllocator *IRNodeAllocator = nullptr; + ThreadSafeArenaAllocator *astAllocator = nullptr; // Dedicated allocator for AST nodes compiler::CompileQueue *queue = nullptr; std::vector const *plugins = nullptr; std::vector contextLiterals; CodeGenCb codeGenCb; + // Two-stage compilation support + CodeGenCb stage1Cb; + CodeGenCb stage2Cb; compiler::PhaseManager *phaseManager = nullptr; + + // Switch to Stage2 and cleanup frontend components + void SwitchToStage2() { + codeGenCb = stage2Cb; + // Clear frontend components to save memory + //ClearCheckers(); + //ClearAnalyzers(); + // Clear parser and AST to free memory + //parser = nullptr; + // Note: parserProgram contains AST, but we keep it for now + // as Stage2 might still need some AST information + } parser::Program *parserProgram = nullptr; parser::ParserImpl *parser = nullptr;