From 6c52054c362604811fdeccd9e181aa14f5428af6 Mon Sep 17 00:00:00 2001 From: Konstantin Grebenschikov Date: Tue, 9 Sep 2025 07:40:06 +0800 Subject: [PATCH 01/10] StringFlatteningCache: concurrency prerequisites Signed-off-by: Konstantin Grebenschikov --- static_core/irtoc/scripts/common.irt | 1 + .../plugins/ets/runtime/ets_coroutine.cpp | 19 ++++++++ static_core/plugins/ets/runtime/ets_vm.cpp | 12 +++++ .../runtime/asm_defines/asm_defines.def | 1 + .../runtime/coroutines/coroutine_manager.h | 13 ++++++ .../runtime/coroutines/coroutine_worker.cpp | 44 ++++++++++++++++++- .../runtime/coroutines/coroutine_worker.h | 13 +++++- .../runtime/coroutines/local_storage.h | 19 +++++++- .../coroutines/stackful_coroutine_manager.cpp | 11 +++++ .../coroutines/stackful_coroutine_manager.h | 2 + .../coroutines/stackful_coroutine_worker.cpp | 8 ++-- .../coroutines/stackful_coroutine_worker.h | 1 + .../coroutines/threaded_coroutine_manager.cpp | 11 +++++ .../coroutines/threaded_coroutine_manager.h | 2 + static_core/runtime/include/managed_thread.h | 11 +++++ static_core/runtime/thread.cpp | 16 ++++++- 16 files changed, 176 insertions(+), 8 deletions(-) diff --git a/static_core/irtoc/scripts/common.irt b/static_core/irtoc/scripts/common.irt index 3fc4376ef9..c661816a63 100644 --- a/static_core/irtoc/scripts/common.irt +++ b/static_core/irtoc/scripts/common.irt @@ -127,6 +127,7 @@ module Constants THREAD_FRAME_OFFSET = "cross_values::GetManagedThreadFrameOffset(GetArch())" THREAD_EXCEPTION_OFFSET = "cross_values::GetManagedThreadExceptionOffset(GetArch())" THREAD_INTERPRETER_CACHE_OFFSET = "cross_values::GetManagedThreadInterpreterCacheOffset(GetArch())" + THREAD_FLATTENED_STRING_CACHE_OFFSET = "cross_values::GetManagedThreadFlattenedStringCacheOffset(GetArch())" THREAD_FLAG_OFFSET = "cross_values::GetManagedThreadFlagOffset(GetArch())" THREAD_VM_OFFSET = "cross_values::GetThreadVmOffset(GetArch())" MARK_WORD_OFFSET = "cross_values::GetObjectHeaderMarkWordOffset(GetArch())" diff --git a/static_core/plugins/ets/runtime/ets_coroutine.cpp b/static_core/plugins/ets/runtime/ets_coroutine.cpp index ad8b3bd087..109f93ab0e 100644 --- a/static_core/plugins/ets/runtime/ets_coroutine.cpp +++ b/static_core/plugins/ets/runtime/ets_coroutine.cpp @@ -259,6 +259,25 @@ void EtsCoroutine::OnHostWorkerChanged() auto *worker = GetWorker(); auto *ptr = worker->GetLocalStorage().Get(); GetLocalStorage().Set(ptr); + + if (GetType() == Coroutine::Type::MUTATOR) { + // update the string cache pointer + auto *curCoro = EtsCoroutine::GetCurrent(); + ASSERT(curCoro != nullptr); + auto setStringCachePtr = [this, worker]() { + auto *cache = worker->GetLocalStorage().Get(); + SetFlattenedStringCache(cache); + }; + // We need to put the current coro into the managed state to be GC-safe, because we manipulate a raw + // ObjectHeader* + if (ManagedThread::IsManagedScope()) { + setStringCachePtr(); + } else { + // maybe we will find a more performant solution in future... + ScopedManagedCodeThread s(curCoro); + setStringCachePtr(); + } + } } void EtsCoroutine::OnContextSwitchedTo() diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index ab1dff4cbf..02d2b107c3 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -691,6 +691,12 @@ void PandaEtsVM::VisitVmRoots(const GCRootVisitor &visitor) } return true; }); + // NOTE(konstanting): worth moving to gc_root.cpp with a separate root type. Requires the ManagedCpu introduction. + GetCoroutineManager()->EnumerateWorkers([visitor](CoroutineWorker *worker) { + // apply visitor to worker + worker->VisitGCRoots(visitor); + return true; + }); if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) { visitor(mem::GCRoot(mem::RootType::ROOT_VM, doubleToStringCache_->GetCoreType())); visitor(mem::GCRoot(mem::RootType::ROOT_VM, floatToStringCache_->GetCoreType())); @@ -766,6 +772,12 @@ void PandaEtsVM::UpdateVmRefs(const GCRootUpdater &gcRootUpdater) UpdateManagedEntrypointArgRefs(coroutine, gcRootUpdater); return true; }); + // NOTE(konstanting): worth moving to gc_root.cpp with a separate root type. Requires the ManagedCpu introduction. + GetCoroutineManager()->EnumerateWorkers([gcRootUpdater](CoroutineWorker *worker) { + // apply updater to worker + worker->UpdateGCRoots(gcRootUpdater); + return true; + }); objStateTable_->EnumerateObjectStates([&gcRootUpdater](EtsObjectStateInfo *info) { auto *obj = info->GetEtsObject()->GetCoreType(); diff --git a/static_core/runtime/asm_defines/asm_defines.def b/static_core/runtime/asm_defines/asm_defines.def index 92e6a6f763..0426b73502 100644 --- a/static_core/runtime/asm_defines/asm_defines.def +++ b/static_core/runtime/asm_defines/asm_defines.def @@ -90,6 +90,7 @@ DEFINE_VALUE(MANAGED_THREAD_LANGUAGE_EXTENSION_DATA_OFFSET, ManagedThread::GetLa DEFINE_VALUE(MANAGED_THREAD_INTERNAL_ID_OFFSET, ManagedThread::GetInternalIdOffset()) DEFINE_VALUE(MANAGED_THREAD_RUNTIME_CALL_ENABLED_OFFSET, ManagedThread::GetRuntimeCallEnabledOffset()) DEFINE_VALUE(MANAGED_THREAD_INTERPRETER_CACHE_OFFSET, ManagedThread::GetInterpreterCacheOffset()) +DEFINE_VALUE(MANAGED_THREAD_FLATTENED_STRING_CACHE_OFFSET, ManagedThread::GetFlattenedStringCacheOffset()) DEFINE_VALUE(MT_MANAGED_THREAD_LOCKED_OBJECT_CAPACITY_OFFSET, MTManagedThread::GetLockedObjectCapacityOffset()) DEFINE_VALUE(MT_MANAGED_THREAD_LOCKED_OBJECT_SIZE_OFFSET, MTManagedThread::GetLockedObjectSizeOffset()) diff --git a/static_core/runtime/coroutines/coroutine_manager.h b/static_core/runtime/coroutines/coroutine_manager.h index fa83e20796..87d1d8e545 100644 --- a/static_core/runtime/coroutines/coroutine_manager.h +++ b/static_core/runtime/coroutines/coroutine_manager.h @@ -111,6 +111,7 @@ public: std::optional &&epInfo, Coroutine::Type type, CoroutinePriority priority); using NativeEntrypointFunc = Coroutine::NativeEntrypointInfo::NativeEntrypointFunc; + using EnumerateWorkerCallback = std::function; NO_COPY_SEMANTIC(CoroutineManager); NO_MOVE_SEMANTIC(CoroutineManager); @@ -248,6 +249,15 @@ public: { } + /** + * @brief enumerate workers and apply @param cb to them + * @return true if @param cb call was successful (returned true) for every worekr and false otherwise + */ + bool EnumerateWorkers(const EnumerateWorkerCallback &cb) const + { + return EnumerateWorkersImpl(cb); + } + virtual bool IsExclusiveWorkersLimitReached() const { return false; @@ -372,6 +382,9 @@ protected: /// Can be used in descendants to create custom coroutines manually CoroutineFactory GetCoroutineFactory(); + /// Worker enumerator, returns true iff cb call succeeds for every worker + virtual bool EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const = 0; + /// limit the number of IDs for performance reasons static constexpr size_t MAX_COROUTINE_ID = std::min(0xffffU, Coroutine::MAX_COROUTINE_ID); static constexpr size_t UNINITIALIZED_COROUTINE_ID = 0x0U; diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index 4c5eeafe13..a126477afe 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -13,8 +13,9 @@ * limitations under the License. */ -#include "coroutines/coroutine_manager.h" -#include "coroutines/coroutine_worker.h" +#include "runtime/coroutines/coroutine_manager.h" +#include "runtime/coroutines/coroutine_worker.h" +#include "runtime/include/thread_scopes.h" namespace ark { @@ -33,4 +34,43 @@ void CoroutineWorker::OnCoroBecameActive(Coroutine *co) TriggerSchedulerExternally(co); } +void CoroutineWorker::OnBeforeScheduleLoopStart() +{ + CreateWorkerLocalObjects(); +} + +void CoroutineWorker::VisitGCRoots(const GCRootVisitor &visitor) +{ + auto *stringCache = GetLocalStorage().Get(); + // NOTE(konstanting): worker local objects deserve a special root type + visitor(mem::GCRoot(mem::RootType::ROOT_VM, stringCache)); +} + +void CoroutineWorker::UpdateGCRoots(const GCRootUpdater &gcRootUpdater) +{ + auto *stringCachePtr = GetLocalStorage().GetPtr(); + gcRootUpdater(stringCachePtr); +} + +void CoroutineWorker::CreateWorkerLocalObjects() +{ + // native part + // ... + + // managed part + auto *schLoopCoro = Coroutine::GetCurrent(); + ASSERT(schLoopCoro != nullptr); + ASSERT_NATIVE_CODE(); + ScopedManagedCodeThread s(schLoopCoro); + // create the string flattening cache + /* + * probably should be done like: + * auto* cache = StringFlatteningCache::Create(...); + */ + //Class *klass = nullptr; + //auto *cache = coretypes::Array::Create(klass, 100500); + //ASSERT(cache != nullptr); + //GetLocalStorage().Set(cache); +} + } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_worker.h b/static_core/runtime/coroutines/coroutine_worker.h index 9cfae6cf44..0c98aca989 100644 --- a/static_core/runtime/coroutines/coroutine_worker.h +++ b/static_core/runtime/coroutines/coroutine_worker.h @@ -39,7 +39,7 @@ enum class CoroutinePriority { /// Represents a coroutine worker, which can host multiple coroutines and schedule them. class CoroutineWorker { public: - enum class DataIdx { INTEROP_CTX_PTR, EXTERNAL_IFACES, LAST_ID }; + enum class DataIdx { INTEROP_CTX_PTR, EXTERNAL_IFACES, STRING_CACHE, LAST_ID }; using LocalStorage = StaticLocalStorage; using Id = int32_t; @@ -111,6 +111,17 @@ public: void TriggerSchedulerExternally(Coroutine *requester); + // GC stuff + virtual void VisitGCRoots(const GCRootVisitor &visitor); + virtual void UpdateGCRoots(const GCRootUpdater &gcRootUpdater); + +protected: + /// should be called right before the schedule loop is entered for the first time + void OnBeforeScheduleLoopStart(); + +private: + void CreateWorkerLocalObjects(); + private: Runtime *runtime_ = nullptr; PandaVM *vm_ = nullptr; diff --git a/static_core/runtime/coroutines/local_storage.h b/static_core/runtime/coroutines/local_storage.h index cddec61436..00e871f564 100644 --- a/static_core/runtime/coroutines/local_storage.h +++ b/static_core/runtime/coroutines/local_storage.h @@ -72,13 +72,30 @@ public: } template - T Get() + T Get() const { static_assert((ToIndex(IDX) < NUM_ENTRIES), "idx should be correct"); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return reinterpret_cast(entries_[ToIndex(IDX)].data.ptr); } + template + T *GetPtr() + { + static_assert((ToIndex(IDX) < NUM_ENTRIES), "idx should be correct"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + return reinterpret_cast(&(entries_[ToIndex(IDX)].data.ptr)); + } + + template + constexpr size_t GetOffset() const + { + static_assert((ToIndex(IDX) < NUM_ENTRIES), "idx should be correct"); + // NOTE(konstanting): TO BE IMPLEMENTED! + UNREACHABLE(); + return 0; + } + private: std::array entries_; }; diff --git a/static_core/runtime/coroutines/stackful_coroutine_manager.cpp b/static_core/runtime/coroutines/stackful_coroutine_manager.cpp index 78f5df0241..ed3f7777a5 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_manager.cpp +++ b/static_core/runtime/coroutines/stackful_coroutine_manager.cpp @@ -539,6 +539,17 @@ bool StackfulCoroutineManager::EnumerateThreadsImpl(const ThreadManager::Callbac return true; } +bool StackfulCoroutineManager::EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const +{ + os::memory::LockHolder lock(workersLock_); + for (auto *w : workers_) { + if (!cb(w)) { + return false; + } + } + return true; +} + void StackfulCoroutineManager::SuspendAllThreads() { os::memory::LockHolder lock(coroListLock_); diff --git a/static_core/runtime/coroutines/stackful_coroutine_manager.h b/static_core/runtime/coroutines/stackful_coroutine_manager.h index 97370c47e2..db8af86b2d 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_manager.h +++ b/static_core/runtime/coroutines/stackful_coroutine_manager.h @@ -145,6 +145,8 @@ protected: bool EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask, unsigned int xorMask) const override; + bool EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const override; + CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) override; void DeleteCoroutineContext(CoroutineContext *ctx) override; diff --git a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp index bcc7671d1f..db01960fe4 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp +++ b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp @@ -64,17 +64,17 @@ void StackfulCoroutineWorker::AddCreatedCoroutineAndSwitchToIt(Coroutine *newCor { // precondition: called within the current worker, no cross-worker calls allowed ASSERT(GetCurrentContext()->GetWorker() == this); + RegisterIncomingActiveCoroutine(newCoro); + // suspend current coro... auto *coro = Coroutine::GetCurrent(); ScopedNativeCodeThread n(coro); coro->RequestSuspend(false); - - newCoro->LinkToExternalHolder(IsMainWorker() || InExclusiveMode()); + // ..and resume the new one auto *currentCtx = GetCurrentContext(); auto *nextCtx = newCoro->GetContext(); nextCtx->RequestResume(); Coroutine::SetCurrent(newCoro); - RegisterIncomingActiveCoroutine(newCoro); SwitchCoroutineContext(currentCtx, nextCtx); @@ -282,6 +282,8 @@ void StackfulCoroutineWorker::ScheduleLoop() void StackfulCoroutineWorker::ScheduleLoopBody() { + OnBeforeScheduleLoopStart(); + // run the loop while (IsActive()) { RequestScheduleImpl(); os::memory::LockHolder lkRunnables(runnablesLock_); diff --git a/static_core/runtime/coroutines/stackful_coroutine_worker.h b/static_core/runtime/coroutines/stackful_coroutine_worker.h index 4e66c4fa97..24effa925d 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_worker.h +++ b/static_core/runtime/coroutines/stackful_coroutine_worker.h @@ -255,6 +255,7 @@ private: bool IsPotentiallyBlocked(); void MigrateCoroutinesImpl(StackfulCoroutineWorker *to, size_t migrateCount) REQUIRES(runnablesLock_); + /* events */ /// called right before the coroutineContext is switched void OnBeforeContextSwitch(StackfulCoroutineContext *from, StackfulCoroutineContext *to); /// called right after the coroutineContext is switched (in case if no migration happened) diff --git a/static_core/runtime/coroutines/threaded_coroutine_manager.cpp b/static_core/runtime/coroutines/threaded_coroutine_manager.cpp index c3da01a118..8c1eb7334f 100644 --- a/static_core/runtime/coroutines/threaded_coroutine_manager.cpp +++ b/static_core/runtime/coroutines/threaded_coroutine_manager.cpp @@ -314,6 +314,17 @@ bool ThreadedCoroutineManager::EnumerateThreadsImpl(const ThreadManager::Callbac return true; } +bool ThreadedCoroutineManager::EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const +{ + os::memory::LockHolder lock(workersLock_); + for (auto *w : workers_) { + if (!cb(w)) { + return false; + } + } + return true; +} + void ThreadedCoroutineManager::SuspendAllThreads() { os::memory::LockHolder lList(coroListLock_); diff --git a/static_core/runtime/coroutines/threaded_coroutine_manager.h b/static_core/runtime/coroutines/threaded_coroutine_manager.h index 7dd1f1830d..cbdc1de84a 100644 --- a/static_core/runtime/coroutines/threaded_coroutine_manager.h +++ b/static_core/runtime/coroutines/threaded_coroutine_manager.h @@ -81,6 +81,8 @@ public: protected: bool EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask, unsigned int xorMask) const override; + bool EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const override; + CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) override; void DeleteCoroutineContext(CoroutineContext *ctx) override; diff --git a/static_core/runtime/include/managed_thread.h b/static_core/runtime/include/managed_thread.h index 2278a385ab..ca855594ff 100644 --- a/static_core/runtime/include/managed_thread.h +++ b/static_core/runtime/include/managed_thread.h @@ -376,6 +376,11 @@ public: return MEMBER_OFFSET(ManagedThread, interpreterCache_); } + static constexpr uint32_t GetFlattenedStringCacheOffset() + { + return MEMBER_OFFSET(ManagedThread, flattenedStringCache_); + } + void *GetLanguageExtensionsData() const { return languageExtensionData_; @@ -437,6 +442,9 @@ public: PANDA_PUBLIC_API void SetCustomTLSData(const char *key, CustomTLSData *data); PANDA_PUBLIC_API bool EraseCustomTLSData(const char *key); + void SetFlattenedStringCache(ObjectHeader *cacheInstance); + ObjectHeader *GetFlattenedStringCache() const; + #if EVENT_METHOD_ENTER_ENABLED || EVENT_METHOD_EXIT_ENABLED uint32_t RecordMethodEnter() { @@ -705,6 +713,9 @@ private: PandaMap> customTlsCache_ GUARDED_BY(Locks::customTlsLock_); + // NOTE(konstanting): this is to be moved once we decouple Thread from ManagedThread + ObjectHeader *flattenedStringCache_ {nullptr}; + mem::GCG1BarrierSet::G1PostBarrierRingBufferType *g1PostBarrierRingBuffer_ {nullptr}; // Keep these here to speed up interpreter mem::BarrierType preBarrierType_ {mem::BarrierType::PRE_WRB_NONE}; diff --git a/static_core/runtime/thread.cpp b/static_core/runtime/thread.cpp index d144747093..32e7fb2150 100644 --- a/static_core/runtime/thread.cpp +++ b/static_core/runtime/thread.cpp @@ -678,9 +678,13 @@ void MTManagedThread::ProcessCreatedThread() void ManagedThread::UpdateGCRoots(const GCRootUpdater &gcRootUpdater) { - if ((exception_ != nullptr)) { + if (exception_ != nullptr) { gcRootUpdater(&exception_); } + if (flattenedStringCache_ != nullptr) { + // This is the cached pointer. We need to update it; visiting it as root is nor required. + gcRootUpdater(&flattenedStringCache_); + } for (auto **localObject : localObjects_) { gcRootUpdater(localObject); } @@ -849,6 +853,16 @@ bool ManagedThread::EraseCustomTLSData(const char *key) return customTlsCache_.erase(key) != 0; } +void ManagedThread::SetFlattenedStringCache(ObjectHeader *cacheInstance) +{ + flattenedStringCache_ = cacheInstance; +} + +ObjectHeader *ManagedThread::GetFlattenedStringCache() const +{ + return flattenedStringCache_; +} + LanguageContext ManagedThread::GetLanguageContext() { return Runtime::GetCurrent()->GetLanguageContext(threadLang_); -- Gitee From cc0232207d87e548c0e274864a729a021a5b5ff1 Mon Sep 17 00:00:00 2001 From: rjgask Date: Tue, 9 Sep 2025 02:17:13 +0800 Subject: [PATCH 02/10] String flattening cache Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICXJDQ Testing: All required pre-merge tests passed Signed-off-by: rjgask --- .../backend/compiler/codegen_fastpath.cpp | 11 ++- static_core/irtoc/scripts/common.irt | 4 +- static_core/irtoc/scripts/string_helpers.irt | 53 ++++++++--- static_core/irtoc/scripts/strings.irt | 40 ++++---- .../plugins/ets/irtoc_scripts/string.irt | 87 ++++++++--------- .../ets/irtoc_scripts/string_builder.irt | 6 +- .../plugins/ets/runtime/ets_coroutine.cpp | 13 ++- static_core/plugins/ets/runtime/ets_vm.cpp | 12 +++ static_core/runtime/coretypes/string.cpp | 9 ++ .../runtime/coroutines/coroutine_worker.cpp | 26 +++-- .../runtime/coroutines/coroutine_worker.h | 8 +- .../runtime/coroutines/local_storage.h | 2 +- .../coroutines/threaded_coroutine_manager.h | 2 +- .../runtime/include/string_flattening_cache.h | 95 +++++++++++++++++++ 14 files changed, 259 insertions(+), 109 deletions(-) create mode 100644 static_core/runtime/include/string_flattening_cache.h diff --git a/static_core/irtoc/backend/compiler/codegen_fastpath.cpp b/static_core/irtoc/backend/compiler/codegen_fastpath.cpp index 8a5cbc5432..cd9fec9dca 100644 --- a/static_core/irtoc/backend/compiler/codegen_fastpath.cpp +++ b/static_core/irtoc/backend/compiler/codegen_fastpath.cpp @@ -322,19 +322,22 @@ void CodegenFastPath::EmitWriteTlabStatsSafeIntrinsic([[maybe_unused]] Intrinsic auto src1 = src[FIRST_OPERAND]; auto src2 = src[SECOND_OPERAND]; - auto tmp = src[THIRD_OPERAND]; + auto src3 = src[THIRD_OPERAND]; - ASSERT(tmp.IsValid()); - ASSERT(tmp != GetRegfile()->GetZeroReg()); + ASSERT(src3.IsValid()); + ASSERT(src3 != GetRegfile()->GetZeroReg()); auto regs = GetCallerRegsMask(GetArch(), false) | GetCalleeRegsMask(GetArch(), false); auto vregs = GetCallerRegsMask(GetArch(), true); GetEncoder()->PushRegisters(regs, vregs); - FillCallParams(src1, src2); + FillCallParams(src1, src2, src3); auto id = RuntimeInterface::EntrypointId::WRITE_TLAB_STATS_NO_BRIDGE; MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), id)); + constexpr size_t NUM_OF_ARGS = 2; + // Temp registers are not available because they are busy in irtoc tlab allocation. So it is faked with param reg. + auto tmp = GetEncoder()->GetTarget().GetParamReg(NUM_OF_ARGS); GetEncoder()->EncodeLdr(tmp, false, entry); GetEncoder()->MakeCall(tmp); diff --git a/static_core/irtoc/scripts/common.irt b/static_core/irtoc/scripts/common.irt index c661816a63..432a9c81bc 100644 --- a/static_core/irtoc/scripts/common.irt +++ b/static_core/irtoc/scripts/common.irt @@ -225,8 +225,10 @@ macro(:call_runtime) { |e, *args| } macro(:call_runtime_save_all) { |e, *args| + # Load entry before save registers because stack SpillFill is posible + entry := LoadI(%tr).Imm(e).ptr Intrinsic(:SAVE_REGISTERS_EP).void - ret = call_runtime(e, *args) + ret := CallIndirect(entry, *args) Intrinsic(:RESTORE_REGISTERS_EP).void ret } diff --git a/static_core/irtoc/scripts/string_helpers.irt b/static_core/irtoc/scripts/string_helpers.irt index c3663a7663..b2b7dd9e63 100644 --- a/static_core/irtoc/scripts/string_helpers.irt +++ b/static_core/irtoc/scripts/string_helpers.irt @@ -39,6 +39,33 @@ macro(:check_string_type) do |str| } end +macro(:try_use_cached_flat_str) do |str| + baseClass := LoadI(str).ref + stringType := LoadI(baseClass).Imm(Constants::STRING_TYPE_OFFSET).u64 + If(stringType, Constants::STRING_TYPE_SLICE).EQ { + Goto(:SlowPathEntrypoint) + } + If(stringType, Constants::STRING_TYPE_TREE).EQ { + stringCache := LoadI(%tr).Imm(Constants::THREAD_FLATTENED_STRING_CACHE_OFFSET).ref + If(stringCache, 0).EQ { + Goto(:SlowPathEntrypoint) + } + + strPtr := Cast(str).ptr + strUint := Bitcast(strPtr).word + strShifted := ShrI(strUint).Imm(3).word + strMasked := AndI(strShifted).Imm(0x3f).word + strIndex := Mul(strMasked, 2).word + key := LoadArray(stringCache, strIndex).ref + If(key, str).NE { + Goto(:SlowPathEntrypoint) + } + strIndex := AddI(strIndex).Imm(1).ref_uint + strCached := LoadArray(stringCache, strIndex).ref + } + result := Phi(str, strCached).ref +end + def GenerateStringEquals(lang, dynamic, compression, cgmode = :FastPath) suffix = (compression ? 'Compressed' : '') + (cgmode == :NativePlus ? 'NativePlus': '') length_shift = Constants::STRING_LENGTH_SHIFT @@ -111,7 +138,7 @@ def GenerateStringEquals(lang, dynamic, compression, cgmode = :FastPath) RegMask.new($full_regmap, :arg0, :arg1, :tmp0, :tmp1, :callee0, :caller1) function("#{lang}StringEquals#{suffix}".to_sym, - params: {str1: 'ref', str2: 'ref'}, + params: {str1_orig: 'ref', str2_orig: 'ref'}, regmap: $full_regmap, regalloc_set: reg_mask, mode: mode, @@ -122,15 +149,15 @@ def GenerateStringEquals(lang, dynamic, compression, cgmode = :FastPath) next end unless dynamic - If(str2, 0).EQ.Unlikely.b { + If(str2_orig, 0).EQ.Unlikely.b { Goto(:NotEqual) } end - If(str1, str2).EQ.Unlikely.b { + If(str1_orig, str2_orig).EQ.Unlikely.b { Return(1).b } - check_string_type(str1) - check_string_type(str2) + str1 := try_use_cached_flat_str(str1_orig) + str2 := try_use_cached_flat_str(str2_orig) if dynamic length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 length2 := LoadI(str2).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -210,10 +237,10 @@ def GenerateStringEquals(lang, dynamic, compression, cgmode = :FastPath) Return(0).b Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str1, str2).Method("CoreStringEquals").b).b + Return(Call(str1_orig, str2_orig).Method("CoreStringEquals").b).b else entrypoint = get_entrypoint_offset("STRING_EQUALS_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str1, str2).AddImm(entrypoint).MethodAsImm("StringEqualsUsualBridge").Terminator.b + Intrinsic(:SLOW_PATH_ENTRY, str1_orig, str2_orig).AddImm(entrypoint).MethodAsImm("StringEqualsUsualBridge").Terminator.b Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } @@ -880,7 +907,7 @@ def GenerateSubstringFromStringTlab(string_compression_enabled) suffix = (string_compression_enabled ? "Compressed" : "") available_regs = $panda_mask function("SubStringFromStringTlab#{suffix}".to_sym, - params: {str: 'ref', begin_index: 'i32', end_index: 'i32'}, + params: {str_orig: 'ref', begin_index: 'i32', end_index: 'i32'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { @@ -891,7 +918,7 @@ def GenerateSubstringFromStringTlab(string_compression_enabled) next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) # Note, 'str' is checked against nullptr in the InstBuilder (see AddArgNullcheckIfNeeded) length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -993,7 +1020,7 @@ def GenerateSubstringFromStringTlab(string_compression_enabled) Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("SUB_STRING_FROM_STRING_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, begin_index, end_index).AddImm(entrypoint).MethodAsImm("SubStringFromStringOddSavedBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig, begin_index, end_index).AddImm(entrypoint).MethodAsImm("SubStringFromStringOddSavedBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } end # def GenerateSubstringFromStringTlab @@ -1003,7 +1030,7 @@ def GenerateStringGetCharsTlab(string_compression_enabled) suffix = (string_compression_enabled ? "Compressed" : "") available_regs = $panda_mask function("StringGetCharsTlab#{suffix}".to_sym, - params: {str: 'ref', begin_index: 'i32', end_index: 'i32', array_klass: 'ref'}, + params: {str_orig: 'ref', begin_index: 'i32', end_index: 'i32', array_klass: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { @@ -1014,7 +1041,7 @@ def GenerateStringGetCharsTlab(string_compression_enabled) next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) If(begin_index, end_index).GT.Unlikely.b { Goto(:SlowPathEntrypoint) # Out of range @@ -1058,7 +1085,7 @@ def GenerateStringGetCharsTlab(string_compression_enabled) Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_GET_CHARS_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, begin_index, end_index).AddImm(entrypoint).MethodAsImm("StringGetChars4ArgBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig, begin_index, end_index).AddImm(entrypoint).MethodAsImm("StringGetChars4ArgBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } end # def GenerateStringGetCharsTlab diff --git a/static_core/irtoc/scripts/strings.irt b/static_core/irtoc/scripts/strings.irt index c0c53eb8ee..0d7313187e 100644 --- a/static_core/irtoc/scripts/strings.irt +++ b/static_core/irtoc/scripts/strings.irt @@ -35,7 +35,7 @@ GenerateStringHashCode(string_compression_enabled=false, :FastPath) available_regs = $panda_mask function(:StringConcat2Tlab, - params: {str1: 'ref', str2: 'ref'}, + params: {str1_orig: 'ref', str2_orig: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { @@ -45,8 +45,8 @@ function(:StringConcat2Tlab, next end - check_string_type(str1) - check_string_type(str2) + str1 := try_use_cached_flat_str(str1_orig) + str2 := try_use_cached_flat_str(str2_orig) klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -105,13 +105,13 @@ function(:StringConcat2Tlab, Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT2_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str1, str2).AddImm(ep_offset).MethodAsImm("StringConcat2UsualBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str1_orig, str2_orig).AddImm(ep_offset).MethodAsImm("StringConcat2UsualBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } available_regs = $panda_mask function(:StringConcat3Tlab, - params: {str1: 'ref', str2: 'ref', str3: 'ref'}, + params: {str1_orig: 'ref', str2_orig: 'ref', str3_orig: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { @@ -121,9 +121,9 @@ function(:StringConcat3Tlab, next end - check_string_type(str1) - check_string_type(str2) - check_string_type(str3) + str1 := try_use_cached_flat_str(str1_orig) + str2 := try_use_cached_flat_str(str2_orig) + str3 := try_use_cached_flat_str(str3_orig) klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -199,13 +199,13 @@ Label(:EndCopy) Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT3_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3).AddImm(ep_offset).MethodAsImm("StringConcat3OddSavedBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str1_orig, str2_orig, str3_orig).AddImm(ep_offset).MethodAsImm("StringConcat3OddSavedBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } available_regs = $panda_mask function(:StringConcat4Tlab, - params: {str1: 'ref', str2: 'ref', str3: 'ref', str4: 'ref'}, + params: {str1_orig: 'ref', str2_orig: 'ref', str3_orig: 'ref', str4_orig: 'ref'}, regmap: $full_regmap, regalloc_set: available_regs, mode: [:FastPath]) { @@ -214,10 +214,10 @@ function(:StringConcat4Tlab, next end - check_string_type(str1) - check_string_type(str2) - check_string_type(str3) - check_string_type(str4) + str1 := try_use_cached_flat_str(str1_orig) + str2 := try_use_cached_flat_str(str2_orig) + str3 := try_use_cached_flat_str(str3_orig) + str4 := try_use_cached_flat_str(str4_orig) klass := load_class(str1) length1 := LoadI(str1).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -312,14 +312,14 @@ Label(:EndCopy) Label(:SlowPathEntrypoint) ep_offset = get_entrypoint_offset("STRING_CONCAT4_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str1, str2, str3, str4).AddImm(ep_offset).MethodAsImm("StringConcat4UsualBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str1_orig, str2_orig, str3_orig, str4_orig).AddImm(ep_offset).MethodAsImm("StringConcat4UsualBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } def GenerateStringCompareTo(cgmode) suffix = (cgmode == :NativePlus ? 'NativePlus': '') function("StringCompareTo#{suffix}", - params: {str1: 'ref', str2: 'ref'}, + params: {str1_orig: 'ref', str2_orig: 'ref'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [cgmode]) { @@ -330,17 +330,17 @@ def GenerateStringCompareTo(cgmode) next end - check_string_type(str1) - check_string_type(str2) + str1 := try_use_cached_flat_str(str1_orig) + str2 := try_use_cached_flat_str(str2_orig) Return(macroStringCompareTo(str1, str2)).i32 Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str1, str2).Method('CoreStringCompareTo').i32).i32 + Return(Call(str1_orig, str2_orig).Method('CoreStringCompareTo').i32).i32 else entrypoint = get_entrypoint_offset("STRING_COMPARE_TO_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str1, str2).AddImm(entrypoint).MethodAsImm("StringCompareToUsualBridge").Terminator.i32 + Intrinsic(:SLOW_PATH_ENTRY, str1_orig, str2_orig).AddImm(entrypoint).MethodAsImm("StringCompareToUsualBridge").Terminator.i32 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } diff --git a/static_core/plugins/ets/irtoc_scripts/string.irt b/static_core/plugins/ets/irtoc_scripts/string.irt index 1b6e316c52..94c8ce0e8a 100644 --- a/static_core/plugins/ets/irtoc_scripts/string.irt +++ b/static_core/plugins/ets/irtoc_scripts/string.irt @@ -247,15 +247,6 @@ Label(:SlowPathEntrypoint) } -if Options.arch == :arm64 - trim_left_regs = $temps_mask + :callee0 + :callee1 - trim_right_regs = $temps_mask + :callee0 + :callee1 -else - trim_left_regs = $temps_mask + :callee0 + :caller0 + :caller1 - trim_right_regs = $temps_mask + :callee0 + :caller0 + :caller1 -end - - function(:StringTrimLeftBase, params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, regmap: $full_regmap, @@ -310,9 +301,9 @@ Label(:SlowPathEntrypoint) function(:StringTrimLeft, - params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, + params: {str_orig: 'ref', unused1: 'i32', unused2: 'i32'}, regmap: $full_regmap, - regalloc_set: $trim_left_regs, + regalloc_set: $panda_mask, mode: [:FastPath]) { if Options.arch == :arm32 @@ -321,7 +312,7 @@ function(:StringTrimLeft, next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 If(length_packed, 1).LE.Unlikely.b { @@ -351,7 +342,7 @@ Label(:L1) Intrinsic(:TAIL_CALL).AddImm(entrypoint2).MethodAsImm("StringTrimLeftBase").Terminator.ptr Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_TRIM_LEFT_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str).AddImm(entrypoint).MethodAsImm("StringTrimLeft3ArgBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig).AddImm(entrypoint).MethodAsImm("StringTrimLeft3ArgBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } @@ -411,9 +402,9 @@ Label(:SlowPathEntrypoint) function(:StringTrimRight, - params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, + params: {str_orig: 'ref', unused1: 'i32', unused2: 'i32'}, regmap: $full_regmap, - regalloc_set: $trim_right_regs, + regalloc_set: $panda_mask, mode: [:FastPath]) { if Options.arch == :arm32 @@ -422,7 +413,7 @@ function(:StringTrimRight, next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 If(length_packed, 1).LE.Unlikely.b { @@ -454,7 +445,7 @@ Label(:L1) Intrinsic(:TAIL_CALL).AddImm(entrypoint2).MethodAsImm("StringTrimRightBase").Terminator.ptr Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_TRIM_RIGHT_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str).AddImm(entrypoint).MethodAsImm("StringTrimRight3ArgBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig).AddImm(entrypoint).MethodAsImm("StringTrimRight3ArgBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } @@ -531,7 +522,7 @@ Label(:SlowPathEntrypoint) function(:StringTrim, - params: {str: 'ref', unused1: 'i32', unused2: 'i32'}, + params: {str_orig: 'ref', unused1: 'i32', unused2: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [:FastPath]) { @@ -542,7 +533,7 @@ function(:StringTrim, next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) length_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 # length == 0 @@ -606,7 +597,7 @@ Label(:FirstCharWhitespace) Intrinsic(:TAIL_CALL).AddImm(entrypoint3).MethodAsImm("StringTrimLeftBase").Terminator.ptr Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_TRIM_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str).AddImm(entrypoint).MethodAsImm("StringTrim3ArgBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig).AddImm(entrypoint).MethodAsImm("StringTrim3ArgBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } @@ -667,7 +658,7 @@ def GenerateStringStartsWith(cgmode) suffix = (cgmode == :NativePlus ? 'NativePlus': '') upsuffix = (cgmode == :NativePlus ? '_NATIVE_PLUS': '') function("StringStartsWith#{suffix}".to_sym, - params: {str: 'ref', pfx: 'ref', from_index: 'i32'}, + params: {str_orig: 'ref', pfx_orig: 'ref', from_index: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [cgmode]) { @@ -678,8 +669,8 @@ def GenerateStringStartsWith(cgmode) next end - check_string_type(str) - check_string_type(pfx) + str := try_use_cached_flat_str(str_orig) + pfx := try_use_cached_flat_str(pfx_orig) pfx_len_packed := LoadI(pfx).Imm(Constants::STRING_LENGTH_OFFSET).u32 # Return 'true' if prefix is empty @@ -724,10 +715,10 @@ def GenerateStringStartsWith(cgmode) Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str, pfx, from_index).Method("StdCoreStringStartsWith").b).b + Return(Call(str_orig, pfx_orig, from_index).Method("StdCoreStringStartsWith").b).b else entrypoint = get_entrypoint_offset("STRING_STARTS_WITH_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, pfx, from_index).AddImm(entrypoint).MethodAsImm("StringStartsWithOddSavedBridge").Terminator.b + Intrinsic(:SLOW_PATH_ENTRY, str_orig, pfx_orig, from_index).AddImm(entrypoint).MethodAsImm("StringStartsWithOddSavedBridge").Terminator.b Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } @@ -782,7 +773,7 @@ def GenerateStringEndsWith(cgmode) suffix = (cgmode == :NativePlus ? 'NativePlus': '') upsuffix = (cgmode == :NativePlus ? '_NATIVE_PLUS': '') function("StringEndsWith#{suffix}".to_sym, - params: {str: 'ref', sfx: 'ref', end_index: 'i32'}, + params: {str_orig: 'ref', sfx_orig: 'ref', end_index: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [cgmode]) { @@ -793,8 +784,8 @@ def GenerateStringEndsWith(cgmode) next end - check_string_type(str) - check_string_type(sfx) + str := try_use_cached_flat_str(str_orig) + sfx := try_use_cached_flat_str(sfx_orig) sfx_len_packed := LoadI(sfx).Imm(Constants::STRING_LENGTH_OFFSET).u32 # Return 'true' if suffix is empty @@ -841,10 +832,10 @@ def GenerateStringEndsWith(cgmode) Intrinsic(:TAIL_CALL).AddImm(entrypoint).MethodAsImm("StringEndsWithBase#{suffix}").Terminator.b Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str, sfx, end_index).Method("StdCoreStringEndsWith").b).b + Return(Call(str_orig, sfx_orig, end_index).Method("StdCoreStringEndsWith").b).b else entrypoint = get_entrypoint_offset("STRING_ENDS_WITH_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, sfx, end_index).AddImm(entrypoint).MethodAsImm("StringEndsWithOddSavedBridge").Terminator.b + Intrinsic(:SLOW_PATH_ENTRY, str_orig, sfx_orig, end_index).AddImm(entrypoint).MethodAsImm("StringEndsWithOddSavedBridge").Terminator.b Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } @@ -855,7 +846,7 @@ GenerateStringEndsWith(:NativePlus) function(:StringGetBytesTlab, - params: {str: 'ref', begin_index: 'i32', end_index: 'i32', array_klass: 'ref'}, + params: {str_orig: 'ref', begin_index: 'i32', end_index: 'i32', array_klass: 'ref'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [:FastPath]) { @@ -866,7 +857,7 @@ function(:StringGetBytesTlab, next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) If(begin_index, end_index).GT.Unlikely.b { Goto(:SlowPathEntrypoint) # Out of range @@ -876,8 +867,6 @@ function(:StringGetBytesTlab, Goto(:SlowPathEntrypoint) # Out of range } - check_string_type(str) - # Note, 'str' is checked against nullptr in the InstBuilder (see AddArgNullcheckIfNeeded) length := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32; uncompressed := AndI(length).Imm(1).u32; @@ -906,7 +895,7 @@ function(:StringGetBytesTlab, Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_GET_BYTES_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, begin_index, end_index).AddImm(entrypoint).MethodAsImm("StringGetBytes4ArgBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig, begin_index, end_index).AddImm(entrypoint).MethodAsImm("StringGetBytes4ArgBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } @@ -1480,7 +1469,7 @@ def GenerateStringIndexOf(cgmode) suffix = (cgmode == :NativePlus ? 'NativePlus': '') upsuffix = (cgmode == :NativePlus ? '_NATIVE_PLUS': '') function("StringIndexOf#{suffix}".to_sym, - params: {str: 'ref', ch: 'u16', fake: 'i32'}, + params: {str_orig: 'ref', ch: 'u16', fake: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [cgmode]) { @@ -1491,7 +1480,7 @@ def GenerateStringIndexOf(cgmode) next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) str_len_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -1600,10 +1589,10 @@ def GenerateStringIndexOf(cgmode) end Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str, ch).Method("StdCoreStringIndexOf").i32).i32 + Return(Call(str_orig, ch).Method("StdCoreStringIndexOf").i32).i32 else entrypoint = get_entrypoint_offset("STRING_INDEX_OF_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, ch, fake).AddImm(entrypoint).MethodAsImm("StringIndexOfOddSavedBridge").Terminator.i32 + Intrinsic(:SLOW_PATH_ENTRY, str_orig, ch, fake).AddImm(entrypoint).MethodAsImm("StringIndexOfOddSavedBridge").Terminator.i32 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } @@ -1622,7 +1611,7 @@ def GenerateStringIndexOfAfter(cgmode) suffix = (cgmode == :NativePlus ? 'NativePlus': '') upsuffix = (cgmode == :NativePlus ? '_NATIVE_PLUS': '') function("StringIndexOfAfter#{suffix}".to_sym, - params: {str: 'ref', ch: 'u16', start_index: 'i32'}, + params: {str_orig: 'ref', ch: 'u16', start_index: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [cgmode]) { @@ -1643,7 +1632,7 @@ def GenerateStringIndexOfAfter(cgmode) start_index := Phi(start_index0, start_index1).i32 end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) str_len_packed := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 # Return '-1' if 'str' is empty. @@ -1791,10 +1780,10 @@ def GenerateStringIndexOfAfter(cgmode) end Label(:SlowPathEntrypoint) if cgmode == :NativePlus - Return(Call(str, ch, start_index).Method("StdCoreStringIndexOfAfter").i32).i32 + Return(Call(str_orig, ch, start_index).Method("StdCoreStringIndexOfAfter").i32).i32 else entrypoint = get_entrypoint_offset("STRING_INDEX_OF_AFTER_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, ch, start_index).AddImm(entrypoint).MethodAsImm("StringIndexOfAfterOddSavedBridge").Terminator.i32 + Intrinsic(:SLOW_PATH_ENTRY, str_orig, ch, start_index).AddImm(entrypoint).MethodAsImm("StringIndexOfAfterOddSavedBridge").Terminator.i32 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG end } @@ -1805,7 +1794,7 @@ GenerateStringIndexOfAfter(:NativePlus) function(:StringRepeatTlab, - params: {str: 'ref', cnt: 'i32'}, + params: {str_orig: 'ref', cnt: 'i32'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [:FastPath]) { @@ -1820,7 +1809,7 @@ function(:StringRepeatTlab, Goto(:SlowPathEntrypoint) } - check_string_type(str) + str := try_use_cached_flat_str(str_orig) IfImm(Compare(count, 0).EQ.b).Imm(0).NE { klass := load_class(str) @@ -1878,12 +1867,12 @@ Label(:End) Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("STRING_REPEAT_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, str, count).AddImm(entrypoint).MethodAsImm("StringRepeatUsualBridge").Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, str_orig, count).AddImm(entrypoint).MethodAsImm("StringRepeatUsualBridge").Terminator.ptr Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } function(:WriteStringToMem, - params: {mem: 'i64', str: 'ref'}, + params: {mem: 'i64', str_orig: 'ref'}, regmap: $full_regmap, regalloc_set: $panda_mask, mode: [:FastPath]) { @@ -1893,7 +1882,7 @@ function(:WriteStringToMem, next end - check_string_type(str) + str := try_use_cached_flat_str(str_orig) buf := Bitcast(mem).ptr len := LoadI(str).Imm(Constants::STRING_LENGTH_OFFSET).u32 @@ -1926,7 +1915,7 @@ Label(:End) Return(len).u32 Label(:SlowPathEntrypoint) entrypoint = get_entrypoint_offset("WRITE_STRING_TO_MEM_SLOW_PATH") - Intrinsic(:SLOW_PATH_ENTRY, mem, str).AddImm(entrypoint).MethodAsImm("WriteStringToMemUsualBridge").Terminator.u32 + Intrinsic(:SLOW_PATH_ENTRY, mem, str_orig).AddImm(entrypoint).MethodAsImm("WriteStringToMemUsualBridge").Terminator.u32 Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG } diff --git a/static_core/plugins/ets/irtoc_scripts/string_builder.irt b/static_core/plugins/ets/irtoc_scripts/string_builder.irt index 37759df9c7..9b02395eee 100644 --- a/static_core/plugins/ets/irtoc_scripts/string_builder.irt +++ b/static_core/plugins/ets/irtoc_scripts/string_builder.irt @@ -528,9 +528,9 @@ Label(:ForEachBufferSlot) # ------------------- # Object is a string # ------------------- - check_string_type(obj) - str_len := LoadI(obj).Imm(Constants::STRING_LENGTH_OFFSET).i32 - src_data := AddI(Cast(obj).SrcType(Constants::COMPILER_REFERENCE).ptr).Imm(Constants::STRING_DATA_OFFSET).ptr + flat_str := try_use_cached_flat_str(obj) + str_len := LoadI(flat_str).Imm(Constants::STRING_LENGTH_OFFSET).i32 + src_data := AddI(Cast(flat_str).SrcType(Constants::COMPILER_REFERENCE).ptr).Imm(Constants::STRING_DATA_OFFSET).ptr src_len := ShrI(str_len).Imm(Constants::STRING_LENGTH_SHIFT).i32 If(sb_compress, 0).EQ.Unlikely.b { Goto(:DoNotCompressString) diff --git a/static_core/plugins/ets/runtime/ets_coroutine.cpp b/static_core/plugins/ets/runtime/ets_coroutine.cpp index 109f93ab0e..845c638ee6 100644 --- a/static_core/plugins/ets/runtime/ets_coroutine.cpp +++ b/static_core/plugins/ets/runtime/ets_coroutine.cpp @@ -26,6 +26,7 @@ #include "plugins/ets/runtime/types/ets_object.h" #include "plugins/ets/runtime/types/ets_box_primitive-inl.h" #include "intrinsics.h" +#include "runtime/include/string_flattening_cache.h" namespace ark::ets { @@ -260,12 +261,20 @@ void EtsCoroutine::OnHostWorkerChanged() auto *ptr = worker->GetLocalStorage().Get(); GetLocalStorage().Set(ptr); - if (GetType() == Coroutine::Type::MUTATOR) { + if (GetType() == Coroutine::Type::MUTATOR && Runtime::GetCurrent()->IsInitialized()) { // update the string cache pointer auto *curCoro = EtsCoroutine::GetCurrent(); ASSERT(curCoro != nullptr); auto setStringCachePtr = [this, worker]() { - auto *cache = worker->GetLocalStorage().Get(); + os::memory::LockHolder h(worker->GetStringCacheLock()); + auto *cache = + worker->GetLocalStorage().Get(); + if (cache == nullptr) { + cache = StringFlatteningCache::Create(GetVM()); + worker->GetLocalStorage().Set(cache); + } + + ASSERT(cache != nullptr); SetFlattenedStringCache(cache); }; // We need to put the current coro into the managed state to be GC-safe, because we manipulate a raw diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index 02d2b107c3..e4ea771611 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -52,6 +52,7 @@ #include "plugins/ets/runtime/types/ets_escompat_array.h" #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h" #include "plugins/ets/runtime/hybrid/mem/static_object_operator.h" +#include "runtime/include/string_flattening_cache.h" #include "plugins/ets/runtime/ets_object_state_table.h" #include "libpandabase/taskmanager/task_manager.h" @@ -325,6 +326,17 @@ bool PandaEtsVM::Initialize() longToStringCache_ = LongToStringCache::Create(coro); } + auto *worker = coro->GetWorker(); + auto *cache = + worker->GetLocalStorage().Get(); + if (cache == nullptr) { + cache = StringFlatteningCache::Create(this); + worker->GetLocalStorage().Set(cache); + } + + ASSERT(cache != nullptr); + coro->SetFlattenedStringCache(cache); + referenceProcessor_->Initialize(); } [[maybe_unused]] bool cachesCreated = diff --git a/static_core/runtime/coretypes/string.cpp b/static_core/runtime/coretypes/string.cpp index 7a997e464d..6871b10a8e 100644 --- a/static_core/runtime/coretypes/string.cpp +++ b/static_core/runtime/coretypes/string.cpp @@ -34,6 +34,7 @@ #include "runtime/include/panda_vm.h" #include "runtime/include/coretypes/string.h" #include "runtime/include/coretypes/line_string.h" +#include "runtime/include/string_flattening_cache.h" namespace ark::coretypes { @@ -888,7 +889,15 @@ FlatStringInfo FlatStringInfo::FlattenTreeString(VMHandle &treeStr, cons return FlatStringInfo(String::Cast(first), 0, treeString->GetLength()); } + StringFlatteningCache cache(ManagedThread::GetCurrent()); + auto *cachedFlatStr = cache.Get(treeStr.GetPtr()); + + if (cachedFlatStr != nullptr) { + return FlatStringInfo(cachedFlatStr, 0, cachedFlatStr->GetLength()); + } + String *s = SlowFlatten(treeStr, ctx); + cache.Update(treeStr.GetPtr(), s); return FlatStringInfo(s, 0, treeString->GetLength()); } diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index a126477afe..100b50c5fd 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -16,6 +16,8 @@ #include "runtime/coroutines/coroutine_manager.h" #include "runtime/coroutines/coroutine_worker.h" #include "runtime/include/thread_scopes.h" +#include "runtime/include/string_flattening_cache.h" +#include "runtime/include/thread-inl.h" namespace ark { @@ -41,15 +43,20 @@ void CoroutineWorker::OnBeforeScheduleLoopStart() void CoroutineWorker::VisitGCRoots(const GCRootVisitor &visitor) { - auto *stringCache = GetLocalStorage().Get(); - // NOTE(konstanting): worker local objects deserve a special root type - visitor(mem::GCRoot(mem::RootType::ROOT_VM, stringCache)); + auto *stringCache = GetLocalStorage().Get(); + if (stringCache != nullptr) { + // NOTE(konstanting): worker local objects deserve a special root type + visitor(mem::GCRoot(mem::RootType::ROOT_VM, stringCache)); + } } void CoroutineWorker::UpdateGCRoots(const GCRootUpdater &gcRootUpdater) { - auto *stringCachePtr = GetLocalStorage().GetPtr(); - gcRootUpdater(stringCachePtr); + auto *stringCachePtr = + GetLocalStorage().GetPtr(); + if (*stringCachePtr != nullptr) { + gcRootUpdater(stringCachePtr); + } } void CoroutineWorker::CreateWorkerLocalObjects() @@ -62,15 +69,6 @@ void CoroutineWorker::CreateWorkerLocalObjects() ASSERT(schLoopCoro != nullptr); ASSERT_NATIVE_CODE(); ScopedManagedCodeThread s(schLoopCoro); - // create the string flattening cache - /* - * probably should be done like: - * auto* cache = StringFlatteningCache::Create(...); - */ - //Class *klass = nullptr; - //auto *cache = coretypes::Array::Create(klass, 100500); - //ASSERT(cache != nullptr); - //GetLocalStorage().Set(cache); } } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_worker.h b/static_core/runtime/coroutines/coroutine_worker.h index 0c98aca989..6589e454f7 100644 --- a/static_core/runtime/coroutines/coroutine_worker.h +++ b/static_core/runtime/coroutines/coroutine_worker.h @@ -39,7 +39,7 @@ enum class CoroutinePriority { /// Represents a coroutine worker, which can host multiple coroutines and schedule them. class CoroutineWorker { public: - enum class DataIdx { INTEROP_CTX_PTR, EXTERNAL_IFACES, STRING_CACHE, LAST_ID }; + enum class DataIdx { INTEROP_CTX_PTR, EXTERNAL_IFACES, STRING_FLATTENING_CACHE, LAST_ID }; using LocalStorage = StaticLocalStorage; using Id = int32_t; @@ -115,6 +115,11 @@ public: virtual void VisitGCRoots(const GCRootVisitor &visitor); virtual void UpdateGCRoots(const GCRootUpdater &gcRootUpdater); + os::memory::Mutex &GetStringCacheLock() + { + return stringCacheLock_; + } + protected: /// should be called right before the schedule loop is entered for the first time void OnBeforeScheduleLoopStart(); @@ -132,6 +137,7 @@ private: // event loop poster os::memory::Mutex posterLock_; PandaUniquePtr extSchedulingPoster_; + os::memory::Mutex stringCacheLock_; }; } // namespace ark diff --git a/static_core/runtime/coroutines/local_storage.h b/static_core/runtime/coroutines/local_storage.h index 00e871f564..1e7bf6c896 100644 --- a/static_core/runtime/coroutines/local_storage.h +++ b/static_core/runtime/coroutines/local_storage.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/static_core/runtime/coroutines/threaded_coroutine_manager.h b/static_core/runtime/coroutines/threaded_coroutine_manager.h index cbdc1de84a..065aec8283 100644 --- a/static_core/runtime/coroutines/threaded_coroutine_manager.h +++ b/static_core/runtime/coroutines/threaded_coroutine_manager.h @@ -82,7 +82,7 @@ protected: bool EnumerateThreadsImpl(const ThreadManager::Callback &cb, unsigned int incMask, unsigned int xorMask) const override; bool EnumerateWorkersImpl(const EnumerateWorkerCallback &cb) const override; - + CoroutineContext *CreateCoroutineContext(bool coroHasEntrypoint) override; void DeleteCoroutineContext(CoroutineContext *ctx) override; diff --git a/static_core/runtime/include/string_flattening_cache.h b/static_core/runtime/include/string_flattening_cache.h new file mode 100644 index 0000000000..622635cd68 --- /dev/null +++ b/static_core/runtime/include/string_flattening_cache.h @@ -0,0 +1,95 @@ +/** + * 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. + */ +#ifndef PANDA_RUNTIME_STRING_FLATTENING_CACHE_H +#define PANDA_RUNTIME_STRING_FLATTENING_CACHE_H + +#include "runtime/include/coretypes/string.h" +#include "runtime/include/runtime.h" +#include "runtime/include/panda_vm.h" + +namespace ark { +class StringFlatteningCache { +public: + explicit StringFlatteningCache(ManagedThread *thread) : thread_(thread) {} + + static coretypes::Array *Create(PandaVM *vm) + { + auto *linker = Runtime::GetCurrent()->GetClassLinker(); + auto *ext = linker->GetExtension(vm->GetLanguageContext()); + auto *klass = ext->GetClassRoot(ClassRoot::ARRAY_STRING); + return coretypes::Array::Create(klass, ENTRY_SIZE * SIZE); + } + + coretypes::String *Get(coretypes::String *treeStr) + { + auto *cache = GetCache(); + auto index = GetIndex(treeStr); + auto *key = GetKey(cache, index); + if (key != treeStr) { + return nullptr; + } + return GetValue(cache, index); + } + + void Update(coretypes::String *treeStr, coretypes::String *flatStr) + { + auto *cache = GetCache(); + auto index = GetIndex(treeStr); + SetKey(cache, index, treeStr); + SetValue(cache, index, flatStr); + } + +private: + static constexpr size_t SHIFT = 3; + static constexpr size_t MASK = 0x3f; + static constexpr size_t SIZE = MASK + 1; + static constexpr size_t ENTRY_SIZE = 2; + static constexpr size_t VALUE_OFFSET = 1; + + static size_t GetIndex(coretypes::String *treeStr) + { + return ((ToUintPtr(treeStr) >> SHIFT) & MASK) * ENTRY_SIZE; + } + + static coretypes::String *GetKey(coretypes::Array *cache, size_t index) + { + return cache->Get(index); + } + + static void SetKey(coretypes::Array *cache, size_t index, coretypes::String *key) + { + cache->Set(index, key); + } + + static coretypes::String *GetValue(coretypes::Array *cache, size_t index) + { + return cache->Get(index + VALUE_OFFSET); + } + + static void SetValue(coretypes::Array *cache, size_t index, coretypes::String *value) + { + cache->Set(index + VALUE_OFFSET, value); + } + + coretypes::Array *GetCache() + { + return coretypes::Array::Cast(thread_->GetFlattenedStringCache()); + } + + ManagedThread *thread_; +}; +} // namespace ark + +#endif // PANDA_RUNTIME_STRING_FLATTENING_CACHE_H -- Gitee From ca1deb7dbff504312a5f8e2825d8b11af2e1bc8f Mon Sep 17 00:00:00 2001 From: rjgask Date: Fri, 12 Sep 2025 00:14:43 +0800 Subject: [PATCH 03/10] cosmetic Signed-off-by: rjgask --- .../backend/compiler/codegen_fastpath.cpp | 6 +----- static_core/irtoc/scripts/common.irt | 2 +- static_core/irtoc/scripts/string_helpers.irt | 16 +++++++++++---- static_core/runtime/arch/asm_support.cpp | 1 + .../runtime/asm_defines/asm_defines.def | 5 +++++ static_core/runtime/asm_defines/defines.cpp | 1 + .../runtime/include/string_flattening_cache.h | 20 +++++++++++++++++++ 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/static_core/irtoc/backend/compiler/codegen_fastpath.cpp b/static_core/irtoc/backend/compiler/codegen_fastpath.cpp index cd9fec9dca..ef9ea20523 100644 --- a/static_core/irtoc/backend/compiler/codegen_fastpath.cpp +++ b/static_core/irtoc/backend/compiler/codegen_fastpath.cpp @@ -322,16 +322,12 @@ void CodegenFastPath::EmitWriteTlabStatsSafeIntrinsic([[maybe_unused]] Intrinsic auto src1 = src[FIRST_OPERAND]; auto src2 = src[SECOND_OPERAND]; - auto src3 = src[THIRD_OPERAND]; - - ASSERT(src3.IsValid()); - ASSERT(src3 != GetRegfile()->GetZeroReg()); auto regs = GetCallerRegsMask(GetArch(), false) | GetCalleeRegsMask(GetArch(), false); auto vregs = GetCallerRegsMask(GetArch(), true); GetEncoder()->PushRegisters(regs, vregs); - FillCallParams(src1, src2, src3); + FillCallParams(src1, src2); auto id = RuntimeInterface::EntrypointId::WRITE_TLAB_STATS_NO_BRIDGE; MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), id)); diff --git a/static_core/irtoc/scripts/common.irt b/static_core/irtoc/scripts/common.irt index 432a9c81bc..afc4655a8c 100644 --- a/static_core/irtoc/scripts/common.irt +++ b/static_core/irtoc/scripts/common.irt @@ -225,7 +225,7 @@ macro(:call_runtime) { |e, *args| } macro(:call_runtime_save_all) { |e, *args| - # Load entry before save registers because stack SpillFill is posible + # Load entry before save registers because stack SpillFill is possible entry := LoadI(%tr).Imm(e).ptr Intrinsic(:SAVE_REGISTERS_EP).void ret := CallIndirect(entry, *args) diff --git a/static_core/irtoc/scripts/string_helpers.irt b/static_core/irtoc/scripts/string_helpers.irt index b2b7dd9e63..ce75448a57 100644 --- a/static_core/irtoc/scripts/string_helpers.irt +++ b/static_core/irtoc/scripts/string_helpers.irt @@ -39,6 +39,13 @@ macro(:check_string_type) do |str| } end +module StringFlatteningCacheConstants + ADDRESS_SHIFT = "cross_values::GetStringFlatteningCacheAddressShift(GetArch())" + ADDRESS_MASK = "cross_values::GetStringFlatteningCacheAddressMask(GetArch())" + ENTRY_SIZE = "cross_values::GetStringFlatteningCacheEntrySize(GetArch())" + VALUE_OFFSET = "cross_values::GetStringFlatteningCacheValueOffset(GetArch())" +end + macro(:try_use_cached_flat_str) do |str| baseClass := LoadI(str).ref stringType := LoadI(baseClass).Imm(Constants::STRING_TYPE_OFFSET).u64 @@ -51,16 +58,17 @@ macro(:try_use_cached_flat_str) do |str| Goto(:SlowPathEntrypoint) } + # cannot cast directly from ref to uint strPtr := Cast(str).ptr strUint := Bitcast(strPtr).word - strShifted := ShrI(strUint).Imm(3).word - strMasked := AndI(strShifted).Imm(0x3f).word - strIndex := Mul(strMasked, 2).word + strShifted := ShrI(strUint).Imm(StringFlatteningCacheConstants::ADDRESS_SHIFT).word + strMasked := AndI(strShifted).Imm(StringFlatteningCacheConstants::ADDRESS_MASK).word + strIndex := Mul(strMasked, StringFlatteningCacheConstants::ENTRY_SIZE).word key := LoadArray(stringCache, strIndex).ref If(key, str).NE { Goto(:SlowPathEntrypoint) } - strIndex := AddI(strIndex).Imm(1).ref_uint + strIndex := AddI(strIndex).Imm(StringFlatteningCacheConstants::VALUE_OFFSET).ref_uint strCached := LoadArray(stringCache, strIndex).ref } result := Phi(str, strCached).ref diff --git a/static_core/runtime/arch/asm_support.cpp b/static_core/runtime/arch/asm_support.cpp index b4d23b18ab..71953ad8e5 100644 --- a/static_core/runtime/arch/asm_support.cpp +++ b/static_core/runtime/arch/asm_support.cpp @@ -21,6 +21,7 @@ #include "runtime/include/method.h" #include "runtime/include/mtmanaged_thread.h" #include "runtime/include/thread.h" +#include "runtime/include/string_flattening_cache.h" #include "plugins_defines.h" namespace ark { diff --git a/static_core/runtime/asm_defines/asm_defines.def b/static_core/runtime/asm_defines/asm_defines.def index 0426b73502..5c4f831d5c 100644 --- a/static_core/runtime/asm_defines/asm_defines.def +++ b/static_core/runtime/asm_defines/asm_defines.def @@ -219,5 +219,10 @@ DEFINE_VALUE(RUNTIME_MODE_ODD_SAVED, 3) DEFINE_VALUE(MIN_PREFIX_OPCODE_INDEX, BytecodeInstruction::GetMinPrefixOpcodeIndex()) DEFINE_VALUE(TAGGED_VALUE_UNDEFINED, coretypes::TaggedValue::VALUE_UNDEFINED) + +DEFINE_VALUE(STRING_FLATTENING_CACHE_ADDRESS_SHIFT, ark::StringFlatteningCache::GetAddressShift()) +DEFINE_VALUE(STRING_FLATTENING_CACHE_ADDRESS_MASK, ark::StringFlatteningCache::GetAddressMask()) +DEFINE_VALUE(STRING_FLATTENING_CACHE_ENTRY_SIZE, ark::StringFlatteningCache::GetEntrySize()) +DEFINE_VALUE(STRING_FLATTENING_CACHE_VALUE_OFFSET, ark::StringFlatteningCache::GetValueOffset()) // NOLINTEND(readability-identifier-naming) #include "plugins_asm_defines.def" diff --git a/static_core/runtime/asm_defines/defines.cpp b/static_core/runtime/asm_defines/defines.cpp index dbef986831..c8bc146b37 100644 --- a/static_core/runtime/asm_defines/defines.cpp +++ b/static_core/runtime/asm_defines/defines.cpp @@ -25,6 +25,7 @@ #include "runtime/include/mtmanaged_thread.h" #include "runtime/mem/tlab.h" #include "utils/cframe_layout.h" +#include "runtime/include/string_flattening_cache.h" #include "plugins_defines.h" diff --git a/static_core/runtime/include/string_flattening_cache.h b/static_core/runtime/include/string_flattening_cache.h index 622635cd68..ac7f7a778d 100644 --- a/static_core/runtime/include/string_flattening_cache.h +++ b/static_core/runtime/include/string_flattening_cache.h @@ -51,6 +51,26 @@ public: SetValue(cache, index, flatStr); } + static constexpr size_t GetAddressShift() + { + return SHIFT; + } + + static constexpr size_t GetAddressMask() + { + return MASK; + } + + static constexpr size_t GetEntrySize() + { + return ENTRY_SIZE; + } + + static constexpr size_t GetValueOffset() + { + return VALUE_OFFSET; + } + private: static constexpr size_t SHIFT = 3; static constexpr size_t MASK = 0x3f; -- Gitee From c6d3cb60eb73fd90e7f73a63372bd0b07d761ac9 Mon Sep 17 00:00:00 2001 From: Panferov Ivan Date: Wed, 10 Sep 2025 22:20:59 +0800 Subject: [PATCH 04/10] fix cache initialization Signed-off-by: Panferov Ivan --- static_core/plugins/ets/runtime/ets_vm.cpp | 1 + .../runtime/coroutines/coroutine_manager.cpp | 8 +++++ .../runtime/coroutines/coroutine_manager.h | 3 ++ .../runtime/coroutines/coroutine_worker.cpp | 31 +++++++++++++------ .../runtime/coroutines/coroutine_worker.h | 12 +++---- .../coroutines/stackful_coroutine_manager.cpp | 1 + .../coroutines/stackful_coroutine_worker.cpp | 1 - 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index e4ea771611..02f9017705 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -359,6 +359,7 @@ bool PandaEtsVM::Initialize() nativeLibraryProvider_.AddLibraryPath(ConvertToString(path)); } + coroutineManager_->OnRuntimeInitialization(); return true; } diff --git a/static_core/runtime/coroutines/coroutine_manager.cpp b/static_core/runtime/coroutines/coroutine_manager.cpp index fa224958ea..362674e8ce 100644 --- a/static_core/runtime/coroutines/coroutine_manager.cpp +++ b/static_core/runtime/coroutines/coroutine_manager.cpp @@ -169,4 +169,12 @@ CoroutineSchedulingPolicy CoroutineManager::GetSchedulingPolicy() const return schedulingPolicy_; } +void CoroutineManager::OnRuntimeInitialization() +{ + EnumerateWorkers([](CoroutineWorker *worker) { + worker->OnRuntimeInitialization(); + return true; + }); +} + } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_manager.h b/static_core/runtime/coroutines/coroutine_manager.h index 87d1d8e545..f67b4ab6c4 100644 --- a/static_core/runtime/coroutines/coroutine_manager.h +++ b/static_core/runtime/coroutines/coroutine_manager.h @@ -356,6 +356,9 @@ public: return config_; } + /// should be called during runtime initialization + void OnRuntimeInitialization(); + protected: using EntrypointInfo = Coroutine::EntrypointInfo; /// Create native coroutine context instance (implementation dependent) diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index 100b50c5fd..70fcc585db 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -36,11 +36,20 @@ void CoroutineWorker::OnCoroBecameActive(Coroutine *co) TriggerSchedulerExternally(co); } -void CoroutineWorker::OnBeforeScheduleLoopStart() +void CoroutineWorker::OnRuntimeInitialization() { + ASSERT(!GetRuntime()->IsInitialized()); CreateWorkerLocalObjects(); } +void CoroutineWorker::OnWorkerStartup() +{ + ASSERT(Coroutine::GetCurrent()->GetWorker() == this); + if (GetRuntime()->IsInitialized()) { + CreateWorkerLocalObjects(); + } +} + void CoroutineWorker::VisitGCRoots(const GCRootVisitor &visitor) { auto *stringCache = GetLocalStorage().Get(); @@ -61,14 +70,18 @@ void CoroutineWorker::UpdateGCRoots(const GCRootUpdater &gcRootUpdater) void CoroutineWorker::CreateWorkerLocalObjects() { - // native part - // ... - - // managed part - auto *schLoopCoro = Coroutine::GetCurrent(); - ASSERT(schLoopCoro != nullptr); - ASSERT_NATIVE_CODE(); - ScopedManagedCodeThread s(schLoopCoro); + ASSERT((GetLocalStorage().Get() == nullptr)); + auto *coro = Coroutine::GetCurrent(); + ASSERT(coro != nullptr); + coretypes::Array *stringFlatteningCache = nullptr; + if (coro->IsInNativeCode()) { + ScopedManagedCodeThread s(coro); + stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); + } else { + stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); + } + ASSERT(stringFlatteningCache != nullptr); + GetLocalStorage().Set(stringFlatteningCache); } } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_worker.h b/static_core/runtime/coroutines/coroutine_worker.h index 6589e454f7..a680889e9a 100644 --- a/static_core/runtime/coroutines/coroutine_worker.h +++ b/static_core/runtime/coroutines/coroutine_worker.h @@ -115,14 +115,11 @@ public: virtual void VisitGCRoots(const GCRootVisitor &visitor); virtual void UpdateGCRoots(const GCRootUpdater &gcRootUpdater); - os::memory::Mutex &GetStringCacheLock() - { - return stringCacheLock_; - } + /// should be called during runtime initialization + void OnRuntimeInitialization(); -protected: - /// should be called right before the schedule loop is entered for the first time - void OnBeforeScheduleLoopStart(); + /// should be called from CoroutineManager after worker creation from the coroutine assigned to the worker + void OnWorkerStartup(); private: void CreateWorkerLocalObjects(); @@ -137,7 +134,6 @@ private: // event loop poster os::memory::Mutex posterLock_; PandaUniquePtr extSchedulingPoster_; - os::memory::Mutex stringCacheLock_; }; } // namespace ark diff --git a/static_core/runtime/coroutines/stackful_coroutine_manager.cpp b/static_core/runtime/coroutines/stackful_coroutine_manager.cpp index ed3f7777a5..6575041291 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_manager.cpp +++ b/static_core/runtime/coroutines/stackful_coroutine_manager.cpp @@ -192,6 +192,7 @@ void StackfulCoroutineManager::OnWorkerStartup(StackfulCoroutineWorker *worker) void StackfulCoroutineManager::OnWorkerStartupImpl(StackfulCoroutineWorker *worker) { + worker->OnWorkerStartup(); workers_.push_back(worker); ++activeWorkersCount_; workersCv_.Signal(); diff --git a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp index db01960fe4..969c328ffe 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp +++ b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp @@ -282,7 +282,6 @@ void StackfulCoroutineWorker::ScheduleLoop() void StackfulCoroutineWorker::ScheduleLoopBody() { - OnBeforeScheduleLoopStart(); // run the loop while (IsActive()) { RequestScheduleImpl(); -- Gitee From fd992e4b46c7a3c868f1bd2b176a5fe8b4a5b180 Mon Sep 17 00:00:00 2001 From: rjgask Date: Thu, 11 Sep 2025 22:48:45 +0800 Subject: [PATCH 05/10] fix after cherry-pick --- static_core/plugins/ets/runtime/ets_coroutine.cpp | 9 +-------- static_core/plugins/ets/runtime/ets_vm.cpp | 12 ------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/static_core/plugins/ets/runtime/ets_coroutine.cpp b/static_core/plugins/ets/runtime/ets_coroutine.cpp index 845c638ee6..4ff712b77c 100644 --- a/static_core/plugins/ets/runtime/ets_coroutine.cpp +++ b/static_core/plugins/ets/runtime/ets_coroutine.cpp @@ -26,7 +26,6 @@ #include "plugins/ets/runtime/types/ets_object.h" #include "plugins/ets/runtime/types/ets_box_primitive-inl.h" #include "intrinsics.h" -#include "runtime/include/string_flattening_cache.h" namespace ark::ets { @@ -261,19 +260,13 @@ void EtsCoroutine::OnHostWorkerChanged() auto *ptr = worker->GetLocalStorage().Get(); GetLocalStorage().Set(ptr); - if (GetType() == Coroutine::Type::MUTATOR && Runtime::GetCurrent()->IsInitialized()) { + if (GetType() == Coroutine::Type::MUTATOR) { // update the string cache pointer auto *curCoro = EtsCoroutine::GetCurrent(); ASSERT(curCoro != nullptr); auto setStringCachePtr = [this, worker]() { - os::memory::LockHolder h(worker->GetStringCacheLock()); auto *cache = worker->GetLocalStorage().Get(); - if (cache == nullptr) { - cache = StringFlatteningCache::Create(GetVM()); - worker->GetLocalStorage().Set(cache); - } - ASSERT(cache != nullptr); SetFlattenedStringCache(cache); }; diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index 02f9017705..f3292a0df2 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -52,7 +52,6 @@ #include "plugins/ets/runtime/types/ets_escompat_array.h" #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h" #include "plugins/ets/runtime/hybrid/mem/static_object_operator.h" -#include "runtime/include/string_flattening_cache.h" #include "plugins/ets/runtime/ets_object_state_table.h" #include "libpandabase/taskmanager/task_manager.h" @@ -326,17 +325,6 @@ bool PandaEtsVM::Initialize() longToStringCache_ = LongToStringCache::Create(coro); } - auto *worker = coro->GetWorker(); - auto *cache = - worker->GetLocalStorage().Get(); - if (cache == nullptr) { - cache = StringFlatteningCache::Create(this); - worker->GetLocalStorage().Set(cache); - } - - ASSERT(cache != nullptr); - coro->SetFlattenedStringCache(cache); - referenceProcessor_->Initialize(); } [[maybe_unused]] bool cachesCreated = -- Gitee From 52363e14f8919909a9a89c78ba98d656413106de Mon Sep 17 00:00:00 2001 From: Panferov Ivan Date: Fri, 12 Sep 2025 17:55:46 +0800 Subject: [PATCH 06/10] fix local objects caching in coros Signed-off-by: Panferov Ivan --- .../plugins/ets/runtime/ets_coroutine.cpp | 12 ++++++-- .../plugins/ets/runtime/ets_coroutine.h | 2 ++ static_core/plugins/ets/runtime/ets_vm.cpp | 12 -------- static_core/runtime/coroutines/coroutine.h | 2 ++ .../runtime/coroutines/coroutine_worker.cpp | 28 ++++++------------- .../runtime/coroutines/coroutine_worker.h | 5 +--- .../coroutines/stackful_coroutine_worker.cpp | 12 ++++++++ .../coroutines/stackful_coroutine_worker.h | 3 ++ 8 files changed, 37 insertions(+), 39 deletions(-) diff --git a/static_core/plugins/ets/runtime/ets_coroutine.cpp b/static_core/plugins/ets/runtime/ets_coroutine.cpp index 4ff712b77c..1e8b293b5d 100644 --- a/static_core/plugins/ets/runtime/ets_coroutine.cpp +++ b/static_core/plugins/ets/runtime/ets_coroutine.cpp @@ -14,6 +14,7 @@ */ #include "plugins/ets/runtime/ets_coroutine.h" +#include "mem/refstorage/global_object_storage.h" #include "runtime/include/value.h" #include "macros.h" #include "mem/refstorage/reference.h" @@ -254,6 +255,11 @@ ExternalIfaceTable *EtsCoroutine::GetExternalIfaceTable() } void EtsCoroutine::OnHostWorkerChanged() +{ + UpdateCachedObjects(); +} + +void EtsCoroutine::UpdateCachedObjects() { // update the interop context pointer auto *worker = GetWorker(); @@ -265,9 +271,9 @@ void EtsCoroutine::OnHostWorkerChanged() auto *curCoro = EtsCoroutine::GetCurrent(); ASSERT(curCoro != nullptr); auto setStringCachePtr = [this, worker]() { - auto *cache = - worker->GetLocalStorage().Get(); - ASSERT(cache != nullptr); + auto *cacheRef = + worker->GetLocalStorage().Get(); + auto *cache = GetVM()->GetGlobalObjectStorage()->Get(cacheRef); SetFlattenedStringCache(cache); }; // We need to put the current coro into the managed state to be GC-safe, because we manipulate a raw diff --git a/static_core/plugins/ets/runtime/ets_coroutine.h b/static_core/plugins/ets/runtime/ets_coroutine.h index 8ad028b3f9..34a0140995 100644 --- a/static_core/plugins/ets/runtime/ets_coroutine.h +++ b/static_core/plugins/ets/runtime/ets_coroutine.h @@ -152,6 +152,8 @@ public: static constexpr CoroutinePriority TIMER_CALLBACK = CoroutinePriority::MEDIUM_PRIORITY; static constexpr CoroutinePriority LAUNCH = CoroutinePriority::MEDIUM_PRIORITY; + void UpdateCachedObjects() override; + protected: // we would like everyone to use the factory to create a EtsCoroutine explicit EtsCoroutine(ThreadId id, mem::InternalAllocatorPtr allocator, PandaVM *vm, PandaString name, diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index f3292a0df2..ab73334ece 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -692,12 +692,6 @@ void PandaEtsVM::VisitVmRoots(const GCRootVisitor &visitor) } return true; }); - // NOTE(konstanting): worth moving to gc_root.cpp with a separate root type. Requires the ManagedCpu introduction. - GetCoroutineManager()->EnumerateWorkers([visitor](CoroutineWorker *worker) { - // apply visitor to worker - worker->VisitGCRoots(visitor); - return true; - }); if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) { visitor(mem::GCRoot(mem::RootType::ROOT_VM, doubleToStringCache_->GetCoreType())); visitor(mem::GCRoot(mem::RootType::ROOT_VM, floatToStringCache_->GetCoreType())); @@ -773,12 +767,6 @@ void PandaEtsVM::UpdateVmRefs(const GCRootUpdater &gcRootUpdater) UpdateManagedEntrypointArgRefs(coroutine, gcRootUpdater); return true; }); - // NOTE(konstanting): worth moving to gc_root.cpp with a separate root type. Requires the ManagedCpu introduction. - GetCoroutineManager()->EnumerateWorkers([gcRootUpdater](CoroutineWorker *worker) { - // apply updater to worker - worker->UpdateGCRoots(gcRootUpdater); - return true; - }); objStateTable_->EnumerateObjectStates([&gcRootUpdater](EtsObjectStateInfo *info) { auto *obj = info->GetEtsObject()->GetCoreType(); diff --git a/static_core/runtime/coroutines/coroutine.h b/static_core/runtime/coroutines/coroutine.h index 27111a2a03..d9e1dc8bdd 100644 --- a/static_core/runtime/coroutines/coroutine.h +++ b/static_core/runtime/coroutines/coroutine.h @@ -344,6 +344,8 @@ public: return abortFlag_; } + virtual void UpdateCachedObjects() {}; + protected: // We would like everyone to use the factory to create a Coroutine, thus ctor is protected explicit Coroutine(ThreadId id, mem::InternalAllocatorPtr allocator, PandaVM *vm, diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index 70fcc585db..319fcb7469 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -18,6 +18,7 @@ #include "runtime/include/thread_scopes.h" #include "runtime/include/string_flattening_cache.h" #include "runtime/include/thread-inl.h" +#include "mem/refstorage/global_object_storage.h" namespace ark { @@ -40,6 +41,7 @@ void CoroutineWorker::OnRuntimeInitialization() { ASSERT(!GetRuntime()->IsInitialized()); CreateWorkerLocalObjects(); + CacheLocalObjectsInCoroutines(); } void CoroutineWorker::OnWorkerStartup() @@ -47,30 +49,13 @@ void CoroutineWorker::OnWorkerStartup() ASSERT(Coroutine::GetCurrent()->GetWorker() == this); if (GetRuntime()->IsInitialized()) { CreateWorkerLocalObjects(); - } -} - -void CoroutineWorker::VisitGCRoots(const GCRootVisitor &visitor) -{ - auto *stringCache = GetLocalStorage().Get(); - if (stringCache != nullptr) { - // NOTE(konstanting): worker local objects deserve a special root type - visitor(mem::GCRoot(mem::RootType::ROOT_VM, stringCache)); - } -} - -void CoroutineWorker::UpdateGCRoots(const GCRootUpdater &gcRootUpdater) -{ - auto *stringCachePtr = - GetLocalStorage().GetPtr(); - if (*stringCachePtr != nullptr) { - gcRootUpdater(stringCachePtr); + CacheLocalObjectsInCoroutines(); } } void CoroutineWorker::CreateWorkerLocalObjects() { - ASSERT((GetLocalStorage().Get() == nullptr)); + ASSERT((GetLocalStorage().Get() == nullptr)); auto *coro = Coroutine::GetCurrent(); ASSERT(coro != nullptr); coretypes::Array *stringFlatteningCache = nullptr; @@ -81,7 +66,10 @@ void CoroutineWorker::CreateWorkerLocalObjects() stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); } ASSERT(stringFlatteningCache != nullptr); - GetLocalStorage().Set(stringFlatteningCache); + auto *refStorage = coro->GetVM()->GetGlobalObjectStorage(); + auto *cacheRef = refStorage->Add(stringFlatteningCache, mem::Reference::ObjectType::GLOBAL); + GetLocalStorage().Set( + cacheRef, [refStorage](void *ref) { refStorage->Remove(static_cast(ref)); }); } } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_worker.h b/static_core/runtime/coroutines/coroutine_worker.h index a680889e9a..881d7dd82d 100644 --- a/static_core/runtime/coroutines/coroutine_worker.h +++ b/static_core/runtime/coroutines/coroutine_worker.h @@ -111,10 +111,6 @@ public: void TriggerSchedulerExternally(Coroutine *requester); - // GC stuff - virtual void VisitGCRoots(const GCRootVisitor &visitor); - virtual void UpdateGCRoots(const GCRootUpdater &gcRootUpdater); - /// should be called during runtime initialization void OnRuntimeInitialization(); @@ -123,6 +119,7 @@ public: private: void CreateWorkerLocalObjects(); + virtual void CacheLocalObjectsInCoroutines() {} private: Runtime *runtime_ = nullptr; diff --git a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp index 969c328ffe..a126c8733b 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_worker.cpp +++ b/static_core/runtime/coroutines/stackful_coroutine_worker.cpp @@ -611,6 +611,18 @@ void StackfulCoroutineWorker::OnAfterContextSwitch(StackfulCoroutineContext *to) coroTo->OnContextSwitchedTo(); } +void StackfulCoroutineWorker::CacheLocalObjectsInCoroutines() +{ + os::memory::LockHolder lock(runnablesLock_); + runnables_.IterateOverCoroutines([](Coroutine *co) { co->UpdateCachedObjects(); }); + { + os::memory::LockHolder lh(waitersLock_); + for (auto &[_, co] : waiters_) { + co->UpdateCachedObjects(); + } + } +} + void StackfulCoroutineWorker::GetFullWorkerStateInfo(StackfulCoroutineWorkerStateInfo *info) const { os::memory::LockHolder lock(runnablesLock_); diff --git a/static_core/runtime/coroutines/stackful_coroutine_worker.h b/static_core/runtime/coroutines/stackful_coroutine_worker.h index 24effa925d..01a68ca59c 100644 --- a/static_core/runtime/coroutines/stackful_coroutine_worker.h +++ b/static_core/runtime/coroutines/stackful_coroutine_worker.h @@ -261,6 +261,9 @@ private: /// called right after the coroutineContext is switched (in case if no migration happened) void OnAfterContextSwitch(StackfulCoroutineContext *to); + /// worker local storage + void CacheLocalObjectsInCoroutines() override; + private: // data members StackfulCoroutineManager *coroManager_; Coroutine *scheduleLoopCtx_ = nullptr; -- Gitee From f93e4e1fcec969742c6f99a0ac74953e6cea2f1a Mon Sep 17 00:00:00 2001 From: Panferov Ivan Date: Fri, 12 Sep 2025 21:37:31 +0800 Subject: [PATCH 07/10] fix cache init in running coro Signed-off-by: Panferov Ivan --- static_core/runtime/coroutines/coroutine_manager.cpp | 2 ++ static_core/runtime/coroutines/coroutine_worker.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/static_core/runtime/coroutines/coroutine_manager.cpp b/static_core/runtime/coroutines/coroutine_manager.cpp index 362674e8ce..4fc031cc93 100644 --- a/static_core/runtime/coroutines/coroutine_manager.cpp +++ b/static_core/runtime/coroutines/coroutine_manager.cpp @@ -171,10 +171,12 @@ CoroutineSchedulingPolicy CoroutineManager::GetSchedulingPolicy() const void CoroutineManager::OnRuntimeInitialization() { + ASSERT(Coroutine::GetCurrent() == GetMainThread()); EnumerateWorkers([](CoroutineWorker *worker) { worker->OnRuntimeInitialization(); return true; }); + Coroutine::GetCurrent()->UpdateCachedObjects(); } } // namespace ark diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index 319fcb7469..56f241e660 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -50,6 +50,7 @@ void CoroutineWorker::OnWorkerStartup() if (GetRuntime()->IsInitialized()) { CreateWorkerLocalObjects(); CacheLocalObjectsInCoroutines(); + Coroutine::GetCurrent()->UpdateCachedObjects(); } } -- Gitee From 8b9b8e9b298cc69ba890b8b713ea7d5c8e12c9f2 Mon Sep 17 00:00:00 2001 From: rjgask Date: Fri, 12 Sep 2025 21:57:29 +0800 Subject: [PATCH 08/10] assert cache --- static_core/runtime/include/string_flattening_cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static_core/runtime/include/string_flattening_cache.h b/static_core/runtime/include/string_flattening_cache.h index ac7f7a778d..2998b1d958 100644 --- a/static_core/runtime/include/string_flattening_cache.h +++ b/static_core/runtime/include/string_flattening_cache.h @@ -35,6 +35,7 @@ public: coretypes::String *Get(coretypes::String *treeStr) { auto *cache = GetCache(); + ASSERT(cache != nullptr); auto index = GetIndex(treeStr); auto *key = GetKey(cache, index); if (key != treeStr) { @@ -46,6 +47,7 @@ public: void Update(coretypes::String *treeStr, coretypes::String *flatStr) { auto *cache = GetCache(); + ASSERT(cache != nullptr); auto index = GetIndex(treeStr); SetKey(cache, index, treeStr); SetValue(cache, index, flatStr); -- Gitee From 987c7da2fa723dd39168bea5f5ef8d648bef71d4 Mon Sep 17 00:00:00 2001 From: Panferov Ivan Date: Fri, 12 Sep 2025 23:52:10 +0800 Subject: [PATCH 09/10] fix coroman init Signed-off-by: Panferov Ivan --- static_core/plugins/ets/runtime/ets_vm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static_core/plugins/ets/runtime/ets_vm.cpp b/static_core/plugins/ets/runtime/ets_vm.cpp index ab73334ece..f6d1493b7d 100644 --- a/static_core/plugins/ets/runtime/ets_vm.cpp +++ b/static_core/plugins/ets/runtime/ets_vm.cpp @@ -326,6 +326,7 @@ bool PandaEtsVM::Initialize() } referenceProcessor_->Initialize(); + coroutineManager_->OnRuntimeInitialization(); } [[maybe_unused]] bool cachesCreated = (doubleToStringCache_ != nullptr && floatToStringCache_ != nullptr && longToStringCache_ != nullptr); @@ -347,7 +348,6 @@ bool PandaEtsVM::Initialize() nativeLibraryProvider_.AddLibraryPath(ConvertToString(path)); } - coroutineManager_->OnRuntimeInitialization(); return true; } -- Gitee From 731e03bd0f1ff731894b95e2630fc5e1ff69d217 Mon Sep 17 00:00:00 2001 From: rjgask Date: Sat, 13 Sep 2025 00:16:22 +0800 Subject: [PATCH 10/10] fix static analyzer issues --- .../runtime/coroutines/coroutine_worker.cpp | 20 +++++++++++-------- .../runtime/include/string_flattening_cache.h | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/static_core/runtime/coroutines/coroutine_worker.cpp b/static_core/runtime/coroutines/coroutine_worker.cpp index 56f241e660..086728a135 100644 --- a/static_core/runtime/coroutines/coroutine_worker.cpp +++ b/static_core/runtime/coroutines/coroutine_worker.cpp @@ -59,18 +59,22 @@ void CoroutineWorker::CreateWorkerLocalObjects() ASSERT((GetLocalStorage().Get() == nullptr)); auto *coro = Coroutine::GetCurrent(); ASSERT(coro != nullptr); - coretypes::Array *stringFlatteningCache = nullptr; + auto setFlattenedStringCache = [this, coro] { + auto *stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); + ASSERT(stringFlatteningCache != nullptr); + auto *refStorage = coro->GetVM()->GetGlobalObjectStorage(); + auto *cacheRef = refStorage->Add(stringFlatteningCache, mem::Reference::ObjectType::GLOBAL); + GetLocalStorage().Set( + cacheRef, [refStorage](void *ref) { refStorage->Remove(static_cast(ref)); }); + }; + // We need to put the current coro into the managed state to be GC-safe, because we manipulate a raw + // ObjectHeader* if (coro->IsInNativeCode()) { ScopedManagedCodeThread s(coro); - stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); + setFlattenedStringCache(); } else { - stringFlatteningCache = StringFlatteningCache::Create(GetPandaVM()); + setFlattenedStringCache(); } - ASSERT(stringFlatteningCache != nullptr); - auto *refStorage = coro->GetVM()->GetGlobalObjectStorage(); - auto *cacheRef = refStorage->Add(stringFlatteningCache, mem::Reference::ObjectType::GLOBAL); - GetLocalStorage().Set( - cacheRef, [refStorage](void *ref) { refStorage->Remove(static_cast(ref)); }); } } // namespace ark diff --git a/static_core/runtime/include/string_flattening_cache.h b/static_core/runtime/include/string_flattening_cache.h index 2998b1d958..94a20a3200 100644 --- a/static_core/runtime/include/string_flattening_cache.h +++ b/static_core/runtime/include/string_flattening_cache.h @@ -48,6 +48,8 @@ public: { auto *cache = GetCache(); ASSERT(cache != nullptr); + ASSERT(treeStr != nullptr); + ASSERT(flatStr != nullptr); auto index = GetIndex(treeStr); SetKey(cache, index, treeStr); SetValue(cache, index, flatStr); -- Gitee