diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 5b08e72fe6e600776d6686a52e1bd0f32e7d4ef2..65ae475f4fdcfa779a03a9ec268c3c909706873b 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -193,6 +193,19 @@ IRNode *ETSGen::AllocMov(const ir::AstNode *const node, const VReg vd, const VRe return mov; } +void ETSGen::AddMov(const ir::AstNode *const node, const VReg vd, const VReg vs) +{ + const auto *const sourceType = GetVRegType(vs); + if (sourceType->IsETSReferenceType()) { + Ra().Emit(node, vd, vs); + } else if (IsWidePrimitiveType(sourceType)) { + Ra().Emit(node, vd, vs); + } else { + Ra().Emit(node, vd, vs); + } + SetVRegType(vd, sourceType); +} + IRNode *ETSGen::AllocMov(const ir::AstNode *const node, OutVReg vd, const VReg vs) { ES2PANDA_ASSERT(vd.type != OperandType::ANY && vd.type != OperandType::NONE); @@ -830,16 +843,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); @@ -1698,7 +1712,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) @@ -1734,15 +1748,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); } } diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 71ce602b2cb96500fec0689a1ffb17d324e71db5..dafb3351606130769f5f583dbb0e75863343871a 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -325,7 +325,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, @@ -590,20 +590,30 @@ private: } } + void AddMov(const ir::AstNode *const node, const VReg vd, const VReg vs); template void BinaryArithmetic(const ir::AstNode *node, VReg lhs); template void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs); - // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) -#define COMPILE_ARG(idx) \ +#define PREPARE_ARG_WITH_IDX(idx) \ ES2PANDA_ASSERT((idx) < arguments.size()); \ ES2PANDA_ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr); \ auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \ auto *paramType##idx = param##idx->TsType(); \ auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ - arguments[idx]->Compile(this); \ + arguments[idx]->Compile(this) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) +#define COMPILE_ARG_WITH_REG(idx, arg) \ + PREPARE_ARG_WITH_IDX(idx); \ + ApplyConversion(arguments[idx], nullptr); \ + ApplyConversionAndStoreAccumulator(arguments[idx], arg, paramType##idx) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) +#define COMPILE_ARG(idx) \ + PREPARE_ARG_WITH_IDX(idx); \ VReg arg##idx = AllocReg(); \ ApplyConversion(arguments[idx], nullptr); \ ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) @@ -639,11 +649,22 @@ private: break; } default: { + auto realArgStart = argStart; + // if start and args are not consecutive, alloc a new reg for start + if (argStart.GetIndex() != NextReg().GetIndex() - 1) { + realArgStart = AllocReg(); + AddMov(node, realArgStart, argStart); + } + std::vector args; + args.reserve(arguments.size()); for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ARG(idx); + args.emplace_back(AllocReg()); + } + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ARG_WITH_REG(idx, args[idx]); } - Rra().Emit(node, argStart, arguments.size() + 1, name, argStart); + Rra().Emit(node, realArgStart, arguments.size() + 1, name, realArgStart); break; } } @@ -689,9 +710,13 @@ private: } default: { VReg argStart = NextReg(); - + std::vector args; + args.reserve(arguments.size()); + for (size_t idx = 0; idx < arguments.size(); idx++) { + args.emplace_back(AllocReg()); + } for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ARG(idx); + COMPILE_ARG_WITH_REG(idx, args[idx]); } Rra().Emit(node, argStart, arguments.size(), signature->InternalName(), argStart); @@ -700,14 +725,24 @@ private: } } #undef COMPILE_ARG +#undef COMPILE_ARG_WITH_REG +#undef PREPARE_ARG_WITH_IDX -#define COMPILE_ARG(idx, shift) \ +#define PREPARE_ARG_WITH_IDX(idx, shift) \ ES2PANDA_ASSERT((idx) < arguments.size()); \ ES2PANDA_ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \ auto *paramType##idx = (idx) + (shift) < signature->Params().size() \ ? signature->Params()[(idx) + (shift)]->TsType() \ : signature->RestVar()->TsType(); \ - auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ + auto ttctx##idx = TargetTypeContext(this, paramType##idx) + +#define COMPILE_ARG_WITH_REG(idx, shift, argReg) \ + PREPARE_ARG_WITH_IDX(idx, shift); \ + arguments[idx]->Compile(this); \ + ApplyConversionAndStoreAccumulator(arguments[idx], argReg, paramType##idx) + +#define COMPILE_ARG(idx, shift) \ + PREPARE_ARG_WITH_IDX(idx, shift); \ VReg arg##idx = AllocReg(); \ arguments[idx]->Compile(this); \ ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) @@ -736,11 +771,23 @@ private: break; } default: { + auto argStart = data.obj; + // if start and args are not consecutive, alloc a new reg for start + if (argStart.GetIndex() != NextReg().GetIndex() - 1) { + auto newReg = AllocReg(); + AddMov(data.node, newReg, argStart); + argStart = newReg; + } + std::vector args; + args.reserve(arguments.size()); + for (size_t idx = 0; idx < arguments.size(); idx++) { + args.emplace_back(AllocReg()); + } for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ARG(idx, 2U); + COMPILE_ARG_WITH_REG(idx, 2U, args[idx]); } - Rra().Emit(data.node, data.obj, arguments.size() + 2U, name, data.obj); + Rra().Emit(data.node, argStart, arguments.size() + 2U, name, argStart); break; } } @@ -764,24 +811,46 @@ private: break; } default: { + auto argStart = data.obj; + // if start and args are not consecutive, alloc a new reg for start + if (argStart.GetIndex() != NextReg().GetIndex() - 1) { + auto newReg = AllocReg(); + AddMov(data.node, newReg, argStart); + argStart = newReg; + } + std::vector args; + args.reserve(arguments.size()); + for (size_t idx = 0; idx < arguments.size(); idx++) { + args.emplace_back(AllocReg()); + } for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ARG(idx, 3U); + COMPILE_ARG_WITH_REG(idx, 3U, args[idx]); } - Rra().Emit(data.node, data.obj, arguments.size() + 3U, name, data.obj); + Rra().Emit(data.node, argStart, arguments.size() + 3U, name, argStart); break; } } } #undef COMPILE_ARG +#undef COMPILE_ARG_WITH_REG +#undef PREPARE_ARG_WITH_IDX -#define COMPILE_ANY_ARG(idx) \ +#define PREPARE_ANY_ARG_WITH_IDX(idx) \ ES2PANDA_ASSERT((idx) < arguments.size()); \ auto *param##idx = arguments[idx]; \ auto *paramType##idx = param##idx->TsType(); \ auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ - arguments[idx]->Compile(this); \ + arguments[idx]->Compile(this) + +#define COMPILE_ANY_ARG_WITH_REG(idx, argReg) \ + PREPARE_ANY_ARG_WITH_IDX(idx); \ + ApplyConversion(arguments[idx], nullptr); \ + ApplyConversionAndStoreAccumulator(arguments[idx], argReg, paramType##idx) + +#define COMPILE_ANY_ARG(idx) \ + PREPARE_ANY_ARG_WITH_IDX(idx); \ VReg arg##idx = AllocReg(); \ ApplyConversion(arguments[idx], nullptr); \ ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) @@ -805,8 +874,13 @@ private: } default: { VReg argStart = NextReg(); + std::vector args; + args.reserve(arguments.size()); for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ANY_ARG(idx); + args.emplace_back(AllocReg()); + } + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ANY_ARG_WITH_REG(idx, args[idx]); } Rra().Emit(node, argStart, arguments.size(), ident->Name(), athis, argStart, arguments.size()); @@ -831,8 +905,13 @@ private: } default: { VReg argStart = NextReg(); + std::vector args; + args.reserve(arguments.size()); + for (size_t idx = 0; idx < arguments.size(); idx++) { + args.emplace_back(AllocReg()); + } for (size_t idx = 0; idx < arguments.size(); idx++) { - COMPILE_ANY_ARG(idx); + COMPILE_ANY_ARG_WITH_REG(idx, args[idx]); } Rra().Emit(node, argStart, arguments.size(), athis, argStart, arguments.size()); @@ -840,6 +919,8 @@ private: } } #undef COMPILE_ANY_ARG +#undef COMPILE_ANY_ARG_WITH_REG +#undef PREPARE_ANY_ARG_WITH_IDX // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) void ToBinaryResult(const ir::AstNode *node, Label *ifFalse); diff --git a/ets2panda/compiler/core/ETSfunction.cpp b/ets2panda/compiler/core/ETSfunction.cpp index 672f29e5f6d859490a0ed258dfe6b640d5853390..73a087c4e7e8643f71dbce2bb14314ef169f55b5 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.cpp b/ets2panda/compiler/core/codeGen.cpp index fd01a1c9f22233b98eef8ca278a813014eea0587..70fd51db07c15b0c9b4b0de4d8f5d88ce4539016 100644 --- a/ets2panda/compiler/core/codeGen.cpp +++ b/ets2panda/compiler/core/codeGen.cpp @@ -101,6 +101,23 @@ void CodeGen::SetVRegType(const VReg vreg, const checker::Type *const type) typeMap_.insert_or_assign(vreg, type); } +void CodeGen::UpdateVRegTypeMapWithSpill(VReg::Index spillRegs) +{ + // since map key is const, we have to make a copy rather than modify regs directly + TypeMap newMap(allocator_->Adapter()); + newMap.reserve(typeMap_.size()); + for (const auto &[reg, type] : typeMap_) { + VReg::Index regIdx = reg.GetIndex(); + // parameter and acc are fixed regs, we should not modify them + if (!reg.IsParameter() && regIdx != std::numeric_limits::max()) { + ES2PANDA_ASSERT(regIdx >= spillRegs); + regIdx = regIdx - spillRegs; + } + newMap.insert_or_assign(VReg(regIdx), type); + } + typeMap_ = newMap; +} + const checker::Type *CodeGen::GetVRegType(const VReg vreg) const { const auto it = typeMap_.find(vreg); diff --git a/ets2panda/compiler/core/codeGen.h b/ets2panda/compiler/core/codeGen.h index 8fbeeb010606d820b488a2ddc2cbb144342c4f79..75ae7805663f6710529243f2d567c78cbda50558 100644 --- a/ets2panda/compiler/core/codeGen.h +++ b/ets2panda/compiler/core/codeGen.h @@ -145,6 +145,7 @@ public: [[noreturn]] static void Unimplemented(); void SetVRegType(VReg vreg, const checker::Type *type); + void UpdateVRegTypeMapWithSpill(VReg::Index spillRegs); [[nodiscard]] virtual const checker::Type *GetVRegType(VReg vreg) const; @@ -154,10 +155,39 @@ public: compiler::AstCompiler *GetAstCompiler() const; + const ArenaVector &GetInsns() const noexcept + { + return insns_; + } + + void SetInsns(const ArenaVector &insns) + { + insns_ = insns; + } + + void AddSpillRegs(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); + } + + uint32_t GetIndexOfNextReg() const + { + return usedRegs_; + } + + [[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 65863727490ecbd49f91417349b2595efb460fba..3fb7517712d807511a91995b6b6c8402024236ff 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,10 @@ 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 +167,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 +175,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 +216,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 e5c05ca78bfe3d808b61f171bb5099dc07e970d8..2304df5f7c146ed79c34e7093eab30302b841068 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 d4602f2bea1f95b76eb8524b511ff0543b2329a4..4e33b755fe3ec2a14b6a30230bf3f5fc3aab416c 100644 --- a/ets2panda/compiler/core/pandagen.cpp +++ b/ets2panda/compiler/core/pandagen.cpp @@ -1061,7 +1061,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; } @@ -1803,7 +1803,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/regAllocator.cpp b/ets2panda/compiler/core/regAllocator.cpp index 9f177114742c440a418767d1fbd172df2aa11f15..e2d148c744af4cb281117baf8da449913bcd65ea 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,177 @@ 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) +// to be optimized: use ruby to generate code which return result directly to avoid loop +static uint32_t GetLimit(IRNode *ins) +{ + auto formats = ins->GetFormats(); + uint32_t limit = 0; + for (const auto &format : formats) { + for (const auto &formatItem : format.GetFormatItem()) { + if (formatItem.IsVReg()) { + uint32_t tmp = 1 << formatItem.BitWidth(); + limit = std::max(tmp, limit); + } + } + } + return limit; +} +// to be optimized: use ruby to generate code which return result directly to avoid loop +static void GetOperandKind(IRNode *ins, std::vector *regsKind) +{ + ES2PANDA_ASSERT(regsKind != nullptr); + auto formats = ins->GetFormats(); + for (const auto &format : formats) { + for (const auto &formatItem : format.GetFormatItem()) { + if (formatItem.IsVReg()) { + regsKind->push_back(formatItem.Kind()); + } + } + } +} + +static bool CheckRegIndices(IRNode *ins, const Span ®isters, uint32_t regsNum) +{ + uint32_t limit = GetLimit(ins); + 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); + + if (!Spiller().HasSpill() && !CheckRegIndices(ins, registers, GetCodeGen().GetRegsNum())) { + Spiller().SetHasSpill(); + } + } + ins->SetRealRegCount(realRegCnt); + PushBack(ins); +} - std::array dstRegs {}; - ins->OutRegisters(&dstRegs); +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 CheckRegIndices(ins, registers, GetCodeGen().TotalRegsNum()); + }); +} - const auto [indices_valid, limit] = RegIndicesValid(ins, registers); - if (indices_valid) { - PushBack(ins); +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().UpdateVRegTypeMapWithSpill(spillRegCount); + GetCodeGen().AddSpillRegs(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); + } + } - const auto rs = Spiller().Start(GetCodeGen()); - - std::unordered_set validRegs; - for (auto *const reg : registers) { - if (!reg->IsValid(limit)) { + if (CheckRegIndices(ins, registers, funcRegsNum)) { + // current ins has no spill, continue iterating + newInsns.push_back(ins); continue; } - validRegs.insert(*reg); + // current ins has spill + const auto limit = GetLimit(ins); + if (ins->IsRangeInst()) { + AdjustRangeInsSpill(ins, newInsns, limit); + } else { + AdjustInsSpill(registers, ins, newInsns, limit); + } } + GetCodeGen().SetInsns(newInsns); + Spiller().ResetSpill(); +} - std::vector dstMoves; - size_t i = 0; - for (auto *const reg : registers) { - auto dstInfo = dstRegs[i++]; - if (reg->IsValid(limit)) { +void RegAllocator::AdjustInsSpill(const Span ®isters, IRNode *ins, ArenaVector &newInsns, + uint32_t limit) +{ + uint32_t idx = 0; + VReg::Index spillIndex = VReg::REG_START; + std::vector> dstRegSpills; + const auto realRegCount = ins->GetRealRegCount(); + + std::vector regsKind; + GetOperandKind(ins, ®sKind); + + for (auto *reg : registers) { + if (idx >= realRegCount) { break; } + if (reg->IsRegOrParamValid(limit, GetCodeGen().GetRegsNum())) { + idx++; continue; } - Spiller().Adjust(validRegs); - - auto r = Spill(ins, *reg); - - if (dstInfo.reg != nullptr) { - dstMoves.push_back(GetCodeGen().AllocMov(ins->Node(), dstInfo, r)); + const auto originReg = *reg; + VReg spillReg(spillIndex); + auto *originRegType = GetCodeGen().GetVRegType(*reg); + GetCodeGen().SetVRegType(originReg, originRegType); + GetCodeGen().SetVRegType(spillReg, originRegType); + ES2PANDA_ASSERT(idx < regsKind.size()); + const auto &kind = regsKind[idx]; + if (kind == OperandKind::SRC_VREG || kind == OperandKind::SRC_DST_VREG) { + auto *mov = GetCodeGen().AllocMov(ins->Node(), spillReg, originReg); + newInsns.push_back(mov); } - - *reg = r; + if (kind == OperandKind::DST_VREG || kind == OperandKind::SRC_DST_VREG) { + dstRegSpills.push_back(std::make_pair(originReg, spillReg)); + } + reg->SetIndex(spillIndex--); + idx++; } - PushBack(ins); - - for (auto *mov : dstMoves) { - PushBack(mov); - } + newInsns.push_back(ins); - while (!Spiller().Restored()) { - Restore(ins); + for (auto spillPair : dstRegSpills) { + auto *mov = GetCodeGen().AllocMov(ins->Node(), spillPair.first, spillPair.second); + newInsns.push_back(mov); } - - Spiller().Finalize(); } // RangeRegAllocator @@ -210,47 +300,65 @@ 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 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); + const auto realRegCount = regCnt + argCount - 1; // minus 1: because of double counting for start range reg + Spiller().UpdateSpillRegCount(realRegCount); + if (!Spiller().HasSpill() && !CheckRegIndices(ins, registers, GetCodeGen().GetRegsNum())) { + Spiller().SetHasSpill(); } - + ins->SetRealRegCount(realRegCount); PushBack(ins); +} - while (!Spiller().Restored()) { - Restore(ins); +void RegAllocator::AdjustRangeInsSpill(IRNode *ins, ArenaVector &newInsns, uint32_t limit) +{ + 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(); + 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 *originRegType = GetCodeGen().GetVRegType(originReg); + ES2PANDA_ASSERT(originRegType != nullptr); + GetCodeGen().SetVRegType(originReg, originRegType); + GetCodeGen().SetVRegType(spillReg, originRegType); + auto *mov = GetCodeGen().AllocMov(ins->Node(), spillReg, originReg); + 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 2f48a9a674721672c9b08917471833c38ca41fa7..18bba7e6f84c602a1d45c1bc57caa118e8735d76 100644 --- a/ets2panda/compiler/core/regAllocator.h +++ b/ets2panda/compiler/core/regAllocator.h @@ -33,7 +33,6 @@ public: NO_COPY_SEMANTIC(AllocatorBase); NO_MOVE_SEMANTIC(AllocatorBase); ~AllocatorBase() = default; - protected: void PushBack(IRNode *ins) const; [[nodiscard]] CodeGen &GetCodeGen() noexcept; @@ -88,8 +87,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 +98,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, uint32_t limit); + void AdjustRangeInsSpill(IRNode *ins, ArenaVector &newInsns, uint32_t limit); + bool CheckFinalInsNeedSpill(); }; class RangeRegAllocator final : public RegAllocatorBase { @@ -124,6 +129,7 @@ public: { auto *const ins = Alloc(node, std::forward(args)...); Run(ins, rangeStart, argCount); + ins->SetIsRangeInst(); } private: diff --git a/ets2panda/compiler/core/regScope.cpp b/ets2panda/compiler/core/regScope.cpp index f0ef27d3b8f76cce4480d69f41aa5c050eee71ff..e2c59886df4c361747a1f8f2a4e14ff1189dfaa3 100644 --- a/ets2panda/compiler/core/regScope.cpp +++ b/ets2panda/compiler/core/regScope.cpp @@ -31,7 +31,7 @@ RegScope::RegScope(CodeGen *cg) : cg_(cg), regBase_(cg_->usedRegs_) {} RegScope::~RegScope() { cg_->totalRegs_ = std::min(cg_->totalRegs_, cg_->usedRegs_); - cg_->usedRegs_ = regBase_; + cg_->usedRegs_ = std::min(regBase_, cg_->usedRegs_); } void RegScope::DebuggerCloseScope() diff --git a/ets2panda/compiler/core/regSpiller.h b/ets2panda/compiler/core/regSpiller.h index 24326f7b590e90aa346597e4c3de535f5194ff9e..1112792bb3bf6db939d817fbad9d07f1819c1a32 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 d8a8e763778ba66256623d054950dbf48a769d8b..3cd27614f5774fef03f4adbac139255fe847ca3d 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/ir/irnode.h b/ets2panda/ir/irnode.h index 84f269da1f3a6988cc6c3effb51e17278f6a8b0b..c9d5e28aa7c1136c004b08449f882d60d953fe19 100644 --- a/ets2panda/ir/irnode.h +++ b/ets2panda/ir/irnode.h @@ -151,9 +151,28 @@ 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 0d4c20f3e599456a6a09b59b89859efae1e91f9c..6b783c417fad22a0a8d9ce81e61a6372dc793df4 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 0000000000000000000000000000000000000000..01f03ff23547423d160cc7bf75739e8020d9a2ca --- /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 0000000000000000000000000000000000000000..d803ee4cd776b57d116699183641f28699104646 --- /dev/null +++ b/ets2panda/test/unit/reg_allocator/reg_allocator.cpp @@ -0,0 +1,76 @@ +/* + * 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-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); + } + +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