From 148bd000197d9daba1ed2c954ea5d93f7dde52f3 Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Tue, 28 Mar 2023 19:55:09 +0300 Subject: [PATCH 1/2] Fix inlining compare for doubles Signed-off-by: Andrey Efremov --- irtoc_scripts/interpreter_handlers.irt | 18 +++++++++++++++--- tests/checked/type_resolving.js | 19 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/irtoc_scripts/interpreter_handlers.irt b/irtoc_scripts/interpreter_handlers.irt index 261ce4a94..ef65f31d6 100644 --- a/irtoc_scripts/interpreter_handlers.irt +++ b/irtoc_scripts/interpreter_handlers.irt @@ -784,20 +784,32 @@ end # Create cpp functions for compare instructions # [:eq, :ne, :ge, :le, :gt, :lt].each do |op| + doubles_macro = :"#{op}2dyn_double" + macro(doubles_macro) do |l, r| + case op + when :eq, :ne + booltoany(Compare(l, r).CC(:"CC_#{op.upcase}").b) + when :lt, :le + booltoany(Compare(Cmp(l, r).Fcmpg().i32, 0).CC(:"CC_#{op.upcase}").b) + when :gt, :ge + booltoany(Compare(Cmp(l, r).Fcmpl().i32, 0).CC(:"CC_#{op.upcase}").b) + end + end + macro(:"#{op}2dyn_smi_smi") do |l, r| booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:"CC_#{op.upcase}").b) end macro(:"#{op}2dyn_smi_double") do |l, r| - booltoany(Compare(i32tof64(anytoi32(l.any)), anytof64(r.any)).CC(:"CC_#{op.upcase}").b) + send(doubles_macro, i32tof64(anytoi32(l.any)), anytof64(r.any)) end macro(:"#{op}2dyn_double_smi") do |l, r| - booltoany(Compare(anytof64(l.any), i32tof64(anytoi32(r.any))).CC(:"CC_#{op.upcase}").b) + send(doubles_macro, anytof64(l.any), i32tof64(anytoi32(r.any))) end macro(:"#{op}2dyn_double_double") do |l, r| - booltoany(Compare(anytof64(l.any), anytof64(r.any)).CC(:"CC_#{op.upcase}").b) + send(doubles_macro, anytof64(l.any), anytof64(r.any)) end i_i_macro = :"#{op}2dyn_smi_smi" diff --git a/tests/checked/type_resolving.js b/tests/checked/type_resolving.js index c73befbe5..676926f1e 100644 --- a/tests/checked/type_resolving.js +++ b/tests/checked/type_resolving.js @@ -90,7 +90,13 @@ //! PASS_AFTER "TypesResolving" //! insts.each do |op, intrinsic| //! INST_NOT "Intrinsic.#{intrinsic}Dyn" -//! INST "Compare #{op.upcase} #{type}" +//! if (['ge', 'le', 'gt', 'lt'].include? op) && type == 'f64' +//! new_op = 'Fcmp' + (op[0] == 'g' ? 'l' : 'g') +//! INST_NEXT new_op +//! INST_NEXT "Compare #{op.upcase} i32" +//! else +//! INST_NEXT "Compare #{op.upcase} #{type}" +//! end //! end //! end //! @@ -582,6 +588,11 @@ function test_cmp_i_f() { if (a <= b) res += 14; if (a > b) res += 15; if (a < b) res += 16; + // comparisons below are false + if (a <= NaN) res += 17; + if (a >= NaN) res += 18; + if (a < NaN) res += 19; + if (a > NaN) res += 20; return res; // 40 } @@ -608,7 +619,9 @@ function test_cmp_f_f() { if (a <= b) res += 34; if (a > b) res += 35; if (a < b) res += 36; - return res; // 102 + if (NaN == NaN) res += 37; // false + if (NaN != NaN) res += 38; // true + return res; // 140 } function test_cmp() { @@ -617,7 +630,7 @@ function test_cmp() { res += test_cmp_i_f(); res += test_cmp_f_i(); res += test_cmp_f_f(); - if (res != 226) { + if (res != 264) { throw "test_add is failed"; } } -- Gitee From bf5ebcda1380c3597b26b530f13ff97009108b3c Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Wed, 22 Mar 2023 21:06:18 +0300 Subject: [PATCH 2/2] Support Undefined type in profiling Signed-off-by: Andrey Efremov --- .../code_generator/compiler_base_types.cpp | 36 ++++++---- .../ecmascript_inst_builder_gen.cpp.erb | 12 ++-- .../compiler/ecmascript_runtime_interface.cpp | 64 +++++++++++------- .../compiler/ecmascript_runtime_interface.h | 3 +- runtime/ecma_profiling.h | 22 +++++-- runtime/interpreter/ecma-interpreter-inl.h | 4 ++ tests/checked/ecma_profiling.js | 51 ++++++++++++++- .../compiler/checks_elimination_ecma_test.cpp | 65 +++++++++++++++++++ 8 files changed, 207 insertions(+), 50 deletions(-) diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index 4820fdfda..591a8eaf9 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -23,7 +23,7 @@ namespace panda::compiler { -static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, bool is_int_possible, +static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, profiling::AnyInputType allowed_input_type, LabelHolder::LabelId not_number_label = LabelHolder::INVALID_LABEL, LabelHolder::LabelId double_with_int_label = LabelHolder::INVALID_LABEL) { @@ -35,11 +35,15 @@ static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, bool is_int_ LabelHolder::LabelId end_label = enc->CreateLabel(); auto jump = not_number_label != LabelHolder::INVALID_LABEL; - if (is_int_possible && jump) { + ASSERT(allowed_input_type == profiling::AnyInputType::DEFAULT || jump); + if ((allowed_input_type & profiling::AnyInputType::INTEGER) != 0) { enc->EncodeShr(dst_ext, src, Imm(coretypes::TaggedValue::TAG_BITS_SHIFT)); enc->EncodeJump(end_label, dst_ext, Imm(coretypes::TaggedValue::TAG_INT >> coretypes::TaggedValue::TAG_BITS_SHIFT), Condition::EQ); } + if ((allowed_input_type & profiling::AnyInputType::UNDEFINED) != 0) { + enc->EncodeJump(end_label, src, Imm(coretypes::TaggedValue::VALUE_UNDEFINED), Condition::EQ); + } // (u16) tag + 0xffff is [0xffff for Object, 0xfffe for Int, other - Double] enc->EncodeAdd(dst_ext, src, Imm(panda::coretypes::TaggedValue::TAG_MASK)); // Examine tag and shift it by one -> 0x7fff for both Object and Int @@ -101,12 +105,12 @@ static void IsHasTagMaskGen(Encoder *enc, Reg dst, Reg src, Imm val_tag, Imm val static void IsEqualToValGen(Encoder *enc, Reg dst, Reg src, Imm val, LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) { - ScopedTmpReg reg_val(enc, TypeInfo(TypeInfo::TypeId::INT64)); - enc->EncodeMov(reg_val, val); if (id == LabelHolder::INVALID_LABEL) { + ScopedTmpReg reg_val(enc, TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeMov(reg_val, val); enc->EncodeCompare(dst, src, reg_val, Condition::EQ); } else { - enc->EncodeJump(id, src, reg_val, Condition::NE); + enc->EncodeJump(id, src, val, Condition::NE); } } @@ -190,7 +194,7 @@ bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor CompareAnyTypeGenObject(enc, dst, src); return true; case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: - CompareAnyTypeGenDouble(enc, dst, src, cati->IsIntegerWasSeen()); + CompareAnyTypeGenDouble(enc, dst, src, cati->GetAllowedInputType()); return true; case AnyBaseType::ECMASCRIPT_STRING_TYPE: CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeString(codegen->GetArch())); @@ -270,18 +274,26 @@ bool ecmascript::CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVis ASSERT(dst.IsFloat()); ScopedTmpReg reg_tmp(enc, TypeInfo(TypeInfo::INT64)); auto end_label = enc->CreateLabel(); - auto double_label = enc->CreateLabel(); - if (cati->IsIntegerWasSeen()) { + auto return_label = enc->CreateLabel(); + if ((cati->GetAllowedInputType() & profiling::AnyInputType::INTEGER) != 0) { SCOPED_DISASM_STR(enc_v->GetCodegen(), "Try cast from SMI"); + auto not_int_label = enc->CreateLabel(); enc->EncodeShr(reg_tmp, src, Imm(coretypes::TaggedValue::TAG_BITS_SHIFT)); - enc->EncodeJump(double_label, reg_tmp, + enc->EncodeJump(not_int_label, reg_tmp, Imm(coretypes::TaggedValue::TAG_INT >> coretypes::TaggedValue::TAG_BITS_SHIFT), Condition::NE); enc->EncodeCast(dst.As(FLOAT64_TYPE), true, src.As(INT32_TYPE), true); enc->EncodeJump(end_label); + enc->BindLabel(not_int_label); + } + if ((cati->GetAllowedInputType() & profiling::AnyInputType::UNDEFINED) != 0) { + SCOPED_DISASM_STR(enc_v->GetCodegen(), "Try cast from Undefined"); + enc->EncodeMov(reg_tmp, + Imm(coretypes::ReinterpretDoubleToTaggedType(coretypes::TaggedValue::VALUE_NAN))); + enc->EncodeJump(return_label, src, Imm(coretypes::TaggedValue::VALUE_UNDEFINED), Condition::EQ); } - enc->BindLabel(double_label); enc->EncodeSub(reg_tmp, src, Imm(panda::coretypes::TaggedValue::DOUBLE_ENCODE_OFFSET)); + enc->BindLabel(return_label); enc->EncodeMov(dst, reg_tmp); enc->BindLabel(end_label); return true; @@ -407,7 +419,7 @@ static bool AnyTypeCheckGenCustomDeoptimization(Encoder *enc, Codegen *codegen, codegen->CreateSlowPath(check_inst, DeoptimizeType::NOT_NUMBER)->GetLabel(); auto double_with_int = codegen->CreateSlowPath(check_inst, DeoptimizeType::DOUBLE_WITH_INT)->GetLabel(); - CompareAnyTypeGenDouble(enc, tmp_reg, src, check_inst->IsIntegerWasSeen(), not_number, double_with_int); + CompareAnyTypeGenDouble(enc, tmp_reg, src, check_inst->GetAllowedInputType(), not_number, double_with_int); return true; } default: @@ -450,7 +462,7 @@ bool ecmascript::AnyTypeCheckGen(AnyTypeCheckInst *check_inst, EncodeVisitor *en CheckAnyTypeGenObject(enc, src, id); return true; case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: - CompareAnyTypeGenDouble(enc, tmp_reg, src, check_inst->IsIntegerWasSeen(), id); + CompareAnyTypeGenDouble(enc, tmp_reg, src, check_inst->GetAllowedInputType(), id); return true; case AnyBaseType::ECMASCRIPT_STRING_TYPE: CheckAnyTypeGenObjectType(codegen, enc, src, cross_values::GetJstypeString(codegen->GetArch()), id); diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb index 3cd5dcca3..21d85aaec 100644 --- a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -160,10 +160,10 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N % if has_profile && (inst.profile.properties.include?('operand_types_1') || inst.profile.properties.include?('operand_types_2')) // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) if constexpr (with_speculative) { - bool is_integer_seen = false; + profiling::AnyInputType allowed_input_type {}; bool is_type_profiled = false; - auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= input_index %>, &is_integer_seen, &is_type_profiled); - input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, is_integer_seen); + auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= input_index %>, &allowed_input_type, &is_type_profiled); + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, allowed_input_type); } % end inst->AppendInput(input); @@ -221,11 +221,11 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) if constexpr (with_speculative) { - bool is_integer_seen = false; + profiling::AnyInputType allowed_input_type {}; bool is_type_profiled = false; % idx = inst.profile.properties.include?('operand_types_2') ? 1 : 0 - auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= idx %>, &is_integer_seen, &is_type_profiled); - input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, is_integer_seen); + auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= idx %>, &allowed_input_type, &is_type_profiled); + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, allowed_input_type); } % end diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 61103c436..b48d8ecee 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -165,26 +165,41 @@ Expected EcmaRuntimeInterface::AddProfile(std::string_view f return true; } +static bool IsCastingUndefined(BytecodeInstruction::Opcode opcode) +{ + switch (opcode) { + case BytecodeInstruction::Opcode::ECMA_EQDYN_PREF_V8_PROF16: + case BytecodeInstruction::Opcode::ECMA_NOTEQDYN_PREF_V8_PROF16: + case BytecodeInstruction::Opcode::ECMA_STRICTEQDYN_PREF_V8_PROF16: + case BytecodeInstruction::Opcode::ECMA_STRICTNOTEQDYN_PREF_V8_PROF16: + return false; + default: + return true; + } +} + compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface::BytecodeProfile profile, const BytecodeInstruction *bc_inst, unsigned index, - bool *is_integer_seen, bool *is_type_profiled) + profiling::AnyInputType *allowed_input_type, + bool *is_type_profiled) { auto kind = profiling::GetProfileKind(bc_inst->GetOpcode()); auto ecma_prof = reinterpret_cast(profile); - panda::ecmascript::ProfilingTypeBits::Type type = panda::ecmascript::ProfilingTypeBits::NONE; + ProfilingTypeBits::Type type = ProfilingTypeBits::NONE; switch (kind) { case profiling::ProfilingKind::UNARY_ARITH: { - panda::ecmascript::UnaryOperationProfile p(ecma_prof); + UnaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); + if ((type & ProfilingTypeBits::OBJECT) != 0) { + return compiler::AnyBaseType::UNDEFINED_TYPE; + } break; } case profiling::ProfilingKind::BINARY_ARITH: { - panda::ecmascript::BinaryOperationProfile p(ecma_prof); + BinaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); auto other_operand_type = p.GetOperandType(1 - index).GetType(); - if ((type & panda::ecmascript::ProfilingTypeBits::OBJECT) == panda::ecmascript::ProfilingTypeBits::OBJECT && - (other_operand_type & panda::ecmascript::ProfilingTypeBits::OBJECT) == - panda::ecmascript::ProfilingTypeBits::OBJECT) { + if ((type & ProfilingTypeBits::OBJECT) != 0 && (other_operand_type & ProfilingTypeBits::OBJECT) != 0) { *is_type_profiled = true; return compiler::AnyBaseType::UNDEFINED_TYPE; } @@ -194,32 +209,33 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface LOG(FATAL, COMMON) << "Unknown profile"; } - if (type == panda::ecmascript::ProfilingTypeBits::NONE) { + if (type == ProfilingTypeBits::NONE) { return compiler::AnyBaseType::UNDEFINED_TYPE; } *is_type_profiled = true; - auto is_int = - (type & panda::ecmascript::ProfilingTypeBits::INTEGER) == panda::ecmascript::ProfilingTypeBits::INTEGER; - // ignore non number types to avoid compiled method destruction on deoptimization - if ((type & panda::ecmascript::ProfilingTypeBits::DOUBLE) == panda::ecmascript::ProfilingTypeBits::DOUBLE) { - *is_integer_seen = is_int; - return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + if (type == ProfilingTypeBits::INTEGER) { + return compiler::AnyBaseType::ECMASCRIPT_INT_TYPE; } - if (is_int) { - auto is_obj_seen = - (type & panda::ecmascript::ProfilingTypeBits::OBJECT) == panda::ecmascript::ProfilingTypeBits::OBJECT; - if (is_obj_seen) { - // return number type to avoid compiled method destruction on deoptimization - *is_integer_seen = true; - return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + if ((type & ProfilingTypeBits::NUMBER_UNDEFINED) != 0) { + uint8_t allowed_type_mask = 0; + if ((type & ProfilingTypeBits::INTEGER) != 0) { + allowed_type_mask |= profiling::AnyInputType::INTEGER; } - - return compiler::AnyBaseType::ECMASCRIPT_INT_TYPE; + if ((type & ProfilingTypeBits::UNDEFINED) != 0) { + if (IsCastingUndefined(bc_inst->GetOpcode())) { + allowed_type_mask |= profiling::AnyInputType::UNDEFINED; + } else { + // some instructions treat NaN and undefined differently + return compiler::AnyBaseType::UNDEFINED_TYPE; + } + } + *allowed_input_type = static_cast(allowed_type_mask); + return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; } - if (type == panda::ecmascript::ProfilingTypeBits::BOOLEAN) { + if (type == ProfilingTypeBits::BOOLEAN) { return compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE; } return compiler::AnyBaseType::UNDEFINED_TYPE; diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index f81fbbeb5..cf77c53ed 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -142,7 +142,8 @@ public: } compiler::AnyBaseType GetProfilingAnyType(RuntimeInterface::BytecodeProfile profile, - const BytecodeInstruction *bc_inst, unsigned index, bool *is_integer_seen, + const BytecodeInstruction *bc_inst, unsigned index, + profiling::AnyInputType *allowed_input_type, bool *is_type_profiled) override; compiler::AnyBaseType ResolveSpecialAnyTypeByConstant(coretypes::TaggedValue any_const) override; diff --git a/runtime/ecma_profiling.h b/runtime/ecma_profiling.h index 1124d174d..df314b75f 100644 --- a/runtime/ecma_profiling.h +++ b/runtime/ecma_profiling.h @@ -31,10 +31,18 @@ public: DOUBLE = (1U << 1U), BOOLEAN = (1U << 2U), STRING = (1U << 3U), - OBJECT = (1U << 4U), + UNDEFINED = (1U << 4U), + OBJECT = (1U << 5U), LAST = OBJECT, - DOUBLE_INTEGER = DOUBLE | INTEGER + DOUBLE_INTEGER = DOUBLE | INTEGER, + NUMBER_UNDEFINED = DOUBLE_INTEGER | UNDEFINED }; + + static bool WasSeen(Type profiled_type, Type type) + { + return (profiled_type & type) != 0; + } + template static void Dump(S &stream, Type type) { @@ -164,6 +172,9 @@ inline ProfilingTypeBits::Type GetTypeFromValue(JSTaggedValue value) if (value.IsBoolean()) { return ProfilingTypeBits::BOOLEAN; } + if (value.IsUndefined()) { + return ProfilingTypeBits::UNDEFINED; + } return ProfilingTypeBits::OBJECT; } @@ -282,9 +293,9 @@ protected: SET_LAST_FIELD_FINAL(OperandType); }; -class BinaryOperationProfile : public ResultProfile { +class BinaryOperationProfile : public ValueProfileBase { public: - using Base = ResultProfile; + using Base = ValueProfileBase; using Base::Base; ProfilingType GetOperandType(size_t index) const @@ -311,8 +322,7 @@ public: } protected: - using LeftOperandType = - Base::LastField::NextField; + using LeftOperandType = BitField; using RightOperandType = LeftOperandType::NextField; SET_LAST_FIELD_FINAL(RightOperandType); diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index a6f2a7b7f..3b1ed020d 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -1580,6 +1580,8 @@ public: uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); uint64_t right = GetAccAsTaggedValue().GetRawData(); + UPDATE_BINARY_ARITH_PROFILE(left, right); + INTRINSIC_CALL_SETACC(intrinsics::StrictNotEqDyn(this->GetJSThread(), left, right)); this->template MoveToNextInst(); } @@ -1593,6 +1595,8 @@ public: uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); uint64_t right = GetAccAsTaggedValue().GetRawData(); + UPDATE_BINARY_ARITH_PROFILE(left, right); + INTRINSIC_CALL_CHECK_SETACC(intrinsics::StrictEqDyn(this->GetJSThread(), left, right)); this->template MoveToNextInst(); } diff --git a/tests/checked/ecma_profiling.js b/tests/checked/ecma_profiling.js index aba4bd51e..6b2c472d7 100644 --- a/tests/checked/ecma_profiling.js +++ b/tests/checked/ecma_profiling.js @@ -14,7 +14,8 @@ */ //! CHECKER Common JIT profiling -//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_profile", entry: "_GLOBAL::func_main_0" +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_.*", entry: "_GLOBAL::func_main_0" +//! EVENT_NOT /Deoptimization.*/ //! METHOD "test_profile" //! PASS_AFTER "IrBuilder" //! INST /Intrinsic.Add.*/ @@ -22,6 +23,18 @@ //! PASS_AFTER "Codegen" //! INST_NOT /Intrinsic.Add.*/ //! INST_NOT /Intrinsic.Neg.*/ +//! METHOD "test_update" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.Or.*/ +//! PASS_AFTER "Codegen" +//! INST_NOT /Intrinsic.Or.*/ +//! INST "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i u" +//! INST_NEXT "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i u" +//! INST_NEXT "Cast" +//! INST_NEXT "Or" +//! METHOD "test_eq" +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.EqDyn.*/ function test_profile(a, b) { a = a + b; @@ -36,3 +49,39 @@ for (var i = 0; i < 20; i++) { if (res != -19) { throw "Wrong result: " + res; } + +var arr = new Array(5); +function test_update(idx, val) { + arr[idx] |= val; +} + +for (var i = 0; i < 20; i++) { + test_update(i >> 2, 1 << i); +} +var sum = 0; +for (var i = 0; i < 5; i++) { + sum += arr[i]; +} +if (sum != (1 << 20) - 1) { + throw "Wrong array sum: " + sum; +} + +function test_eq(x, y) { + return x == y; +} + +sum = 0; +for (var i = 0; i < 20; i++) { + if ((i & 3) == 3) { + sum += test_eq(undefined, undefined); + } else if (i & 2) { + sum += test_eq(NaN, NaN); + } else if (i & 1) { + sum += test_eq(NaN, undefined); + } else { + sum += test_eq(1.1, 1.1); + } +} +if (sum != 10) { + throw "Wrong sum for equals: " + sum; +} diff --git a/tests/compiler/checks_elimination_ecma_test.cpp b/tests/compiler/checks_elimination_ecma_test.cpp index 5f86e7a91..07855ce54 100644 --- a/tests/compiler/checks_elimination_ecma_test.cpp +++ b/tests/compiler/checks_elimination_ecma_test.cpp @@ -22,6 +22,52 @@ namespace panda::compiler { class CheckEliminationEcmaTest : public AsmTest { public: CheckEliminationEcmaTest() = default; + + // NOLINTBEGIN(readability-magic-numbers) + template + void TestAnyTypeCheck(AnyBaseType type1, profiling::AnyInputType input_type1, AnyBaseType type2, + profiling::AnyInputType input_type2) + { + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(type1).Inputs(0, 2).AllowedInputType(input_type1); + INST(4, Opcode::AnyTypeCheck).any().AnyType(type2).Inputs(0, 2).AllowedInputType(input_type2); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + + Graph *graph_opt; + if constexpr (IS_APPLIED) { + graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(type1).Inputs(0, 2).AllowedInputType(input_type1); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + + EXPECT_TRUE(graph->RunPass()); + EXPECT_TRUE(graph->RunPass()); + } else { + graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + EXPECT_FALSE(graph->RunPass()); + } + GraphChecker(graph).Check(); + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); + } + // NOLINTEND(readability-magic-numbers) }; // NOLINTBEGIN(readability-magic-numbers) @@ -380,6 +426,25 @@ TEST_F(CheckEliminationEcmaTest, NotEliminateAnyTypeCheckIntWasSeen) EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); } +TEST_F(CheckEliminationEcmaTest, TestAnyTypeCheckInputTypes) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INT_UNDEFINED); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::UNDEFINED); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::UNDEFINED, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INT_UNDEFINED); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::UNDEFINED, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INTEGER); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INTEGER, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::DEFAULT); + + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INT_UNDEFINED, + AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::INTEGER); +} + TEST_F(CheckEliminationEcmaTest, NotEliminateAnyTypeCheck) { auto graph = CreateGraphDynWithDefaultRuntime(); -- Gitee