From 6550418d46122a9693d48c99c3da95627fe04430 Mon Sep 17 00:00:00 2001 From: Ishin Pavel Date: Wed, 13 Jul 2022 20:21:43 +0300 Subject: [PATCH] Support St/LdGlobalVar in codegen Signed-off-by: Ishin Pavel --- CMakeLists.txt | 2 + .../ir_builder/ecmascript_inst_builder.cpp | 76 +++++++++++++++++++ .../ir_builder/ecmascript_inst_builder.h | 4 + .../ir_builder/ecmascript_inst_templates.yaml | 4 + runtime/CMakeLists.txt | 1 + runtime/asm_defines/asm_defines.def | 1 + runtime/asm_defines/defines.h | 1 + .../compiler/ecmascript_runtime_interface.cpp | 49 +++++++++++- .../compiler/ecmascript_runtime_interface.h | 27 ++++++- runtime/ecma_entrypoints.cpp | 40 ++++++++++ runtime/ecma_entrypoints.h | 24 ++++++ runtime/ecma_entrypoints.yaml | 30 ++++++++ runtime/ecma_vm.cpp | 2 +- runtime/js_thread.h | 5 ++ runtime/object_factory.cpp | 2 +- runtime/runtime_sources.gn | 1 + subproject_sources.gn | 1 + tests/checked/CMakeLists.txt | 1 + tests/checked/global_var.js | 29 +++++++ 19 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 runtime/ecma_entrypoints.cpp create mode 100644 runtime/ecma_entrypoints.h create mode 100644 runtime/ecma_entrypoints.yaml create mode 100644 tests/checked/global_var.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 289deba69..9db7310db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,3 +53,5 @@ endif() add_plugin_options(${CMAKE_CURRENT_SOURCE_DIR}/ecmascript_plugin_options.yaml) add_runtime_options(${CMAKE_CURRENT_SOURCE_DIR}/runtime_options.yaml) + +add_entrypoints_yaml(${CMAKE_CURRENT_SOURCE_DIR}/runtime/ecma_entrypoints.yaml) diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp index 1ccd1c1b5..4ebb174a9 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -95,3 +95,79 @@ void InstBuilder::BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_ra } // namespace panda::compiler #include "ecmascript_inst_builder_gen.cpp" + +namespace panda::compiler { + +template +void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id) +{ + if (GetGraph()->IsBytecodeOptimizer()) { + BuildEcmaAsIntrinsics(bc_inst); + } + auto pc = GetPc(bc_inst->GetAddress()); + auto save_state = CreateSaveState(Opcode::SaveState, pc); + auto get_address = graph_->CreateInstGetGlobalVarAddress(DataType::REFERENCE, pc); + + get_address->SetTypeId(type_id); + get_address->SetMethod(GetGraph()->GetMethod()); + get_address->SetInput(0, save_state); + + auto store_object = graph_->CreateInstStoreObject(DataType::ANY, pc); + store_object->SetTypeId(type_id); + store_object->SetMethod(GetGraph()->GetMethod()); + store_object->SetObjField(nullptr); + store_object->SetNeedBarrier(true); + store_object->SetObjectType(ObjectType::MEM_GLOBAL); + store_object->SetInput(0, get_address); + Inst *store_val = nullptr; + if constexpr (is_acc_read) { + store_val = GetDefinitionAcc(); + } else { + store_val = GetDefinition(bc_inst->GetVReg(0)); + } + + store_object->SetInput(1, store_val); + AddInstruction(save_state); + AddInstruction(get_address); + AddInstruction(store_object); +} + +template +void InstBuilder::BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id) +{ + if (GetGraph()->IsBytecodeOptimizer()) { + BuildEcmaAsIntrinsics(bc_inst); + } + auto pc = GetPc(bc_inst->GetAddress()); + auto save_state = CreateSaveState(Opcode::SaveState, pc); + auto get_address = graph_->CreateInstGetGlobalVarAddress(DataType::REFERENCE, pc); + + get_address->SetTypeId(type_id); + get_address->SetMethod(GetGraph()->GetMethod()); + get_address->SetInput(0, save_state); + + auto load_object = graph_->CreateInstLoadObject(DataType::ANY, pc); + load_object->SetTypeId(type_id); + load_object->SetMethod(GetGraph()->GetMethod()); + load_object->SetObjField(nullptr); + load_object->SetNeedBarrier(true); + load_object->SetObjectType(ObjectType::MEM_GLOBAL); + load_object->SetInput(0, get_address); + AddInstruction(save_state); + AddInstruction(get_address); + AddInstruction(load_object); + + if constexpr (is_acc_write) { + UpdateDefinitionAcc(load_object); + } else { + UpdateDefinition(bc_inst->GetVReg(0), load_object); + } +} + +template void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); +template void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); + +template void InstBuilder::BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); +template void InstBuilder::BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); + +} // namespace panda::compiler diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h index 20a5c069c..1ddfdf57e 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h @@ -20,6 +20,10 @@ void BuildEcma([[maybe_unused]] const BytecodeInstruction *bc_inst); template void BuildEcmaAsIntrinsics([[maybe_unused]] const BytecodeInstruction *bc_inst); void BuildEcmaFromIrtoc([[maybe_unused]] const BytecodeInstruction *bc_inst); +template +void BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); +template +void BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); void BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_range, bool call_this, uint64_t num_args = 0); diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml index 1a93dab60..c2b13a526 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml +++ b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml @@ -68,6 +68,10 @@ } else { BuildEcmaFnCall(instruction, true, true); } + % when "STGLOBALVAR" + BuildStGlobalVar< <%= inst.acc_read? %> >(instruction, instruction->template GetId<<%= inst.get_format %>>().AsFileId().GetOffset()); + % when "LDGLOBALVAR" + BuildLdGlobalVar< <%= inst.acc_write? %> >(instruction, instruction->template GetId<<%= inst.get_format %>>().AsFileId().GetOffset()); % else BuildEcma(instruction); % end diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index c61cb593d..9754c491f 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -93,6 +93,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/containers/containers_private.cpp ${ECMA_SRC_DIR}/dump.cpp ${ECMA_SRC_DIR}/ecma_class_linker_extension.cpp + ${ECMA_SRC_DIR}/ecma_entrypoints.cpp ${ECMA_SRC_DIR}/ecma_exceptions.cpp ${ECMA_SRC_DIR}/ecma_language_context.cpp ${ECMA_SRC_DIR}/ecma_module.cpp diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index d4527b405..c0fca877e 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -47,3 +47,4 @@ DEFINE_VALUE(IC_HANDLER_INLINED_PROPS_BIT_MASK, panda::ecmascript::HandlerBase:: DEFINE_VALUE(IC_HANDLER_HANDLER_KIND_FIELD, static_cast(panda::ecmascript::HandlerBase::HandlerKind::FIELD)) DEFINE_VALUE(HCLASS_DATA_OFFSET, HClass::GetDataOffset()) DEFINE_VALUE(JSMETHOD_IC_OFFSET_MAPPING_OFFSET, panda::ecmascript::JSMethod::GetICOffsetMappingOffset()) +DEFINE_VALUE(JSTHREAD_GLOBAL_OBJECT_OFFSET, panda::ecmascript::JSThread::GetGlobalObjectOffset()) diff --git a/runtime/asm_defines/defines.h b/runtime/asm_defines/defines.h index e6bdbc86d..73cfdd6b0 100644 --- a/runtime/asm_defines/defines.h +++ b/runtime/asm_defines/defines.h @@ -23,6 +23,7 @@ #include "plugins/ecmascript/runtime/js_function.h" #include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/ecma_string.h" #include "plugins/ecmascript/runtime/js_for_in_iterator.h" #include "plugins/ecmascript/runtime/tagged_array.h" diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 84202a180..fddc273ab 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -14,10 +14,15 @@ */ #include "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" #include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" -namespace panda { +namespace panda::ecmascript { + +EcmaRuntimeInterface::EcmaRuntimeInterface(const EcmaVM *ecma_vm) : ecma_vm_(ecma_vm) {} size_t EcmaRuntimeInterface::GetConstantPoolOffset() { @@ -48,4 +53,44 @@ uint32_t EcmaRuntimeInterface::GetFunctionTargetOffset([[maybe_unused]] Arch arc return ecmascript::JSFunction::METHOD_OFFSET; } -} // namespace panda +uintptr_t EcmaRuntimeInterface::GetGlobalVarAddress(MethodPtr method, size_t id) +{ + if (ecma_vm_ == nullptr) { + return 0; + } + auto thread = ecma_vm_->GetJSThread(); + // Thread can be removed during destroy runtime + if (thread == nullptr || !thread->IsThreadAlive()) { + return 0; + } + auto panda_file = MethodCast(method)->GetPandaFile(); + JSTaggedValue constant_pool(JSTaggedValue::VALUE_HOLE); + + auto func = [&](Program *p) { constant_pool = p->GetConstantPool(); }; + ecma_vm_->EnumerateProgram(func, panda_file->GetFilename()); + ASSERT(!constant_pool.IsHole()); + + JSTaggedValue key = ConstantPool::Cast(constant_pool.GetHeapObject())->GetObjectFromCache(id); + auto global_obj = thread->GetGlobalObject(); + auto js_obj = JSObject::Cast(global_obj.GetTaggedObject()); + TaggedArray *array = TaggedArray::Cast(js_obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return 0; + } + + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry == -1) { + return 0; + } + + JSTaggedValue res(dict->GetBox(entry)); + // we can't set an attribute in compiler thread. + if (res.IsUndefined() || !res.IsPropertyBox()) { + return 0; + } + ASSERT(res.IsPropertyBox()); + return reinterpret_cast(res.GetHeapObject()); +} + +} // namespace panda::ecmascript \ No newline at end of file diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index b77f64e2e..aea1ee38b 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -18,8 +18,12 @@ #include "runtime/compiler.h" #include "plugins/ecmascript/runtime/js_method.h" -namespace panda { +namespace panda::ecmascript { +class EcmaVM; + class EcmaRuntimeInterface : public PandaRuntimeInterface { +public: + explicit EcmaRuntimeInterface(const EcmaVM *ecma_vm); size_t GetConstantPoolOffset() override; size_t GetLexicalEnvOffset() override; @@ -36,6 +40,8 @@ class EcmaRuntimeInterface : public PandaRuntimeInterface { return ext_size; } + uintptr_t GetGlobalVarAddress(MethodPtr method, size_t id) override; + std::string GetMethodFullName(MethodPtr method, [[maybe_unused]] bool with_signature) const override { ASSERT(panda::panda_file::IsDynamicLanguage(MethodCast(method)->GetClass()->GetSourceLang())); @@ -43,7 +49,24 @@ class EcmaRuntimeInterface : public PandaRuntimeInterface { } uint32_t GetFunctionTargetOffset(Arch arch) const override; + uint32_t GetTlsGlobalObjectOffset(Arch arch) const override + { + return panda::cross_values::GetJsthreadGlobalObjectOffset(arch); + } + + size_t GetPropertyBoxOffset(Arch arch) const override + { + return panda::cross_values::GetJspropertyBoxValueOffset(arch); + } + + EntrypointId GetGlobalVarEntrypointId() override + { + return EntrypointId::GET_GLOBAL_VAR_ADDRESS; + } + +private: + const EcmaVM *ecma_vm_ {nullptr}; }; -} // namespace panda +} // namespace panda::ecmascript #endif // PANDA_RUNTIME_ECMASCRIPT_RUNTIME_INTERFACE_H diff --git a/runtime/ecma_entrypoints.cpp b/runtime/ecma_entrypoints.cpp new file mode 100644 index 000000000..bc7d38476 --- /dev/null +++ b/runtime/ecma_entrypoints.cpp @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#include "plugins/ecmascript/runtime/ecma_entrypoints.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +extern "C" uintptr_t JSGetGlobalVarAddress(uint32_t id) +{ + auto thread = JSThread::GetCurrent(); + JSTaggedValue key = GetConstantPool(thread)->GetObjectFromCache(id); + auto global_obj = thread->GetGlobalObject(); + ObjectOperator op(thread, global_obj, key); + auto res = op.GetValue(); + if (res.IsUndefined() || !res.IsPropertyBox()) { + PropertyAttributes attributes = PropertyAttributes::Default(true, true, false); + JSHandle global_handle(thread, global_obj); + JSHandle key_handle(thread, key); + op.AddProperty(global_handle, key_handle, attributes); + res = op.GetValue(); + } + ASSERT(res.IsPropertyBox()); + return reinterpret_cast(res.GetHeapObject()); +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_entrypoints.h b/runtime/ecma_entrypoints.h new file mode 100644 index 000000000..3483d5788 --- /dev/null +++ b/runtime/ecma_entrypoints.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_ECMA_ENTRYPOINTS_H +#define ECMASCRIPT_ECMA_ENTRYPOINTS_H + +#include + +namespace panda::ecmascript { +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_ECMA_ENTRYPOINTS_H \ No newline at end of file diff --git a/runtime/ecma_entrypoints.yaml b/runtime/ecma_entrypoints.yaml new file mode 100644 index 000000000..6941cf0e7 --- /dev/null +++ b/runtime/ecma_entrypoints.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 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. +# +# This file describes copmiler-to-runtime entrypoints. +# Fields: +# * properties: +# - no_return: entrypoint doesn't jump back to the caller. +# - external: don't generate entrypoint and bridge declarations, initialize table's element by nullptr. +# - irtoc: entrypoint is generated by irtoc tool +# - intrinsic: this is call of intrinsic wrapped in entrypoint bridge +# * signature: signature of the entrypoint, the first element is the return value, the rest are arguments. +# * entrypoint: entrypoint function name. + +- name: GetGlobalVarAddress + entrypoint: JSGetGlobalVarAddress + bridge: entrypoint + properties: [] + signature: + - uintptr_t + - uint32_t diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index dd7d1e4aa..bab172412 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -213,7 +213,7 @@ EcmaVM::EcmaVM(JSRuntimeOptions options) auto heap_manager = mm_->GetHeapManager(); auto internal_allocator = heap_manager->GetInternalAllocator(); - runtime_iface_ = internal_allocator->New(); + runtime_iface_ = internal_allocator->New(this); compiler_ = internal_allocator->New(heap_manager->GetCodeAllocator(), internal_allocator, options_, heap_manager->GetMemStats(), runtime_iface_); SetFrameExtSize(ecmascript::JSExtFrame::GetExtSize()); diff --git a/runtime/js_thread.h b/runtime/js_thread.h index 25901d3da..a42557415 100644 --- a/runtime/js_thread.h +++ b/runtime/js_thread.h @@ -253,6 +253,11 @@ public: return MEMBER_OFFSET(JSThread, globalStorage_); } + static constexpr uint32_t GetGlobalObjectOffset() + { + return MEMBER_OFFSET(JSThread, globalObj_); + } + static constexpr uint32_t GetCurrentFrameOffset() { return MEMBER_OFFSET(JSThread, currentFrame_); diff --git a/runtime/object_factory.cpp b/runtime/object_factory.cpp index 2095e362a..c93e1b01d 100644 --- a/runtime/object_factory.cpp +++ b/runtime/object_factory.cpp @@ -1902,7 +1902,7 @@ EcmaString *ObjectFactory::GetRawStringFromStringTable(const uint8_t *mutf8Data, JSHandle ObjectFactory::NewPropertyBox(const JSHandle &value) { NewObjectHook(); - TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(PropertyBoxClass_); + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(PropertyBoxClass_); JSHandle box(thread_, header); box->SetValue(thread_, value); return box; diff --git a/runtime/runtime_sources.gn b/runtime/runtime_sources.gn index 5477ac554..40cb33b94 100644 --- a/runtime/runtime_sources.gn +++ b/runtime/runtime_sources.gn @@ -64,6 +64,7 @@ srcs = [ "containers/containers_private.cpp", "dump.cpp", "ecma_class_linker_extension.cpp", + "ecma_entrypoints.cpp", "ecma_exceptions.cpp", "ecma_language_context.cpp", "ecma_module.cpp", diff --git a/subproject_sources.gn b/subproject_sources.gn index 466f34311..8ee17cffd 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -19,6 +19,7 @@ srcs_compiler_path = "compiler/compiler_sources.gn" option_yaml_path = "ecmascript_plugin_options.yaml" inst_templates_yaml_path = "compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml" runtime_option_yaml_path = "runtime_options.yaml" +entrypoints_yaml_path = "runtime/ecma_entrypoints.yaml" irtoc_plugins = "irtoc_scripts/irtoc_scripts.gn" arkruntime_header_sub_deps = [ ":ecma_intrinsics_gen_arkruntime", diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 548930d2b..3ccbcb36e 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -116,4 +116,5 @@ endfunction() if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/type_resolving.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/global_var.js SUPPORT_RELEASE true) endif() diff --git a/tests/checked/global_var.js b/tests/checked/global_var.js new file mode 100644 index 000000000..bde9224ea --- /dev/null +++ b/tests/checked/global_var.js @@ -0,0 +1,29 @@ +/* + * 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 Test global var +//! RUN_PAOC options: "" + +var a; +var b; +var n = 20; +for (a = 0; a < n; a++) { + if (a % 2 == 0) { + b = a; + } else { + b = Array(a); + } +} +n = b; \ No newline at end of file -- Gitee