From 94dd54a6d6c45e15214cc2b39ee94e55456b453f Mon Sep 17 00:00:00 2001 From: schernykh Date: Tue, 18 Apr 2023 15:23:47 +0300 Subject: [PATCH 1/2] Support ecma static inlininig Signed-off-by: Sergey Chernykh --- .../intrinsics_type_resolving_ecmascript.cpp | 32 +--- .../optimizer/optimizations/ecma_inlining.cpp | 47 ++++-- .../optimizer/optimizations/ecma_inlining.h | 6 + .../compiler/ecmascript_runtime_interface.cpp | 141 ++++++++---------- .../compiler/ecmascript_runtime_interface.h | 17 +-- runtime/ecma_compiler.cpp | 3 + tests/CMakeLists.txt | 2 +- tests/checked/CMakeLists.txt | 1 + tests/checked/ecma_inlining_static.js | 65 ++++++++ 9 files changed, 190 insertions(+), 124 deletions(-) create mode 100644 tests/checked/ecma_inlining_static.js diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index c5cae8209..3a6b14605 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -429,42 +429,26 @@ bool TypesResolving::InlineStObjByValue(IntrinsicInst *intrinsic) bool TypesResolving::GetICForMemNamedAccess(IntrinsicInst *intrinsic) { - auto pc = intrinsic->GetPc(); - auto runtime = GetGraph()->GetRuntime(); + auto method = GetGraph()->GetMethod(); auto save_state = intrinsic->GetSaveState(); ASSERT(save_state != nullptr); - auto caller_inst = save_state->GetCallerInst(); - if (caller_inst != nullptr) { + if (auto caller_inst = save_state->GetCallerInst(); caller_inst != nullptr) { ASSERT(caller_inst->IsInlined()); - auto func = caller_inst->GetFunctionObject(); - ASSERT(func != 0); - if (!runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), func, pc, &named_access_profile_)) { - return false; - } - } else if (!runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), pc, &named_access_profile_)) { - return false; + method = caller_inst->GetCallMethod(); } - return true; + return GetGraph()->GetRuntime()->GetProfileDataForNamedAccess(method, intrinsic->GetPc(), &named_access_profile_); } bool TypesResolving::GetICForMemValueAccess(IntrinsicInst *intrinsic) { - auto pc = intrinsic->GetPc(); - auto runtime = GetGraph()->GetRuntime(); + auto method = GetGraph()->GetMethod(); auto save_state = intrinsic->GetSaveState(); ASSERT(save_state != nullptr); - auto caller_inst = save_state->GetCallerInst(); - if (caller_inst != nullptr) { + if (auto caller_inst = save_state->GetCallerInst(); caller_inst != nullptr) { ASSERT(caller_inst->IsInlined()); - auto func = caller_inst->GetFunctionObject(); - ASSERT(func != 0); - if (!runtime->GetProfileDataForValueAccess(GetGraph()->GetMethod(), func, pc, &named_access_profile_)) { - return false; - } - } else if (!runtime->GetProfileDataForValueAccess(GetGraph()->GetMethod(), pc, &named_access_profile_)) { - return false; + method = caller_inst->GetCallMethod(); } - return true; + return GetGraph()->GetRuntime()->GetProfileDataForValueAccess(method, intrinsic->GetPc(), &named_access_profile_); } Inst *TypesResolving::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic) diff --git a/compiler/optimizer/optimizations/ecma_inlining.cpp b/compiler/optimizer/optimizations/ecma_inlining.cpp index 071666464..dd3ffc224 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.cpp +++ b/compiler/optimizer/optimizations/ecma_inlining.cpp @@ -68,14 +68,42 @@ bool EcmaInlining::CheckCallKind(CallInst *call_inst) return true; } +bool EcmaInlining::IsInstSuitableForEcmaStaticInlining(Inst *define_func) const +{ + if (define_func->GetOpcode() != Opcode::Intrinsic) { + return false; + } + switch (define_func->CastToIntrinsic()->GetIntrinsicId()) { + case RuntimeInterface::IntrinsicId::INTRINSIC_DEFINEFUNC_DYN: + case RuntimeInterface::IntrinsicId::INTRINSIC_DEFINE_NC_FUNC_DYN: + case RuntimeInterface::IntrinsicId::INTRINSIC_DEFINE_METHOD: + return true; + default: + return false; + } +} + +RuntimeInterface::MethodPtr EcmaInlining::TryResolveTargetStatic(CallInst *call_inst) const +{ + auto define_func = call_inst->GetDataFlowInput(0); + if (IsInstSuitableForEcmaStaticInlining(define_func)) { + return GetGraph()->GetRuntime()->GetMethodByIdAndSaveJsFunction(GetGraph()->GetMethod(), + define_func->CastToIntrinsic()->GetImm(0)); + } + return nullptr; +} + InlineContext EcmaInlining::ResolveTargets(CallInst *call_inst) { js_functions_.clear(); - if (!CheckCallKind(call_inst)) { - return {}; + if (auto method = TryResolveTargetStatic(call_inst); method != nullptr) { + return {method}; + } + if (CheckCallKind(call_inst)) { + ASSERT(js_functions_[0] != 0); + return {GetGraph()->GetRuntime()->GetMethodFromFunctionAndSaveJsFunction(js_functions_[0])}; } - ASSERT(js_functions_[0] != 0); - return {GetGraph()->GetRuntime()->GetMethodFromFunction(js_functions_[0])}; + return {}; } bool EcmaInlining::CheckMethod(CallInst *call_inst, InlineContext *ctx) @@ -159,7 +187,9 @@ void EcmaInlining::InsertGraph(CallInst *call_inst, const InlineContext &ctx, Gr GetGraph()->SetCurrentInstructionId(graph_inl->GetCurrentInstructionId()); GetGraph()->SetMaxInliningDepth(graph_inl->GetMaxInliningDepth() + 1); - BuildGuard(call_inst); + if (!IsStaticInlining()) { + BuildGuard(call_inst); + } auto call_bb = call_inst->GetBasicBlock(); auto call_cont_bb = call_bb->SplitBlockAfterInstruction(call_inst, false); @@ -180,10 +210,6 @@ void EcmaInlining::InsertGraph(CallInst *call_inst, const InlineContext &ctx, Gr call_inst->SetCallMethod(ctx.method); call_inst->SetCallMethodId(GetGraph()->GetRuntime()->GetMethodId(ctx.method)); - - CHECK_EQ(js_functions_.size(), 1U); - ASSERT(js_functions_[0] != 0); - call_inst->SetFunctionObject(js_functions_[0]); } bool EcmaInlining::TryInline(CallInst *call_inst) @@ -215,7 +241,8 @@ bool EcmaInlining::TryInline(CallInst *call_inst) methods_inlined_++; EVENT_INLINE(GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod()), GetGraph()->GetRuntime()->GetMethodFullName(ctx.method), call_inst->GetId(), - events::InlineKind::DYNAMIC_MONOMORPHIC, events::InlineResult::SUCCESS); + js_functions_.empty() ? events::InlineKind::STATIC : events::InlineKind::DYNAMIC_MONOMORPHIC, + events::InlineResult::SUCCESS); LOG_INLINING(DEBUG) << "Successfully inlined: " << GetGraph()->GetRuntime()->GetMethodFullName(ctx.method); return true; } diff --git a/compiler/optimizer/optimizations/ecma_inlining.h b/compiler/optimizer/optimizations/ecma_inlining.h index 413e61e5c..5f9789a60 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.h +++ b/compiler/optimizer/optimizations/ecma_inlining.h @@ -39,11 +39,17 @@ private: bool TryInline(CallInst *call_inst) override; InlineContext ResolveTargets(CallInst *call_inst); + RuntimeInterface::MethodPtr TryResolveTargetStatic(CallInst *call_inst) const; + bool IsInstSuitableForEcmaStaticInlining(Inst *define_func) const; bool CheckCallKind(CallInst *call_inst); bool CheckMethod(CallInst *call_inst, InlineContext *ctx); Graph *BuildGraph(CallInst *call_inst, InlineContext *ctx); void BuildGuard(CallInst *call_inst); void InsertGraph(CallInst *call_inst, const InlineContext &ctx, Graph *graph_inl); + bool IsStaticInlining() const + { + return js_functions_.empty(); + } private: // This vector contains pointers to element in EcmaCallProfilingTable diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 9ecb89da6..fd8f9aa6a 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -520,44 +520,22 @@ bool EcmaRuntimeInterface::AddProfileValueInfo(PandaRuntimeInterface::MethodPtr inline ProfileTypeInfo *EcmaRuntimeInterface::GetProfileTypeInfo(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, uint8_t *slot) { - os::memory::LockHolder lock_m(mutex_); + // ProfileTypeInfo is manage object + // we need to have lock in caller method, because we return ProfileTypeInfo + ASSERT(ecma_vm_->GetMutatorLock()->HasLock()); uint8_t *mapping = JsMethodCast(m)->GetICMapping(); if (mapping == nullptr) { return nullptr; } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) *slot = mapping[slot_id]; - auto it = js_function_table_.find(MethodCast(m)); - if (it == js_function_table_.end()) { - return nullptr; - } - - auto func = JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); - return ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); -} - -inline ProfileTypeInfo *EcmaRuntimeInterface::GetProfileTypeInfo(uintptr_t func_address, uintptr_t slot_id, - uint8_t *slot) -{ - os::memory::LockHolder lock_m(mutex_); - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - auto js_func = TaggedValue(reinterpret_cast(func_address)[0]); - if (!js_func.IsHeapObject()) { - return nullptr; - } - auto func = JSFunction::Cast(js_func.GetHeapObject()); - uint8_t *mapping = func->GetMethod()->GetICMapping(); - if (mapping == nullptr) { - return nullptr; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - *slot = mapping[slot_id]; - auto profile_info_obj = func->GetProfileTypeInfo(); - if (profile_info_obj.IsUndefined()) { - return nullptr; + if (auto func = GetJSFunctionByMethod(m); func != nullptr) { + auto pti = func->GetProfileTypeInfo(); + if (pti.IsHeapObject()) { + return ProfileTypeInfo::Cast(pti.GetTaggedObject()); + } } - - return ProfileTypeInfo::Cast(profile_info_obj.GetTaggedObject()); + return nullptr; } bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, @@ -580,28 +558,6 @@ bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::M return false; } -bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, - uintptr_t slot_id, ArenaVector *profile) -{ - if (profile == nullptr) { - return false; - } - profile->clear(); - ASSERT(func_address != 0); - ScopedMutatorLock lock; - - uint8_t slot; - ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(func_address, slot_id, &slot); - if (profile_type_info == nullptr) { - return false; - } - if (AddProfileInfo(m, profile, profile_type_info, slot)) { - AddProfileInfo(m, profile, profile_type_info, slot + 2); - return true; - } - return false; -} - bool EcmaRuntimeInterface::GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, ArenaVector *profile) { @@ -619,24 +575,6 @@ bool EcmaRuntimeInterface::GetProfileDataForValueAccess(PandaRuntimeInterface::M return AddProfileValueInfo(m, profile, profile_type_info, slot); } -bool EcmaRuntimeInterface::GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, - uintptr_t slot_id, ArenaVector *profile) -{ - if (profile == nullptr) { - return false; - } - profile->clear(); - ASSERT(func_address != 0); - ScopedMutatorLock lock; - - uint8_t slot; - ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(func_address, slot_id, &slot); - if (profile_type_info == nullptr) { - return false; - } - - return AddProfileValueInfo(m, profile, profile_type_info, slot); -} void EcmaRuntimeInterface::CleanFunction(Method *method) { os::memory::LockHolder lock(mutex_); @@ -771,20 +709,21 @@ PandaRuntimeInterface::GlobalVarInfo EcmaRuntimeInterface::GetGlobalVarInfo(Pand return {GlobalVarInfo::Type::NON_CONFIGURABLE, address}; } - os::memory::LockHolder lock_m(mutex_); uint8_t *mapping = JsMethodCast(method)->GetICMapping(); if (mapping == nullptr) { return {}; } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto slot = mapping[slot_id]; - auto it = js_function_table_.find(MethodCast(method)); - if (it == js_function_table_.end()) { + auto func = GetJSFunctionByMethod(method); + if (func == nullptr) { return {}; } - - auto func = JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); - ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); + auto pti = func->GetProfileTypeInfo(); + if (!pti.IsHeapObject()) { + return {}; + } + ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(pti.GetTaggedObject()); JSTaggedValue handler = profile_type_info->Get(slot); if (!handler.IsHeapObject()) { @@ -835,4 +774,52 @@ std::pair EcmaRuntimeInterface::GetGlobalDictionaryEntr auto dict = GlobalDictionary::Cast(array); return {dict, dict->FindEntry(key)}; } + +JSFunction *EcmaRuntimeInterface::GetJSFunctionByMethod(PandaRuntimeInterface::MethodPtr m) const +{ + os::memory::LockHolder lock(mutex_); + auto it = js_function_table_.find(MethodCast(m)); + if (it == js_function_table_.end()) { + return nullptr; + } + return JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); +} + +PandaRuntimeInterface::MethodPtr EcmaRuntimeInterface::GetMethodByIdAndSaveJsFunction( + PandaRuntimeInterface::MethodPtr parent_method, MethodId id) +{ + ScopedMutatorLock lock; + auto func = GetJSFunctionByMethod(parent_method); + if (func == nullptr) { + return nullptr; + } + auto constpool = ConstantPool::Cast(func->GetConstantPool().GetHeapObject()); + auto target = JSFunction::Cast(constpool->GetObjectFromCache(id).GetHeapObject()); + auto method = target->GetCallTarget(); + + // Store JsFunction for recursive static inlining and futher optimizations + auto gos = ecma_vm_->GetGlobalObjectStorage(); + auto func_ref = gos->Add(JSTaggedValue(target).GetHeapObject(), panda::mem::Reference::ObjectType::GLOBAL); + AddFunctionInMap(method, func_ref); + + return method; +} + +PandaRuntimeInterface::MethodPtr EcmaRuntimeInterface::GetMethodFromFunctionAndSaveJsFunction(uintptr_t function) +{ + ScopedMutatorLock lock; + auto *js_func = *(reinterpret_cast(function)); + if (js_func == nullptr) { + return nullptr; + } + auto method = js_func->GetCallTarget(); + + // Store JsFunction for futher optimizations + auto gos = ecma_vm_->GetGlobalObjectStorage(); + auto func_ref = gos->Add(JSTaggedValue(js_func).GetHeapObject(), panda::mem::Reference::ObjectType::GLOBAL); + AddFunctionInMap(method, func_ref); + + return method; +} + } // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 04ef97c43..00ee1f086 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -139,12 +139,9 @@ public: return utf::Mutf8AsCString(JsMethodCast(method)->GetName().data); } - MethodPtr GetMethodFromFunction(uintptr_t function) const override - { - ScopedMutatorLock lock; - auto *js_func = *(reinterpret_cast(function)); - return js_func == nullptr ? nullptr : js_func->GetCallTarget(); - } + MethodPtr GetMethodByIdAndSaveJsFunction(MethodPtr parent_method, MethodId id) override; + + MethodPtr GetMethodFromFunctionAndSaveJsFunction(uintptr_t function) override; compiler::AnyBaseType GetProfilingAnyType(RuntimeInterface::BytecodeProfile profile, const BytecodeInstruction *bc_inst, unsigned index, @@ -157,13 +154,8 @@ public: NewObjDynInfo GetNewObjDynInfo(uintptr_t ctor) const override; bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, ArenaVector *profile) override; - bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, uintptr_t slot_id, - ArenaVector *profile) override; bool GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, ArenaVector *profile) override; - bool GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, uintptr_t slot_id, - ArenaVector *profile) override; - void CleanFunction(Method *method); void AddFunctionInMap(Method *method, panda::mem::Reference *func) @@ -183,6 +175,8 @@ public: } private: + JSFunction *GetJSFunctionByMethod(PandaRuntimeInterface::MethodPtr m) const; + void CleanObjectHandles(Method *method); void AddObjectHandle(Method *method, ObjectHeader *obj); uintptr_t AddFixedObjectHandle(Method *method, ObjectHeader *obj); @@ -192,7 +186,6 @@ private: ProfileTypeInfo *profile_type_info, uint8_t slot); bool AddElementInfo(ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot); ProfileTypeInfo *GetProfileTypeInfo(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, uint8_t *slot); - ProfileTypeInfo *GetProfileTypeInfo(uintptr_t func_address, uintptr_t slot_id, uint8_t *slot); size_t GetLexicalEnvParentEnvIndex() const override; diff --git a/runtime/ecma_compiler.cpp b/runtime/ecma_compiler.cpp index 1f65697ba..14e48f2d9 100644 --- a/runtime/ecma_compiler.cpp +++ b/runtime/ecma_compiler.cpp @@ -38,6 +38,9 @@ void EcmaCompiler::AddTask(CompilerTask &&task, TaggedValue func) } } } + if (func.IsHole()) { + func = GetJSThread()->GetFunctionalObject(); + } if (!func.IsHole()) { auto gos = task.GetVM()->GetGlobalObjectStorage(); auto func_ref = gos->Add(func.GetHeapObject(), panda::mem::Reference::ObjectType::GLOBAL); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc748bed2..c35cfd61d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -274,7 +274,7 @@ function(compile_file_ecma) if (${FILE_TYPE} MATCHES "js") add_custom_command(OUTPUT "${ARG_OUTPUT_FILE}" COMMENT "Run es2panda for ${ARG_FILE}" - COMMAND ${es2panda_bin} ${ARG_OPTIONS} --opt-level 0 --output ${ARG_OUTPUT_FILE} "${ARG_FILE}" + COMMAND ${es2panda_bin} ${ARG_OPTIONS} --opt-level 2 --output ${ARG_OUTPUT_FILE} "${ARG_FILE}" DEPENDS ${es2panda_target} "${ARG_FILE}" WORKING_DIRECTORY "${ARG_WORKING_DIR}") else() diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index d3bf71611..3746fce9f 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -107,6 +107,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_name.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_value.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_static.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_megamorphic.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_deoptimize.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_call_profile_clear.js SUPPORT_RELEASE true) diff --git a/tests/checked/ecma_inlining_static.js b/tests/checked/ecma_inlining_static.js new file mode 100644 index 000000000..b384a9737 --- /dev/null +++ b/tests/checked/ecma_inlining_static.js @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +//! CHECKER Ecma Static Inlining. Must inline. +//! RUN options: "--no-async-jit --compiler-hotness-threshold=0", entry: "_GLOBAL::func_main_0" +//! EVENT_NOT /Inline,_GLOBAL::main,_GLOBAL::global,.*STATIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::global,_GLOBAL::test_inside_func1,.*STATIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::global,_GLOBAL::test_inside_func2,.*STATIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::main,_GLOBAL::test_global_anonimus,.*STATIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::main,_GLOBAL::arrow_func,.*STATIC,SUCCESS/ +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! EVENT_NOT /Deoptimization,.*,.*,.*/ + +// we can't statically inline global functions +// because we need to eliminate StoreGlobalVar/LoadGlobalVar befor +function global() { + let x = 1; + let test_inside_func1 = function() { + return x + 10; + } + let t1 = test_inside_func1() + if (t1 != 11) { + throw new Error("Wrong anonimus inside function result") + } + + function test_inside_func2() { + return x + 9; + } + let t2 = test_inside_func2() + if (t2 != 10) { + throw new Error("Wrong anonimus inside function result") + } + + return 21 + t1 + t2; +} +if (global() != 42) { + throw new Error("Wrong named function result") +} + + +let test_global_anonimus = function() { + return 42; +} +if (test_global_anonimus() != 42) { + throw new Error("Wrong anonimus function result") +} + + +let a = 10; +let arrow_func = (a) => (a + 21) +if (arrow_func(21) != 42) { + throw new Error("Wrong lambda function result") +} \ No newline at end of file -- Gitee From c14096ccb394ac7465efd7db9112398c9ddd6876 Mon Sep 17 00:00:00 2001 From: Sergey Chernykh Date: Mon, 15 May 2023 13:36:29 +0300 Subject: [PATCH 2/2] implement ecma polymorphic inlining --- .../optimizer/optimizations/ecma_inlining.cpp | 202 ++++++++++++++---- .../optimizer/optimizations/ecma_inlining.h | 12 +- isa/isa.yaml | 2 +- .../compiler/ecmascript_runtime_interface.cpp | 9 +- runtime/ecma_profiling.h | 70 ++++-- runtime/interpreter/fast_runtime_stub-inl.h | 2 +- tests/checked/CMakeLists.txt | 7 +- tests/checked/ecma_inlining_megamorphic.js | 47 ++-- ...lining.js => ecma_inlining_monomorphic.js} | 2 +- tests/checked/ecma_inlining_polymorphic.js | 61 ++++++ 10 files changed, 329 insertions(+), 85 deletions(-) rename tests/checked/{ecma_inlining.js => ecma_inlining_monomorphic.js} (96%) create mode 100644 tests/checked/ecma_inlining_polymorphic.js diff --git a/compiler/optimizer/optimizations/ecma_inlining.cpp b/compiler/optimizer/optimizations/ecma_inlining.cpp index dd3ffc224..80dfd1e6f 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.cpp +++ b/compiler/optimizer/optimizations/ecma_inlining.cpp @@ -52,19 +52,20 @@ bool EcmaInlining::IsInstSuitableForInline(Inst *inst) const return inst->GetOpcode() == Opcode::CallDynamic; } -bool EcmaInlining::CheckCallKind(CallInst *call_inst) +bool EcmaInlining::ResolveTargets(CallInst *call_inst) { + ASSERT(js_functions_.empty()); auto runtime = GetGraph()->GetRuntime(); auto kind = runtime->GetCallProfile(GetGraph()->GetMethod(), call_inst->GetPc(), &js_functions_, GetGraph()->IsAotMode()); - if (kind != profiling::CallKind::MONOMORPHIC) { + if (kind == profiling::CallKind::MEGAMORPHIC || kind == profiling::CallKind::UNKNOWN) { LOG_INLINING(DEBUG) << "Call have " << CallKindToString(kind) << " type"; EVENT_INLINE(runtime->GetMethodFullName(GetGraph()->GetMethod()), "-", call_inst->GetId(), - events::InlineKind::DYNAMIC_MONOMORPHIC, events::InlineResult::FAIL_MEGAMORPHIC); + events::InlineKind::DYNAMIC_POLYMORPHIC, + kind == profiling::CallKind::MEGAMORPHIC ? events::InlineResult::FAIL_MEGAMORPHIC + : events::InlineResult::FAIL_RESOLVE); return false; } - // TODO(schernykh): support polimorphic inlining - CHECK_EQ(js_functions_.size(), 1U); return true; } @@ -93,19 +94,6 @@ RuntimeInterface::MethodPtr EcmaInlining::TryResolveTargetStatic(CallInst *call_ return nullptr; } -InlineContext EcmaInlining::ResolveTargets(CallInst *call_inst) -{ - js_functions_.clear(); - if (auto method = TryResolveTargetStatic(call_inst); method != nullptr) { - return {method}; - } - if (CheckCallKind(call_inst)) { - ASSERT(js_functions_[0] != 0); - return {GetGraph()->GetRuntime()->GetMethodFromFunctionAndSaveJsFunction(js_functions_[0])}; - } - return {}; -} - bool EcmaInlining::CheckMethod(CallInst *call_inst, InlineContext *ctx) { if (!CheckMethodCanBeInlined(call_inst, ctx)) { @@ -118,7 +106,7 @@ bool EcmaInlining::CheckMethod(CallInst *call_inst, InlineContext *ctx) return true; } -Graph *EcmaInlining::BuildGraph(CallInst *call_inst, InlineContext *ctx) +Graph *EcmaInlining::BuildGraph(InlineContext *ctx, CallInst *call_inst, CallInst *new_call_inst) { auto graph_inl = GetGraph()->CreateChildGraph(ctx->method); @@ -128,7 +116,7 @@ Graph *EcmaInlining::BuildGraph(CallInst *call_inst, InlineContext *ctx) auto stats = GetGraph()->GetPassManager()->GetStatistics(); auto saved_pbc_inst_num = stats->GetPbcInstNum(); - if (!TryBuildGraph(*ctx, graph_inl, call_inst, call_inst)) { + if (!TryBuildGraph(*ctx, graph_inl, call_inst, new_call_inst)) { stats->SetPbcInstNum(saved_pbc_inst_num); return nullptr; } @@ -155,11 +143,15 @@ Graph *EcmaInlining::BuildGraph(CallInst *call_inst, InlineContext *ctx) graph_inl->RunPass(instructions_count_ + inlined_insts_count, depth_ + 1, methods_inlined_ + 1); } instructions_count_ += CalculateInstructionsCount(graph_inl); + GetGraph()->SetMaxMarkerIdx(graph_inl->GetCurrentMarkerIdx()); + GetGraph()->SetCurrentInstructionId(graph_inl->GetCurrentInstructionId()); + GetGraph()->SetMaxInliningDepth(graph_inl->GetMaxInliningDepth() + 1); return graph_inl; } void EcmaInlining::BuildGuard(CallInst *call_inst) { + ASSERT(js_functions_.size() == 1U); auto load_function = GetGraph()->CreateInstFunctionImmediate(DataType::ANY, call_inst->GetPc(), js_functions_[0]); auto cmp_inst = GetGraph()->CreateInstCompare(DataType::BOOL, call_inst->GetPc()); auto deopt_inst = GetGraph()->CreateInstDeoptimizeIf(DataType::BOOL, call_inst->GetPc()); @@ -183,10 +175,6 @@ void EcmaInlining::BuildGuard(CallInst *call_inst) void EcmaInlining::InsertGraph(CallInst *call_inst, const InlineContext &ctx, Graph *graph_inl) { - GetGraph()->SetMaxMarkerIdx(graph_inl->GetCurrentMarkerIdx()); - GetGraph()->SetCurrentInstructionId(graph_inl->GetCurrentInstructionId()); - GetGraph()->SetMaxInliningDepth(graph_inl->GetMaxInliningDepth() + 1); - if (!IsStaticInlining()) { BuildGuard(call_inst); } @@ -212,17 +200,12 @@ void EcmaInlining::InsertGraph(CallInst *call_inst, const InlineContext &ctx, Gr call_inst->SetCallMethodId(GetGraph()->GetRuntime()->GetMethodId(ctx.method)); } -bool EcmaInlining::TryInline(CallInst *call_inst) +bool EcmaInlining::DoEcmaMonomorphicInilning(CallInst *call_inst, InlineContext &ctx) { - LOG_INLINING(DEBUG) << "Try to inline DynamicCall (id=" << call_inst->GetId() << ")"; - - InlineContext ctx = ResolveTargets(call_inst); - if (ctx.method == nullptr) { - LOG_INLINING(DEBUG) << "Target methods were not found"; - return false; - } - - LOG_INLINING(DEBUG) << "Found methods: " << GetGraph()->GetRuntime()->GetMethodFullName(ctx.method, true); + ASSERT(js_functions_.size() <= 1U); + LOG_INLINING(DEBUG) << "Try inline monomorphic" << (IsStaticInlining() ? "(static)" : "") << " CallDynamic:"; + LOG_INLINING(DEBUG) << " instruction: " << *call_inst; + LOG_INLINING(DEBUG) << "Method: " << GetGraph()->GetRuntime()->GetMethodFullName(ctx.method, true); if (!CheckMethod(call_inst, &ctx)) { LOG_INLINING(DEBUG) << "Unsuitable bytecode"; @@ -230,21 +213,166 @@ bool EcmaInlining::TryInline(CallInst *call_inst) } // Build graph for current target and run EcmaInlining recursively - auto graph_inl = BuildGraph(call_inst, &ctx); + auto graph_inl = BuildGraph(&ctx, call_inst); if (graph_inl == nullptr) { return false; } - + // vregs_count_ += graph_inl->GetVRegsCount(); InsertGraph(call_inst, ctx, graph_inl); GetGraph()->GetPassManager()->GetStatistics()->AddInlinedMethods(1); methods_inlined_++; EVENT_INLINE(GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod()), GetGraph()->GetRuntime()->GetMethodFullName(ctx.method), call_inst->GetId(), - js_functions_.empty() ? events::InlineKind::STATIC : events::InlineKind::DYNAMIC_MONOMORPHIC, + IsStaticInlining() ? events::InlineKind::STATIC : events::InlineKind::DYNAMIC_MONOMORPHIC, events::InlineResult::SUCCESS); LOG_INLINING(DEBUG) << "Successfully inlined: " << GetGraph()->GetRuntime()->GetMethodFullName(ctx.method); return true; } +void EcmaInlining::CreateCompareFunctions(CallInst *call_inst, uintptr_t js_function, BasicBlock *call_bb) +{ + auto load_function_inst = GetGraph()->CreateInstFunctionImmediate(DataType::ANY, call_inst->GetPc(), js_function); + auto cmp_inst = GetGraph()->CreateInstCompare(DataType::BOOL, call_inst->GetPc()); + auto if_inst = GetGraph()->CreateInstIfImm(DataType::BOOL, call_inst->GetPc(), ConditionCode::CC_NE, 0); + cmp_inst->SetCc(ConditionCode::CC_EQ); + cmp_inst->SetInput(0, load_function_inst); + cmp_inst->SetInput(1, call_inst->GetInput(0).GetInst()); + cmp_inst->SetOperandsType(DataType::ANY); + if_inst->SetInput(0, cmp_inst); + if_inst->SetOperandsType(DataType::BOOL); + call_bb->AppendInst(load_function_inst); + call_bb->AppendInst(cmp_inst); + call_bb->AppendInst(if_inst); +} + +bool EcmaInlining::DoEcmaPolymorphicInilning(CallInst *call_inst) +{ + ASSERT(js_functions_.size() > 1U); + ASSERT(js_functions_.size() <= profiling::MAX_FUNC_NUMBER); + LOG_INLINING(DEBUG) << "Try inline polymorphic call(" << js_functions_.size() << " receivers):"; + LOG_INLINING(DEBUG) << " instruction: " << *call_inst; + + bool has_unreachable_blocks = false; + PhiInst *phi_inst = nullptr; + BasicBlock *call_bb = nullptr; + BasicBlock *call_cont_bb = nullptr; + auto runtime = GetGraph()->GetRuntime(); + for (uintptr_t func : js_functions_) { + InlineContext ctx {runtime->GetMethodFromFunctionAndSaveJsFunction(func)}; + if (!CheckMethod(call_inst, &ctx)) { + continue; + } + CallInst *new_call_inst = call_inst->Clone(GetGraph())->CastToCallDynamic(); + new_call_inst->SetCallMethodId(runtime->GetMethodId(ctx.method)); + new_call_inst->SetCallMethod(ctx.method); + + // Build graph for current target and run EcmaInlining recursively + auto graph_inl = BuildGraph(&ctx, call_inst, new_call_inst); + if (graph_inl == nullptr) { + continue; + } + // vregs_count_ += graph_inl->GetVRegsCount(); + if (call_bb == nullptr) { + // Split block by call instruction + call_bb = call_inst->GetBasicBlock(); + call_cont_bb = call_bb->SplitBlockAfterInstruction(call_inst, false); + if (call_inst->GetType() != DataType::VOID) { + phi_inst = GetGraph()->CreateInstPhi(call_inst->GetType(), call_inst->GetPc()); + phi_inst->ReserveInputs(js_functions_.size() << 1U); + call_cont_bb->AppendPhi(phi_inst); + } + } else { + auto new_call_bb = GetGraph()->CreateEmptyBlock(call_bb); + call_bb->GetLoop()->AppendBlock(new_call_bb); + call_bb->AddSucc(new_call_bb); + call_bb = new_call_bb; + } + CreateCompareFunctions(call_inst, func, call_bb); + + // Create call_inlined_block + auto call_inlined_block = GetGraph()->CreateEmptyBlock(call_bb); + call_bb->GetLoop()->AppendBlock(call_inlined_block); + call_bb->AddSucc(call_inlined_block); + + // Insert Call.inlined in call_inlined_block + new_call_inst->AppendInput(call_inst->GetObjectInst()); + new_call_inst->AppendInput(call_inst->GetSaveState()); + new_call_inst->SetInlined(true); + new_call_inst->SetFlag(inst_flags::NO_DST); + call_inlined_block->PrependInst(new_call_inst); + + // Create return_inlined_block and inster PHI for non void functions + auto return_inlined_block = GetGraph()->CreateEmptyBlock(call_bb); + call_bb->GetLoop()->AppendBlock(return_inlined_block); + PhiInst *local_phi_inst = nullptr; + if (call_inst->GetType() != DataType::VOID) { + local_phi_inst = GetGraph()->CreateInstPhi(call_inst->GetType(), call_inst->GetPc()); + local_phi_inst->ReserveInputs(js_functions_.size() << 1U); + return_inlined_block->AppendPhi(local_phi_inst); + } + + // Inlined graph between call_inlined_block and return_inlined_block + UpdateParameterDataflow(graph_inl, call_inst); + UpdateDataflow(graph_inl, call_inst, local_phi_inst, phi_inst); + MoveConstants(graph_inl); + UpdateControlflow(graph_inl, call_inlined_block, return_inlined_block); + if (!return_inlined_block->GetPredsBlocks().empty()) { + auto inlined_return = GetGraph()->CreateInstReturnInlined(DataType::VOID, INVALID_PC); + return_inlined_block->PrependInst(inlined_return); + inlined_return->SetInput(0, new_call_inst->GetSaveState()); + if (call_inst->GetType() != DataType::VOID) { + ASSERT(phi_inst); + // clang-tidy think that phi_inst can be nullptr + phi_inst->AppendInput(local_phi_inst); // NOLINT + } + return_inlined_block->AddSucc(call_cont_bb); + } else { + // We need remove return_inlined_block if inlined graph doesn't have Return inst(only Throw or Deoptimize) + has_unreachable_blocks = true; + } + GetGraph()->GetPassManager()->GetStatistics()->AddInlinedMethods(1); + EVENT_INLINE(runtime->GetMethodFullName(GetGraph()->GetMethod()), runtime->GetMethodFullName(ctx.method), + call_inst->GetId(), events::InlineKind::DYNAMIC_POLYMORPHIC, events::InlineResult::SUCCESS); + LOG_INLINING(DEBUG) << "Successfully polymorphic inlined: " << GetMethodFullName(GetGraph(), ctx.method); + methods_inlined_++; + } + if (call_bb == nullptr) { + // Nothing was inlined + return false; + } + if (call_cont_bb->GetPredsBlocks().empty() || has_unreachable_blocks) { + GetGraph()->RemoveUnreachableBlocks(); + } + InsertDeoptimizeInst(call_inst, call_bb, DeoptimizeType::INLINE_DYN); + + if (call_inst->GetType() != DataType::VOID) { + call_inst->ReplaceUsers(phi_inst); + } + + ProcessCallReturnInstructions(call_inst, call_cont_bb, true); + call_inst->GetBasicBlock()->RemoveInst(call_inst); + return true; +} + +bool EcmaInlining::TryInline(CallInst *call_inst) +{ + js_functions_.clear(); + auto method = TryResolveTargetStatic(call_inst); + if (method != nullptr) { + InlineContext ctx {method}; + return DoEcmaMonomorphicInilning(call_inst, ctx); + } + if (!ResolveTargets(call_inst)) { + LOG_INLINING(DEBUG) << "Target methods were not found for CallDynaimc(id = " << call_inst->GetId() << ")"; + return false; + } + ASSERT(!js_functions_.empty()); + if (IsMonomorphicInlining()) { + InlineContext ctx {GetGraph()->GetRuntime()->GetMethodFromFunctionAndSaveJsFunction(js_functions_[0])}; + return DoEcmaMonomorphicInilning(call_inst, ctx); + } + return DoEcmaPolymorphicInilning(call_inst); +} + } // namespace panda::compiler::ecmascript diff --git a/compiler/optimizer/optimizations/ecma_inlining.h b/compiler/optimizer/optimizations/ecma_inlining.h index 5f9789a60..077a367ef 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.h +++ b/compiler/optimizer/optimizations/ecma_inlining.h @@ -38,12 +38,15 @@ private: bool SkipBlock(const BasicBlock *block) const override; bool TryInline(CallInst *call_inst) override; - InlineContext ResolveTargets(CallInst *call_inst); + bool DoEcmaMonomorphicInilning(CallInst *call_inst, InlineContext &ctx); + bool DoEcmaPolymorphicInilning(CallInst *call_inst); + bool ResolveTargets(CallInst *call_inst); RuntimeInterface::MethodPtr TryResolveTargetStatic(CallInst *call_inst) const; bool IsInstSuitableForEcmaStaticInlining(Inst *define_func) const; bool CheckCallKind(CallInst *call_inst); bool CheckMethod(CallInst *call_inst, InlineContext *ctx); - Graph *BuildGraph(CallInst *call_inst, InlineContext *ctx); + Graph *BuildGraph(InlineContext *ctx, CallInst *call_inst, CallInst *new_call_inst = nullptr); + void CreateCompareFunctions(CallInst *call_inst, uintptr_t js_function, BasicBlock *call_bb); void BuildGuard(CallInst *call_inst); void InsertGraph(CallInst *call_inst, const InlineContext &ctx, Graph *graph_inl); bool IsStaticInlining() const @@ -51,6 +54,11 @@ private: return js_functions_.empty(); } + bool IsMonomorphicInlining() const + { + return js_functions_.size() == 1U; + } + private: // This vector contains pointers to element in EcmaCallProfilingTable // This element contains pointer to js function diff --git a/isa/isa.yaml b/isa/isa.yaml index 9d988d88f..d382e2c0f 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -62,7 +62,7 @@ profiles: size: 1 properties: [] - name: Call - size: 2 + size: 8 properties: [] groups: diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index fd8f9aa6a..80dbc3680 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -143,8 +143,13 @@ profiling::CallKind EcmaRuntimeInterface::GetCallProfile(MethodPtr method, uint3 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto call_prof = CallProfile::FromBuffer(reinterpret_cast(profile) + profile_id); auto call_kind = call_prof->GetCallKind(); - if (call_kind == profiling::CallKind::MONOMORPHIC) { - methods->push_back(call_prof->GetCalleePtr(ecma_vm_->GetEcmaCallProfileTable())); + if (call_kind != profiling::CallKind::MEGAMORPHIC) { + for (auto ptr : call_prof->GetCalleesPtr(ecma_vm_->GetEcmaCallProfileTable())) { + if (ptr == CallProfile::UNKNOWN) { + break; + } + methods->push_back(ptr); + } } return call_kind; } diff --git a/runtime/ecma_profiling.h b/runtime/ecma_profiling.h index ce07cf5ae..56803fd83 100644 --- a/runtime/ecma_profiling.h +++ b/runtime/ecma_profiling.h @@ -465,11 +465,12 @@ protected: class ECMAObject; class CallProfile { -public: using Type = uint16_t; static constexpr uintptr_t MEGAMORPHIC = std::numeric_limits::max(); - static constexpr uintptr_t UNKNOWN = 0; +public: + static constexpr Type MAX_FUNC_NUMBER = panda::profiling::MAX_FUNC_NUMBER; + static constexpr uintptr_t UNKNOWN = 0; static CallProfile *FromBuffer(uint8_t *data) { return reinterpret_cast(data); @@ -478,52 +479,79 @@ public: panda::profiling::CallKind GetCallKind() const { // Atomic with acquire order reason: profile data may be updated while the compiler thread loads it - auto callee_idx = reinterpret_cast *>(&callee_idx_)->load(std::memory_order_acquire); + auto callee_idx = + reinterpret_cast *>(&callees_idx_[0])->load(std::memory_order_acquire); if (callee_idx == MEGAMORPHIC) { return panda::profiling::CallKind::MEGAMORPHIC; } if (callee_idx == UNKNOWN) { return panda::profiling::CallKind::UNKNOWN; } - return panda::profiling::CallKind::MONOMORPHIC; + // Atomic with acquire order reason: profile data may be updated while the compiler thread loads it + if (reinterpret_cast *>(&callees_idx_[1])->load(std::memory_order_acquire) == UNKNOWN) { + return panda::profiling::CallKind::MONOMORPHIC; + } + return panda::profiling::CallKind::POLYMORPHIC; } - uintptr_t GetCalleePtr(EcmaCallProfilingTable *table) const + std::array GetCalleesPtr(EcmaCallProfilingTable *table) const { - return table->GetObjectPtr(callee_idx_); + std::array obj_ptrs {}; + for (size_t i = 0; i < MAX_FUNC_NUMBER; ++i) { + if (callees_idx_[i] == UNKNOWN) { + return obj_ptrs; + } + obj_ptrs[i] = table->GetObjectPtr(callees_idx_[i]); + } + return obj_ptrs; } void Clear(EcmaCallProfilingTable *table) { - if (callee_idx_ != UNKNOWN && callee_idx_ != MEGAMORPHIC) { - table->ClearObject(callee_idx_); + if (callees_idx_[0] == MEGAMORPHIC) { + return; + } + for (size_t i = 0; i < MAX_FUNC_NUMBER; ++i) { + if (callees_idx_[i] == UNKNOWN) { + break; + } + table->ClearObject(callees_idx_[i]); + callees_idx_[i] = UNKNOWN; } - callee_idx_ = UNKNOWN; } void Update(ECMAObject *js_func, EcmaCallProfilingTable *table) { - if (callee_idx_ == MEGAMORPHIC) { + if (callees_idx_[0] == MEGAMORPHIC) { return; } - if (callee_idx_ == UNKNOWN) { - auto idx = table->InsertNewObject(js_func); - if (idx) { - // Atomic with release order reason: profile data may be updated while the compiler thread loads it - reinterpret_cast *>(&callee_idx_)->store(idx.value(), std::memory_order_release); + for (size_t i = 0; i < MAX_FUNC_NUMBER; ++i) { + if (callees_idx_[i] == UNKNOWN) { + auto idx = table->InsertNewObject(js_func); + if (idx) { + // Atomic with release order reason: profile data may be updated while the compiler thread loads it + reinterpret_cast *>(&callees_idx_[i]) + ->store(idx.value(), std::memory_order_release); + } + return; + } + ASSERT(callees_idx_[i] > 0 && callees_idx_[i] <= std::numeric_limits::max() - 1U); + if (table->GetObject(callees_idx_[i]) == js_func) { + return; } - return; } - ASSERT(callee_idx_ > 0 && callee_idx_ <= std::numeric_limits::max() - 1U); - if (table->GetObject(callee_idx_) != js_func) { - table->ClearObject(callee_idx_); + for (size_t i = 0; i < MAX_FUNC_NUMBER; ++i) { + table->ClearObject(callees_idx_[i]); // Atomic with release order reason: profile data may be updated while the compiler thread loads it - reinterpret_cast *>(&callee_idx_)->store(MEGAMORPHIC, std::memory_order_release); + reinterpret_cast *>(&callees_idx_[i])->store(MEGAMORPHIC, std::memory_order_release); } } private: - Type callee_idx_ {UNKNOWN}; + std::array callees_idx_ {}; + // if you want to increase callees_idx_ size, + // you need to update size in profiles::Call in ecmascript/isa/isa.yaml + static_assert(sizeof(callees_idx_) == 8); }; /** diff --git a/runtime/interpreter/fast_runtime_stub-inl.h b/runtime/interpreter/fast_runtime_stub-inl.h index d4b761b39..672c418aa 100644 --- a/runtime/interpreter/fast_runtime_stub-inl.h +++ b/runtime/interpreter/fast_runtime_stub-inl.h @@ -90,7 +90,7 @@ JSTaggedValue FastRuntimeStub::FastMod(JSTaggedValue left, JSTaggedValue right) if (right.IsInt() && left.IsInt()) { int i_right = right.GetInt(); int i_left = left.GetInt(); - if (i_right > 0 && i_left > 0) { + if (i_right > 0 && i_left >= 0) { return JSTaggedValue(i_left % i_right); } } diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 3746fce9f..09107a1d3 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -106,10 +106,11 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_index.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_name.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_value.js SUPPORT_RELEASE true) - panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining.js SUPPORT_RELEASE true) - panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_static.js SUPPORT_RELEASE true) - panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_megamorphic.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_deoptimize.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_megamorphic.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_polymorphic.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_monomorphic.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_static.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_call_profile_clear.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/peephole_negoverflowandzerocheck.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ldlex.js SUPPORT_RELEASE true) diff --git a/tests/checked/ecma_inlining_megamorphic.js b/tests/checked/ecma_inlining_megamorphic.js index 0bf2192b0..07d0839a1 100644 --- a/tests/checked/ecma_inlining_megamorphic.js +++ b/tests/checked/ecma_inlining_megamorphic.js @@ -13,12 +13,6 @@ * limitations under the License. */ - -//! CHECKER Ecma Inlining. Do not inline, megamorphic call. -//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test ", entry: "_GLOBAL::func_main_0" -//! EVENT /Inline,_GLOBAL::test,-,.*,DYNAMIC_MONOMORPHIC,FAIL_MEGAMORPHIC/ -//! EVENT /Compilation,_GLOBAL::test,.*,COMPILED/ - function one() { return 1; } @@ -27,17 +21,36 @@ function two() { return 2; } -function test(i) { - var a = one; - if (i % 2 == 0) - a = two; - return a(); +function three() { + return 3; +} + +function four() { + return 4; +} + +function five() { + return 5; +} + +function test(func) { + return func(); } -var sum = 0; -for (var i = 0; i < 20; i++) { - sum += test(i); +var funcs = [one, two, three, four, five]; + +function test_inlining(N, except_res) { + var sum = 0; + for (var i = 0; i < 20; i++) { + sum += test(funcs[i % N]); + } + if (sum != except_res) { + throw "Wrong result for N = " + N + ". Result = " + sum + ", excpected = " + except_res; + } } -if (sum != 30) { - throw "Wrong result: " + sum; -} \ No newline at end of file + +//! CHECKER Ecma Inlining. Do not inline, megamorphic call. +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test ", entry: "_GLOBAL::func_main_0" +//! EVENT /Inline,_GLOBAL::test,-,.*,DYNAMIC_POLYMORPHIC,FAIL_MEGAMORPHIC/ +//! EVENT /Compilation,_GLOBAL::test,.*,COMPILED/ +test_inlining(5, 60) \ No newline at end of file diff --git a/tests/checked/ecma_inlining.js b/tests/checked/ecma_inlining_monomorphic.js similarity index 96% rename from tests/checked/ecma_inlining.js rename to tests/checked/ecma_inlining_monomorphic.js index 7e026efd3..ebc103feb 100644 --- a/tests/checked/ecma_inlining.js +++ b/tests/checked/ecma_inlining_monomorphic.js @@ -13,7 +13,7 @@ * limitations under the License. */ -//! CHECKER Ecma Inlining. Must inline. +//! CHECKER Ecma Inlining. Must inline, monomorphic call. //! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test", entry: "_GLOBAL::func_main_0" //! EVENT /Inline,_GLOBAL::test,_GLOBAL::one,.*DYNAMIC_MONOMORPHIC,SUCCESS/ //! EVENT /Compilation,_GLOBAL::test,.*,COMPILED/ diff --git a/tests/checked/ecma_inlining_polymorphic.js b/tests/checked/ecma_inlining_polymorphic.js new file mode 100644 index 000000000..e879e4223 --- /dev/null +++ b/tests/checked/ecma_inlining_polymorphic.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +function one() { + return 1; +} + +function two() { + return 2; +} + +function three() { + return 3; +} + +function four() { + return 4; +} + +function five() { + return 5; +} + +function test(func) { + return func(); +} + +var funcs = [one, two, three, four, five]; + +function test_inlining(N, except_res) { + var sum = 0; + for (var i = 0; i < 20; i++) { + sum += test(funcs[i % N]); + } + if (sum != except_res) { + throw "Wrong result for N = " + N + ". Result = " + sum + ", excpected = " + except_res; + } +} + +//! CHECKER Ecma Inlining. Must inline, polymorphic call. +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test ", entry: "_GLOBAL::func_main_0" +//! EVENT /Inline,_GLOBAL::test,_GLOBAL::one,.*DYNAMIC_POLYMORPHIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::test,_GLOBAL::two,.*DYNAMIC_POLYMORPHIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::test,_GLOBAL::three,.*DYNAMIC_POLYMORPHIC,SUCCESS/ +//! EVENT /Inline,_GLOBAL::test,_GLOBAL::four,.*DYNAMIC_POLYMORPHIC,SUCCESS/ +//! EVENT /Compilation,_GLOBAL::test,.*,COMPILED/ +//! EVENT_NOT /Deoptimization,_GLOBAL::.*test.*,.*,IFRAME/ +test_inlining(4, 50) + -- Gitee