From 7a634e26c9e11e3e25d5a6059ffbb73fedd111f6 Mon Sep 17 00:00:00 2001 From: "Sergey V. Ignatov" Date: Mon, 1 May 2023 18:51:32 +0300 Subject: [PATCH] [Compiler] Implemented inlinining for StLexVarDyn/StLexDyn Signed-off-by: Sergey V. Ignatov --- compiler/codegen_intrinsics_ecmascript.cpp | 122 ++++++++-- .../optimizations/expand_intrinsics.cpp | 145 ++++++++++-- .../optimizations/expand_intrinsics.h | 5 +- runtime/ecma_runtime.yaml | 2 + tests/checked/CMakeLists.txt | 1 + tests/checked/stlex.js | 107 +++++++++ .../compiler/expand_intrinsics_ecma_test.cpp | 210 +++++++++++++++++- 7 files changed, 554 insertions(+), 38 deletions(-) create mode 100644 tests/checked/stlex.js diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index 91400e9fd..07a138f68 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -46,35 +46,24 @@ void Codegen::LdlexenvDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] GetEncoder()->EncodeLdr(dst, false, MemRef(src[0], cross_values::GetJsfunctionLexicalEnvOffset(GetArch()))); } -void Codegen::LdLexVarDyn(IntrinsicInst *inst, Reg dst, SRCREGS src) +static void EncodeLoadParentLexEnv(Codegen *cg, IntrinsicInst *inst, uint32_t level_imm, Reg level_reg, Reg lex_env, + Reg lex_env_ptr) { - ASSERT(inst->HasImms() == (inst->GetInputsCount() == 1U)); - ASSERT(GetGraph()->GetMode().IsInterpreter() != inst->HasImms()); - - auto *enc = GetEncoder(); + auto *enc = cg->GetEncoder(); auto arch = enc->GetArch(); - 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()); + auto *runtime = cg->GetRuntime(); - ASSERT(GetGraph()->SupportManagedCode() == (inst->GetSaveState() == nullptr)); - enc->EncodeMov(tmp, src[0]); - - constexpr size_t LEVEL_INDEX = 0U; auto data = runtime->GetArrayDataOffset(arch); auto elem_size = runtime->GetTaggedArrayElementSize(); - if (!inst->HasImms() || inst->GetImms()[LEVEL_INDEX] > 0U) { + if (!inst->HasImms() || level_imm > 0U) { auto head = enc->CreateLabel(); auto exit = enc->CreateLabel(); ScopedTmpRegU32 counter(enc); if (inst->HasImms()) { - enc->EncodeMov(counter, Imm(inst->GetImms()[LEVEL_INDEX])); + enc->EncodeMov(counter, Imm(level_imm)); } 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->EncodeJump(exit, level_reg.As(INT32_TYPE), Condition::EQ); // fast path for level 0 + enc->EncodeMov(counter, level_reg.As(INT32_TYPE)); } enc->BindLabel(head); // get parent env @@ -83,7 +72,30 @@ void Codegen::LdLexVarDyn(IntrinsicInst *inst, Reg dst, SRCREGS src) enc->EncodeJump(head, counter, Condition::NE); enc->BindLabel(exit); } +} + +void Codegen::LdLexVarDyn(IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + ASSERT(inst->HasImms() == (inst->GetInputsCount() == 1U)); + ASSERT(GetGraph()->GetMode().IsInterpreter() != inst->HasImms()); + auto *enc = GetEncoder(); + auto arch = enc->GetArch(); + 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)); + std::size_t lex_env_index = GetGraph()->GetMode().IsInterpreter() ? 2 : 0; + enc->EncodeMov(tmp, src[lex_env_index]); + + EncodeLoadParentLexEnv(this, inst, inst->HasImms() ? inst->GetImm(0) : 0, src[0], lex_env, lex_env_ptr); + + auto data = runtime->GetArrayDataOffset(arch); + auto elem_size = runtime->GetTaggedArrayElementSize(); auto start = runtime->GetLexicalEnvStartDataIndex(); constexpr size_t SLOT_INDEX = 1U; if (!GetGraph()->GetMode().IsInterpreter()) { @@ -100,6 +112,78 @@ void Codegen::LdLexVarDyn(IntrinsicInst *inst, Reg dst, SRCREGS src) } } +void Codegen::StLexVarDyn(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src) +{ + ASSERT(inst->HasImms() == (inst->GetInputsCount() == 2U)); + ASSERT(GetGraph()->GetMode().IsInterpreter() != inst->HasImms()); + + auto *enc = GetEncoder(); + auto arch = enc->GetArch(); + 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)); + std::size_t lex_env_index = GetGraph()->GetMode().IsInterpreter() ? 3 : 1; + enc->EncodeMov(tmp, src[lex_env_index]); + + EncodeLoadParentLexEnv(this, inst, inst->HasImms() ? inst->GetImm(0) : 0, src[0], lex_env, lex_env_ptr); + + auto data = runtime->GetArrayDataOffset(arch); + auto elem_size = runtime->GetTaggedArrayElementSize(); + auto start = runtime->GetLexicalEnvStartDataIndex(); + constexpr size_t SLOT_INDEX = 1U; + std::size_t acc_index = GetGraph()->GetMode().IsInterpreter() ? 2 : 0; + if (!GetGraph()->GetMode().IsInterpreter()) { + enc->EncodeStr(src[acc_index], MemRef(lex_env_ptr, data + (start + inst->GetImms()[SLOT_INDEX]) * elem_size)); + } else { + enc->EncodeStr(src[acc_index], + MemRef(lex_env_ptr, src[SLOT_INDEX].As(INT32_TYPE), elem_size, data + start * elem_size)); + } +} + +void Codegen::StLexDyn(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src) +{ + ASSERT(inst->HasImms() == (inst->GetInputsCount() == 4U)); + ASSERT(GetGraph()->GetMode().IsInterpreter() != inst->HasImms()); + + if (GetGraph()->GetMode().IsInterpreter()) { + CreateCallIntrinsic(inst); + return; + } + + auto *enc = GetEncoder(); + auto arch = enc->GetArch(); + 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()); + + std::size_t lex_env_index = GetGraph()->GetMode().IsInterpreter() ? 4 : 1; + enc->EncodeMov(tmp, src[lex_env_index]); + + EncodeLoadParentLexEnv(this, inst, inst->HasImms() ? inst->GetImm(1) : 0, src[1], lex_env, lex_env_ptr); + + auto data = runtime->GetArrayDataOffset(arch); + auto elem_size = runtime->GetTaggedArrayElementSize(); + auto start = runtime->GetLexicalEnvStartDataIndex(); + constexpr size_t SLOT_INDEX = 2U; + auto mem_ref = MemRef(lex_env_ptr, data + (start + inst->GetImms()[SLOT_INDEX]) * elem_size); + + ScopedTmpReg tmp_reg(enc, ConvertDataType(DataType::REFERENCE, arch)); + enc->EncodeLdr(tmp_reg, false, mem_ref); + auto slow_path = CreateSlowPath(inst, DeoptimizeType::HOLE); + enc->EncodeJump(slow_path->GetLabel(), tmp_reg, Imm(panda::coretypes::TaggedValue::VALUE_HOLE), Condition::EQ); + + std::size_t acc_index = 0; + enc->EncodeStr(src[acc_index], mem_ref); +} + void Codegen::GetObjectClassTypeIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src) { ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); diff --git a/compiler/optimizer/optimizations/expand_intrinsics.cpp b/compiler/optimizer/optimizations/expand_intrinsics.cpp index 664b52ab8..e0d7f33dd 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.cpp +++ b/compiler/optimizer/optimizations/expand_intrinsics.cpp @@ -50,8 +50,12 @@ bool ExpandIntrinsics::Expand(IntrinsicInst *inst) return ExpandNewObjDynRange(inst); case RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_DYN: return ExpandLdLexDyn(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_DYN: + return ExpandStLexDyn(inst); case RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN: return ExpandLdLexVarDyn(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN: + return ExpandStLexVarDyn(inst); case RuntimeInterface::IntrinsicId::INTRINSIC_TRY_LD_GLOBAL_BY_NAME: return ExpandTryLdGlobalByName(inst); case RuntimeInterface::IntrinsicId::INTRINSIC_LDLEXENV_DYN: @@ -386,10 +390,93 @@ bool ExpandIntrinsics::ExpandLdLexDyn(IntrinsicInst *intrinsic) return true; } +Inst *ExpandIntrinsics::GetParentLexEnv(InstAppender *appender, Inst *current_lex_env, uint32_t level, uint32_t pc) +{ + auto *runtime = GetGraph()->GetRuntime(); + 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; + } + return current_lex_env; +} + +bool ExpandIntrinsics::ExpandStLexDyn(IntrinsicInst *intrinsic) +{ + ASSERT(intrinsic->GetImms().size() == 3U); + // Acc, ConstPool, LexEnv, SaveState + ASSERT(intrinsic->GetInputsCount() == 4U); + + auto level = intrinsic->GetImms()[1U]; + if (level > GetLdStLexVarDynLevelThreshold()) { + return false; + } + + auto slot = intrinsic->GetImms()[2U]; + + auto *store_acc_val = intrinsic->GetInput(0U).GetInst(); + auto *store_lex_env = intrinsic->GetInput(2U).GetInst(); + + InstAppender appender(intrinsic->GetBasicBlock(), intrinsic); + + auto pc = intrinsic->GetPc(); + auto *env_ref = GetGraph()->CreateInstCastAnyTypeValue(pc, AnyBaseType::ECMASCRIPT_ARRAY_TYPE); + env_ref->SetInput(0U, store_lex_env); + env_ref->SetFlag(inst_flags::NO_HOIST); + appender.Append(env_ref); + + Inst *current_lex_env = env_ref; + current_lex_env = GetParentLexEnv(&appender, current_lex_env, level, pc); + + auto start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex(); + auto elem_offset = start + slot; + + auto save_state = intrinsic->GetSaveState(); + ASSERT(save_state != nullptr); + + auto *get_prop = GetGraph()->CreateInstLoadArray(DataType::ANY, pc); + get_prop->SetInput(0U, current_lex_env); + get_prop->SetInput(1U, GetGraph()->FindOrCreateConstant(elem_offset)); + appender.Append(get_prop); + + 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, get_prop); + 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); + + auto *store_lex_var = GetGraph()->CreateInstStoreArray(DataType::ANY, pc); + store_lex_var->SetNeedBarrier(true); + store_lex_var->SetInput(0U, current_lex_env); + store_lex_var->SetInput(1U, GetGraph()->FindOrCreateConstant(elem_offset)); + store_lex_var->SetInput(2U, store_acc_val); + appender.Append(store_lex_var); + + if (intrinsic->HasUsers()) { + intrinsic->ReplaceUsers(store_acc_val); + } + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); + return true; +} + bool ExpandIntrinsics::ExpandLdLexVarDyn(IntrinsicInst *intrinsic) { auto level = intrinsic->GetImms()[0U]; - if (level > GetLdLexVarDynLevelThreshold()) { + if (level > GetLdStLexVarDynLevelThreshold()) { return false; } @@ -405,22 +492,10 @@ bool ExpandIntrinsics::ExpandLdLexVarDyn(IntrinsicInst *intrinsic) 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); + current_lex_env = GetParentLexEnv(&appender, current_lex_env, level, pc); - 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 start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex(); auto elem_offset = start + slot; auto *load_lex_var = GetGraph()->CreateInstLoadArray(DataType::ANY, pc); load_lex_var->SetInput(0U, current_lex_env); @@ -444,6 +519,46 @@ bool ExpandIntrinsics::ExpandLdlexenvDyn([[maybe_unused]] IntrinsicInst *intrins return true; } +bool ExpandIntrinsics::ExpandStLexVarDyn(IntrinsicInst *intrinsic) +{ + auto level = intrinsic->GetImms()[0U]; + if (level > GetLdStLexVarDynLevelThreshold()) { + return false; + } + + auto slot = intrinsic->GetImms()[1U]; + + auto *store_acc_val = intrinsic->GetInput(0U).GetInst(); + auto *store_lex_env = intrinsic->GetInput(1U).GetInst(); + + InstAppender appender(intrinsic->GetBasicBlock(), intrinsic); + + auto pc = intrinsic->GetPc(); + auto *env_ref = GetGraph()->CreateInstCastAnyTypeValue(pc, AnyBaseType::ECMASCRIPT_ARRAY_TYPE); + env_ref->SetInput(0U, store_lex_env); + env_ref->SetFlag(inst_flags::NO_HOIST); + appender.Append(env_ref); + + Inst *current_lex_env = env_ref; + current_lex_env = GetParentLexEnv(&appender, current_lex_env, level, pc); + + auto start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex(); + auto elem_offset = start + slot; + + auto *store_lex_var = GetGraph()->CreateInstStoreArray(DataType::ANY, pc); + store_lex_var->SetNeedBarrier(true); + store_lex_var->SetInput(0U, current_lex_env); + store_lex_var->SetInput(1U, GetGraph()->FindOrCreateConstant(elem_offset)); + store_lex_var->SetInput(2U, store_acc_val); + appender.Append(store_lex_var); + + if (intrinsic->HasUsers()) { + intrinsic->ReplaceUsers(store_acc_val); + } + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); + return true; +} + bool ExpandIntrinsics::ExpandTryLdGlobalByName(IntrinsicInst *inst) { if (GetGraph()->IsAotMode()) { diff --git a/compiler/optimizer/optimizations/expand_intrinsics.h b/compiler/optimizer/optimizations/expand_intrinsics.h index d1f86c64c..0d7bb3df0 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.h +++ b/compiler/optimizer/optimizations/expand_intrinsics.h @@ -39,7 +39,7 @@ public: return OPTIONS.IsCompilerEcmaExpandIntrinsics(); } - static constexpr size_t GetLdLexVarDynLevelThreshold() + static constexpr size_t GetLdStLexVarDynLevelThreshold() { constexpr size_t MAX_LEVEL = 2U; return MAX_LEVEL; @@ -73,9 +73,12 @@ private: Inst *NewObjResolveCtorResult(InstAppender *appender, Inst *orig_alloc, Inst *alloc_obj, Inst *call_ctor, uint32_t pc); void BuildGuard(Inst *inst, uintptr_t target); + Inst *GetParentLexEnv(InstAppender *appender, Inst *current_lex_env, uint32_t level, uint32_t pc); bool ExpandLdLexDyn(IntrinsicInst *intrinsic); + bool ExpandStLexDyn(IntrinsicInst *intrinsic); bool ExpandLdLexVarDyn(IntrinsicInst *intrinsic); bool ExpandLdlexenvDyn(IntrinsicInst *intrinsic); + bool ExpandStLexVarDyn(IntrinsicInst *intrinsic); bool ExpandTryLdGlobalByName(IntrinsicInst *inst); template diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 1397e4b88..9b3b1ae45 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -802,6 +802,7 @@ intrinsics: args: [u16, u16, acc, any] impl: panda::ecmascript::intrinsics::StLexVarDyn set_flags: [heap_inv] + codegen_func: StLexVarDyn - name: StLexDyn space: ecmascript @@ -814,6 +815,7 @@ intrinsics: args: [string_id, u16, u16, acc, any, any] impl: panda::ecmascript::intrinsics::StLexDyn set_flags: [heap_inv] + codegen_func: StLexDyn - name: LdLexVarDyn space: ecmascript diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index d3ce938d8..b2493ec75 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -116,6 +116,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/typeof.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/functionimmediate_check.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/string_equals.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/stlex.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/stlex.js b/tests/checked/stlex.js new file mode 100644 index 000000000..f59d99170 --- /dev/null +++ b/tests/checked/stlex.js @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function testfn() { + let a = 1; + function testfn_in() { + a = 2; + } + testfn_in(); + if (a != 2) { + throw "Wrong result: " + a; + } +} + +function testfn_l0(x) { + return function(a) { return a + x; } +} + +class NumberWrapper { + constructor(n) { + this.number = n; + } +} + +function testfn_level0(x) { + let a = new NumberWrapper(1); + return function testfn_level1(xx) { + a = new NumberWrapper(2); + let b = new NumberWrapper(1 + a.number); + return function testfn_level2(xxx) { + a = new NumberWrapper(3); + let c = new NumberWrapper(1 + a.number); + return function testfn_level3(xxxx) { + a = new NumberWrapper(4); + let d = new NumberWrapper(1 + a.number); + return function testfn_level4(xxxx) { + a = new NumberWrapper(5); + let e = new NumberWrapper(1 + a.number); + return a.number + b.number + c.number + d.number + e.number; + } + } + } + } +} + +//! CHECKER Expand StLexDyn/StLexVarDyn intrinsics +//! RUN options: "--compiler-hotness-threshold=0 --no-async-jit=true --compiler-regex='_GLOBAL::testfn.*'", entry: "_GLOBAL::func_main_0" +//! METHOD "testfn" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexVarDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_l0" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexVarDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_in" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_level0" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexVarDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_level1" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_level2" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT /Intrinsic.StLexDyn.*/ +//! INST_NOT /Intrinsic.StLexVarDyn.*/ +//! METHOD "testfn_level3" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StLexDyn.*/ +//! PASS_AFTER "ExpandIntrinsics" +//! INST /Intrinsic.StLexDyn.*/ + +testfn(); +var a = testfn_l0(3)(2); +if (a != 5) { + throw "Wrong result of capt: " + a; +} +testfn_level0(1)(2)(3)(4); diff --git a/tests/compiler/expand_intrinsics_ecma_test.cpp b/tests/compiler/expand_intrinsics_ecma_test.cpp index fa44bdd77..c85ac7ecf 100644 --- a/tests/compiler/expand_intrinsics_ecma_test.cpp +++ b/tests/compiler/expand_intrinsics_ecma_test.cpp @@ -29,7 +29,7 @@ TEST_F(ExpandIntrinsicsTest, LdLexVarDynApply) { constexpr uint32_t SLOT = 10U; - for (size_t level = 0U; level <= ExpandIntrinsics::GetLdLexVarDynLevelThreshold(); ++level) { + for (size_t level = 0U; level <= ExpandIntrinsics::GetLdStLexVarDynLevelThreshold(); ++level) { auto graph = CreateGraphDynStubWithDefaultRuntime(); GRAPH(graph) { @@ -88,7 +88,7 @@ TEST_F(ExpandIntrinsicsTest, LdLexVarDynApply) TEST_F(ExpandIntrinsicsTest, LdLexVarDynSkip) { - constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdLexVarDynLevelThreshold() + 1U; + constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdStLexVarDynLevelThreshold() + 1U; constexpr uint32_t SLOT = 10U; auto graph = CreateGraphDynStubWithDefaultRuntime(); @@ -135,7 +135,7 @@ TEST_F(ExpandIntrinsicsTest, LdLexVarDynSkip) TEST_F(ExpandIntrinsicsTest, LdLexDyn) { constexpr uint32_t STRING_ID = 0xABCDU; - constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdLexVarDynLevelThreshold() + 1U; + constexpr uint32_t LEVEL = ExpandIntrinsics::GetLdStLexVarDynLevelThreshold() + 1U; constexpr uint32_t SLOT = 10U; auto graph = CreateGraphDynStubWithDefaultRuntime(); @@ -221,5 +221,209 @@ TEST_F(ExpandIntrinsicsTest, ExpandLdlexenvDyn) GraphChecker(graph_opt).Check(); ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); } + +TEST_F(ExpandIntrinsicsTest, StLexVarDyn) +{ + constexpr uint32_t SLOT = 10U; + + size_t level = 0U; + auto graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN) + .any() + .Inputs({{DataType::INT32, 1}, {DataType::ANY, 2}}) + .AddImm(level) + .AddImm(SLOT) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + auto graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + CONSTANT(7, graph_opt->GetRuntime()->GetLexicalEnvStartDataIndex() + SLOT); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::CastAnyTypeValue) + .ref() + .Inputs(2) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(5, Opcode::StoreArray).any().Inputs(3).Inputs(7).Inputs(1).SetNeedBarrier(true); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + graph_opt->RunPass(); + GraphChecker(graph_opt).Check(); + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + + level = 1U; + graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN) + .any() + .Inputs({{DataType::INT32, 1}, {DataType::ANY, 2}}) + .AddImm(level) + .AddImm(SLOT) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + CONSTANT(7, graph_opt->GetRuntime()->GetLexicalEnvParentEnvIndex()); + CONSTANT(10, graph_opt->GetRuntime()->GetLexicalEnvStartDataIndex() + SLOT); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(5, Opcode::CastAnyTypeValue) + .ref() + .Inputs(2) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(6, Opcode::LoadArray).any().Inputs(5, 7); + INST(8, Opcode::CastAnyTypeValue) + .ref() + .Inputs(6) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(9, Opcode::StoreArray).any().Inputs(8).Inputs(10).Inputs(1).SetNeedBarrier(true); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + graph_opt->RunPass(); + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + + level = 2U; + graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN) + .any() + .Inputs({{DataType::INT32, 1}, {DataType::ANY, 2}}) + .AddImm(level) + .AddImm(SLOT) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + CONSTANT(7, graph_opt->GetRuntime()->GetLexicalEnvParentEnvIndex()); + CONSTANT(12, graph_opt->GetRuntime()->GetLexicalEnvStartDataIndex() + SLOT); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(5, Opcode::CastAnyTypeValue) + .ref() + .Inputs(2) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(6, Opcode::LoadArray).any().Inputs(5, 7); + INST(8, Opcode::CastAnyTypeValue) + .ref() + .Inputs(6) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(9, Opcode::LoadArray).any().Inputs(8, 7); + INST(10, Opcode::CastAnyTypeValue) + .ref() + .Inputs(9) + .AnyType(AnyBaseType::ECMASCRIPT_ARRAY_TYPE) + .SetFlag(inst_flags::NO_HOIST); + INST(11, Opcode::StoreArray).any().Inputs(10).Inputs(12).Inputs(1).SetNeedBarrier(true); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + graph_opt->RunPass(); + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + + level = 3U; + graph = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN) + .any() + .Inputs({{DataType::INT32, 1}, {DataType::ANY, 2}}) + .AddImm(level) + .AddImm(SLOT) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + graph_opt = CreateGraphDynStubWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::LoadLexicalEnv).any().Inputs(0); + INST(3, Opcode::Intrinsic) + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_ST_LEX_VAR_DYN) + .any() + .Inputs({{DataType::INT32, 1}, {DataType::ANY, 2}}) + .AddImm(level) + .AddImm(SLOT) + .ClearFlag(inst_flags::REQUIRE_STATE); + INST(4, Opcode::ReturnVoid).v0id(); + } + } + graph_opt->RunPass(); + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} // NOLINTEND(readability-magic-numbers) } // namespace panda::compiler -- Gitee