From ed4b4ea7664302f764019c70b452381d663ed29f Mon Sep 17 00:00:00 2001 From: baf9884bc6e6 Date: Fri, 7 Oct 2022 12:22:04 +0300 Subject: [PATCH] [Compiler] Implement NewobjDynrange intrinsic Implemented NewobjDynrange intrinsic. Original bytecode instruction fuses object allocation with constructor creation. This change introduce additional optimization pass aimed to expand corresponding intrinsic into two separate parts: - object allocation within TLAB using dedicated stub; - constructor invocation from within the compiled method. In case of success object allocation stub may return either undefined value (if ctor corresponds to some builtin type) or reference to a newly allocated (and correctly initialized) object. In this case constructor will called with returned value passed as a parameter. If allocation had failed or ctor's type is not supported by the stub then the stub will return null value. To handle null value the code will jump into a slow path represented by the implementation of original intrinsic. Change-Id: Ic2929dfef0ece50caec439bcdffeb1775eca7bf6 Signed-off-by: baf9884bc6e6 --- compiler/CMakeLists.txt | 1 + compiler/codegen_intrinsics_ecmascript.cpp | 10 + compiler/optimizer/ecma_pipeline.cpp | 2 + .../optimizations/expand_intrinsics.cpp | 197 ++++++++++++++++ .../optimizations/expand_intrinsics.h | 34 +++ ecmastdlib/ecmastdlib.pa | 6 +- irtoc_scripts/common.irt | 35 ++- irtoc_scripts/new_obj_dyn.irt | 222 ++++++++++++++++++ runtime/CMakeLists.txt | 1 + runtime/asm_defines/asm_defines.def | 21 +- runtime/asm_defines/defines.h | 1 + runtime/ecma_entrypoints.cpp | 18 ++ runtime/ecma_entrypoints.yaml | 33 +++ runtime/ecma_runtime.yaml | 23 ++ runtime/ecma_vm.h | 5 + runtime/interpreter/slow_runtime_stub.h | 3 +- runtime/intrinsics-inl.h | 13 + subproject_sources.gn | 2 +- tests/checked/CMakeLists.txt | 1 + tests/checked/new_obj.js | 199 ++++++++++++++++ 20 files changed, 822 insertions(+), 5 deletions(-) create mode 100644 compiler/optimizer/optimizations/expand_intrinsics.cpp create mode 100644 compiler/optimizer/optimizations/expand_intrinsics.h create mode 100644 irtoc_scripts/new_obj_dyn.irt create mode 100644 tests/checked/new_obj.js diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index dada7ceeb..687ab8ea1 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -34,6 +34,7 @@ set(COMPILER_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/codegen_intrinsics_ecmascript.cpp ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ir_builder/ecmascript_inst_builder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ecma_pipeline.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/optimizations/expand_intrinsics.cpp ) target_sources(arkcompiler PRIVATE ${COMPILER_SOURCES}) diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index 975677a28..1742b3597 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -359,4 +359,14 @@ void Codegen::CreateStObjDynByName([[maybe_unused]] IntrinsicInst *inst, Reg dst CallFastPath(inst, EntrypointId::STORE_OBJECT_DYNAMIC_BY_NAME, dst, {}, src[0], src[1U], src[2U], src[3U]); } +void Codegen::CreateAllocDynObject([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + CallFastPath(inst, EntrypointId::ALLOC_DYN_OBJECT_STUB, dst, {}, src[0]); +} + +void Codegen::CreateResolveAllocResult([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + CallFastPath(inst, EntrypointId::RESOLVE_CTOR_RESULT, dst, {}, src[0], src[1U], src[2U]); +} + } // namespace panda::compiler diff --git a/compiler/optimizer/ecma_pipeline.cpp b/compiler/optimizer/ecma_pipeline.cpp index e2f9ae6c0..13a102ff3 100644 --- a/compiler/optimizer/ecma_pipeline.cpp +++ b/compiler/optimizer/ecma_pipeline.cpp @@ -45,6 +45,7 @@ #include "optimizer/optimizations/cse.h" #include "optimizer/optimizations/move_constants.h" #include "optimizer/optimizations/adjust_arefs.h" +#include "plugins/ecmascript/compiler/optimizer/optimizations/expand_intrinsics.h" namespace panda::compiler::ecmascript { @@ -59,6 +60,7 @@ bool EcmaPipeline::RunOptimizations() LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy"; return false; } + graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); diff --git a/compiler/optimizer/optimizations/expand_intrinsics.cpp b/compiler/optimizer/optimizations/expand_intrinsics.cpp new file mode 100644 index 000000000..43bbb927d --- /dev/null +++ b/compiler/optimizer/optimizations/expand_intrinsics.cpp @@ -0,0 +1,197 @@ +#include "plugins/ecmascript/compiler/optimizer/optimizations/expand_intrinsics.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "optimizer/ir/basicblock.h" + +namespace panda::compiler { + +bool ExpandIntrinsics::RunImpl() +{ + bool success = false; + for (auto bb : GetGraph()->GetVectorBlocks()) { + if (bb == nullptr) { + continue; + } + + for (auto inst : bb->Insts()) { + if (!inst->IsIntrinsic()) { + continue; + } + auto intrin_inst = inst->CastToIntrinsic(); + success |= Expand(intrin_inst); + } + } + return success; +} + +bool ExpandIntrinsics::Expand(IntrinsicInst *inst) +{ + auto id = inst->GetIntrinsicId(); + if (id == RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE || + id == RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE_HANDLED) { + return ExpandNewObjDynRange(inst); + } + return false; +} + +Inst *ExpandIntrinsics::CreateAllocDynObjIntrinsic(Inst *ctor, SaveStateInst *ss, uint32_t pc) +{ + auto alloc_obj = + GetGraph()->CreateInstIntrinsic(DataType::ANY, pc, RuntimeInterface::IntrinsicId::INTRINSIC_ALLOC_DYN_OBJECT); + alloc_obj->ReserveInputs(2); + alloc_obj->AllocateInputTypes(GetGraph()->GetAllocator(), 2); + alloc_obj->AppendInput(ctor); + alloc_obj->AddInputType(DataType::ANY); + alloc_obj->AppendInput(ss); + alloc_obj->AddInputType(DataType::NO_TYPE); + return alloc_obj; +} + +Inst *ExpandIntrinsics::NewObjCreateConstructorCall(Inst *orig_call, Inst *alloc_obj, SaveStateInst *ss, uint32_t pc) +{ + auto call_ctor = GetGraph()->CreateInstCallDynamic(DataType::ANY, pc); + call_ctor->ReserveInputs(orig_call->GetInputsCount() + 1); + call_ctor->AllocateInputTypes(GetGraph()->GetAllocator(), orig_call->GetInputsCount() + 1); + call_ctor->AppendInput(orig_call->GetInput(1)); + call_ctor->AddInputType(DataType::ANY); + call_ctor->AppendInput(orig_call->GetInput(2)); + call_ctor->AddInputType(DataType::ANY); + call_ctor->AppendInput(alloc_obj); + call_ctor->AddInputType(DataType::ANY); + for (size_t i = 3; i < orig_call->GetInputsCount() - 1; ++i) { + call_ctor->AppendInput(orig_call->GetInput(i)); + call_ctor->AddInputType(orig_call->GetInputType(i)); + } + call_ctor->AppendInput(ss); + call_ctor->AddInputType(DataType::NO_TYPE); + return call_ctor; +} + +Inst *ExpandIntrinsics::NewObjFillCurrBlock(BasicBlock *curr_block, Inst *orig_alloc, uint32_t pc) +{ + auto ctor_inst = orig_alloc->GetInput(1).GetInst(); + + auto alloc_obj = CreateAllocDynObjIntrinsic(ctor_inst, orig_alloc->GetSaveState(), pc); + auto cmp = GetGraph()->CreateInstCompareAnyType(DataType::BOOL, pc); + cmp->SetAnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE); + cmp->SetInput(0, alloc_obj); + // if alloc returned null -> jump into a slow path + auto ifimm = GetGraph()->CreateInstIfImm(DataType::BOOL, pc, ConditionCode::CC_NE, 0); + ifimm->SetOperandsType(DataType::BOOL); + ifimm->SetInput(0, cmp); + + curr_block->AppendInsts({alloc_obj, cmp, ifimm}); + return alloc_obj; +} + +void ExpandIntrinsics::NewObjFillSlowPathBlock(BasicBlock *slow_path, Inst *orig_alloc) +{ + auto curr_block = orig_alloc->GetBasicBlock(); + auto ss = orig_alloc->CastToIntrinsic()->GetSaveState(); + auto ss_copy = static_cast(ss->Clone(GetGraph())); + for (size_t input_idx = 0; input_idx < ss->GetInputsCount(); ++input_idx) { + ss_copy->AppendInput(ss->GetInput(input_idx)); + ss_copy->SetVirtualRegister(input_idx, ss->GetVirtualRegister(input_idx)); + } + orig_alloc->SetSaveState(ss_copy); + // move original intrinsic and its SaveState into a slow path + curr_block->EraseInst(orig_alloc); + slow_path->AppendInsts({ss_copy, orig_alloc}); +} + +Inst *ExpandIntrinsics::NewObjFillCallConstructorBlock(BasicBlock *block, Inst *orig_alloc, Inst *alloc_obj, + uint32_t pc) +{ + auto ss = orig_alloc->GetSaveState(); + auto ss_copy = static_cast(ss->Clone(GetGraph())); + for (size_t input_idx = 0; input_idx < ss->GetInputsCount(); ++input_idx) { + ss_copy->AppendInput(ss->GetInput(input_idx)); + ss_copy->SetVirtualRegister(input_idx, ss->GetVirtualRegister(input_idx)); + } + ss_copy->AppendInput(alloc_obj); + VirtualRegister vreg {VirtualRegister::BRIDGE, false}; + ss_copy->SetVirtualRegister(ss->GetInputsCount(), vreg); + auto call_ctor = NewObjCreateConstructorCall(orig_alloc, alloc_obj, ss_copy, pc); + + auto ss_copy_2 = static_cast(ss_copy->Clone(GetGraph())); + for (size_t input_idx = 0; input_idx < ss_copy->GetInputsCount(); ++input_idx) { + ss_copy_2->AppendInput(ss_copy->GetInput(input_idx)); + ss_copy_2->SetVirtualRegister(input_idx, ss_copy->GetVirtualRegister(input_idx)); + } + ss_copy_2->AppendInput(call_ctor); + ss_copy_2->SetVirtualRegister(ss_copy->GetInputsCount(), vreg); + + auto resolve_result = GetGraph()->CreateInstIntrinsic( + DataType::ANY, pc, RuntimeInterface::IntrinsicId::INTRINSIC_RESOLVE_ALLOC_RESULT); + resolve_result->ReserveInputs(4); + resolve_result->AllocateInputTypes(GetGraph()->GetAllocator(), 4); + resolve_result->AppendInput(orig_alloc->GetInput(1)); + resolve_result->AddInputType(DataType::ANY); + resolve_result->AppendInput(alloc_obj); + resolve_result->AddInputType(DataType::ANY); + resolve_result->AppendInput(call_ctor); + resolve_result->AddInputType(DataType::ANY); + resolve_result->AppendInput(ss_copy_2); + resolve_result->AddInputType(DataType::NO_TYPE); + + block->AppendInsts({ss_copy, call_ctor, ss_copy_2, resolve_result}); + + return resolve_result; +} + +/* + * Expands NewObjDynRange intrinsics into a fast path consisting of memory + * allocation via AllocDynObject intrinsic, constructor's call, resolution + * of a result via ResolveAllocResult intrinsic and a slow path consisting of + * NewObjDynRange intrinsic call. + * + * Expanded IR has following shape: + * + * allocated_value := AllocDynObject + * if allocated_value is null + * / \ + * / \ + * slow_path_res := NewObjDynRange | + * | ctor_res := call Ctor(allocated_value) + * | fast_path_res := ResolveAllocResult(Ctor, allocated_value, ctor_res) + * \ / + * \_________________________/ + * | + * res := Phi(slow_path_res, fast_path_res) + * + * Such expansion improves performance of object allocation by using specialized + * stubs outperforming generic runtime code and allows inlining of a constructor. + */ +bool ExpandIntrinsics::ExpandNewObjDynRange(IntrinsicInst *inst) +{ + Inst *ctor_inst = inst->GetInput(1).GetInst(); + Inst *new_target_inst = inst->GetInput(2).GetInst(); + + if (ctor_inst != new_target_inst) { + return false; + } + + auto curr_block = inst->GetBasicBlock(); + auto succ_block = curr_block->SplitBlockAfterInstruction(inst, false); + auto pc = inst->GetPc(); + + auto slow_path_block = GetGraph()->CreateEmptyBlock(curr_block); + auto call_ctor_block = GetGraph()->CreateEmptyBlock(curr_block); + + auto alloc_obj = NewObjFillCurrBlock(curr_block, inst, pc); + NewObjFillSlowPathBlock(slow_path_block, inst); + auto ctor_res = NewObjFillCallConstructorBlock(call_ctor_block, inst, alloc_obj, pc); + + curr_block->AddSucc(slow_path_block); + curr_block->AddSucc(call_ctor_block); + slow_path_block->AddSucc(succ_block); + call_ctor_block->AddSucc(succ_block); + + auto phi = GetGraph()->CreateInstPhi(DataType::ANY, pc); + inst->ReplaceUsers(phi); + phi->AppendInput(inst); + phi->AppendInput(ctor_res); + succ_block->AppendPhi(phi); + return true; +} + +} // namespace panda::compiler \ No newline at end of file diff --git a/compiler/optimizer/optimizations/expand_intrinsics.h b/compiler/optimizer/optimizations/expand_intrinsics.h new file mode 100644 index 000000000..80b390682 --- /dev/null +++ b/compiler/optimizer/optimizations/expand_intrinsics.h @@ -0,0 +1,34 @@ +#ifndef _COMPILER_OPTIMIZER_OPTIMIZATIONS_EXPAND_INTRINSICS_H +#define _COMPILER_OPTIMIZER_OPTIMIZATIONS_EXPAND_INTRINSICS_H + +#include "optimizer/ir/graph.h" +#include "optimizer/pass.h" +#include "utils/arena_containers.h" + +namespace panda::compiler { + +// Pass aimed to expand various intrinsics into some IR +class ExpandIntrinsics : public Optimization { +public: + explicit ExpandIntrinsics(Graph *graph) : Optimization(graph) {} + + const char *GetPassName() const override + { + return "ExpandIntrinsics"; + } + + bool RunImpl() override; + +private: + bool Expand(IntrinsicInst *inst); + bool ExpandNewObjDynRange(IntrinsicInst *inst); + Inst *CreateAllocDynObjIntrinsic(Inst *ctor, SaveStateInst *ss, uint32_t pc); + Inst *NewObjCreateConstructorCall(Inst *orig_call, Inst *alloc_obj, SaveStateInst *ss, uint32_t pc); + Inst *NewObjFillCallConstructorBlock(BasicBlock *block, Inst *orig_alloc, Inst *alloc_obj, uint32_t pc); + void NewObjFillSlowPathBlock(BasicBlock *slow_path, Inst *orig_alloc); + Inst *NewObjFillCurrBlock(BasicBlock *curr_block, Inst *orig_alloc, uint32_t pc); +}; + +} // namespace panda::compiler + +#endif // _COMPILER_OPTIMIZER_OPTIMIZATIONS_EXPAND_INTRINSICS_H \ No newline at end of file diff --git a/ecmastdlib/ecmastdlib.pa b/ecmastdlib/ecmastdlib.pa index 6e07af2ce..8a73062ff 100644 --- a/ecmastdlib/ecmastdlib.pa +++ b/ecmastdlib/ecmastdlib.pa @@ -208,4 +208,8 @@ .function u32 Ecmascript.Intrinsics.DynClassGetHash(any a0) .function any Ecmascript.Intrinsics.LdObjDynByName(any a0, any a1, u16 a2) -.function any Ecmascript.Intrinsics.StObjDynByName(any a0, any a1, any a2, u16 a3) \ No newline at end of file +.function any Ecmascript.Intrinsics.StObjDynByName(any a0, any a1, any a2, u16 a3) + +.function any Ecmascript.Intrinsics.allocDynObject(any a0, any a1) +.function void Ecmascript.Intrinsics.throwDerivedCtorTypeError() +.function any Ecmascript.Intrinsics.resolveAllocResult(any a0, any a1, any a2) \ No newline at end of file diff --git a/irtoc_scripts/common.irt b/irtoc_scripts/common.irt index 2dec337cb..658faa2d7 100644 --- a/irtoc_scripts/common.irt +++ b/irtoc_scripts/common.irt @@ -44,18 +44,34 @@ module Constants ECMASCRIPT_TAGGEDQUEUE_END_INDEX = "TAGGEDQUEUE_END_INDEX" ECMASCRIPT_TAGGEDQUEUE_ELEMENTS_START_INDEX = "TAGGEDQUEUE_ELEMENTS_START_INDEX" SIZEOF_UINT64_T = "sizeof(uint64_t)" - TAGGED_TYPE_SIZE = "coretypes::TaggedValue::TaggedTypeSize()" + TAGGED_TYPE_SIZE = "cross_values::GetTaggedTypeSize(graph->GetArch())" JSTYPE_JS_ARRAY = "cross_values::GetJstypeJsArray(graph->GetArch())" JSTYPE_JS_OBJECT_BEGIN = "cross_values::GetJstypeJsObjectBegin(graph->GetArch())" JSTYPE_JS_OBJECT_END = "cross_values::GetJstypeJsObjectEnd(graph->GetArch())" + JSTYPE_JS_FUNCTION_BEGIN = "cross_values::GetJstypeJsFunctionBegin(graph->GetArch())" + JSTYPE_JS_FUNCTION_END = "cross_values::GetJstypeJsFunctionEnd(graph->GetArch())" + JSTYPE_HCLASS = "cross_values::GetJstypeHclass(graph->GetArch())" + JSTYPE_FINALIZATION_REGISTRY = "cross_values::GetJstypeFinalizationRegistry(graph->GetArch())" + JSTYPE_ECMA_OBJECT_END = "cross_values::GetJstypeEcmaObjectEnd(graph->GetArch())" + + JS_OBJECT_PROPERTIES_OFFSET = "cross_values::GetJsobjectPropertiesOffset(graph->GetArch())" + JS_OBJECT_ELEMENTS_OFFSET = "cross_values::GetJsobjectElementsOffset(graph->GetArch())" JS_OBJECT_PROPERTIES_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectPropertiesOffset(graph->GetArch()))" JS_OBJECT_ELEMENTS_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectElementsOffset(graph->GetArch()))" JS_OBJECT_MAX_ELEMENT_INDEX = "cross_values::GetJsobjectMaxElementIndex(graph->GetArch())" JS_ARRAY_LENGTH_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsarrayLengthOffset(graph->GetArch()))" + JS_FUNCTION_METHOD_OFFSET = "cross_values::GetJsfunctionMethodOffset(graph->GetArch())" JS_FUNCTION_METHOD_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsfunctionMethodOffset(graph->GetArch()))" JS_FUNCTION_PROFILE_TYPE_INFO_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsfunctionProfileTypeInfoOffset(graph->GetArch()))" + JS_FUNCTION_INFO_FLAG_OFFSET = "cross_values::GetJsfunctionFunctionInfoFlagOffset(graph->GetArch())" + JS_FUNCTION_PROTO_OR_DYNCLASS_OFFSET = "cross_values::GetJsfunctionProtoOrDynClassOffset(graph->GetArch())" + JS_FUNCTION_KIND_MASK = "cross_values::GetJsfunctionKindMask(graph->GetArch())" + JS_FUNCTION_KIND_CLASS_CTOR = "cross_values::GetFunctionKindClassCtor(graph->GetArch())" + JS_FUNCTION_KIND_BUILTIN_PROXY_CONSTRUCTOR = "cross_values::GetFunctionKindBuiltinProxyCtor(graph->GetArch())" + JS_FUNCTION_KIND_BUILTIN_CONSTRUCTOR = "cross_values::GetFunctionKindBuiltinCtor(graph->GetArch())" + JS_FUNCTION_KIND_DERIVED_CONSTRUCTOR = "cross_values::GetFunctionKindDerivedCtor(graph->GetArch())" JS_PROPERTY_BOX_VALUE_FIELD = "runtime->GetFieldByOffset(cross_values::GetJspropertyBoxValueOffset(graph->GetArch()))" JS_TRANSITION_HANDLER_HANDLER_INFO_FEILD = "runtime->GetFieldByOffset(cross_values::GetJstransitionHandlerHandlerInfoOffset(graph->GetArch()))" JS_TRANSITION_HANDLER_HCLASS_FEILD = "runtime->GetFieldByOffset(cross_values::GetJstransitionHandlerHclassOffset(graph->GetArch()))" @@ -66,6 +82,16 @@ module Constants JS_HCLASS_BITFIELD1_INLINED_PROPS_START_BIT = "cross_values::GetJshclassBitfieldInlinedPropsStartBitsStartBit(graph->GetArch())" JS_HCLASS_BITFIELD1_INLINED_PROPS_START_MASK = "cross_values::GetJshclassBitfieldInlinedPropsStartBitsMask(graph->GetArch())" + JS_HCLASS_BITFIELD_OFFSET = "cross_values::GetJshclassBitfieldOffset(graph->GetArch())" + JS_HCLASS_BITFIELD1_OFFSET = "cross_values::GetJshclassBitfield1Offset(graph->GetArch())" + JS_HCLASS_BITFIELD_CONSTRUCTOR_MASK = "1 << cross_values::GetJshclassBitfieldConstructorStartBit(graph->GetArch())" + JS_HCLASS_BITFIELD_TYPE_MASK = "cross_values::GetJshclassBitfieldTypeMask(graph->GetArch())" + JS_HCLASS_BITFIELD_TYPE_START_BIT = "cross_values::GetJshclassBitfieldTypeStartBit(graph->GetArch())" + JS_HCLASS_OBJECT_SIZE_OFFSET = "cross_values::GetJshclassObjectSizeOffset(graph->GetArch())" + JS_HCLASS_HCLASS_OFFSET = "cross_values::GetJshclassHclassOffset(graph->GetArch())" + + BASE_CLASS_FLAGS_OFFSET = "cross_values::GetBaseClassFlagsOffset(graph->GetArch())" + HCLASS_IS_BUILTINS_CTOR_MASK = "cross_values::GetHclassIsBuiltinsCtorMask(graph->GetArch())" DYN_INT_TYPE = "AnyBaseType::ECMASCRIPT_INT_TYPE" DYN_DOUBLE_TYPE = "AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" @@ -118,6 +144,10 @@ module Constants NUMBER_DICTIONARY_ENTRY_VALUE_INDEX = "cross_values::GetNumberDictionaryEntryValueIndex(graph->GetArch())" NUMBER_DICTIONARY_ENTRY_DETAILS_INDEX = "cross_values::GetNumberDictionaryEntryDetailsIndex(graph->GetArch())" + THREAD_VM_OFFSET = "cross_values::GetThreadVmOffset(graph->GetArch())" + ECMA_VM_GLOBAL_ENV_OFFSET = "cross_values::GetEcmaVmGlobalEnvOffset(graph->GetArch())" + GLOBAL_ENV_EMPTY_ARRAY_OFFSET = "cross_values::GetGlobalEnvHeaderSize(graph->GetArch()) + cross_values::GetGlobalEnvEmptyArrayIndex(graph->GetArch()) * cross_values::GetTaggedTypeSize(graph->GetArch())" + MURMURHASH_SEED = 0x12345678 MURMURHASH_C1 = 0xCC9E2D51 MURMURHASH_C2 = 0x1B873593 @@ -133,6 +163,9 @@ module Constants MURMURHASH_FINALIZE_FIRST_MULTIPLICATOR = 0x85EBCA6B MURMURHASH_FINALIZE_SECOND_MULTIPLICATOR = 0xC2BAE35 + THROW_DERIVED_CTOR_TYPE_ERROR = "cross_values::GetManagedThreadEntrypointOffset(GetArch(), EntrypointId::THROW_DERIVED_CTOR_TYPE_ERROR_SLOW_PATH)" + ALLOC_DYN_OBJECT_SLOW_PATH = "cross_values::GetManagedThreadEntrypointOffset(GetArch(), EntrypointId::ALLOC_DYN_OBJECT_SLOW_PATH)" + $VERBOSE = verbose end diff --git a/irtoc_scripts/new_obj_dyn.irt b/irtoc_scripts/new_obj_dyn.irt new file mode 100644 index 000000000..7061f554e --- /dev/null +++ b/irtoc_scripts/new_obj_dyn.irt @@ -0,0 +1,222 @@ +include_relative 'common.irt' +include_relative '../../../irtoc/scripts/common.irt' + +# AllocDynObjectStub, ResolveCtorResult and an object constructor called in between these stubs +# represents an object allocation fast path: +# SlowRuntimeStub::NewObjDynRange -> SlowRuntimeHelper::Construct -> ConstructGeneric + +# Allocates memory for a dynamic object. +# Returns: +# - null if allocation should be handled via slow path +# - undefined if ctor is derived class ctor or builtin ctor +# - ptr to allocated object +function("AllocDynObjectStub", + params: {ctor: 'any'}, + regmap: $full_regmap, + mode: [:FastPath, :DynamicMethod, :DynamicStub], + regalloc_set: $panda_mask, + lang: 'ECMASCRIPT') { + if Options.arch == :arm32 + Return(Constants::TAGGED_NULL).any + next + end + + # + # Check: if ctor is neither JsFunction, not Constructor then jump into a slow path + # + IfImm(cmpanyheapobj(ctor)).Imm(0).CC(:CC_EQ).b { + Goto(:SlowPath) + } + ctor_class := get_class(ctor) + ctor_class_bitfield := LoadI(ctor_class).Imm(Constants::JS_HCLASS_BITFIELD_OFFSET).u64 + ctor_type := ShlI(AndI(ctor_class_bitfield).Imm(Constants::JS_HCLASS_BITFIELD_TYPE_MASK).u64) + .Imm(Constants::JS_HCLASS_BITFIELD_TYPE_START_BIT).u64 + # Compare ctor_type w/ JS_FUNCTION_BEGIN, JS_FUNCTION_END + If(ctor_type, Constants::JSTYPE_JS_FUNCTION_BEGIN).CC(:CC_LT) { + Goto(:SlowPath) + } + If(ctor_type, Constants::JSTYPE_JS_FUNCTION_END).CC(:CC_GT) { + Goto(:SlowPath) + } + # get constructor bit from class + IfImm(AndI(ctor_class_bitfield).Imm(Constants::JS_HCLASS_BITFIELD_CONSTRUCTOR_MASK).u64).Imm(0).CC(:CC_EQ) { + Goto(:SlowPath) + } + + function_info := AndI(LoadI(anytoheapobj(ctor)).Imm(Constants::JS_FUNCTION_INFO_FLAG_OFFSET).u64).Imm(Constants::JS_FUNCTION_KIND_MASK).u64 + # + # Check if ctor's method is native + # + method := LoadI(anytoheapobj(ctor)).Imm(Constants::JS_FUNCTION_METHOD_OFFSET).ptr + method_access_flags := LoadI(method).Imm(Constants::METHOD_ACCESS_FLAGS_OFFSET).Volatile.u32 + IfImm(AndI(method_access_flags).Imm("ACC_NATIVE").u32).Imm(0).CC(:CC_NE) { + Goto(:NativeCall) + } + # + # Check if ctor is not base + # + If(function_info, Constants::JS_FUNCTION_KIND_CLASS_CTOR).CC(:CC_GT) { + Goto(:DerivedCheck) + } + + # + # TLAB Allocation + # + # get_proto_or_dyn_class, + # if it is not a jshclass -> bail out + # if it is not a heap obj -> bail out + # if it is not an ecma obj -> bail out + proto := LoadI(anytoheapobj(ctor)).Imm(Constants::JS_FUNCTION_PROTO_OR_DYNCLASS_OFFSET).any + IfImm(cmpanyheapobj(proto)).Imm(0).CC(:CC_EQ) { + Goto(:AllocSlowPath) + } + proto_class := get_class(anytoheapobj(proto)) + proto_class_type := Cast(LoadI(proto_class).Imm(Constants::JS_HCLASS_BITFIELD_OFFSET).u64).u8 + If(proto_class_type, Constants::JSTYPE_HCLASS).CC(:CC_NE) { + Goto(:AllocSlowPath) + } + + # if function's prototype is not an ECMA object -> bail out + # !ctor_fun->GetFunctionPrototype().IsECMAObject() + proto_bitfield := LoadI(anytoheapobj(proto)).Imm(Constants::JS_HCLASS_BITFIELD_OFFSET).u64 + proto_type := ShlI(AndI(proto_bitfield).Imm(Constants::JS_HCLASS_BITFIELD_TYPE_MASK).u64) + .Imm(Constants::JS_HCLASS_BITFIELD_TYPE_START_BIT).u64 + If(proto_type, Constants::JSTYPE_FINALIZATION_REGISTRY).CC(:CC_GE) { + Goto(:AllocSlowPath) + } + # get size of dynclass + obj_size := LoadI(anytoheapobj(proto)).Imm(Constants::JS_HCLASS_OBJECT_SIZE_OFFSET).u32 + + # get tlab, alloc + # if not enough space -> bail out + tlab_ptr := LoadI(%tr).Imm(Constants::TLAB_OFFSET).ptr + start := LoadI(tlab_ptr).Imm(Constants::TLAB_CUR_FREE_POSITION_OFFSET).ptr + tls_end = LoadI(tlab_ptr).Imm(Constants::TLAB_MEMORY_END_ADDR_OFFSET).ptr + tls_size := Sub(tls_end, start).u32 + If(tls_size, obj_size).CC(:CC_LT).b { + Goto(:AllocSlowPath) + } + + call_runtime_save_all(Constants::WRITE_TLAB_STATS_NO_BRIDGE, start, obj_size).void if defines.DEBUG + if defines.__SANITIZE_ADDRESS__ || defines.__SANITIZE_THREAD__ + call_runtime_save_all(Constants::ANNOTATE_SANITIZERS_NO_BRIDGE, start, obj_size).void + end + + new_start := Add(start, Cast(obj_size).word).ptr + # write dynclass->GetHClass() to header + hclass := AddI(Cast(anytoheapobj(proto)).ptr).Imm(Constants::JS_HCLASS_HCLASS_OFFSET).ptr + StoreI(start, hclass).Imm(Constants::OBJECT_CLASS_OFFSET).ptr + addr := Add(tlab_ptr, Constants::TLAB_CUR_FREE_POSITION_OFFSET).ptr + StoreI(addr, new_start).Imm(0).Volatile.ptr + + # write undefined to all properties + v0 := Div(obj_size, Constants::TAGGED_TYPE_SIZE).u32 + bits1 := AndI(ShrI(LoadI(anytoheapobj(proto)).Imm(Constants::JS_HCLASS_BITFIELD1_OFFSET).u32) + .Imm(Constants::JS_HCLASS_BITFIELD1_INLINED_PROPS_START_BIT).u32) + .Imm(Constants::JS_HCLASS_BITFIELD1_INLINED_PROPS_START_MASK).u32 + props_count := Sub(v0, bits1).u32 + + Label(:InitPropsLoop) + counter := Phi(props_count, counter_sub).u32 + offset := Phi(obj_size, offset_sub).u32 + IfImm(counter).Imm(0).CC(:CC_EQ) { + Goto(:LoopExit) + } + counter_sub := Sub(counter, 1).u32 + + offset_sub := Sub(offset, Constants::TAGGED_TYPE_SIZE).u32 + Store(start, offset_sub, Constants::TAGGED_UNDEFINED).any + Goto(:InitPropsLoop) + Label(:LoopExit) + + # init hash: no need to as TLAB-allocated object will already have zero value there + + # get empty array ptr + ecma_vm := LoadI(%tr).Imm(Constants::THREAD_VM_OFFSET).ptr + ecma_global_env := LoadI(ecma_vm).Imm(Constants::ECMA_VM_GLOBAL_ENV_OFFSET).ptr + empty_array := LoadI(ecma_global_env).Imm(Constants::GLOBAL_ENV_EMPTY_ARRAY_OFFSET).ptr + # set properties + StoreI(start, empty_array).Imm(Constants::JS_OBJECT_PROPERTIES_OFFSET).ptr + # set elements + StoreI(start, empty_array).Imm(Constants::JS_OBJECT_ELEMENTS_OFFSET).ptr + + Return(CastValueToAnyType(start).AnyType("AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE").any).any + + Label(:NativeCall) + # if ctor is a builtin ctor return undefined, otherwise jump into a slow path + If(function_info, Constants::JS_FUNCTION_KIND_BUILTIN_PROXY_CONSTRUCTOR).CC(:CC_GE) { + If(function_info, Constants::JS_FUNCTION_KIND_BUILTIN_CONSTRUCTOR).CC(:CC_LE) { + Goto(:ReturnUndefined) + } + } + Goto(:SlowPath) + + Label(:DerivedCheck) + # if ctor is a derived ctor return undefined, otherwise jump into a slow path + If(function_info, Constants::JS_FUNCTION_KIND_DERIVED_CONSTRUCTOR).CC(:CC_EQ) { + Goto(:ReturnUndefined) + } + # fallthrough into slow path + + Label(:SlowPath) + Return(Constants::TAGGED_NULL).any + + Label(:ReturnUndefined) + Return(Constants::TAGGED_UNDEFINED).any + + Label(:AllocSlowPath) + Intrinsic(:SLOW_PATH_ENTRY, ctor).AddImm(Constants::ALLOC_DYN_OBJECT_SLOW_PATH).Terminator.any + Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG +} + +# Resolves a value representing constructed object by choosing a value returned from ctor, +# a value returned by AllocDynObjectStub or throwing TypeError exception. +function("ResolveCtorResult", + params: {ctor: 'any', alloc_res: 'any', ctor_res: 'any'}, + regmap: $full_regmap, + mode: [:FastPath, :DynamicMethod, :DynamicStub], + regalloc_set: $alloc_mask, + lang: 'ECMASCRIPT') { + if Options.arch == :arm32 + Return(Constants::TAGGED_NULL).any + next + end + + # result is EcmaObject + IfImm(cmpanyheapobj(ctor_res)).Imm(0).CC(:CC_NE) { + klass := get_class(ctor_res) + class_type := ShrI(AndI(LoadI(klass).Imm(Constants::JS_HCLASS_BITFIELD_OFFSET).u32) + .Imm(Constants::JS_HCLASS_BITFIELD_TYPE_MASK).u32) + .Imm(Constants::JS_HCLASS_BITFIELD_TYPE_START_BIT).u32 + If(class_type, Constants::JSTYPE_ECMA_OBJECT_END).CC(:CC_LE) { + Goto(:ReturnCtorRes) + } + } + + # ctor is builtin ctor + function_info := AndI(LoadI(anytoheapobj(ctor)).Imm(Constants::JS_FUNCTION_INFO_FLAG_OFFSET).u64).Imm(Constants::JS_FUNCTION_KIND_MASK).u64 + + If(function_info, Constants::JS_FUNCTION_KIND_BUILTIN_PROXY_CONSTRUCTOR).CC(:CC_GE) { + If(function_info, Constants::JS_FUNCTION_KIND_BUILTIN_CONSTRUCTOR).CC(:CC_LE) { + Goto(:ReturnCtorRes) + } + } + + # ctor is base ctor + If(function_info, Constants::JS_FUNCTION_KIND_CLASS_CTOR).CC(:CC_LE) { + Goto(:ReturnAllocRes) + } + + # result is not undefined + IfImm(cmpanyundefined(ctor_res)).Imm(0).CC(:CC_EQ) { + # call intrinsic + Intrinsic(:SLOW_PATH_ENTRY).AddImm(Constants::THROW_DERIVED_CTOR_TYPE_ERROR).Terminator.ptr + Intrinsic(:UNREACHABLE).Terminator.void if defines.DEBUG + } + + Label(:ReturnAllocRes) + Return(alloc_res).any + + Label(:ReturnCtorRes) + Return(ctor_res).any +} \ No newline at end of file diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 8542c2b1d..ea9800a16 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -197,6 +197,7 @@ set(IRTOC_ECMASCRIPT_SCRIPTS ${IRTOC_ECMASCRIPT_SCRIPTS} PARENT_SCOPE) if(NOT (CMAKE_CROSSCOMPILING OR PANDA_TARGET_OHOS)) irtoc_compile(TARGET_NAME irtoc_ecmascript_fastpath INPUT_FILES ${IRTOC_ECMASCRIPT_SCRIPTS}/object.irt + ${IRTOC_ECMASCRIPT_SCRIPTS}/new_obj_dyn.irt TARGET_VARIABLE IRTOC_ECMASCRIPT_FASTPATH_OBJ) if (NOT EXISTS ${IRTOC_ECMASCRIPT_FASTPATH_OBJ}) diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index 8b943fa1a..879225c41 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -13,9 +13,15 @@ DEFINE_VALUE(JSTYPE_STRING, static_cast(panda::ecmascript::JSType::STRI DEFINE_VALUE(JSTYPE_SYMBOL, static_cast(panda::ecmascript::JSType::SYMBOL)) DEFINE_VALUE(JSTYPE_BIGINT, static_cast(panda::ecmascript::JSType::BIGINT)) DEFINE_VALUE(JSTYPE_JS_ARRAY, static_cast(panda::ecmascript::JSType::JS_ARRAY)) +DEFINE_VALUE(JSTYPE_HCLASS, static_cast(panda::ecmascript::JSType::HCLASS)) DEFINE_VALUE(JSTYPE_PROTOTYPE_HANDLER, static_cast(panda::ecmascript::JSType::PROTOTYPE_HANDLER)) DEFINE_VALUE(JSTYPE_TRANSITION_HANDLER, static_cast(panda::ecmascript::JSType::TRANSITION_HANDLER)) DEFINE_VALUE(JSTYPE_JS_FUNCTION, static_cast(panda::ecmascript::JSType::JS_FUNCTION)) +DEFINE_VALUE(JSTYPE_JS_FUNCTION_BEGIN, static_cast(panda::ecmascript::JSType::JS_FUNCTION_BEGIN)) +DEFINE_VALUE(JSTYPE_JS_FUNCTION_END, static_cast(panda::ecmascript::JSType::JS_FUNCTION_END)) +DEFINE_VALUE(JSTYPE_ECMA_OBJECT_BEGIN, static_cast(panda::ecmascript::JSType::ECMA_OBJECT_BEGIN)) +DEFINE_VALUE(JSTYPE_ECMA_OBJECT_END, static_cast(panda::ecmascript::JSType::ECMA_OBJECT_END)) +DEFINE_VALUE(JSTYPE_FINALIZATION_REGISTRY, static_cast(panda::ecmascript::JSType::FINALIZATION_REGISTRY)) DEFINE_VALUE(ECMASTRING_MIX_LENGTH_OFFSET, panda::ecmascript::EcmaString::GetMixLengthOffset()) DEFINE_VALUE(ECMASTRING_DATA_OFFSET, panda::ecmascript::EcmaString::DATA_OFFSET) DEFINE_VALUE(ECMASTRING_STRING_COMPRESSED_MASK, panda::ecmascript::EcmaString::GetStringCompressionMask()) @@ -29,16 +35,17 @@ DEFINE_VALUE(JSHCLASS_BITFIELD_OFFSET, panda::ecmascript::JSHClass::BIT_FIELD_OF DEFINE_VALUE(JSHCLASS_BITFIELD_TYPE_MASK, panda::ecmascript::JSHClass::ObjectTypeBits::MaxValue()) DEFINE_VALUE(JSHCLASS_BITFIELD_TYPE_START_BIT, panda::ecmascript::JSHClass::ObjectTypeBits::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_IS_DICTIONARY_START_BIT, panda::ecmascript::JSHClass::IsDictionaryBit::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD_CONSTRUCTOR_START_BIT, panda::ecmascript::JSHClass::ConstructorBit::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_CLASS_CONSTRUCTOR_START_BIT, panda::ecmascript::JSHClass::ClassConstructorBit::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_EXTENSIBLE_START_BIT, panda::ecmascript::JSHClass::ExtensibleBit::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_START_BIT, panda::ecmascript::JSHClass::InlinedPropsStartBits::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_MASK, panda::ecmascript::JSHClass::InlinedPropsStartBits::MaxValue()) +DEFINE_VALUE(JSHCLASS_OBJECT_SIZE_OFFSET, panda::ecmascript::JSHClass::OBJECT_SIZE_OFFSET) DEFINE_VALUE(JSHCLASS_HCLASS_OFFSET, panda::ecmascript::JSHClass::GetHClassOffset()) DEFINE_VALUE(JSHCLASS_LAYOUT_OFFSET, panda::ecmascript::JSHClass::GetLayoutOffset()) DEFINE_VALUE(JSHCLASS_BITFIELD1_OFFSET, panda::ecmascript::JSHClass::BIT_FIELD1_OFFSET) DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_START_BIT, panda::ecmascript::JSHClass::NumberOfPropsBits::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_MASK, panda::ecmascript::JSHClass::NumberOfPropsBits::MaxValue()) -DEFINE_VALUE(JSHCLASS_OBJECT_SIZE_OFFSET, panda::ecmascript::JSHClass::OBJECT_SIZE_OFFSET) DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_OFFSET, panda::ecmascript::LayoutInfo::GetPropertiesOffset()) DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_SIZE, sizeof(panda::ecmascript::Properties)) DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_KEY_OFFSET, panda::ecmascript::Properties::GetKeyOffset()) @@ -54,6 +61,8 @@ DEFINE_VALUE(JSFUNCTION_CONSTANT_POOL_OFFSET, panda::ecmascript::JSFunction::Get DEFINE_VALUE(JSFUNCTION_LEXICAL_ENV_OFFSET, panda::ecmascript::JSFunction::GetLexicalEnvOffset()) DEFINE_VALUE(JSFUNCTION_FUNCTION_INFO_FLAG_OFFSET, panda::ecmascript::JSFunction::GetFunctionInfoFlagOffset()) DEFINE_VALUE(JSFUNCTION_FUNCTION_INFO_FLAG_IS_STRICT_START_BIT, panda::ecmascript::JSFunction::StrictBit::START_BIT) +DEFINE_VALUE(JSFUNCTION_PROTO_OR_DYN_CLASS_OFFSET, panda::ecmascript::JSFunction::GetProtoOrDynClassOffset()) +DEFINE_VALUE(JSFUNCTION_KIND_MASK, panda::ecmascript::JSFunction::FunctionKindBit::Mask()) DEFINE_VALUE(JSPROPERTY_BOX_VALUE_OFFSET, panda::ecmascript::PropertyBox::GetValueOffset()) DEFINE_VALUE(JSTRANSITION_HANDLER_HANDLER_INFO_OFFSET, panda::ecmascript::TransitionHandler::GetHandlerInfoOffset()) DEFINE_VALUE(JSTRANSITION_HANDLER_HCLASS_OFFSET, panda::ecmascript::TransitionHandler::GetTransitionHClassOffset()) @@ -72,6 +81,7 @@ 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_MAPPING_OFFSET, panda::ecmascript::JSMethod::GetICMappingOffset()) +DEFINE_VALUE(HCLASS_IS_BUILTINS_CTOR_MASK, HClass::IS_BUILTINS_CTOR) DEFINE_VALUE(JSTHREAD_GLOBAL_OBJECT_OFFSET, panda::ecmascript::JSThread::GetGlobalObjectOffset()) DEFINE_VALUE(THREAD_PROPERTIES_CACHE_OFFSET, panda::ecmascript::JSThread::GetPropertiesCacheOffset()) DEFINE_VALUE(PROPERTIES_CACHE_LENGTH, panda::ecmascript::PropertiesCache::CACHE_LENGTH) @@ -100,3 +110,12 @@ DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_SIZE, panda::ecmascript::NumberDictionary:: DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_KEY_INDEX, panda::ecmascript::NumberDictionary::ENTRY_KEY_INDEX) DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_VALUE_INDEX, panda::ecmascript::NumberDictionary::ENTRY_VALUE_INDEX) DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_DETAILS_INDEX, panda::ecmascript::NumberDictionary::ENTRY_DETAILS_INDEX) +DEFINE_VALUE(FUNCTION_KIND_CLASS_CTOR, panda::ecmascript::FunctionKind::CLASS_CONSTRUCTOR) +DEFINE_VALUE(FUNCTION_KIND_BUILTIN_PROXY_CTOR, panda::ecmascript::FunctionKind::BUILTIN_PROXY_CONSTRUCTOR) +DEFINE_VALUE(FUNCTION_KIND_BUILTIN_CTOR, panda::ecmascript::FunctionKind::BUILTIN_CONSTRUCTOR) +DEFINE_VALUE(FUNCTION_KIND_DERIVED_CTOR, panda::ecmascript::FunctionKind::DERIVED_CONSTRUCTOR) +DEFINE_VALUE(TAGGED_TYPE_SIZE, panda::ecmascript::JSTaggedValue::TaggedTypeSize()) +DEFINE_VALUE(ECMA_VM_GLOBAL_ENV_OFFSET, panda::ecmascript::EcmaVM::GetGlobalEnvOffset()) +DEFINE_VALUE(THREAD_VM_OFFSET, panda::Thread::GetVmOffset()) +DEFINE_VALUE(GLOBAL_ENV_HEADER_SIZE, panda::ecmascript::GlobalEnv::HEADER_SIZE) +DEFINE_VALUE(GLOBAL_ENV_EMPTY_ARRAY_INDEX, panda::ecmascript::GlobalEnv::EMPTY_ARRAY_OBJECT_INDEX) \ No newline at end of file diff --git a/runtime/asm_defines/defines.h b/runtime/asm_defines/defines.h index 3a098269e..ea28c137b 100644 --- a/runtime/asm_defines/defines.h +++ b/runtime/asm_defines/defines.h @@ -30,5 +30,6 @@ #include "plugins/ecmascript/runtime/tagged_dictionary.h" #include "plugins/ecmascript/runtime/tagged_queue.h" #include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/global_env.h" #endif // PLUGINS_ECMASCRIPT_RUNTIME_ASM_DEFINES_DEFINES_H diff --git a/runtime/ecma_entrypoints.cpp b/runtime/ecma_entrypoints.cpp index c41b4facc..580499087 100644 --- a/runtime/ecma_entrypoints.cpp +++ b/runtime/ecma_entrypoints.cpp @@ -18,6 +18,7 @@ #include "plugins/ecmascript/runtime/object_operator.h" #include "plugins/ecmascript/runtime/js_object.h" #include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" namespace panda::ecmascript { extern "C" uintptr_t JSGetGlobalVarAddress(uint32_t id) @@ -39,4 +40,21 @@ extern "C" uintptr_t JSGetGlobalVarAddress(uint32_t id) ASSERT(res.IsPropertyBox()); return reinterpret_cast(res.GetHeapObject()); } + +extern "C" void ThrowDerivedCtorTypeErrorSlowPath() +{ + auto thread = JSThread::GetCurrent(); + SlowRuntimeStub::ThrowTypeError(thread, "Derived constructor must return object or undefined"); +} + +extern "C" uint64_t AllocDynObjectSlowPath(uint64_t ctor_raw) +{ + auto thread = JSThread::GetCurrent(); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + JSHandle ctor(thread, JSTaggedValue(ctor_raw)); + JSHandle new_tgt(thread, JSTaggedValue(ctor_raw)); + ASSERT(!ctor->IsBuiltinConstructor() && ctor->IsBase()); + return thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(ctor, new_tgt).GetTaggedValue().GetRawData(); +} + } // namespace panda::ecmascript diff --git a/runtime/ecma_entrypoints.yaml b/runtime/ecma_entrypoints.yaml index 760ecad65..b25067f2c 100644 --- a/runtime/ecma_entrypoints.yaml +++ b/runtime/ecma_entrypoints.yaml @@ -87,3 +87,36 @@ - uint64_t - panda::ObjectHeader* - uint64_t + +- name: AllocDynObjectStub + entrypoint: AllocDynObjectStub + bridge: none + properties: [irtoc] + signature: + - uint64_t + - uint64_t + +- name: AllocDynObjectSlowPath + entrypoint: AllocDynObjectSlowPath + bridge: slow_path + properties: [] + signature: + - uint64_t + - uint64_t + +- name: ResolveCtorResult + entrypoint: ResolveCtorResult + bridge: none + properties: [irtoc] + signature: + - uint64_t + - uint64_t + - uint64_t + - uint64_t + +- name: ThrowDerivedCtorTypeErrorSlowPath + entrypoint: ThrowDerivedCtorTypeErrorSlowPath + bridge: slow_path + properties: [] + signature: + - void diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index afaab46c4..9e733b931 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -607,6 +607,18 @@ intrinsics: impl: panda::ecmascript::intrinsics::NewobjDynrangeHandled set_flags: [heap_inv] +- name: AllocDynObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: allocDynObject + static: true + exception: false + signature: + ret: any + args: [any, any] + set_flags: [heap_inv] + codegen_func: CreateAllocDynObject + - name: RefeqDyn space: ecmascript class_name: Ecmascript.Intrinsics @@ -2279,3 +2291,14 @@ intrinsics: use_thread: false is_fastpath: true clear_flags: [barrier, require_state] + +- name: ResolveAllocResult + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: resolveAllocResult + static: true + use_thread: false + signature: + ret: any + args: [any, any, any] + codegen_func: CreateResolveAllocResult diff --git a/runtime/ecma_vm.h b/runtime/ecma_vm.h index 756412a8f..37cd473ae 100644 --- a/runtime/ecma_vm.h +++ b/runtime/ecma_vm.h @@ -439,6 +439,11 @@ public: profiles_methods_.insert(method); } + static constexpr size_t GetGlobalEnvOffset() + { + return MEMBER_OFFSET(EcmaVM, global_env_); + } + protected: bool CheckEntrypointSignature([[maybe_unused]] Method *entrypoint) override { diff --git a/runtime/interpreter/slow_runtime_stub.h b/runtime/interpreter/slow_runtime_stub.h index a881b0521..3141b61ef 100644 --- a/runtime/interpreter/slow_runtime_stub.h +++ b/runtime/interpreter/slow_runtime_stub.h @@ -179,8 +179,9 @@ public: JSTaggedValue value); /* -------------- Common API End, Don't change those interface!!! ----------------- */ -private: static JSTaggedValue ThrowTypeError(JSThread *thread, const char *message); + +private: static JSTaggedValue ThrowSyntaxError(JSThread *thread, const char *message); static JSTaggedValue GetCallSpreadArgs(JSThread *thread, JSTaggedValue array); static JSTaggedValue SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base); diff --git a/runtime/intrinsics-inl.h b/runtime/intrinsics-inl.h index 5a7fcbd64..69887e70f 100644 --- a/runtime/intrinsics-inl.h +++ b/runtime/intrinsics-inl.h @@ -144,6 +144,7 @@ INLINE_ECMA_INTRINSICS uint64_t Ldstring([[maybe_unused]] JSThread *thread, [[ma INLINE_ECMA_INTRINSICS uint64_t Ldbigint([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint32_t stringId) { INTERPRETER_TRACE(thread, Ldbigint); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSTaggedValue numberBigInt = GetConstantPool(thread)->GetObjectFromCache(stringId); JSHandle bigIntHandle(thread, numberBigInt); return JSTaggedValue::ToBigInt(thread, bigIntHandle).GetRawData(); @@ -544,6 +545,7 @@ INLINE_ECMA_INTRINSICS uint64_t Definefuncexpr([[maybe_unused]] JSThread *thread // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t DefinefuncDyn(JSThread *thread, uint32_t method_id, uint64_t env) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSHandle constpool(thread, GetConstantPool(thread)); JSMutableHandle result(thread, constpool->GetObjectFromCache(method_id)); JSHandle lex_env(thread, JSTaggedValue(env)); @@ -1066,6 +1068,8 @@ INLINE_ECMA_INTRINSICS uint64_t LdObjByName(JSThread *thread, uint32_t string_id INLINE_ECMA_INTRINSICS uint64_t StObjByName(JSThread *thread, uint32_t string_id, uint64_t object, uint64_t val, [[maybe_unused]] uint16_t slot_id) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + auto obj = JSTaggedValue(object); auto value = JSTaggedValue(val); @@ -1110,6 +1114,8 @@ INLINE_ECMA_INTRINSICS uint64_t LdObjByIndex(JSThread *thread, uint32_t idx, uin // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t StObjByIndex(JSThread *thread, uint32_t idx, uint64_t uobj, uint64_t uval) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + auto obj = JSTaggedValue(uobj); auto val = JSTaggedValue(uval); @@ -1340,6 +1346,7 @@ INLINE_ECMA_INTRINSICS uint64_t Delobjprop(JSThread *thread, uint64_t obj, uint6 // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t DefineNCFuncDyn(JSThread *thread, uint32_t method_id, uint64_t env, uint64_t home_obj) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSHandle home_obj_handle(thread, JSTaggedValue(home_obj)); JSHandle constpool(thread, GetConstantPool(thread)); JSMutableHandle result(thread, constpool->GetObjectFromCache(method_id)); @@ -1440,6 +1447,7 @@ INLINE_ECMA_INTRINSICS uint64_t ReturnDyn([[maybe_unused]] JSThread *thread, [[m // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t NewlexenvDyn(JSThread *thread, uint16_t num_slots) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle env(thread, GetEcmascriptEnvironment(thread)->GetLexicalEnv()); @@ -1540,6 +1548,7 @@ INLINE_ECMA_INTRINSICS uint64_t GetPropIterator(JSThread *thread, uint64_t obj) // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t DefineGeneratorFunc(JSThread *thread, uint32_t method_id, uint64_t env) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSMutableHandle result(thread, GetConstantPool(thread)->GetObjectFromCache(method_id)); JSHandle lex_env(thread, JSTaggedValue(env)); ASSERT(!result.IsEmpty()); @@ -1596,6 +1605,7 @@ INLINE_ECMA_INTRINSICS uint64_t CreateAsyncGeneratorObj(JSThread *thread, uint64 // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t DefineAsyncFunc(JSThread *thread, uint32_t method_id, uint64_t env) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSMutableHandle result(thread, GetConstantPool(thread)->GetObjectFromCache(method_id)); JSHandle lex_env(thread, JSTaggedValue(env)); ASSERT(!result.IsEmpty()); @@ -1614,6 +1624,7 @@ INLINE_ECMA_INTRINSICS uint64_t DefineAsyncFunc(JSThread *thread, uint32_t metho // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t DefineAsyncGeneratorFunc(JSThread *thread, uint32_t method_id, uint64_t env) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSMutableHandle result(thread, GetConstantPool(thread)->GetObjectFromCache(method_id)); JSHandle lex_env(thread, JSTaggedValue(env)); ASSERT(!result.IsEmpty()); @@ -1984,6 +1995,7 @@ INLINE_ECMA_INTRINSICS uint64_t ClassPrivateFieldIn(JSThread *thread, uint32_t s INLINE_ECMA_INTRINSICS uint64_t DefineClassWithBuffer(JSThread *thread, uint32_t method_id, uint16_t imm, uint64_t lexenv, uint64_t proto) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); auto *constpool = GetConstantPool(thread); JSHandle lex_env(thread, JSTaggedValue(lexenv)); JSFunction *class_template = JSFunction::Cast(constpool->GetObjectFromCache(method_id).GetTaggedObject()); @@ -2056,6 +2068,7 @@ INLINE_ECMA_INTRINSICS uint64_t SuperCallSpread(JSThread *thread, uint64_t array INLINE_ECMA_INTRINSICS uint64_t DefineMethod(JSThread *thread, uint32_t method_id, uint64_t tagged_cur_env, uint64_t home_object) { + [[maybe_unused]] EcmaHandleScope handle_scope(thread); JSHandle constpool(thread, GetConstantPool(thread)); JSMutableHandle result(thread, constpool->GetObjectFromCache(method_id)); JSHandle home(thread, JSTaggedValue(home_object)); diff --git a/subproject_sources.gn b/subproject_sources.gn index 34d3a8ecb..b09a01025 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -31,7 +31,7 @@ inst_templates_yaml_path = runtime_option_yaml_path = "runtime_options.yaml" entrypoints_yaml_path = "runtime/ecma_entrypoints.yaml" -irtoc_scripts = [ "irtoc_scripts/object.irt" ] +irtoc_scripts = [ "irtoc_scripts/object.irt", "irtoc_scripts/new_obj_dyn.irt" ] irtoc_plugins = [ "irtoc_scripts/interpreter_handlers.irt", "irtoc_scripts/interpreter_main_loop.irt", diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 6485fab88..05f8db365 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -101,5 +101,6 @@ if (NOT PANDA_TARGET_ARM32) # there is flaky bug when turning on TSAN if (NOT PANDA_ENABLE_THREAD_SANITIZER) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/acc_after_deopt.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/new_obj.js SUPPORT_RELEASE true) endif() endif() diff --git a/tests/checked/new_obj.js b/tests/checked/new_obj.js new file mode 100644 index 000000000..828fc40e8 --- /dev/null +++ b/tests/checked/new_obj.js @@ -0,0 +1,199 @@ +function custom_ctor(a, b) { + this.a = a; + this.b = b; +} + +function custom_ctor_returning_val() { + this.a = 42 + return 123 +} + +class BaseClass { + constructor() { + this.prop = 42 + } +} + +class SubclassWithoutCtor extends BaseClass { +} + +class SubclassWithCtor extends BaseClass { + constructor() { + super() + this.subprop = 0 + } +} + +function f1() { + this.constructor.prototype.arguments = "123" + return arguments +} + +class InvalidSubclass extends BaseClass { + constructor() { + super() + return Number() + } +} + +function alloc_1() { + var obj = new custom_ctor(1, 2) + if (obj.a != 1 || obj.b != 2) { + throw "invalid object" + } +} + +function alloc_2() { + var obj = new custom_ctor_returning_val() + if (obj == 123) { + throw "not an object" + } + if (obj.a != 42) { + throw "invalid object" + } +} + +function alloc_3() { + var obj = new Number(42) + if (obj != 42) { + throw "invalid value: " + obj + } +} + +function alloc_4() { + var obj = new BaseClass() + if (obj.prop != 42) { + throw "invalid object" + } +} + +function alloc_5() { + var obj = new SubclassWithCtor() + if (obj.prop != 42 || obj.subprop != 0) { + throw "invalid object" + } +} + +function alloc_6() { + var obj = new SubclassWithoutCtor() + if (obj.prop != 42) { + throw "invalid object" + } +} + +function alloc_7() { + var obj = new f1(1, 2, 3, 4, 5) + if (obj[2] != 3) { + throw "invalid object" + } +} + +function alloc_8() { + var obj = null + try { + obj = new InvalidSubclass() + } catch (e) { + if (e instanceof TypeError) { + return + } + throw e + } + throw new "invalid object" +} + +//! CHECKER Expand new object dyn range +//! RUN options: "--compiler-hotness-threshold=0 --compiler-regex _GLOBAL::alloc_.*", entry: "_GLOBAL::func_main_0" +//! METHOD "alloc_1" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_2" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_3" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_4" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_5" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_6" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_7" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_8" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ + +//! CHECKER Expand new object dyn range in AOT mode +//! RUN_PAOC options: "--compiler-regex '_GLOBAL::alloc_.*'" +//! METHOD "alloc_1" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_2" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_3" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_4" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_5" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_6" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_7" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! METHOD "alloc_8" +//! PASS_AFTER "IrBuilder" +//! INST_NOT /Intrinsic.AllocDynObject.*/ +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.AllocDynObject.*/ +//! RUN options: "--compiler-enable-jit=false", entry: "_GLOBAL::func_main_0", force_jit: false +for (var i = 0; i < 100000; ++i) { + alloc_1() + alloc_2() + alloc_3() + alloc_4() + alloc_5() + alloc_6() + alloc_7() + alloc_8() +} \ No newline at end of file -- Gitee