From 20ac0203b707e8c778b0f33d500a91fdeb7b9b05 Mon Sep 17 00:00:00 2001 From: qiuyu22 Date: Tue, 15 Jul 2025 01:45:05 +0800 Subject: [PATCH] bugfix for RegAllocator Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICMKDD Description: Current register allocator uses occupied regs for spill, and then add restore movs after the target ins. If the target ins throws in runtime, then the restore movs will not be executed, and thus unexpected behaviors happen. To fix it, we update the algorithm by reserving regs for spill thus the restore operation is not needed. Tested-by: ninja tests (passed) ets_testrunner (passed) Signed-off-by: qiuyu22 --- ets2panda/compiler/core/ETSGen.cpp | 55 ++-- ets2panda/compiler/core/ETSGen.h | 3 +- ets2panda/compiler/core/ETSfunction.cpp | 1 + ets2panda/compiler/core/codeGen.h | 30 +- ets2panda/compiler/core/emitter.cpp | 11 +- ets2panda/compiler/core/function.cpp | 1 + ets2panda/compiler/core/pandagen.cpp | 32 ++- ets2panda/compiler/core/pandagen.h | 1 + ets2panda/compiler/core/regAllocator.cpp | 257 ++++++++++++------ ets2panda/compiler/core/regAllocator.h | 15 +- ets2panda/compiler/core/regSpiller.h | 24 ++ ets2panda/compiler/core/vReg.h | 13 + ets2panda/compiler/scripts/signatures.yaml | 4 + ets2panda/compiler/templates/formats.h.erb | 39 +++ ets2panda/compiler/templates/isa.h.erb | 149 ++++++++++ ets2panda/ir/irnode.h | 23 ++ ets2panda/test/unit/CMakeLists.txt | 1 + .../test/unit/reg_allocator/CMakeLists.txt | 16 ++ .../test/unit/reg_allocator/reg_allocator.cpp | 81 ++++++ 19 files changed, 625 insertions(+), 131 deletions(-) create mode 100644 ets2panda/test/unit/reg_allocator/CMakeLists.txt create mode 100644 ets2panda/test/unit/reg_allocator/reg_allocator.cpp diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 672a2ace60..bb9c50bd8f 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -199,23 +199,31 @@ IRNode *ETSGen::AllocMov(const ir::AstNode *const node, const VReg vd, const VRe IRNode *ETSGen::AllocMov(const ir::AstNode *const node, OutVReg vd, const VReg vs) { - ES2PANDA_ASSERT(vd.type != OperandType::ANY && vd.type != OperandType::NONE); + return AllocSpillMov(node, *vd.reg, vs, vd.type); +} + +checker::Type const *ETSGen::TypeForVar(varbinder::Variable const *var) const noexcept +{ + return var->TsType(); +} - switch (vd.type) { +IRNode *ETSGen::AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, OperandType type) +{ + ES2PANDA_ASSERT(type != OperandType::ANY); + ES2PANDA_ASSERT(type != OperandType::NONE); + switch (type) { case OperandType::REF: - return Allocator()->New(node, *vd.reg, vs); + return Allocator()->New(node, vd, vs); case OperandType::B64: - return Allocator()->New(node, *vd.reg, vs); + return Allocator()->New(node, vd, vs); + case OperandType::B32: + return Allocator()->New(node, vd, vs); default: + ES2PANDA_UNREACHABLE(); break; } - return Allocator()->New(node, *vd.reg, vs); -} - -checker::Type const *ETSGen::TypeForVar(varbinder::Variable const *var) const noexcept -{ - return var->TsType(); + return Allocator()->New(node, vd, vs); } void ETSGen::MoveVreg(const ir::AstNode *const node, const VReg vd, const VReg vs) @@ -831,16 +839,17 @@ void ETSGen::EmitFailedTypeCastException(const ir::AstNode *node, const VReg src bool isUndef) { const RegScope rs(this); - const auto errorReg = AllocReg(); + const auto boolReg = AllocReg(); if (isUndef) { - Sa().Emit(node, errorReg, 1.0); + Ra().Emit(node, boolReg, 1.0); } else { - Sa().Emit(node, errorReg, 0.0); + Ra().Emit(node, boolReg, 0.0); } - SetVRegType(errorReg, Checker()->GlobalETSBooleanType()); + SetVRegType(boolReg, Checker()->GlobalETSBooleanType()); LoadAccumulatorString(node, util::UString(target->ToString(), Allocator()).View()); - Ra().Emit(node, Signatures::BUILTIN_RUNTIME_FAILED_TYPE_CAST_EXCEPTION, src, errorReg, dummyReg_, 1); + Ra().Emit(node, Signatures::BUILTIN_RUNTIME_FAILED_TYPE_CAST_EXCEPTION, src, boolReg, dummyReg_, 1); + const auto errorReg = AllocReg(); StoreAccumulator(node, errorReg); EmitThrow(node, errorReg); SetAccumulatorType(nullptr); @@ -1699,7 +1708,7 @@ void ETSGen::ConditionalFloat(const ir::AstNode *node) Ra().Emit(node, zeroReg); } Sa().Emit(node, 0); - Sa().Emit(node, isNaNReg); + Ra().Emit(node, isNaNReg); } void ETSGen::BranchConditionalIfFalse(const ir::AstNode *node, Label *endLabel) @@ -1735,15 +1744,15 @@ void ETSGen::BranchIfNullish(const ir::AstNode *node, Label *ifNullish) Sa().Emit(node, ifNullish); } - Sa().Emit(node, tmpObj); + StoreAccumulator(node, tmpObj); EmitIsNull(node); Sa().Emit(node, notTaken); - Sa().Emit(node, tmpObj); + LoadAccumulator(node, tmpObj); Sa().Emit(node, ifNullish); SetLabel(node, notTaken); - Sa().Emit(node, tmpObj); + LoadAccumulator(node, tmpObj); } } @@ -1769,12 +1778,10 @@ void ETSGen::AssumeNonNullish(const ir::AstNode *node, checker::Type const *targ void ETSGen::EmitNullishException(const ir::AstNode *node) { RegScope ra(this); - VReg undef = AllocReg(); - LoadAccumulatorUndefined(node); - StoreAccumulator(node, undef); VReg exception = AllocReg(); - NewObject(node, Signatures::BUILTIN_NULLPOINTER_ERROR, exception); - CallExact(node, Signatures::BUILTIN_NULLPOINTER_ERROR_CTOR, exception, undef, undef); + Ra().Emit(node, Signatures::BUILTIN_NULLPOINTER_ERROR_CTOR, dummyReg_, dummyReg_); + SetAccumulatorType(Checker()->GlobalETSObjectType()); + StoreAccumulator(node, exception); EmitThrow(node, exception); SetAccumulatorType(nullptr); } diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 4b306cccb2..d6e54a5f7f 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -50,6 +50,7 @@ public: void LoadAccumulator(const ir::AstNode *node, VReg vreg); [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override; [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override; + [[nodiscard]] IRNode *AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, OperandType type) override; void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); [[nodiscard]] checker::Type const *TypeForVar(varbinder::Variable const *var) const noexcept override; @@ -327,7 +328,7 @@ public: void EmitAnyIsInstance(const ir::AstNode *node, VReg objReg) { - Sa().Emit(node, objReg); + Ra().Emit(node, objReg); } void CallExact(const ir::AstNode *node, checker::Signature *signature, diff --git a/ets2panda/compiler/core/ETSfunction.cpp b/ets2panda/compiler/core/ETSfunction.cpp index 672f29e5f6..73a087c4e7 100644 --- a/ets2panda/compiler/core/ETSfunction.cpp +++ b/ets2panda/compiler/core/ETSfunction.cpp @@ -174,6 +174,7 @@ void ETSFunction::Compile(ETSGen *etsg) CompileSourceBlock(etsg, etsg->RootNode()->AsBlockStatement()); } + etsg->Ra().AdjustInsRegWhenHasSpill(); etsg->SortCatchTables(); } diff --git a/ets2panda/compiler/core/codeGen.h b/ets2panda/compiler/core/codeGen.h index 8fbeeb0106..543f05f3a1 100644 --- a/ets2panda/compiler/core/codeGen.h +++ b/ets2panda/compiler/core/codeGen.h @@ -92,6 +92,8 @@ public: NO_COPY_SEMANTIC(CodeGen); NO_MOVE_SEMANTIC(CodeGen); + virtual IRNode *AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, OperandType type) = 0; + [[nodiscard]] virtual IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) = 0; [[nodiscard]] virtual IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) = 0; @@ -154,10 +156,36 @@ public: compiler::AstCompiler *GetAstCompiler() const; + const ArenaVector &GetInsns() const noexcept + { + return insns_; + } + + void SetInsns(const ArenaVector &insns) + { + insns_ = insns; + } + + void AddSpillRegsToUsedRegs(VReg::Index spillRegs) + { + ES2PANDA_ASSERT(totalRegs_ >= spillRegs); + totalRegs_ -= spillRegs; + ES2PANDA_ASSERT(usedRegs_ >= spillRegs); + usedRegs_ -= spillRegs; + } + + uint32_t GetRegsNum() const + { + uint32_t min = std::min(totalRegs_, usedRegs_); + ES2PANDA_ASSERT(min <= VReg::REG_START); + return static_cast(VReg::REG_START - min); + } + + [[nodiscard]] RegAllocator &Ra() noexcept; + protected: [[nodiscard]] SimpleAllocator &Sa() noexcept; [[nodiscard]] const SimpleAllocator &Sa() const noexcept; - [[nodiscard]] RegAllocator &Ra() noexcept; [[nodiscard]] const RegAllocator &Ra() const noexcept; [[nodiscard]] RangeRegAllocator &Rra() noexcept; [[nodiscard]] const RangeRegAllocator &Rra() const noexcept; diff --git a/ets2panda/compiler/core/emitter.cpp b/ets2panda/compiler/core/emitter.cpp index 6586372749..9d936ebbfe 100644 --- a/ets2panda/compiler/core/emitter.cpp +++ b/ets2panda/compiler/core/emitter.cpp @@ -137,7 +137,7 @@ util::StringView FunctionEmitter::SourceCode() const return cg_->VarBinder()->Program()->SourceCode(); } -static Format MatchFormat(const IRNode *node, const Formats &formats) +static Format MatchFormat(const IRNode *node, const Formats &formats, uint32_t regNum) { std::array regs {}; auto regCnt = node->Registers(®s); @@ -155,7 +155,8 @@ static Format MatchFormat(const IRNode *node, const Formats &formats) } } - if (std::all_of(registers.begin(), registers.end(), [limit](const VReg *reg) { return reg->IsValid(limit); })) { + auto isValidCallback = [limit, regNum](const VReg *reg) { return reg->IsRegOrParamValid(limit, regNum); }; + if (std::all_of(registers.begin(), registers.end(), isValidCallback)) { return format; } } @@ -164,7 +165,7 @@ static Format MatchFormat(const IRNode *node, const Formats &formats) return *iter; } -static size_t GetIRNodeWholeLength(const IRNode *node) +static size_t GetIRNodeWholeLength(const IRNode *node, uint32_t regNum) { Formats formats = node->GetFormats(); if (formats.empty()) { @@ -172,7 +173,7 @@ static size_t GetIRNodeWholeLength(const IRNode *node) } size_t len = 1; - const auto format = MatchFormat(node, formats); + const auto format = MatchFormat(node, formats, regNum); for (auto fi : format.GetFormatItem()) { len += fi.BitWidth() / 8U; @@ -213,7 +214,7 @@ void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *p pandaIns->insDebug.SetLineNumber(nodeRange.start.line + 1U); if (cg_->IsDebug()) { - size_t insLen = GetIRNodeWholeLength(ins); + size_t insLen = GetIRNodeWholeLength(ins, cg_->GetRegsNum()); if (insLen != 0) { pandaIns->insDebug.SetBoundLeft(offset_); pandaIns->insDebug.SetBoundRight(offset_ + insLen); diff --git a/ets2panda/compiler/core/function.cpp b/ets2panda/compiler/core/function.cpp index e5c05ca78b..2304df5f7c 100644 --- a/ets2panda/compiler/core/function.cpp +++ b/ets2panda/compiler/core/function.cpp @@ -278,6 +278,7 @@ void Function::Compile(PandaGen *pg) } } + pg->Ra().AdjustInsRegWhenHasSpill(); pg->SortCatchTables(); } } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/pandagen.cpp b/ets2panda/compiler/core/pandagen.cpp index d4602f2bea..787c4065e7 100644 --- a/ets2panda/compiler/core/pandagen.cpp +++ b/ets2panda/compiler/core/pandagen.cpp @@ -72,6 +72,21 @@ public: ES2PANDA_UNREACHABLE(); } + OperandType GetOperandRegType([[maybe_unused]] size_t idx) const override + { + return OperandType::NONE; + } + + uint32_t GetRegLimit() const noexcept override + { + return 0U; + } + + OperandKind GetOperandRegKind([[maybe_unused]] size_t idx) const override + { + return LABEL_OPERAND_REG_KIND[idx]; + } + void Transform([[maybe_unused]] pandasm::Ins *ins, [[maybe_unused]] ProgramElement *programElement, [[maybe_unused]] uint32_t totalRegs) const override { @@ -350,6 +365,11 @@ IRNode *PandaGen::AllocMov(const ir::AstNode *node, OutVReg vd, const VReg vs) return Allocator()->New(node, *vd.reg, vs); } +IRNode *PandaGen::AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, [[maybe_unused]] OperandType type) +{ + return Allocator()->New(node, vd, vs); +} + void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs) { Ra().Emit(node, vd, vs); @@ -1061,7 +1081,7 @@ void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, if (hasThis) { Ra().Emit(node, callee, thisReg); } else { - Sa().Emit(node, callee); + Ra().Emit(node, callee); } return; } @@ -1088,7 +1108,7 @@ void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, void PandaGen::SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount) { - Rra().Emit(node, startReg, argCount, static_cast(argCount), startReg); + Rra().Emit(node, startReg, argCount + 1, static_cast(argCount), startReg); } void PandaGen::SuperCallSpread(const ir::AstNode *node, VReg vs) @@ -1461,7 +1481,7 @@ void PandaGen::CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, V argStart = obj; } - Rra().Emit(node, argStart, argCount, static_cast(argCount), obj, + Rra().Emit(node, argStart, argCount + 1, static_cast(argCount), obj, argStart); } @@ -1493,12 +1513,12 @@ void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::String void PandaGen::LoadClassComputedInstanceFields(const ir::AstNode *node, VReg ctor) { - Sa().Emit(node, ctor); + Ra().Emit(node, ctor); } void PandaGen::DefineClassPrivateFields(const ir::AstNode *node, uint32_t privateBufIdx) { - Sa().Emit(node, util::Helpers::ToStringView(Allocator(), privateBufIdx), LexEnv()); + Ra().Emit(node, util::Helpers::ToStringView(Allocator(), privateBufIdx), LexEnv()); } void PandaGen::ClassFieldAdd(const ir::AstNode *node, VReg obj, VReg prop) @@ -1803,7 +1823,7 @@ void PandaGen::DirectEval(const ir::AstNode *node, uint32_t parserStatus) LoadAccumulator(node, evalBindings); StOwnByIndex(node, bindings, index++); - Sa().Emit(node, static_cast(parserStatus), arg0, bindings); + Ra().Emit(node, static_cast(parserStatus), arg0, bindings); } void PandaGen::LoadLexicalContext(const ir::AstNode *node) diff --git a/ets2panda/compiler/core/pandagen.h b/ets2panda/compiler/core/pandagen.h index ba4176d6d3..330ea7f422 100644 --- a/ets2panda/compiler/core/pandagen.h +++ b/ets2panda/compiler/core/pandagen.h @@ -72,6 +72,7 @@ public: [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override; [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override; + [[nodiscard]] IRNode *AllocSpillMov(const ir::AstNode *node, VReg vd, VReg vs, OperandType type) override; void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); void LoadAccumulatorDouble(const ir::AstNode *node, double num); diff --git a/ets2panda/compiler/core/regAllocator.cpp b/ets2panda/compiler/core/regAllocator.cpp index 9f17711474..bcfda2de50 100644 --- a/ets2panda/compiler/core/regAllocator.cpp +++ b/ets2panda/compiler/core/regAllocator.cpp @@ -84,28 +84,6 @@ const RegSpiller &RegAllocatorBase::Spiller() const noexcept return *spiller_; } -std::pair RegAllocatorBase::RegIndicesValid(const IRNode *const ins, const Span ®isters) -{ - const auto &formats = ins->GetFormats(); - std::size_t limit = 0; - - for (const auto &format : formats) { - for (const auto &formatItem : format.GetFormatItem()) { - if (formatItem.IsVReg()) { - limit = 1U << formatItem.BitWidth(); - break; - } - } - - if (std::all_of(registers.begin(), registers.end(), - [limit](const VReg *const reg) { return reg->IsValid(limit); })) { - return {true, limit}; - } - } - - return {false, limit}; -} - VReg RegAllocatorBase::Spill(IRNode *const ins, const VReg reg) const { const auto [spill_info, origin_type] = spiller_->New(); @@ -142,65 +120,149 @@ void RegAllocatorBase::Restore(const IRNode *const ins) const RegAllocator::RegAllocator(CodeGen *const cg, RegSpiller *const spiller) noexcept : RegAllocatorBase(cg, spiller) {} -void RegAllocator::Run(IRNode *const ins, const int32_t spillMax) +static bool IsInsAllRegsValid(IRNode *ins, const Span ®isters, uint32_t regsNum) +{ + const auto limit = ins->GetRegLimit(); + return std::all_of(registers.begin(), registers.end(), + [limit, regsNum](const VReg *reg) { return reg->IsRegOrParamValid(limit, regsNum); }); +} + +void RegAllocator::Run(IRNode *const ins, uint32_t realRegCount) { ES2PANDA_ASSERT(Spiller().Restored()); ES2PANDA_ASSERT(ins != nullptr); std::array regs {}; const auto regCnt = ins->Registers(®s); - const auto registers = - Span(regs.data(), regs.data() + (spillMax == std::numeric_limits::max() ? regCnt : spillMax)); + auto realRegCnt = std::min(realRegCount, static_cast(regCnt)); + if (realRegCnt > 0) { + const auto registers = Span(regs.data(), regs.data() + realRegCnt); + Spiller().UpdateSpillRegCount(realRegCnt); - std::array dstRegs {}; - ins->OutRegisters(&dstRegs); - - const auto [indices_valid, limit] = RegIndicesValid(ins, registers); - if (indices_valid) { - PushBack(ins); - return; + if (!Spiller().HasSpill() && !IsInsAllRegsValid(ins, registers, GetCodeGen().GetRegsNum())) { + Spiller().SetHasSpill(); + } } + ins->SetRealRegCount(realRegCnt); + PushBack(ins); +} - const auto rs = Spiller().Start(GetCodeGen()); - - std::unordered_set validRegs; - for (auto *const reg : registers) { - if (!reg->IsValid(limit)) { - continue; +bool RegAllocator::CheckFinalInsNeedSpill() +{ + const auto &insns = GetCodeGen().GetInsns(); + return std::all_of(insns.begin(), insns.end(), [this](IRNode *ins) { + uint32_t checkRegCnt = 0; + if (!ins->IsRangeInst()) { + checkRegCnt = ins->GetRealRegCount(); + } else { + std::array regs {}; + checkRegCnt = ins->Registers(®s); } + if (checkRegCnt == 0) { + return true; + } + std::array regs {}; + ins->Registers(®s); + const auto registers = Span(regs.data(), regs.data() + checkRegCnt); + return IsInsAllRegsValid(ins, registers, GetCodeGen().TotalRegsNum()); + }); +} - validRegs.insert(*reg); +void RegAllocator::AdjustInsRegWhenHasSpill() +{ + const auto spillRegCount = Spiller().GetSpillRegCount(); + if (spillRegCount == 0 || + // here we need to check again because during allocation, + // used regs is changing and parameter regs may become invalid + (!Spiller().HasSpill() && CheckFinalInsNeedSpill())) { + Spiller().ResetSpill(); + return; } + ES2PANDA_ASSERT(spillRegCount + GetCodeGen().GetRegsNum() < VReg::REG_MAX); + GetCodeGen().AddSpillRegsToUsedRegs(spillRegCount); + + ArenaVector newInsns(GetCodeGen().Allocator()->Adapter()); + auto &insns = GetCodeGen().GetInsns(); + const auto funcRegsNum = GetCodeGen().GetRegsNum(); + for (auto *ins : insns) { + std::array regs {}; + auto regCnt = ins->Registers(®s); + if (regCnt == 0) { // skip ins without regs + newInsns.push_back(ins); + continue; + } + // min is for excluding dummy regs + auto registersSize = std::min(regCnt, static_cast(ins->GetRealRegCount())); + auto registers = Span(regs.data(), regs.data() + registersSize); + for (auto *reg : registers) { + ES2PANDA_ASSERT(reg != nullptr); + if (!reg->IsParameter()) { + reg->SetIndex(reg->GetIndex() - spillRegCount); + } + } - std::vector dstMoves; - size_t i = 0; - for (auto *const reg : registers) { - auto dstInfo = dstRegs[i++]; - if (reg->IsValid(limit)) { + if (IsInsAllRegsValid(ins, registers, funcRegsNum)) { + // current ins has no spill, continue iterating + newInsns.push_back(ins); continue; } - Spiller().Adjust(validRegs); + // current ins has spill + if (ins->IsRangeInst()) { + AdjustRangeInsSpill(ins, newInsns); + } else { + AdjustInsSpill(registers, ins, newInsns); + } + } + GetCodeGen().SetInsns(newInsns); + Spiller().ResetSpill(); +} - auto r = Spill(ins, *reg); +class DstRegSpillInfo { +public: + VReg vd; + VReg vs; + OperandType type; + DstRegSpillInfo(VReg d, VReg s, OperandType t) : vd(d), vs(s), type(t) {} +}; - if (dstInfo.reg != nullptr) { - dstMoves.push_back(GetCodeGen().AllocMov(ins->Node(), dstInfo, r)); +void RegAllocator::AdjustInsSpill(const Span ®isters, IRNode *ins, ArenaVector &newInsns) +{ + VReg::Index spillIndex = VReg::REG_START; + std::vector dstRegSpills; + const auto realRegCount = ins->GetRealRegCount(); + + uint32_t idx = 0; + const auto limit = ins->GetRegLimit(); + for (auto *reg : registers) { + if (idx >= realRegCount) { + break; + } + if (reg->IsRegOrParamValid(limit, GetCodeGen().GetRegsNum())) { + idx++; + continue; } - *reg = r; + const auto &originReg = *reg; + VReg spillReg(spillIndex); + auto opType = ins->GetOperandRegType(idx); + auto kind = ins->GetOperandRegKind(idx); + if (kind == OperandKind::SRC_VREG || kind == OperandKind::SRC_DST_VREG) { + auto *mov = GetCodeGen().AllocSpillMov(ins->Node(), spillReg, originReg, opType); + newInsns.push_back(mov); + } + if (kind == OperandKind::DST_VREG || kind == OperandKind::SRC_DST_VREG) { + dstRegSpills.emplace_back(originReg, spillReg, opType); + } + reg->SetIndex(spillIndex--); + idx++; } - PushBack(ins); - - for (auto *mov : dstMoves) { - PushBack(mov); - } + newInsns.push_back(ins); - while (!Spiller().Restored()) { - Restore(ins); + for (auto spill : dstRegSpills) { + auto *mov = GetCodeGen().AllocSpillMov(ins->Node(), spill.vd, spill.vs, spill.type); + newInsns.push_back(mov); } - - Spiller().Finalize(); } // RangeRegAllocator @@ -210,47 +272,62 @@ RangeRegAllocator::RangeRegAllocator(CodeGen *const cg, RegSpiller *const spille { } -void RangeRegAllocator::Run(IRNode *const ins, VReg rangeStart, const std::size_t argCount) +void RangeRegAllocator::Run(IRNode *const ins, [[maybe_unused]] VReg rangeStart, const std::size_t argCount) { ES2PANDA_ASSERT(Spiller().Restored()); ES2PANDA_ASSERT(ins != nullptr); - const auto rangeEnd = rangeStart + argCount; - std::array regs {}; const auto regCnt = ins->Registers(®s); + ES2PANDA_ASSERT(regCnt > 0); const auto registers = Span(regs.data(), regs.data() + regCnt); - if (RegIndicesValid(ins, registers).first) { - PushBack(ins); - return; + const auto realRegCount = regCnt + argCount - 1; // minus 1: because of double counting for start range reg + Spiller().UpdateSpillRegCount(realRegCount); + if (!Spiller().HasSpill() && !IsInsAllRegsValid(ins, registers, GetCodeGen().GetRegsNum())) { + Spiller().SetHasSpill(); } - - const auto rs = Spiller().Start(GetCodeGen()); - - auto regIter = registers.begin(); - const auto regIterEnd = regIter + registers.size() - 1; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - - while (regIter != regIterEnd) { - auto *const reg = *regIter; - - *reg = Spill(ins, *reg); - regIter++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - } - - auto *const regStartReg = *regIter; - auto reg = rangeStart++; - *regStartReg = Spill(ins, reg); - - while (rangeStart != rangeEnd) { - reg = rangeStart++; - Spill(ins, reg); - } - + ins->SetRealRegCount(realRegCount); PushBack(ins); +} - while (!Spiller().Restored()) { - Restore(ins); +void RegAllocator::AdjustRangeInsSpill(IRNode *ins, ArenaVector &newInsns) +{ + const auto realRegCount = ins->GetRealRegCount(); + std::array regs {}; + const auto regCnt = ins->Registers(®s); + ES2PANDA_ASSERT(regCnt >= 1); + const uint32_t insLastRegIdx = regCnt - 1; + VReg::Index spillIndex = VReg::REG_START; + const auto startRegIndex = regs[insLastRegIdx]->GetIndex(); // the last reg is the range start reg + const auto funcRegsNum = GetCodeGen().GetRegsNum(); + const auto limit = ins->GetRegLimit(); + for (uint32_t idx = 0; idx < realRegCount; ++idx) { + VReg::Index regIndex; + if (idx <= insLastRegIdx) { + // regs[0] ~ regs[insLastRegIdx]: if not suits for limit, then need to + // record reg for spill, and update reg with spillIndex + auto *currentReg = regs[idx]; + if (!currentReg->IsRegOrParamValid(limit, funcRegsNum)) { + regIndex = currentReg->GetIndex(); + currentReg->SetIndex(spillIndex); + } else if (idx < insLastRegIdx) { + continue; // no need to spill current reg + } else { + ES2PANDA_ASSERT(idx == insLastRegIdx); + ES2PANDA_ASSERT(currentReg->IsRegOrParamValid(limit, funcRegsNum)); + // if range start reg suits for limit, no need to spill all range regs + break; + } + } else { + // obtain range reg index from the start reg + regIndex = startRegIndex + (insLastRegIdx - idx); + } + // do spill + VReg spillReg(spillIndex); + const auto originReg = VReg(regIndex); + auto *mov = GetCodeGen().AllocSpillMov(ins->Node(), spillReg, originReg, ins->GetOperandRegType(idx)); + newInsns.push_back(mov); + spillIndex--; } - - Spiller().Finalize(); + newInsns.push_back(ins); } } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/regAllocator.h b/ets2panda/compiler/core/regAllocator.h index 2f48a9a674..34fcee3b65 100644 --- a/ets2panda/compiler/core/regAllocator.h +++ b/ets2panda/compiler/core/regAllocator.h @@ -88,8 +88,6 @@ protected: VReg Spill(IRNode *ins, VReg reg) const; void Restore(const IRNode *ins) const; - [[nodiscard]] static std::pair RegIndicesValid(const IRNode *ins, const Span ®isters); - private: RegSpiller *spiller_; }; @@ -101,15 +99,23 @@ public: NO_MOVE_SEMANTIC(RegAllocator); ~RegAllocator() = default; - template ::max(), typename... Args> + const constexpr static auto MAX_SPILL_MAX = std::numeric_limits::max(); + + template void Emit(const ir::AstNode *const node, Args &&...args) { auto *const ins = Alloc(node, std::forward(args)...); Run(ins, VALID_VREGS); } + void AdjustInsRegWhenHasSpill(); + private: - void Run(IRNode *ins, int32_t spillMax); + void Run(IRNode *ins, uint32_t realRegCount); + + void AdjustInsSpill(const Span ®isters, IRNode *ins, ArenaVector &newInsns); + void AdjustRangeInsSpill(IRNode *ins, ArenaVector &newInsns); + bool CheckFinalInsNeedSpill(); }; class RangeRegAllocator final : public RegAllocatorBase { @@ -124,6 +130,7 @@ public: { auto *const ins = Alloc(node, std::forward(args)...); Run(ins, rangeStart, argCount); + ins->SetIsRangeInst(); } private: diff --git a/ets2panda/compiler/core/regSpiller.h b/ets2panda/compiler/core/regSpiller.h index 24326f7b59..1112792bb3 100644 --- a/ets2panda/compiler/core/regSpiller.h +++ b/ets2panda/compiler/core/regSpiller.h @@ -56,6 +56,28 @@ public: [[nodiscard]] CodeGen *GetCodeGen() const noexcept; [[nodiscard]] std::pair New() noexcept; void Adjust(const std::unordered_set ®s) noexcept; + bool HasSpill() const + { + return hasSpill_; + } + void SetHasSpill() + { + hasSpill_ = true; + } + uint32_t GetSpillRegCount() const + { + return spillRegs_; + } + void UpdateSpillRegCount(uint32_t arg) + { + spillRegs_ = std::max(spillRegs_, arg); + } + void ResetSpill() + { + spillRegs_ = 0; + hasSpill_ = false; + spillIndex_ = 0; + } protected: void SetCodeGen(CodeGen &cg) noexcept; @@ -65,6 +87,8 @@ protected: private: CodeGen *cg_ {}; std::uint32_t spillIndex_ {0}; + uint32_t spillRegs_ = 0; + bool hasSpill_ = false; }; class DynamicRegSpiller final : public RegSpiller { diff --git a/ets2panda/compiler/core/vReg.h b/ets2panda/compiler/core/vReg.h index d8a8e76377..3cd27614f5 100644 --- a/ets2panda/compiler/core/vReg.h +++ b/ets2panda/compiler/core/vReg.h @@ -54,9 +54,22 @@ public: [[nodiscard]] constexpr bool IsValid(uint32_t limit) const noexcept { + ES2PANDA_ASSERT(limit <= REG_MAX); return (idx_ >= REG_MAX - limit) && (limit == REG_MAX || !IsParameter()); } + [[nodiscard]] constexpr bool IsRegOrParamValid(uint32_t limit, uint32_t regNum) const noexcept + { + if (IsParameter()) { + ES2PANDA_ASSERT(idx_ >= REG_MAX); + uint32_t paramOrder = idx_ - REG_MAX; + ES2PANDA_ASSERT(paramOrder < INVALID_IDX); + ES2PANDA_ASSERT(regNum < INVALID_IDX - paramOrder); + return paramOrder + regNum < limit; + } + return IsValid(limit); + } + [[nodiscard]] constexpr bool IsParameter() const noexcept { return idx_ >= PARAM_START; diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 2f938ace7c..744f17439a 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -30,6 +30,10 @@ defines: ref: GET_INDEX_METHOD - name: $_set ref: SET_INDEX_METHOD + - name: + ref: GETTER_METHOD_BEGIN + - name: + ref: SETTER_METHOD_BEGIN - name: $_iterator ref: ITERATOR_METHOD - name: Iterator diff --git a/ets2panda/compiler/templates/formats.h.erb b/ets2panda/compiler/templates/formats.h.erb index 5468eb38dc..b15bc6ff58 100644 --- a/ets2panda/compiler/templates/formats.h.erb +++ b/ets2panda/compiler/templates/formats.h.erb @@ -4,6 +4,15 @@ % def get_format_item_name(mnemonic, index) % return "#{mnemonic.gsub('.', '_').upcase}" + "_FORMAT_ITEMS_" + index.to_s % end +% def get_reg_limit_name(mnemonic) +% return "#{mnemonic.gsub('.', '_').upcase}" + "_REG_LIMIT" +% end +% def get_operand_reg_kind_name(mnemonic) +% return "#{mnemonic.gsub('.', '_').upcase}" + "_OPERAND_REG_KIND" +% end + + + /** * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -50,14 +59,42 @@ namespace ark::es2panda::compiler { % end % end % +% def get_operand_reg_kind(op) +% if op.reg? +% if op.src? and op.dst? +% return "OperandKind::SRC_DST_VREG" +% elsif op.src? +% return "OperandKind::SRC_VREG" +% elsif op.dst? +% return "OperandKind::DST_VREG" +% end +% else +% return nil +% end +% end +% % def make_format(fmt, insn) % operands = fmt.operands.map {|op| "{#{get_operand_kind(op, insn)}, #{op.width}}"} % return operands % end + +% def get_reg_limit_for_format(fmt) +% limit_arr = fmt.operands.select {|op| op.reg?}.map {|op| op.width} +% return limit_arr.length == 0 ? 0 : limit_arr.max +% end % +% def make_operand_reg_kind(fmt) +% operand_reg_kind = fmt.operands.select {|op| op.reg?}.map{|op| "#{get_operand_reg_kind(op)}"} +% return operand_reg_kind +% end +constexpr std::array LABEL_OPERAND_REG_KIND = { }; % Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| % insn = group.first % formats = group.map {|i| make_format(i,insn) } +% reg_limit_widths = group.map {|i| get_reg_limit_for_format(i)} +% reg_limit = reg_limit_widths.length == 0 ? 0 : 1 << reg_limit_widths.max +% operand_reg_kind_arr = group.map {|i| make_operand_reg_kind(i) } +% operand_reg_kind = operand_reg_kind_arr.length == 0 ? [] : operand_reg_kind_arr[0] % empty = false % formats.each.with_index do |fmt, index| % if fmt.length != 0 @@ -78,6 +115,8 @@ constexpr std::array> <%= get_format_name(mn % }}; % end +constexpr uint32_t <%= get_reg_limit_name(mnemonic) %> = <%= reg_limit %>; +constexpr std::array> <%= get_operand_reg_kind_name(mnemonic) %> = { <%= operand_reg_kind.length == 0 ? " " : operand_reg_kind.join(", ")%>}; % end } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/templates/isa.h.erb b/ets2panda/compiler/templates/isa.h.erb index 87789468d6..27260919d7 100644 --- a/ets2panda/compiler/templates/isa.h.erb +++ b/ets2panda/compiler/templates/isa.h.erb @@ -5,6 +5,13 @@ % def get_format_name(mnemonic) % return "#{mnemonic.gsub('.', '_').upcase}" + "_FORMATS" % end +% def get_reg_limit_name(mnemonic) +% return "#{mnemonic.gsub('.', '_').upcase}" + "_REG_LIMIT" +% end +% def get_operand_reg_kind_name(mnemonic) +% return "#{mnemonic.gsub('.', '_').upcase}" + "_OPERAND_REG_KIND" +% end + /** * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,7 +34,11 @@ #include "ir/irnode.h" #include "generated/formats.h" +#include "generated/signatures.h" + +#include "assembly-function.h" #include "assembly-ins.h" +#include "assembly-type.h" namespace ark::es2panda::compiler { class Label : public IRNode { @@ -61,6 +72,21 @@ public: return 0; } + OperandType GetOperandRegType([[maybe_unused]] size_t idx) const override + { + return OperandType::NONE; + } + + uint32_t GetRegLimit() const noexcept override + { + return 0U; + } + + OperandKind GetOperandRegKind([[maybe_unused]] size_t idx) const override + { + return LABEL_OPERAND_REG_KIND[idx]; + } + void Transform(pandasm::Ins *ins, [[maybe_unused]] ProgramElement *programElement, [[maybe_unused]] uint32_t totalRegs) const override { @@ -72,6 +98,79 @@ private: std::string id_; }; +static bool IsAccessor(const std::string &calleeInternalName) +{ + auto methodEndPos = calleeInternalName.find(Signatures::MANGLE_BEGIN); + if (methodEndPos == std::string::npos) { + return false; + } + auto pos = calleeInternalName.rfind(Signatures::METHOD_SEPARATOR, methodEndPos); + if (pos == std::string::npos) { + return false; + } + auto methodBeginPos = pos + 1; + // case 1. getter: "xxx.PropName:PropType;" name start with '' + auto checkGetterPos = calleeInternalName.find(Signatures::GETTER_METHOD_BEGIN, methodBeginPos); + if (checkGetterPos == methodBeginPos) { + return true; + } + // case 2. setter: "xxx.PropName:PropType;void;" name start with '' + auto checkSetterPos = calleeInternalName.find(Signatures::SETTER_METHOD_BEGIN, methodBeginPos); + if (checkSetterPos == methodBeginPos) { + return true; + } + + // case 3. indexed getter: "xxx.$_get:ValueType;" name is '$_get' + // case 4. indexed setter: "xxx.$_set:IndexType;ValueType;void;" name is '$_set' + ES2PANDA_ASSERT(Signatures::GET_INDEX_METHOD.length() == Signatures::SET_INDEX_METHOD.length()); + const auto indexMethodLen = Signatures::GET_INDEX_METHOD.length(); + if (methodEndPos != methodBeginPos + indexMethodLen) { + return false; + } + auto methodName = calleeInternalName.substr(methodBeginPos, indexMethodLen); + return methodName == Signatures::GET_INDEX_METHOD || methodName == Signatures::SET_INDEX_METHOD; +} + +static OperandType GetOperandRegTypeForNormalCall(const std::string &calleeInternalName, size_t argIdx) +{ + const std::string &str = calleeInternalName; + auto pos = str.find(Signatures::MANGLE_BEGIN); + ES2PANDA_ASSERT(pos != std::string::npos); + for (size_t i = 0; i < argIdx; ++i) { + pos = str.find(Signatures::MANGLE_SEPARATOR, pos + 1); + ES2PANDA_ASSERT(pos != std::string::npos); + } + auto end = str.find(Signatures::MANGLE_SEPARATOR, pos + 1); + ES2PANDA_ASSERT(end != std::string::npos); + ES2PANDA_ASSERT(end > pos + 1); + auto argTypeName = str.substr(pos + 1, end - pos - 1); + auto argType = ark::pandasm::Type::FromName(argTypeName); + if (argType.IsPrim32()) { + return OperandType::B32; + } + if (argType.IsPrim64()) { + return OperandType::B64; + } + return OperandType::REF; +} + +static OperandType GetOperandRegTypeForAccessor(const std::string &calleeInternalName, size_t argIdx) +{ + ES2PANDA_ASSERT(IsAccessor(calleeInternalName)); + if (argIdx == 0) { + return OperandType::REF; // the first arg is the omitted 'this' + } + return GetOperandRegTypeForNormalCall(calleeInternalName, argIdx - 1); +} + +static OperandType GetOperandRegTypeForCall(const std::string &calleeInternalName, size_t argIdx) +{ + if (IsAccessor(calleeInternalName)) { // in such cases method signatures lacks the first arg 'this' + return GetOperandRegTypeForAccessor(calleeInternalName, argIdx); + } + return GetOperandRegTypeForNormalCall(calleeInternalName, argIdx); +} + // NOLINTBEGIN(readability-identifier-naming) % def insn2node(insn) % mnemonic = insn.mnemonic.split('.') @@ -88,6 +187,7 @@ private: % param_name = name % if operand.reg? % param_name = "#{name}#{op_map['reg'].size}" +% op_map['reg_type'].push(["#{param_name}_", operand.type]) % op_map['reg'].push("#{param_name}_") % op_map['dreg'].push(["#{param_name}_", operand.type]) if operand.dst? % op_map['dreg'].push([nil, nil]) unless operand.dst? @@ -169,6 +269,7 @@ public: % return 'B64' if type == 'f64' || type == 'i64' || type == 'b64' % return 'B32' % end + % for reg, type in op_map['dreg'] % if reg (*regs)[<%= reg_cnt %>] = {&<%= reg %>, OperandType::<%= type_to_enum(type) %>}; @@ -180,6 +281,54 @@ public: return <%= reg_cnt %>; } +% def type_to_mov_kind(type) +% return 'REF' if type == 'ref' +% return 'REF' if type.include? '[]' +% return 'B64' if type == 'i64' || type == 'u64' || type == 'f64' || type == 'b64' +% return 'ANY' if type == 'any' +% return 'B32' if type == 'u1' || type == 'u2' || type == 'u4' || \ +% type == 'i8' || type == 'u8' || type == 'b8' || \ +% type == 'i16' || type == 'u16' || type == 'b16' || \ +% type == 'i32' || type == 'u32' || type == 'f32' || type == 'b32' +% return 'NONE' if type == 'top' +% raise "invalid reg type #{type}" +% end + + OperandType GetOperandRegType([[maybe_unused]] size_t idx) const override { +% if insn.properties.any? { |prop| prop.include?('method_id') } # get operand type from method_id + return GetOperandRegTypeForCall(string_id_.Mutf8(), idx); +% elsif insn.mnemonic.start_with?('any.') && insn.mnemonic.end_with?('.range') # any.x.range always have operand type REG + ES2PANDA_ASSERT(imm0_ >= 0); + ES2PANDA_ASSERT(idx <= static_cast(imm0_)); + return OperandType::REF; +% else # get operand type from reg type defined in isa.yaml + switch (idx) { +% i = 0 +% for reg, type in op_map['reg_type'] + case <%= i %>: { + return OperandType::<%= type_to_mov_kind(type) %>; + } +% i+=1 +% end + default: { + ES2PANDA_UNREACHABLE(); + break; + } + } + return OperandType::NONE; +% end + } + + uint32_t GetRegLimit() const noexcept override + { + return <%= get_reg_limit_name(insn.mnemonic) %>; + } + + OperandKind GetOperandRegKind(size_t idx) const override + { + return <%= get_operand_reg_kind_name(insn.mnemonic) %>[idx]; + } + void Transform(pandasm::Ins *ins, [[maybe_unused]] ProgramElement *programElement, [[maybe_unused]] uint32_t totalRegs) const override { ins->opcode = pandasm::Opcode::<%= node_kind %>; diff --git a/ets2panda/ir/irnode.h b/ets2panda/ir/irnode.h index 84f269da1f..9abba2cff3 100644 --- a/ets2panda/ir/irnode.h +++ b/ets2panda/ir/irnode.h @@ -144,6 +144,9 @@ public: } static constexpr auto MAX_REG_OPERAND = 5; + virtual OperandType GetOperandRegType([[maybe_unused]] size_t idx) const = 0; + virtual uint32_t GetRegLimit() const noexcept = 0; + virtual OperandKind GetOperandRegKind(size_t idx) const = 0; virtual Formats GetFormats() const = 0; virtual size_t Registers([[maybe_unused]] std::array *regs) = 0; @@ -151,9 +154,29 @@ public: virtual size_t OutRegisters([[maybe_unused]] std::array *regs) const = 0; virtual void Transform(ark::pandasm::Ins *ins, [[maybe_unused]] ProgramElement *programElement, [[maybe_unused]] uint32_t totalRegs) const = 0; + // for non-range ins, realRegCount_ is the number of non-dummy regs contained in the ins + // for range ins, realRegCount_ is the number of regs contained in the ins and range regs + uint32_t GetRealRegCount() const + { + return realRegCount_; + } + void SetRealRegCount(uint32_t s) + { + realRegCount_ = s; + } + bool IsRangeInst() const + { + return isRange_; + } + void SetIsRangeInst() + { + isRange_ = true; + } private: const ir::AstNode *node_; + uint32_t realRegCount_ = 0; + bool isRange_ = false; }; } // namespace ark::es2panda::compiler diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index 0d4c20f3e5..6b783c417f 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(annotations) add_subdirectory(lsp) add_subdirectory(relative_path) add_subdirectory(any_ins_test) +add_subdirectory(reg_allocator) ets2panda_add_gtest(es2panda_astdumper_tests CPP_SOURCES ast_dumper_test.cpp diff --git a/ets2panda/test/unit/reg_allocator/CMakeLists.txt b/ets2panda/test/unit/reg_allocator/CMakeLists.txt new file mode 100644 index 0000000000..a9e3a6111c --- /dev/null +++ b/ets2panda/test/unit/reg_allocator/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ets2panda_add_gtest(reg_allocator + CPP_SOURCES reg_allocator.cpp +) diff --git a/ets2panda/test/unit/reg_allocator/reg_allocator.cpp b/ets2panda/test/unit/reg_allocator/reg_allocator.cpp new file mode 100644 index 0000000000..ec9e95fa52 --- /dev/null +++ b/ets2panda/test/unit/reg_allocator/reg_allocator.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "assembly-emitter.h" +#include "assembly-program.h" +#include "test/utils/asm_test.h" + +namespace ark::es2panda::compiler::test { + +class RegAllocator : public ::test::utils::AsmTest { +public: + RegAllocator() = default; + + ~RegAllocator() override = default; + + void RunAnnotationEmitTest(const std::string_view text) + { + auto program = GetCurrentProgram(text); + EXPECT_NE(program, nullptr); + pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps; + static const std::string fileName = "reg_allocator_test"; + auto pfile = pandasm::AsmEmitter::Emit(fileName, *program, nullptr, &maps); + EXPECT_NE(pfile, false); + } + +private: + NO_COPY_SEMANTIC(RegAllocator); + NO_MOVE_SEMANTIC(RegAllocator); +}; + +TEST_F(RegAllocator, TryCatch) +{ + std::string_view text = R"( + function throwable(x: int, throwError: boolean): int { + if (throwError) { + throw new Error('err') + } + return x + } + function foo() { + let x0 = 0 + let x1 = 1 + let x2 = 2 + let x3 = 3 + let x4 = 4 + let x5 = 5 + let x6 = 6 + let x7 = 7 + let x8 = 8 + let x9 = 9 + let x10 = 10 + let x11 = 11 + let x12 = 12 + let x13 = 13 + let x14 = 14 + let x15 = 15 + let ret: int = 16 + try { ret = throwable(x15, true) } catch (e) {} + console.log(ret); + } + )"; + + RunAnnotationEmitTest(text); +} + +} // namespace ark::es2panda::compiler::test -- Gitee