From bcb50d9314bddaa575ecef9b4935b3807860e10d Mon Sep 17 00:00:00 2001 From: Vsevolod Pukhov Date: Fri, 29 Jul 2022 13:28:17 +0300 Subject: [PATCH] Support CallDynamic in ECMAScript compiler Co-authored-by: Denis Krylov Signed-off-by: Vsevolod Pukhov --- compiler/codegen_intrinsics_ecmascript.cpp | 16 ++++ .../ecmascript_codegen_extensions.cpp | 3 +- .../intrinsics_type_resolving_ecmascript.cpp | 7 +- .../code_generator/compiler_base_types.cpp | 46 +++++------ .../ir_builder/ecmascript_inst_builder.cpp | 81 +++++++++++++++++++ .../ir_builder/ecmascript_inst_builder.h | 2 + .../ir_builder/ecmascript_inst_templates.yaml | 36 +++++++++ .../ecmascript_inst_builder_gen.cpp.erb | 1 + ecmascript_plugin_options.yaml | 7 +- isa/isa.yaml | 4 +- runtime/CMakeLists.txt | 2 +- runtime/asm_defines/asm_defines.def | 2 + runtime/bridge.h | 27 +++++++ runtime/builtins/builtins_function.cpp | 6 +- .../compiler/ecmascript_runtime_interface.cpp | 5 ++ .../compiler/ecmascript_runtime_interface.h | 2 + runtime/ecma_runtime.yaml | 12 +++ runtime/ecma_runtime_call_info.h | 32 +++++++- runtime/ecma_vm.cpp | 2 + runtime/interpreter/ecma-interpreter-inl.h | 27 ++++--- runtime/interpreter/interpreter-inl.h | 10 ++- runtime/interpreter/js_frame-inl.h | 5 -- runtime/js_function.h | 5 ++ runtime/js_hclass.h | 8 +- runtime/js_invoker.cpp | 26 ++++++ runtime/runtime_sources.gn | 4 +- tests/CMakeLists.txt | 5 +- tests/checked/type_resolving.js | 4 +- tests/compiler/types_resolving_ecma_tests.cpp | 15 ++-- tests/runtime/irtoc/CMakeLists.txt | 5 -- 30 files changed, 332 insertions(+), 75 deletions(-) create mode 100644 runtime/bridge.h diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index d426b68db..7097c9e45 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -304,4 +304,20 @@ void Codegen::CreateDynObjectSetClass(IntrinsicInst *inst, [[maybe_unused]] Reg CreateStackMap(inst); } } + +void Codegen::CreateDynCallCheck([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + auto exit_label = CreateSlowPath(inst, EntrypointId::DEOPTIMIZE)->GetLabel(); + + ScopedTmpReg tmp(GetEncoder()); + + LoadClassFromObject(tmp, src[0]); + GetEncoder()->EncodeLdr(tmp, false, + MemRef(tmp, -cross_values::GetJshclassHclassOffset(GetArch()) + + cross_values::GetJshclassBitfieldOffset(GetArch()))); + GetEncoder()->EncodeBitTestAndBranch(exit_label, tmp, + cross_values::GetJshclassBitfieldConstructorStartBit(GetArch()), true); +} + } // namespace panda::compiler diff --git a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp index 868daf01d..ecd47d84a 100644 --- a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp +++ b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp @@ -32,6 +32,7 @@ namespace panda::compiler { bool Codegen::GenerateEcmascriptEnvInPrologue() { if (GetRuntime()->IsEcmascriptRuntime()) { + SCOPED_DISASM_STR(this, "Ecmascript Environment in Method Prologue"); auto enc = GetEncoder(); auto cp_src_offset = GetGraph()->GetRuntime()->GetConstantPoolOffset(); @@ -66,7 +67,7 @@ bool Codegen::GenerateEcmascriptEnvInEpilogue() if (!GetRuntime()->IsEcmascriptRuntime()) { return false; } - + SCOPED_DISASM_STR(this, "Ecmascript Environment in Method Epilogue"); ScopedTmpReg js_env_reg(GetEncoder()); auto enc = GetEncoder(); diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index 836ccbdb4..c3e6633fd 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -535,17 +535,18 @@ bool TypesResolving::InlineLdFalse(IntrinsicInst *intrinsics) bool TypesResolving::InlineLdHole(IntrinsicInst *intrinsics) { - return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_HOLE_TYPE, TaggedValue::VALUE_HOLE); + return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_HOLE_TYPE, DataType::Any(TaggedValue::VALUE_HOLE)); } bool TypesResolving::InlineLdNull(IntrinsicInst *intrinsics) { - return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::VALUE_NULL); + return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_NULL_TYPE, DataType::Any(TaggedValue::VALUE_NULL)); } bool TypesResolving::InlineLdUndefined(IntrinsicInst *intrinsics) { - return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::VALUE_UNDEFINED); + return InlineLdConstant(intrinsics, AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, + DataType::Any(TaggedValue::VALUE_UNDEFINED)); } bool TypesResolving::InlineLdInfinity(IntrinsicInst *intrinsics) diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index 062282da9..b79084ed8 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -122,9 +122,8 @@ static void CompareAnyTypeGenObject(Encoder *enc, const Reg &dst, const Reg &src } static void CompareAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Reg &dst, const Reg &src, uint32_t type, - Condition fail_cc = Condition::NE) + LabelHolder::LabelId label, Condition fail_cc = Condition::NE) { - auto label = enc->CreateLabel(); enc->EncodeMov(dst, Imm(0)); CompareAnyTypeGenObject(enc, src, label); @@ -141,6 +140,13 @@ static void CompareAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Re enc->EncodeJump(label, tmp_reg, Imm(type), fail_cc); enc->EncodeMov(dst, Imm(1U)); +} + +static void CompareAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Reg &dst, const Reg &src, uint32_t type, + Condition fail_cc = Condition::NE) +{ + auto label = enc->CreateLabel(); + CompareAnyTypeGenObjectType(codegen, enc, dst, src, type, label, fail_cc); enc->BindLabel(label); } @@ -280,24 +286,12 @@ bool ecmascript::CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, Encod case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: enc->EncodeOr(dst, src, Imm(panda::coretypes::TaggedValue::VALUE_FALSE)); return true; - case AnyBaseType::ECMASCRIPT_INT_TYPE: - if (dst.GetId() != src.GetId()) { - if (!cvai->GetInput(0).GetInst()->IsConst()) { - ASSERT(src_reg_type == DataType::INT32 || src_reg_type == DataType::UINT32); - } -#ifndef NDEBUG - enc->EncodeMov(dst, src); - constexpr uint32_t INT32_SIZE = 32U; - enc->EncodeShr(dst, dst, Imm(INT32_SIZE)); - auto ok_label = enc->CreateLabel(); - enc->EncodeJump(ok_label, dst, Imm(0), Condition::EQ); - enc->EncodeAbort(); - enc->BindLabel(ok_label); -#endif - enc->EncodeMov(dst, src); - } + case AnyBaseType::ECMASCRIPT_INT_TYPE: { + auto src_32 = enc_v->GetCodegen()->ConvertRegister(cvai->GetSrcReg(0), DataType::INT32); + enc->EncodeCast(dst, false, src_32, false); enc->EncodeOr(dst, dst, Imm(panda::coretypes::TaggedValue::TAG_INT)); return true; + } case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: enc->EncodeMov(dst, src); enc->EncodeAdd(dst, dst, Imm(panda::coretypes::TaggedValue::DOUBLE_ENCODE_OFFSET)); @@ -353,22 +347,24 @@ bool ecmascript::AnyTypeCheckGen(const AnyTypeCheckInst *check_inst, EncodeVisit CompareAnyTypeGenDouble(enc, tmp_reg, src, id); return true; case AnyBaseType::ECMASCRIPT_STRING_TYPE: - CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeString(codegen->GetArch())); + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeString(codegen->GetArch()), + id); return true; case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: - CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeJsArray(codegen->GetArch())); + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeJsArray(codegen->GetArch()), + id); return true; case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, - cross_values::GetJstypeTransitionHandler(codegen->GetArch())); + cross_values::GetJstypeTransitionHandler(codegen->GetArch()), id); return true; case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, - cross_values::GetJstypePrototypeHandler(codegen->GetArch())); + cross_values::GetJstypePrototypeHandler(codegen->GetArch()), id); return true; case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeJsArray(codegen->GetArch()), - Condition::LE); + id, Condition::LE); return true; case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: { CompareAnyTypeGenBool(enc, tmp_reg, src, id); @@ -380,6 +376,10 @@ bool ecmascript::AnyTypeCheckGen(const AnyTypeCheckInst *check_inst, EncodeVisit case AnyBaseType::ECMASCRIPT_HOLE_TYPE: IsEqualToValGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::VALUE_HOLE), id); return true; + case AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, + cross_values::GetJstypeJsFunction(codegen->GetArch()), id); + return true; default: return false; } diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp index a7f78f200..1ccd1c1b5 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -13,4 +13,85 @@ * limitations under the License. */ +#include "compiler_logger.h" +#include "optimizer/ir_builder/inst_builder.h" +#include "optimizer/ir_builder/ir_builder.h" +#include "optimizer/ir/inst.h" +#include "bytecode_instruction.h" +#include "bytecode_instruction-inl.h" +#include "include/coretypes/tagged_value.h" + +namespace panda::compiler { + +// NOLINTNEXTLINE(misc-definitions-in-headers) +void InstBuilder::BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_range, bool call_this, uint64_t num_args) +{ + constexpr size_t NUMBER_HIDE_ARGS = 3; + auto pc = GetPc(bc_inst->GetAddress()); + auto save_state = CreateSaveState(Opcode::SaveState, pc); + AddInstruction(save_state); + + auto input = GetDefinition(bc_inst->GetVReg(0)); + auto func_check = BuildAnyTypeCheckInst(pc, input, save_state, AnyBaseType::ECMASCRIPT_FUNCTION_TYPE); + + auto fn_class_check = GetGraph()->CreateInstIntrinsic(DataType::ANY, pc); + fn_class_check->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_DYN_CALL_CHECK); + AdjustFlags(fn_class_check->GetIntrinsicId(), fn_class_check); + fn_class_check->SetFlag(inst_flags::CAN_DEOPTIMIZE); + fn_class_check->ReserveInputs(1); + fn_class_check->AllocateInputTypes(GetGraph()->GetAllocator(), 1); + fn_class_check->AppendInput(func_check); + fn_class_check->AddInputType(DataType::ANY); + fn_class_check->AppendInput(save_state); + fn_class_check->AddInputType(DataType::NO_TYPE); + AddInstruction(fn_class_check); + + auto inst = GetGraph()->CreateInstCallDynamic(DataType::ANY, pc); + + inst->SetCanNativeException(true); + + size_t args_count = 0; + + // Take into account this argument for virtual call + if (is_range) { + args_count = bc_inst->GetImm64() + 1; + } else { + args_count = num_args; + } + + inst->ReserveInputs(args_count + 1); // '+1' for SaveState input + inst->AllocateInputTypes(GetGraph()->GetAllocator(), args_count + 1); + + inst->AppendInput(func_check); + inst->AddInputType(DataType::ANY); + + inst->AppendInput(FindOrCreateConstant(coretypes::TaggedValue::VALUE_UNDEFINED)); // newtarget argument + inst->AddInputType(DataType::ANY); + + if (!call_this) { + inst->AppendInput(FindOrCreateConstant(coretypes::TaggedValue::VALUE_UNDEFINED)); // this argument + inst->AddInputType(DataType::ANY); + } + + if (is_range) { + auto start_reg = bc_inst->GetVReg(0); + // start reg for Virtual call was added + for (size_t i = 0; i < args_count - 1; i++) { + inst->AppendInput(GetDefinition(start_reg + i + 1)); + inst->AddInputType(DataType::ANY); + } + } else { + for (size_t i = 0; i < args_count - NUMBER_HIDE_ARGS; i++) { + inst->AppendInput(GetDefinition(bc_inst->GetVReg(i + 1))); + inst->AddInputType(DataType::ANY); + } + } + inst->AppendInput(save_state); + inst->AddInputType(DataType::NO_TYPE); + AddInstruction(inst); + UpdateDefinitionAcc(inst); +} + +} // namespace panda::compiler + #include "ecmascript_inst_builder_gen.cpp" diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h index d4a6ed0ed..20a5c069c 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h @@ -21,4 +21,6 @@ template void BuildEcmaAsIntrinsics([[maybe_unused]] const BytecodeInstruction *bc_inst); void BuildEcmaFromIrtoc([[maybe_unused]] const BytecodeInstruction *bc_inst); +void BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_range, bool call_this, uint64_t num_args = 0); + #endif // PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_BUILDER_ECMASCRIPT_INST_BUILDER_H diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml index c928fd906..1a93dab60 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml +++ b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml @@ -32,6 +32,42 @@ AddInstruction(cvat); AddInstruction(cmp_inst); AddInstruction(jmp_inst); + % when "CALL0DYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, false, false, 3); + } + % when "CALL1DYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, false, false, 4); + } + % when "CALL2DYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, false, false, 5); + } + % when "CALL3DYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, false, false, 6); + } + % when "CALLIRANGEDYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, true, false); + } + % when "CALLITHISRANGEDYN" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildEcmaFnCall(instruction, true, true); + } % else BuildEcma(instruction); % end diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb index 3a70992e2..cfc856db2 100644 --- a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -20,6 +20,7 @@ #include "bytecode_instruction.h" #include "bytecode_instruction-inl.h" #include "include/coretypes/tagged_value.h" + #include "irtoc_builder.cpp" namespace panda::compiler { diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml index 191e9a709..063f289d4 100644 --- a/ecmascript_plugin_options.yaml +++ b/ecmascript_plugin_options.yaml @@ -43,9 +43,9 @@ func_resolve_string_type: panda::compiler::ecmascript::GetAnyStringType func_is_any_type_can_be_subtype_of: panda::compiler::ecmascript::IsAnyTypeCanBeSubtypeOf list_types: - HOLE_TYPE: panda::compiler::DataType::Type::VOID - NULL_TYPE: panda::compiler::DataType::Type::VOID - UNDEFINED_TYPE: panda::compiler::DataType::Type::VOID + HOLE_TYPE: panda::compiler::DataType::Type::ANY + NULL_TYPE: panda::compiler::DataType::Type::ANY + UNDEFINED_TYPE: panda::compiler::DataType::Type::ANY INT_TYPE: panda::compiler::DataType::Type::INT32 DOUBLE_TYPE: panda::compiler::DataType::Type::FLOAT64 OBJECT_TYPE: panda::compiler::DataType::Type::REFERENCE @@ -56,6 +56,7 @@ PROTOTYPE_HANDLER_TYPE: panda::compiler::DataType::Type::REFERENCE SPECIAL_INDEXED_TYPE: panda::compiler::DataType::Type::REFERENCE BOOLEAN_TYPE: panda::compiler::DataType::Type::BOOL + FUNCTION_TYPE: panda::compiler::DataType::Type::REFERENCE Intrinsics: intrinsic_inline_inl: plugins/ecmascript/compiler/intrinsics_inline_ecmascript.inl intrinsic_type_resolving_inl_h: plugins/ecmascript/compiler/intrinsics_type_resolving_ecmascript.inl.h diff --git a/isa/isa.yaml b/isa/isa.yaml index 8c957bf11..cd2108209 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -618,13 +618,13 @@ groups: acc: out:top prefix: ecma format: [pref_op_imm_16_v_8] - properties: [call, not_compilable] + properties: [call] intrinsic_name: INTRINSIC_CALLI_RANGE_DYN - sig: ecma.callithisrangedyn imm, v:in:top acc: out:top prefix: ecma format: [pref_op_imm_16_v_8] - properties: [call, not_compilable] + properties: [call] intrinsic_name: INTRINSIC_CALLI_THIS_RANGE_DYN - sig: ecma.supercall imm, v:in:top acc: inout:top diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 02de1eec5..c61cb593d 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -15,7 +15,6 @@ panda_promote_to_definitions(PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT) set(ECMA_SRC_DIR ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/runtime) -# ${BUILTIN_BRIDGE_SOURCE} is unused. Should be files deleted??? if(PANDA_TARGET_ARM32) SET(BUILTIN_BRIDGE_SOURCE ${ECMA_SRC_DIR}/arch/arm/builtin_bridge_arm.S) elseif(PANDA_TARGET_ARM64) @@ -201,6 +200,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/class_info_extractor.cpp ${ECMA_SRC_DIR}/compiler/ecmascript_runtime_interface.cpp + ${BUILTIN_BRIDGE_SOURCE} ${ECMA_SRC_DIR}/tooling/pt_ecmascript_extension.cpp ) diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index c1c0cd340..d4527b405 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -9,6 +9,7 @@ DEFINE_VALUE(JSTYPE_STRING, static_cast(panda::ecmascript::JSType::STRI DEFINE_VALUE(JSTYPE_JS_ARRAY, static_cast(panda::ecmascript::JSType::JS_ARRAY)) 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(ECMASTRING_MIX_LENGTH_OFFSET, panda::ecmascript::EcmaString::MIX_LENGTH_OFFSET) DEFINE_VALUE(JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET, panda::ecmascript::JSForInIterator::FAST_REMAINING_INDEX_OFFSET) DEFINE_VALUE(JSFORINITERATOR_REMAINING_KEYS_OFFSET, panda::ecmascript::JSForInIterator::REMAINING_KEYS_OFFSET) @@ -20,6 +21,7 @@ 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_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()) diff --git a/runtime/bridge.h b/runtime/bridge.h new file mode 100644 index 000000000..cebc91152 --- /dev/null +++ b/runtime/bridge.h @@ -0,0 +1,27 @@ +/** + * 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_BRIDGE_BRIDGE_H +#define ECMASCRIPT_BRIDGE_BRIDGE_H + +#include "runtime/bridge/bridge.h" + +namespace panda { + +extern "C" DecodedTaggedValue CompiledCodeToBuiltinBridge(Method *, uint32_t, DecodedTaggedValue *, ...); + +} // namespace panda + +#endif // ECMASCRIPT_BRIDGE_BRIDGE_H diff --git a/runtime/builtins/builtins_function.cpp b/runtime/builtins/builtins_function.cpp index 0b243d4b8..16e053ed5 100644 --- a/runtime/builtins/builtins_function.cpp +++ b/runtime/builtins/builtins_function.cpp @@ -25,7 +25,11 @@ namespace panda::ecmascript::builtins { static JSTaggedValue EvalHackReturnThis([[maybe_unused]] EcmaRuntimeCallInfo *argv) { - return argv->GetThis().GetTaggedValue(); + auto this_value = argv->GetThis().GetTaggedValue(); + if (this_value.IsNull() || this_value.IsUndefined()) { // workaround for sloppy call from compiled code + this_value = argv->GetThread()->GetGlobalObject(); + } + return this_value; } static JSTaggedValue EvalHackNop([[maybe_unused]] EcmaRuntimeCallInfo *argv) diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 1107515dd..84202a180 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -43,4 +43,9 @@ size_t EcmaRuntimeInterface::GetEcmascriptLanguageExtensionSize() return 0; } +uint32_t EcmaRuntimeInterface::GetFunctionTargetOffset([[maybe_unused]] Arch arch) const +{ + return ecmascript::JSFunction::METHOD_OFFSET; +} + } // namespace panda diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index decfe58be..b77f64e2e 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -41,6 +41,8 @@ class EcmaRuntimeInterface : public PandaRuntimeInterface { ASSERT(panda::panda_file::IsDynamicLanguage(MethodCast(method)->GetClass()->GetSourceLang())); return std::string(static_cast(MethodCast(method))->GetFullName()); } + + uint32_t GetFunctionTargetOffset(Arch arch) const override; }; } // namespace panda diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 51c45fb92..d4d094d22 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -1849,3 +1849,15 @@ intrinsics: codegen_func: CreateDynObjectSetClass use_thread: false clear_flags: [require_state, runtime_call] + +- name: DynCallCheck + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CreateDynCallCheck + static: true + signature: + ret: void + args: [any] + codegen_func: CreateDynCallCheck + use_thread: false + clear_flags: [] diff --git a/runtime/ecma_runtime_call_info.h b/runtime/ecma_runtime_call_info.h index d5272093a..d3504ca60 100644 --- a/runtime/ecma_runtime_call_info.h +++ b/runtime/ecma_runtime_call_info.h @@ -31,7 +31,19 @@ class EcmaRuntimeCallInfo { public: // For builtins interpreter call EcmaRuntimeCallInfo(JSThread *thread, uint32_t numArgs, interpreter::VRegister *args) - : thread_(thread), numArgs_(numArgs), gprArgs_(args, numArgs), stackArgs_(nullptr, static_cast(0)) + : thread_(thread), + numArgs_(numArgs), + gprArgs_(reinterpret_cast(args), numArgs), + stackArgs_(nullptr, static_cast(0)) + { + ASSERT(numArgs_ >= NUM_MANDATORY_JSFUNC_ARGS); + } + + EcmaRuntimeCallInfo(JSThread *thread, uint32_t numArgs, JSTaggedValue *gprArgs, JSTaggedValue *stackArgs) + : thread_(thread), + numArgs_(numArgs), + gprArgs_(gprArgs, GetGprArgsCount(numArgs)), + stackArgs_(stackArgs, GetStackArgsCount(numArgs)) { ASSERT(numArgs_ >= NUM_MANDATORY_JSFUNC_ARGS); } @@ -117,11 +129,25 @@ private: *reinterpret_cast(GetArgAddress(idx)) = tagged; } + static size_t GetGprArgsCount(uint32_t numArgs) + { + static_assert(arch::ExtArchTraits::NUM_GP_ARG_REGS >= 2); + size_t numArgRegs = arch::ExtArchTraits::NUM_GP_ARG_REGS - + 2; // first two regs are occupied by Method* and numArgs + size_t maxGprArgs = numArgRegs * ArchTraits::POINTER_SIZE / sizeof(JSTaggedValue); + return std::min(static_cast(numArgs), maxGprArgs); + } + + static size_t GetStackArgsCount(uint32_t numArgs) + { + return numArgs - GetGprArgsCount(numArgs); + } + private: JSThread *thread_; uint32_t numArgs_; - Span gprArgs_; - Span stackArgs_; + Span gprArgs_; + Span stackArgs_; }; } // namespace panda::ecmascript diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index 969c9c3ba..0bb9a5797 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -14,6 +14,7 @@ */ #include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/bridge.h" #include "js_tagged_value.h" #include "mem/mark_word.h" @@ -609,6 +610,7 @@ JSMethod *EcmaVM::GetMethodForNativeFunction(const void *func) auto method = chunk_.New(n_wrapper); method->SetNativePointer(const_cast(func)); + method->SetCompiledEntryPoint(reinterpret_cast(CompiledCodeToBuiltinBridge)); nativeMethods_.push_back(method); return nativeMethods_.back(); } diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index f4bac392c..6c175a80f 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -231,25 +231,26 @@ public: ASSERT(JSTaggedValue(ret_value).IsException() == false); SetAccFromTaggedValue(ret_value); this->template MoveToNextInst(); - } else if (method->HasCompiledCode()) { - // AOT, JIT + return; + } + + JSFunction *js_function = JSFunction::Cast(this_func); + if (UNLIKELY(js_function->IsClassConstructor())) { + JSHandle error = + GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "class constructor cannot called without 'new'"); + js_thread->SetException(error.GetTaggedValue()); + this->MoveToExceptionHandler(); + return; + } + + if (method->HasCompiledCode()) { // AOT, JIT EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); this->template CallCompiledCode(method); // Restore EcmascriptEnvironment if epilogue was not executed if (UNLIKELY(js_thread->HasPendingException())) { js_thread->SetEcmascriptEnv(prev_env); } - } else { - // Interpreter - JSFunction *js_function = JSFunction::Cast(this_func); - if (UNLIKELY(js_function->IsClassConstructor())) { - JSHandle error = - GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "class constructor cannot called without 'new'"); - js_thread->SetException(error.GetTaggedValue()); - this->MoveToExceptionHandler(); - return; - } - + } else { // Interpreter EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); // Call stackless interpreter diff --git a/runtime/interpreter/interpreter-inl.h b/runtime/interpreter/interpreter-inl.h index a438eb585..17942adc4 100644 --- a/runtime/interpreter/interpreter-inl.h +++ b/runtime/interpreter/interpreter-inl.h @@ -96,6 +96,7 @@ JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *js_thread, JSTaggedValue const JSTaggedValue *args) { ASSERT(!js_thread->HasPendingException()); + ASSERT(!js_thread->IsCurrentFrameCompiled()); // Boundary frame expected for c2i call Method *method = ECMAObject::Cast(fn_object.GetHeapObject())->GetCallTarget(); ASSERT(method->GetNumVregs() == 0); @@ -150,7 +151,14 @@ JSTaggedValue EcmaInterpreter::Execute(JSThread *thread, JSHandle ca Method *method = callTarget->GetCallTarget(); if (method->IsNative()) { - return EcmaInterpreter::ExecuteNative(thread, callTarget, numActualArgs, args); + if (!thread->IsCurrentFrameCompiled()) { + return ExecuteNative(thread, callTarget, numActualArgs, args); + } + ASSERT(!thread->HasPendingException()); + // TODO(vpukhov): create CFrame without i2c + TaggedValue ret_value = + method->InvokeDyn(thread, numActualArgs, reinterpret_cast(args)); + return JSTaggedValue(ret_value); } return ExecuteInvoke(thread, callTarget, numActualArgs, reinterpret_cast(args)); diff --git a/runtime/interpreter/js_frame-inl.h b/runtime/interpreter/js_frame-inl.h index f8f050f2e..7ea0f9b00 100644 --- a/runtime/interpreter/js_frame-inl.h +++ b/runtime/interpreter/js_frame-inl.h @@ -57,15 +57,10 @@ inline JSTaggedValue JSFrame::ExecuteNativeMethod(JSThread *js_thread, Frame *fr { ASSERT(js_thread == JSThread::GetCurrent()); - bool is_compiled = js_thread->IsCurrentFrameCompiled(); - js_thread->SetCurrentFrameIsCompiled(false); - EcmaRuntimeCallInfo call_info(js_thread, num_actual_args, &frame->GetVReg(0)); auto ecma_entry_point = reinterpret_cast(method->GetNativePointer()); JSTaggedValue ret_value = ecma_entry_point(&call_info); - js_thread->SetCurrentFrameIsCompiled(is_compiled); - return ret_value; } } // namespace panda::ecmascript diff --git a/runtime/js_function.h b/runtime/js_function.h index 731025035..13a703188 100644 --- a/runtime/js_function.h +++ b/runtime/js_function.h @@ -286,6 +286,11 @@ public: ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, PROFILE_TYPE_INFO_OFFSET) ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, SIZE) + // NOLINTNEXTLINE(readability-magic-numbers) + static_assert(LEXICAL_ENV_OFFSET == 0x30); + // NOLINTNEXTLINE(readability-magic-numbers) + static_assert(CONSTANT_POOL_OFFSET == 0x50); + static constexpr uint32_t FUNCTION_KIND_BIT_NUM = 5; using FunctionKindBit = BitField; using StrictBit = FunctionKindBit::NextFlag; diff --git a/runtime/js_hclass.h b/runtime/js_hclass.h index 22ee5eaf4..1fa45a580 100644 --- a/runtime/js_hclass.h +++ b/runtime/js_hclass.h @@ -210,8 +210,8 @@ public: static constexpr int TYPE_BITFIELD_NUM = 8; using ObjectTypeBits = BitField; // 7 using Unused1 = ObjectTypeBits::NextFlag; - using ConstrutorBit = Unused1::NextFlag; // 9 - using Unused2 = ConstrutorBit::NextFlag; // 10 + using ConstructorBit = Unused1::NextFlag; // 9 + using Unused2 = ConstructorBit::NextFlag; // 10 using ExtensibleBit = Unused2::NextFlag; using IsPrototypeBit = ExtensibleBit::NextFlag; using ElementRepresentationBits = IsPrototypeBit::NextField; // 3 means next 3 bit @@ -302,7 +302,7 @@ public: inline void SetConstructor(bool flag) const { - ConstrutorBit::Set(flag, GetBitFieldAddr()); + ConstructorBit::Set(flag, GetBitFieldAddr()); } inline void SetExtensible(bool flag) const @@ -758,7 +758,7 @@ public: inline bool IsConstructor() const { uint32_t bits = GetBitField(); - return ConstrutorBit::Decode(bits); + return ConstructorBit::Decode(bits); } inline bool IsBuiltinsCtor() const diff --git a/runtime/js_invoker.cpp b/runtime/js_invoker.cpp index 0ae06efe7..66984f9b6 100644 --- a/runtime/js_invoker.cpp +++ b/runtime/js_invoker.cpp @@ -30,6 +30,32 @@ JSTaggedValue JsInvoker::Invoke([[maybe_unused]] JSThread *thread) UNREACHABLE(); } +extern "C" uint64_t InvokeBuiltin(JSThread *thread, Method *method, uint32_t numArgs, JSTaggedValue *gprArgs, + JSTaggedValue *stackArgs) +{ + EcmaRuntimeCallInfo ecmaRuntimeCallInfo(thread, numArgs, gprArgs, stackArgs); + + ASSERT(method->GetNativePointer() != nullptr); + + JSTaggedValue retValue = + reinterpret_cast(const_cast(method->GetNativePointer()))(&ecmaRuntimeCallInfo); + + if (thread->HasPendingException()) { + auto stack = StackWalker::Create(thread); + ASSERT(stack.IsCFrame() && stack.GetCFrame().IsNative()); + stack.NextFrame(); + // Native frames can handle exceptions as well + if (!stack.HasFrame() || !stack.IsCFrame() || stack.GetCFrame().IsNative()) { + return retValue.GetRawData(); + } + + FindCatchBlockInCFrames(thread, &stack, nullptr); + UNREACHABLE(); + } + + return retValue.GetRawData(); +} + JSTaggedValue InvokeJsFunction(JSThread *thread, const JSHandle &func, const JSHandle &obj, const JSHandle &newTgt, InternalCallParams *arguments) { diff --git a/runtime/runtime_sources.gn b/runtime/runtime_sources.gn index 31cb5e112..5477ac554 100644 --- a/runtime/runtime_sources.gn +++ b/runtime/runtime_sources.gn @@ -175,7 +175,7 @@ srcs = [ # Should be files deleted??? #srcs_arm = [ "arch/arm/builtin_bridge_arm.S" ] -#srcs_arm64 = [ "arch/aarch64/builtin_bridge_aarch64.S" ] -#srcs_x86 = [ "arch/amd64/builtin_bridge_amd64.S" ] +srcs_arm64 = [ "arch/aarch64/builtin_bridge_aarch64.S" ] +srcs_x86 = [ "arch/amd64/builtin_bridge_amd64.S" ] runtime_yamls = [ "ecma_runtime.yaml" ] diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a4d8dfd61..6e80c9018 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -261,7 +261,10 @@ if (CMAKE_CROSSCOMPILING AND PANDA_TARGET_ARM64) # add_test_file_ecma(FILE "${CMAKE_CURRENT_SOURCE_DIR}/ecmascript-tests/js-bitops-bitwise-and.pa" COMPILER_OPTIONS --compiler-hotness-threshold=0) endif() -add_subdirectory(runtime) +# ARM32 is not supported for ecmascript +if(NOT PANDA_TARGET_ARM32) + add_subdirectory(runtime) +endif() if(PANDA_WITH_COMPILER) add_subdirectory(compiler) diff --git a/tests/checked/type_resolving.js b/tests/checked/type_resolving.js index 8db6aa56e..160da8c04 100644 --- a/tests/checked/type_resolving.js +++ b/tests/checked/type_resolving.js @@ -70,8 +70,8 @@ //! check_aot.call(:Dec, 'DecDyn', 'f', /f64 +Sub/) //! check_aot.call(:Ldtrue, 'Ldtrue', '', /i64 +Constant +0x1/) //! check_aot.call(:Ldfalse, 'Ldfalse', '', /i64 +Constant +0x0/) -//! check_aot.call(:Ldnull, 'Ldnull', '', /i64 +Constant +0x2/) -//! check_aot.call(:Ldundefined, 'Ldundefined', '', /i64 +Constant +0xa/) +//! check_aot.call(:Ldnull, 'Ldnull', '', /any +Constant +0x2/) +//! check_aot.call(:Ldundefined, 'Ldundefined', '', /any +Constant +0xa/) //! check_aot.call(:Ldinfinity, 'Ldinfinity', '', /f64 +Constant +inf/) //! check_aot.call(:Ldnan, 'Ldnan', '', /f64 +Constant +nan/) //! diff --git a/tests/compiler/types_resolving_ecma_tests.cpp b/tests/compiler/types_resolving_ecma_tests.cpp index 9f7d78175..d4f9b225b 100644 --- a/tests/compiler/types_resolving_ecma_tests.cpp +++ b/tests/compiler/types_resolving_ecma_tests.cpp @@ -183,15 +183,20 @@ TEST_F(TypeResolvingTest, LdConst) { using TaggedValue = panda::coretypes::TaggedValue; { - TestArray tests = {{ + TestArray tests = {{ {RuntimeInterface::IntrinsicId::INTRINSIC_LDTRUE, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 1U}, {RuntimeInterface::IntrinsicId::INTRINSIC_LDFALSE, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 0U}, + }}; + TestLdConsts(tests); + } + { + TestArray tests = {{ {RuntimeInterface::IntrinsicId::INTRINSIC_LDHOLE, AnyBaseType::ECMASCRIPT_HOLE_TYPE, - TaggedValue::VALUE_HOLE}, + DataType::Any(TaggedValue::VALUE_HOLE)}, {RuntimeInterface::IntrinsicId::INTRINSIC_LDNULL, AnyBaseType::ECMASCRIPT_NULL_TYPE, - TaggedValue::VALUE_NULL}, + DataType::Any(TaggedValue::VALUE_NULL)}, {RuntimeInterface::IntrinsicId::INTRINSIC_LDUNDEFINED, AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, - TaggedValue::VALUE_UNDEFINED}, + DataType::Any(TaggedValue::VALUE_UNDEFINED)}, }}; TestLdConsts(tests); } @@ -548,7 +553,7 @@ TEST_F(TypeResolvingTest, ResolveLoopPhi) } } ASSERT_TRUE(graph->RunPass()); - ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass(false)); GraphChecker(graph).Check(); auto &phi = INS(4); diff --git a/tests/runtime/irtoc/CMakeLists.txt b/tests/runtime/irtoc/CMakeLists.txt index 2fd563d70..7bd7dab19 100644 --- a/tests/runtime/irtoc/CMakeLists.txt +++ b/tests/runtime/irtoc/CMakeLists.txt @@ -1,8 +1,3 @@ -# We have issues for arm32 -if (PANDA_TARGET_ARM32) - return() -endif() - function(ecmascript_irtoc_interpreter_tests) set(prefix ARG) set(noValues) -- Gitee