diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index 426ac75dc70de37eb0c56df00a218c8fa956c61b..0031d8b55a7f39dc3a460d35cdb40af85c4abdce 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -48,14 +48,6 @@ static void ReadExtensionData(Codegen *cg, Reg dst, uint16_t env_offs, uint32_t } } -MemRef AccMemRef(Encoder *enc, Reg thread, Reg acc_reg) -{ - enc->EncodeAdd(acc_reg, thread, Imm(ManagedThread::GetFrameOffset())); - auto acc_ptr = MemRef(acc_reg); - enc->EncodeLdr(acc_reg, false, MemRef(acc_ptr)); - return MemRef(acc_reg, cross_values::GetFrameAccOffset(enc->GetArch())); -} - void Codegen::LdlexenvDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS src) { auto enc = GetEncoder(); @@ -63,7 +55,10 @@ void Codegen::LdlexenvDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] auto constexpr LE_OFFS = panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset(); - if (!GetGraph()->GetMode().IsInterpreter()) { + ASSERT(GetGraph()->SupportManagedCode() == (inst->GetSaveState() == nullptr)); + if (GetGraph()->SupportManagedCode()) { + ReadExtensionData(this, dst, LE_OFFS, inst->GetImms().back()); + } else if (!GetGraph()->GetMode().IsInterpreter()) { ReadExtensionData(this, dst, LE_OFFS, inst->GetInliningDepth()); } else { auto tmp1 = ConvertInstTmpReg(inst); @@ -78,175 +73,58 @@ void Codegen::LdlexenvDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] } } -void IsObject(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2) -{ - enc->EncodeAnd(tmp1, value, Imm(coretypes::TaggedValue::TAG_MASK)); - enc->EncodeMov(tmp2, Imm(coretypes::TaggedValue::TAG_OBJECT)); - enc->EncodeCompare(tmp1, tmp1, tmp2, Condition::EQ); -} - -void IsSpecial(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2, MemRef tmp_stack_slot) -{ - // IsSpecial(value) = (val <= ~TAG_SPECIAL_MASK) & (((val & TAG_SPECIAL_VALUE) >> 1) | (val == 0)) - - // tmp1 = val == 0 - enc->EncodeMov(tmp2, Imm(0U)); - enc->EncodeCompare(tmp1, value, tmp2, Condition::EQ); - - // tmp2 = ((val & TAG_SPECIAL_VALUE) >> 1) | tmp1 - enc->EncodeAnd(tmp2, value, Imm(coretypes::TaggedValue::TAG_SPECIAL_VALUE)); - enc->EncodeShr(tmp2, tmp1, Imm(1)); - enc->EncodeOr(tmp2, tmp2, tmp1); - - // store(tmp2) - enc->EncodeStr(tmp2, tmp_stack_slot); - - // tmp2 = value & ~TAG_SPECIAL_MASK - enc->EncodeMov(tmp2, Imm(~coretypes::TaggedValue::TAG_SPECIAL_MASK)); - enc->EncodeCompare(tmp1, value, tmp2, Condition::LS); - - // load(tmp2) - enc->EncodeLdr(tmp2, false, tmp_stack_slot); - // tmp1 = tmp1 & tmp2 - enc->EncodeAnd(tmp1, tmp1, tmp2); -} - -void IsHeapObject(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2, MemRef slot1, MemRef slot2) +void Codegen::LdLexVarDyn(IntrinsicInst *inst, Reg dst, SRCREGS src) { - // IsHeapObject(value) = IsObject(value) && !IsSpecial(value) - - // e1 = IsObject(value) - IsObject(enc, value, tmp1, tmp2); // tmp1 = e1 - enc->EncodeStr(tmp1, slot1); // stack[slot1] = e1 - // e2 = IsSpecial(value) - IsSpecial(enc, value, tmp1, tmp2, slot2); // tmp1 = e2 - // e3 = e1 && !e2 - enc->EncodeLdr(tmp2, false, slot1); // tmp2 = e1 - enc->EncodeNot(tmp1, tmp1); - enc->EncodeAnd(tmp1, tmp2, tmp1); // tmp1 = e3 -} + ASSERT(inst->HasImms() == (inst->GetInputsCount() == 0U)); + ASSERT(GetGraph()->GetMode().IsInterpreter() != inst->HasImms()); -void Codegen::LdLexVarDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src) -{ - auto enc = GetEncoder(); + auto *enc = GetEncoder(); auto arch = enc->GetArch(); - auto runtime = GetRuntime(); - - auto tmp1 = ConvertInstTmpReg(inst); - - Reg lex_env(tmp1.GetId(), Codegen::ConvertDataType(DataType::ANY, arch)); - ReadExtensionData(this, lex_env, panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset(), - inst->GetInliningDepth()); - - auto begin_label = enc->CreateLabel(); - auto exit_label = enc->CreateLabel(); - - ScopedTmpReg tmp2(enc); - Reg i_reg(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT16, arch)); - enc->EncodeMov(i_reg, Imm(0)); - - // enter the loop body if i is less then level (level = src[0]) - enc->BindLabel(begin_label); - if (!GetGraph()->GetMode().IsInterpreter()) { - enc->EncodeJump(exit_label, i_reg, Imm(inst->GetImms()[0]), Condition::HS); - } else { - enc->EncodeJump(exit_label, i_reg, src[0], Condition::HS); + auto *runtime = GetRuntime(); + + ScopedTmpRegU64 tmp(enc); + auto lex_env = tmp.GetReg().As(Codegen::ConvertDataType(DataType::ANY, arch)); + auto lex_env_ptr = tmp.GetReg().As(Codegen::ConvertDataType(DataType::POINTER, arch)); + ASSERT(lex_env.GetSize() >= lex_env_ptr.GetSize()); + + ASSERT(GetGraph()->SupportManagedCode() == (inst->GetSaveState() == nullptr)); + auto inline_depth = GetGraph()->SupportManagedCode() ? inst->GetImms().back() : inst->GetInliningDepth(); + ReadExtensionData(this, lex_env, panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset(), inline_depth); + + constexpr size_t LEVEL_INDEX = 0U; + auto data = runtime->GetArrayDataOffset(arch); + auto elem_size = runtime->GetTaggedArrayElementSize(); + if (!inst->HasImms() || inst->GetImms()[LEVEL_INDEX] > 0U) { + auto head = enc->CreateLabel(); + auto exit = enc->CreateLabel(); + ScopedTmpRegU32 counter(enc); + if (inst->HasImms()) { + enc->EncodeMov(counter, Imm(inst->GetImms()[LEVEL_INDEX])); + } else { + enc->EncodeJump(exit, src[LEVEL_INDEX].As(INT32_TYPE), Condition::EQ); // fast path for level 0 + enc->EncodeMov(counter, src[LEVEL_INDEX].As(INT32_TYPE)); + } + enc->BindLabel(head); + // get parent env + enc->EncodeLdr(lex_env, false, MemRef(lex_env_ptr, data + runtime->GetLexicalEnvParentEnvIndex() * elem_size)); + enc->EncodeSub(counter, counter, Imm(1U)); + enc->EncodeJump(head, counter, Condition::NE); + enc->BindLabel(exit); } - // the beginning of the loop body - - // get env tagged array length - ScopedTmpReg length(enc); - Reg length_reg(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); - enc->EncodeLdr(length_reg, false, MemRef(lex_env, runtime->GetArrayLengthOffset(arch))); - // check if the length is greater than zero - auto length_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); - enc->EncodeJump(length_slow_path->GetLabel(), length_reg, Condition::LO); - - // get parent env - enc->EncodeLdr(lex_env, false, MemRef(lex_env, runtime->GetArrayDataOffset(arch))); - // check if the env value is defined - Reg lex_env_u64(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); - enc->EncodeMov(lex_env_u64, lex_env); - auto undefined_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); - enc->EncodeJump(undefined_slow_path->GetLabel(), lex_env_u64, Imm(coretypes::TaggedValue::VALUE_UNDEFINED), - Condition::EQ); - - // store i and env to stack - auto stack_slots_count = GetFrameLayout().GetSpillsCount(); - auto stack_slot_size = GetFrameLayout().GetSlotSize(); - auto stask_slot_1 = MemRef(SpReg(), stack_slots_count * stack_slot_size); - enc->EncodeStr(lex_env, stask_slot_1); - auto stask_slot_2 = MemRef(SpReg(), (stack_slots_count + 1) * stack_slot_size); - enc->EncodeStr(i_reg, stask_slot_2); - - // check if the env value is HeapObject - Reg v1(tmp1.GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); - Reg v2(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); - auto stask_slot_3 = MemRef(SpReg(), (stack_slots_count + 2) * stack_slot_size); - auto stask_slot_4 = MemRef(SpReg(), (stack_slots_count + 3) * stack_slot_size); - IsHeapObject(enc, lex_env_u64, v1, v2, stask_slot_3, stask_slot_4); - enc->EncodeStr(v1, stask_slot_3); // stack[stask_slot_3] = IsHeapObject(value) - - // check if it is possible to convert TaggedValue to HeapObject object - - // assert(IsHeapObject(value) && ((value & TAG_WEAK_FILTER) == 0U)) - enc->EncodeAnd(v1, lex_env_u64, Imm(coretypes::TaggedValue::TAG_WEAK_FILTER)); - enc->EncodeMov(v2, Imm(0U)); - enc->EncodeCompare(v1, v1, v2, Condition::EQ); // v1 = ((value & TAG_WEAK_FILTER) == 0U) - enc->EncodeLdr(v2, false, stask_slot_3); // v2 = IsHeapObject(value) - enc->EncodeAnd(v2, v2, v1); - auto can_not_convert_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); - enc->EncodeJump(can_not_convert_slow_path->GetLabel(), v2, Imm(1), Condition::NE); - - // restore i and env - enc->EncodeLdr(i_reg, false, stask_slot_2); - enc->EncodeLdr(lex_env, false, stask_slot_1); - - // increment i - enc->EncodeAdd(i_reg, i_reg, Imm(1)); - // go to the beginning of the loop - enc->EncodeJump(begin_label); - enc->BindLabel(exit_label); - - enc->EncodeStr(lex_env, stask_slot_1); - - // increase slot by RESERVED_ENV_LENGTH ( = 1 ) - Reg tmp_reg(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); + auto start = runtime->GetLexicalEnvStartDataIndex(); + constexpr size_t SLOT_INDEX = 1U; if (!GetGraph()->GetMode().IsInterpreter()) { - enc->EncodeMov(tmp_reg, Imm(inst->GetImms()[1] + 1)); + enc->EncodeLdr(dst, false, MemRef(lex_env_ptr, data + (start + inst->GetImms()[SLOT_INDEX]) * elem_size)); } else { - enc->EncodeAdd(tmp_reg, src[1], Imm(1)); - } - // get env tagged array length - Reg tmp_reg3(tmp1.GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); - enc->EncodeLdr(tmp_reg3, false, MemRef(lex_env, runtime->GetArrayLengthOffset(arch))); - // check if the slot is less than the length - auto index_out_of_range_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); - enc->EncodeJump(index_out_of_range_slow_path->GetLabel(), tmp_reg3, tmp_reg, Condition::LS); - - // get array data offset - Reg offset(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); - enc->EncodeMov(offset, tmp_reg); - Reg tmp_reg_u64(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); - enc->EncodeMov(tmp_reg_u64, Imm(sizeof(coretypes::TaggedType))); - enc->EncodeMul(offset, offset, tmp_reg_u64); - enc->EncodeAdd(offset, offset, Imm(runtime->GetArrayDataOffset(arch))); - - // get raw data of the array element - Reg lex_env_ptr(tmp1.GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); - enc->EncodeLdr(lex_env, false, stask_slot_1); - enc->EncodeMov(lex_env_ptr, lex_env); - enc->EncodeAdd(lex_env_ptr, lex_env_ptr, offset); - enc->EncodeLdr(offset, false, MemRef(lex_env_ptr)); - - // save to acc - if (!GetGraph()->GetMode().IsInterpreter()) { - enc->EncodeMov(dst, offset); - } else { - Reg iframe_reg(tmp1.GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + auto props = tmp.GetReg().As(Codegen::ConvertDataType(DataType::ANY, arch)); + enc->EncodeLdr(props, false, + MemRef(lex_env_ptr, src[SLOT_INDEX].As(INT32_TYPE), elem_size, data + start * elem_size)); + + // save to acc + ScopedTmpReg iframe_reg(enc, Codegen::ConvertDataType(DataType::POINTER, arch)); LoadIFramePtr(this, iframe_reg); - enc->EncodeStr(offset, IFrameAccMemRef(enc, iframe_reg)); + enc->EncodeStr(props, IFrameAccMemRef(enc, iframe_reg)); } } diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index b65bc0bad43fe35bf2d49fb16779d5001aeab108..443dc48cc6e67b6c99d431ca9de83c6d2fef90ab 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -52,4 +52,19 @@ virtual bool GetProfileDataForNamedAccess( { return false; } + +virtual size_t GetLexicalEnvParentEnvIndex() const +{ + return 0; +} + +virtual size_t GetLexicalEnvStartDataIndex() const +{ + return 0; +} + +virtual size_t GetTaggedArrayElementSize() const +{ + return 0; +} #endif // PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H \ No newline at end of file diff --git a/compiler/optimizer/optimizations/ecma_inlining.cpp b/compiler/optimizer/optimizations/ecma_inlining.cpp index ebc8bd72fdd72bb975161492f96449408a2156e7..fe280f129284fdb0055beaa43e0f289e8cff05e4 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.cpp +++ b/compiler/optimizer/optimizations/ecma_inlining.cpp @@ -106,6 +106,7 @@ Graph *EcmaInlining::BuildGraph(CallInst *call_inst, InlineContext *ctx) } // Run basic optimizations + graph_inl->RunPass(); graph_inl->RunPass(false); if (graph_inl->RunPass()) { graph_inl->RunPass(); diff --git a/compiler/optimizer/optimizations/expand_intrinsics.cpp b/compiler/optimizer/optimizations/expand_intrinsics.cpp index 64f76c03e9d46c1263eafdff3b4280d9eb949927..a690020d63a56ba07473a048d2ec32f907351d31 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.cpp +++ b/compiler/optimizer/optimizations/expand_intrinsics.cpp @@ -25,12 +25,12 @@ namespace panda::compiler { bool ExpandIntrinsics::RunImpl() { bool success = false; - for (auto bb : GetGraph()->GetVectorBlocks()) { + for (auto *bb : GetGraph()->GetVectorBlocks()) { if (bb == nullptr) { continue; } - for (auto inst : bb->Insts()) { + for (auto inst : bb->InstsSafeReverse()) { if (!inst->IsIntrinsic()) { continue; } @@ -44,22 +44,35 @@ bool ExpandIntrinsics::RunImpl() 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); + switch (id) { + case RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE: + case RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE_HANDLED: + return ExpandNewObjDynRange(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_DYN: + return ExpandLdLexDyn(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN: + return ExpandLdLexVarDyn(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_LDLEXENV_DYN: + return ExpandLdlexenvDyn(inst); + default: + return false; } - return false; +} + +void ExpandIntrinsics::InstAppender::Append(Inst *inst) +{ + if (prev_ == nullptr) { + block_->AppendInst(inst); + } else { + block_->InsertAfter(inst, prev_); + } + prev_ = inst; } void ExpandIntrinsics::InstAppender::Append(std::initializer_list instructions) { - for (auto inst : instructions) { - if (prev_ == nullptr) { - block_->AppendInst(inst); - } else { - block_->InsertAfter(inst, prev_); - } - prev_ = inst; + for (auto *inst : instructions) { + Append(inst); } } @@ -329,4 +342,109 @@ void ExpandIntrinsics::BuildGuard(Inst *inst, uintptr_t target) inst->InsertBefore(deopt_inst); } +bool ExpandIntrinsics::ExpandLdLexDyn(IntrinsicInst *intrinsic) +{ + auto *save_state = intrinsic->GetSaveState(); + ASSERT(save_state != nullptr); + + ASSERT(intrinsic->GetImms().size() == 3U); + auto *load_lex_var = intrinsic; + load_lex_var->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN); + load_lex_var->SetImm(0U, load_lex_var->GetImm(1U)); // level + load_lex_var->SetImm(1U, load_lex_var->GetImm(2U)); // slot + load_lex_var->GetImms().pop_back(); // the last is not needed + + InstAppender appender(load_lex_var->GetBasicBlock(), load_lex_var); + + auto pc = intrinsic->GetPc(); + auto *hole = GetGraph()->FindOrCreateConstant(DataType::Any(panda::coretypes::TaggedValue::VALUE_HOLE)); + auto *compare = GetGraph()->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_EQ); + compare->SetOperandsType(DataType::ANY); + compare->SetInput(0U, load_lex_var); + compare->SetInput(1U, hole); + appender.Append(compare); + + auto *deopt_if = GetGraph()->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_if->SetDeoptimizeType(DeoptimizeType::HOLE); + deopt_if->SetInput(0, compare); + deopt_if->SetSaveState(save_state); + appender.Append(deopt_if); + + ExpandLdLexVarDyn(load_lex_var); + return true; +} + +static bool MoveInliningDepthToImms(IntrinsicInst *intrinsic) +{ + if (intrinsic->GetSaveState() == nullptr) { + return false; + } + + CHECK_GT(intrinsic->GetInputsCount(), 0U); + auto save_state_index = intrinsic->GetInputsCount() - 1U; + auto *save_state = intrinsic->GetInput(save_state_index).GetInst(); + ASSERT(save_state->IsSaveState()); + + auto *graph = intrinsic->GetBasicBlock()->GetGraph(); + intrinsic->AddImm(graph->GetAllocator(), save_state->GetInliningDepth()); + intrinsic->RemoveInput(save_state_index); + intrinsic->ClearFlag(inst_flags::Flags::REQUIRE_STATE); + intrinsic->ClearFlag(inst_flags::Flags::RUNTIME_CALL); + return true; +} + +bool ExpandIntrinsics::ExpandLdLexVarDyn(IntrinsicInst *intrinsic) +{ + auto level = intrinsic->GetImms()[0U]; + if (level > GetLdLexVarDynLevelThreshold()) { + return MoveInliningDepthToImms(intrinsic); + } + + auto slot = intrinsic->GetImms()[1U]; + + auto *load_lex_env = intrinsic; + load_lex_env->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LDLEXENV_DYN); + load_lex_env->GetImms().clear(); + MoveInliningDepthToImms(load_lex_env); + + InstAppender appender(load_lex_env->GetBasicBlock(), load_lex_env); + + auto pc = intrinsic->GetPc(); + auto *env_ref = GetGraph()->CreateInstCastAnyTypeValue(pc, AnyBaseType::ECMASCRIPT_ARRAY_TYPE); + // set input load_lex_env later + env_ref->SetFlag(inst_flags::NO_HOIST); + appender.Append(env_ref); + + auto *runtime = GetGraph()->GetRuntime(); + Inst *current_lex_env = env_ref; + for (; level > 0U; --level) { + auto *load_parent = GetGraph()->CreateInstLoadArray(DataType::ANY, pc); + load_parent->SetInput(0U, current_lex_env); + load_parent->SetInput(1U, GetGraph()->FindOrCreateConstant(runtime->GetLexicalEnvParentEnvIndex())); + appender.Append(load_parent); + + auto *parent_ref = GetGraph()->CreateInstCastAnyTypeValue(pc, AnyBaseType::ECMASCRIPT_ARRAY_TYPE); + parent_ref->SetInput(0U, load_parent); + parent_ref->SetFlag(inst_flags::NO_HOIST); + appender.Append(parent_ref); + current_lex_env = parent_ref; + } + + auto start = runtime->GetLexicalEnvStartDataIndex(); + auto elem_offset = start + slot; + auto *load_lex_var = GetGraph()->CreateInstLoadArray(DataType::ANY, pc); + load_lex_var->SetInput(0U, current_lex_env); + load_lex_var->SetInput(1U, GetGraph()->FindOrCreateConstant(elem_offset)); + appender.Append(load_lex_var); + + intrinsic->ReplaceUsers(load_lex_var); + env_ref->SetInput(0U, load_lex_env); // set input here because of replacing users + return true; +} + +bool ExpandIntrinsics::ExpandLdlexenvDyn(IntrinsicInst *intrinsic) +{ + return MoveInliningDepthToImms(intrinsic); +} + } // 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 index 1430b60396e103b6222bb05a8b5df5f9d6f0e90d..74e03389fbec44fdefb7fef7f8ed6dea9f4447af 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.h +++ b/compiler/optimizer/optimizations/expand_intrinsics.h @@ -39,6 +39,12 @@ public: return OPTIONS.IsCompilerEcmaExpandIntrinsics(); } + static constexpr size_t GetLdLexVarDynLevelThreshold() + { + constexpr size_t MAX_LEVEL = 2U; + return MAX_LEVEL; + } + private: class InstAppender { public: @@ -47,7 +53,8 @@ private: DEFAULT_MOVE_SEMANTIC(InstAppender); NO_COPY_SEMANTIC(InstAppender); - void Append(std::initializer_list instruction); + void Append(Inst *inst); + void Append(std::initializer_list instructions); private: BasicBlock *block_; @@ -66,6 +73,9 @@ private: Inst *NewObjResolveCtorResult(InstAppender *appender, Inst *orig_alloc, Inst *alloc_obj, Inst *call_ctor, uint32_t pc); void BuildGuard(Inst *inst, uintptr_t target); + bool ExpandLdLexDyn(IntrinsicInst *intrinsic); + bool ExpandLdLexVarDyn(IntrinsicInst *intrinsic); + bool ExpandLdlexenvDyn(IntrinsicInst *intrinsic); ArenaVector functions_; }; diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb index 047ecec1617212843d21686c122a01b8bc1ba468..3cd5dcca33d8c17898f592cae343f3f3aa2a7e71 100644 --- a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -122,6 +122,9 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N % else size_t args_count {<%= num_inputs %>U}; % end + if (!inst->RequireState()) { + --args_count; + } % if need_newtarget if (GetGraph()->IsBytecodeOptimizer()) { --args_count; @@ -237,8 +240,10 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N inst->AddInputType(DataType::UINT16); } % end - inst->AppendInput(inst_save_state); - inst->AddInputType(DataType::NO_TYPE); + if (inst->RequireState()) { + inst->AppendInput(inst_save_state); + inst->AddInputType(DataType::NO_TYPE); + } AddInstruction(inst); % if acc_write UpdateDefinitionAcc(inst); diff --git a/isa/isa.yaml b/isa/isa.yaml index 293808d44c1ed4883efb9ac3d33d3c627c137d54..a6064ba5a1056c7feb6de766f21f2158e04e4ce6 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -156,6 +156,7 @@ groups: acc: out:top prefix: ecma format: [pref_op_none] + exceptions: [x_none] intrinsic_name: INTRINSIC_LDLEXENV_DYN - sig: ecma.poplexenvdyn @@ -946,18 +947,21 @@ groups: acc: out:top prefix: ecma format: [pref_op_imm1_4_imm2_4] + exceptions: [x_none] intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN - sig: ecma.ldlexvardyn imm1, imm2 acc: out:top prefix: ecma format: [pref_op_imm1_8_imm2_8] + exceptions: [x_none] intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN - sig: ecma.ldlexvardyn imm1, imm2 acc: out:top prefix: ecma format: [pref_op_imm1_16_imm2_16] + exceptions: [x_none] intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN - sig: ecma.ldlexdyn string_id, imm1, imm2 diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 37bd3d6010ab44dcb41b8ca17d3a44b0be714886..d29dc80534a983293a007786fd148be17e6356f7 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -373,4 +373,20 @@ void EcmaRuntimeInterface::CleanFunction(Method *method) ASSERT(ecma_vm_ != nullptr); ecma_vm_->GetGlobalObjectStorage()->Remove(func); } + +size_t EcmaRuntimeInterface::GetLexicalEnvParentEnvIndex() const +{ + return LexicalEnv::PARENT_ENV_INDEX; +} + +size_t EcmaRuntimeInterface::GetLexicalEnvStartDataIndex() const +{ + return LexicalEnv::RESERVED_ENV_LENGTH; +} + +size_t EcmaRuntimeInterface::GetTaggedArrayElementSize() const +{ + return JSTaggedValue::TaggedTypeSize(); +} + } // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 31743db43787e3968524a2c59ca59adc4481fa1c..7886a037a428677b35556d6808014792170d36eb 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -135,6 +135,12 @@ public: bool AddProfileInfo(ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot); + size_t GetLexicalEnvParentEnvIndex() const override; + + size_t GetLexicalEnvStartDataIndex() const override; + + size_t GetTaggedArrayElementSize() const override; + private: const EcmaVM *ecma_vm_ {nullptr}; panda::ecmascript::EcmaProfileContainer profile_; diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 532961830048683ad0ed0ea32e1f76c56a2d71bf..2764c9182dc40ae5c94a07f29ff2fb1a92390fac 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -811,13 +811,11 @@ intrinsics: class_name: Ecmascript.Intrinsics method_name: LdLexVarDyn static: true - exception: true signature: ret: any args: [u16, u16] impl: panda::ecmascript::intrinsics::LdLexVarDyn - set_flags: [require_tmp] - clear_flags: [no_dce] + clear_flags: [no_dce, runtime_call] codegen_func: LdLexVarDyn - name: LdLexDyn @@ -836,12 +834,11 @@ intrinsics: class_name: Ecmascript.Intrinsics method_name: ldlexenvDyn static: true - exception: true signature: ret: any args: [] impl: panda::ecmascript::intrinsics::LdlexenvDyn - clear_flags: [no_dce] + clear_flags: [no_dce, runtime_call] codegen_func: LdlexenvDyn - name: PopLexenvDyn diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index b008ca9fca8e7b40440fac12fd063ab2c06fe5f4..d2ee7e1c8fd7d8f32c5542b43a5c2a9b2d684003 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -107,6 +107,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_deoptimize.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_call_profile_clear.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/peephole_negoverflowandzerocheck.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ldlex.js SUPPORT_RELEASE true) # 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) diff --git a/tests/checked/ldlex.js b/tests/checked/ldlex.js new file mode 100644 index 0000000000000000000000000000000000000000..e6a5c28a1d26ac9f1285af88179ed6e7b09fa02f --- /dev/null +++ b/tests/checked/ldlex.js @@ -0,0 +1,91 @@ +/* + * 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. + */ + +class NumberWrapper { + constructor(n) { + this.number = n; + } +} + +function get_level0() { + return function compile_level0() { + let a = new NumberWrapper(1); + return a.number; + } +} + +function get_level1() { + let a = new NumberWrapper(1); + return function compile_level1() { + let b = new NumberWrapper(2); + return a.number + b.number; + }; +} + +function get_level2() { + let a = new NumberWrapper(1); + return function level1() { + let b = new NumberWrapper(2); + return function compile_level2() { + let c = new NumberWrapper(3); + return a.number + b.number + c.number; + }; + }; +} + +function get_level3() { + let a = new NumberWrapper(1); + return function level1() { + let b = new NumberWrapper(2); + return function level2() { + let c = new NumberWrapper(3); + return function compile_level3() { + let d = new NumberWrapper(4); + return a.number + b.number + c.number + d.number; + }; + }; + }; +} + +//! CHECKER Expand LdLexDyn/LdLexVarDyn intrinsics +//! RUN options: "--compiler-hotness-threshold=0 --no-async-jit=true --compiler-regex='_GLOBAL::compile_.*'", entry: "_GLOBAL::func_main_0" +//! METHOD "compile_level0" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.LdLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.LdLexDyn.*/ +//! INST_NOT /Intrinsic.LdLexVarDyn.*/ +//! METHOD "compile_level1" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.LdLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.LdLexDyn.*/ +//! INST_NOT /Intrinsic.LdLexVarDyn.*/ +//! METHOD "compile_level2" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.LdLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.LdLexDyn.*/ +//! INST_NOT /Intrinsic.LdLexVarDyn.*/ +//! METHOD "compile_level3" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.LdLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.LdLexDyn.*/ +//! INST /Intrinsic.LdLexVarDyn.*/ +get_level0()(); +get_level1()(); +get_level2()()(); +get_level3()()()(); diff --git a/tests/compiler/CMakeLists.txt b/tests/compiler/CMakeLists.txt index 6266712e4efab3d4e94e8345e408972ceb6ed9f3..5ff302f25dd5ccbc4a904e61713005f731650f30 100644 --- a/tests/compiler/CMakeLists.txt +++ b/tests/compiler/CMakeLists.txt @@ -23,6 +23,7 @@ set(PANDA_COMPILER_ECMA_TESTS_SOURCES unit_ecma_test.cpp branch_elimination_ecma_test.cpp checks_elimination_ecma_test.cpp + expand_intrinsics_ecma_test.cpp ir_builder_ecma_test.cpp lowering_ecma_test.cpp peepholes_ecma_test.cpp diff --git a/tests/compiler/expand_intrinsics_ecma_test.cpp b/tests/compiler/expand_intrinsics_ecma_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a2434df93dc362be3ec2285795772aa6a42c43a --- /dev/null +++ b/tests/compiler/expand_intrinsics_ecma_test.cpp @@ -0,0 +1,190 @@ +/* + * 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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/optimizations/cleanup.h" +#include "plugins/ecmascript/compiler/optimizer/optimizations/expand_intrinsics.h" + +namespace panda::compiler { +class ExpandIntrinsicsTest : public AsmTest { +public: + ExpandIntrinsicsTest() = default; +}; + +// NOLINTBEGIN(readability-magic-numbers) +TEST_F(ExpandIntrinsicsTest, LdLexVarDynApply) +{ + constexpr uint32_t SLOT = 10U; + constexpr uint32_t INLINE_DEPTH = 0U; + + for (size_t level = 0U; level <= ExpandIntrinsics::GetLdLexVarDynLevelThreshold(); ++level) { + auto graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + BASIC_BLOCK(2, -1) + { + INST(0, Opcode::SaveState).NoVregs(); + INST(1, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN) + .any() + .Inputs({{DataType::NO_TYPE, 0}}) + .AddImm(level) + .AddImm(SLOT); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(0, graph_opt->GetRuntime()->GetLexicalEnvParentEnvIndex()); + CONSTANT(1, graph_opt->GetRuntime()->GetLexicalEnvStartDataIndex() + SLOT); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Intrinsic) + .any() + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LDLEXENV_DYN) + .AddImm(INLINE_DEPTH) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(3, Opcode::CastAnyTypeValue) + .ref() + .Inputs(2) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + int last_index = 3; + for (auto cur_level = level; cur_level > 0U; --cur_level) { + INST(last_index + 1, Opcode::LoadArray).any().Inputs(last_index, 0); + INST(last_index + 2, Opcode::CastAnyTypeValue) + .ref() + .Inputs(last_index + 1) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + last_index += 2; + } + INST(last_index + 1, Opcode::LoadArray).any().Inputs(last_index, 1); + INST(last_index + 2, Opcode::Return).any().Inputs(last_index + 1); + } + } + + graph_opt->RunPass(); + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + } +} + +TEST_F(ExpandIntrinsicsTest, LdLexVarDynSkip) +{ + constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdLexVarDynLevelThreshold() + 1U; + constexpr uint32_t SLOT = 10U; + constexpr uint32_t INLINE_DEPTH = 0U; + + auto graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + BASIC_BLOCK(2, -1) + { + INST(0, Opcode::SaveState).NoVregs(); + INST(1, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN) + .any() + .Inputs({{DataType::NO_TYPE, 0}}) + .AddImm(LEVEL) + .AddImm(SLOT); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + BASIC_BLOCK(2, -1) + { + INST(0, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN) + .any() + .AddImm(LEVEL) + .AddImm(SLOT) + .AddImm(INLINE_DEPTH) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(1, Opcode::Return).any().Inputs(0); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(ExpandIntrinsicsTest, LdLexDyn) +{ + constexpr uint32_t STRING_ID = 0xABCDU; + constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdLexVarDynLevelThreshold() + 1U; + constexpr uint32_t SLOT = 10U; + constexpr uint32_t INLINE_DEPTH = 0U; + + auto graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + BASIC_BLOCK(2, -1) + { + INST(0, Opcode::SaveState).NoVregs(); + INST(1, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_DYN) + .any() + .Inputs({{DataType::NO_TYPE, 0}}) + .AddImm(STRING_ID) + .AddImm(LEVEL) + .AddImm(SLOT); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(0, DataType::Any(panda::coretypes::TaggedValue::VALUE_HOLE)); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).NoVregs(); + // We do not check LdLexVarDyn expansion in this test! + INST(2, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN) + .any() + .AddImm(LEVEL) + .AddImm(SLOT) + .AddImm(INLINE_DEPTH) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(3, Opcode::Compare).b().Inputs(2, 0).SrcType(DataType::ANY).CC(ConditionCode::CC_EQ); + INST(4, Opcode::DeoptimizeIf).Inputs(3, 1).DeoptimizeType(DeoptimizeType::HOLE); + INST(5, Opcode::Return).any().Inputs(2); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} +// NOLINTEND(readability-magic-numbers) +} // namespace panda::compiler diff --git a/tests/compiler/unit_ecma_test.cpp b/tests/compiler/unit_ecma_test.cpp index 4efae8b949a1151bcead9005cd94cbc2d572d4bf..393ff72cb0b731a1d36291a70b15400e8782ea40 100644 --- a/tests/compiler/unit_ecma_test.cpp +++ b/tests/compiler/unit_ecma_test.cpp @@ -84,6 +84,16 @@ public: { return SourceLanguage::ECMASCRIPT; } + + size_t GetLexicalEnvParentEnvIndex() const override + { + return 0U; + } + + size_t GetLexicalEnvStartDataIndex() const override + { + return 1U; + } }; RuntimeInterface *PandaRuntimeTest::GetDefaultRuntime()