From bffa233d37e98db60a064ab9410fa7d107bc1ded Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Thu, 20 Apr 2023 15:44:12 +0300 Subject: [PATCH] Move irtoc StringEquals intrinsic to core part and optimize stricteq Signed-off-by: Andrey Efremov --- compiler/codegen_intrinsics_ecmascript.cpp | 22 + compiler/intrinsics_inline_ecmascript.inl | 12 +- .../intrinsics_type_resolving_ecmascript.cpp | 52 +++ ...intrinsics_type_resolving_ecmascript.inl.h | 3 + compiler/optimizer/ir/dyn_datatype.h | 5 + ecmastdlib/ecmastdlib.pa | 4 +- irtoc_scripts/common.irt | 2 + irtoc_scripts/interpreter_handlers.irt | 416 +++++++++--------- irtoc_scripts/interpreter_main_loop.irt | 6 +- irtoc_scripts/object.irt | 6 +- .../compiler/ecmascript_runtime_interface.cpp | 6 + runtime/ecma_entrypoints.yaml | 45 ++ runtime/ecma_runtime.yaml | 19 + tests/checked/CMakeLists.txt | 1 + tests/checked/string_equals.js | 76 ++++ tests/checked/type_resolving.js | 88 +++- 16 files changed, 550 insertions(+), 213 deletions(-) create mode 100644 tests/checked/string_equals.js diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index 30534d737..91400e9fd 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -195,4 +195,26 @@ void Codegen::CreateResolveAllocResult([[maybe_unused]] IntrinsicInst *inst, Reg CallFastPath(inst, EntrypointId::RESOLVE_CTOR_RESULT, dst, {}, src[0], src[1U], src[2U]); } +void Codegen::CreateEcmaStringEquals([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + auto entrypoint_id = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::ECMA_STRING_EQUALS_COMPRESSED + : EntrypointId::ECMA_STRING_EQUALS; + CallFastPath(inst, entrypoint_id, dst, {}, src[0], src[1U]); +} + +void Codegen::CreateFastPathStrictEq([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + auto entrypoint_id = EntrypointId::FAST_PATH_ECMA_STRICT_EQ; + CallFastPath(inst, entrypoint_id, dst, {}, src[0], src[1U]); + GetEncoder()->EncodeOr(dst, dst, Imm(panda::coretypes::TaggedValue::VALUE_FALSE)); +} + +void Codegen::CreateFastPathStrictNotEq([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + auto entrypoint_id = EntrypointId::FAST_PATH_ECMA_STRICT_EQ; + CallFastPath(inst, entrypoint_id, dst, {}, src[0], src[1U]); + GetEncoder()->EncodeXor(dst, dst, Imm(1)); + GetEncoder()->EncodeOr(dst, dst, Imm(panda::coretypes::TaggedValue::VALUE_FALSE)); +} + } // namespace panda::compiler diff --git a/compiler/intrinsics_inline_ecmascript.inl b/compiler/intrinsics_inline_ecmascript.inl index 89930bcbc..e1df28117 100644 --- a/compiler/intrinsics_inline_ecmascript.inl +++ b/compiler/intrinsics_inline_ecmascript.inl @@ -31,19 +31,23 @@ case RuntimeInterface::IntrinsicId::INTRINSIC_SUB2_DYN: { } case RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN: { ASSERT(types_.size() == 2); - return panda::compiler::IrtocInlineCompareEqDyn(intrinsic, types_[0], types_[1]); + return InlineCompareWithNull(intrinsic, types_[0], types_[1]) || + panda::compiler::IrtocInlineCompareEqDyn(intrinsic, types_[0], types_[1]); } case RuntimeInterface::IntrinsicId::INTRINSIC_NOT_EQ_DYN: { ASSERT(types_.size() == 2); - return panda::compiler::IrtocInlineCompareNeDyn(intrinsic, types_[0], types_[1]); + return InlineCompareWithNull(intrinsic, types_[0], types_[1]) || + panda::compiler::IrtocInlineCompareNeDyn(intrinsic, types_[0], types_[1]); } case RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_EQ_DYN: { ASSERT(types_.size() == 2); - return panda::compiler::IrtocInlineCompareStrictEqDyn(intrinsic, types_[0], types_[1]); + return InlineStrictCompareDifferentTypes(intrinsic, types_[0], types_[1]) || + panda::compiler::IrtocInlineCompareStrictEqDyn(intrinsic, types_[0], types_[1]); } case RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_NOT_EQ_DYN: { ASSERT(types_.size() == 2); - return panda::compiler::IrtocInlineCompareStrictNeDyn(intrinsic, types_[0], types_[1]); + return InlineStrictCompareDifferentTypes(intrinsic, types_[0], types_[1]) || + panda::compiler::IrtocInlineCompareStrictNotEqDyn(intrinsic, types_[0], types_[1]); } case RuntimeInterface::IntrinsicId::INTRINSIC_LESS_DYN: { ASSERT(types_.size() == 2); diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index 65b63c980..2fe30f781 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -737,4 +737,56 @@ bool TypesResolving::InlineResolveAllocResult(IntrinsicInst *intrinsic) return true; } +bool TypesResolving::InlineStrictCompareDifferentTypes(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_EQ_DYN || + intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_NOT_EQ_DYN); + if ((type1 == AnyBaseType::ECMASCRIPT_INT_TYPE || type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) && + (type2 == AnyBaseType::ECMASCRIPT_INT_TYPE || type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)) { + return false; + } + + auto input1 = intrinsic->GetInput(0).GetInst(); + auto input2 = intrinsic->GetInput(0).GetInst(); + auto allowed_type1 = input1->GetOpcode() == Opcode::AnyTypeCheck + ? input1->CastToAnyTypeCheck()->GetAllowedInputType() + : profiling::AnyInputType::DEFAULT; + auto allowed_type2 = input2->GetOpcode() == Opcode::AnyTypeCheck + ? input1->CastToAnyTypeCheck()->GetAllowedInputType() + : profiling::AnyInputType::DEFAULT; + + if (IsAnyTypeCanBeSubtypeOf(SourceLanguage::ECMASCRIPT, type1, type2, allowed_type1, allowed_type2) == false) { + auto result = intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_EQ_DYN ? 0U : 1U; + return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, result); + } + return false; +} + +bool TypesResolving::InlineCompareWithNull(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN || + intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_NOT_EQ_DYN); + if (type1 == AnyBaseType::UNDEFINED_TYPE || type2 == AnyBaseType::UNDEFINED_TYPE) { + return false; + } + bool first_null = (type1 == AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE || type1 == AnyBaseType::ECMASCRIPT_NULL_TYPE); + bool second_null = (type2 == AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE || type2 == AnyBaseType::ECMASCRIPT_NULL_TYPE); + auto eq_result = intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN ? 1U : 0U; + if (first_null && second_null) { + return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, eq_result); + } + Inst *other_input; + if (first_null) { + other_input = intrinsic->GetInput(1).GetInst(); + } else if (second_null) { + other_input = intrinsic->GetInput(0).GetInst(); + } else { + return false; + } + if (other_input->GetOpcode() == Opcode::AnyTypeCheck && other_input->CastToAnyTypeCheck()->IsSpecialWasSeen()) { + return false; + } + return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 1 - eq_result); +} + } // namespace panda::compiler diff --git a/compiler/intrinsics_type_resolving_ecmascript.inl.h b/compiler/intrinsics_type_resolving_ecmascript.inl.h index 4ba537c9c..a6095fc85 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.inl.h +++ b/compiler/intrinsics_type_resolving_ecmascript.inl.h @@ -55,4 +55,7 @@ void CreateCompareClass(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::Class Inst *CreateCompareClassWithDeopt(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, Inst *save_state); +bool InlineStrictCompareDifferentTypes(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2); +bool InlineCompareWithNull(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2); + #endif // PLUGINS_ECMASCRIPT_COMPILER_INTRINSICS_TYPE_RESOLVING_ECMASCRIPT_INL_H \ No newline at end of file diff --git a/compiler/optimizer/ir/dyn_datatype.h b/compiler/optimizer/ir/dyn_datatype.h index 7bcda1d4c..b22580d38 100644 --- a/compiler/optimizer/ir/dyn_datatype.h +++ b/compiler/optimizer/ir/dyn_datatype.h @@ -187,9 +187,14 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa break; } break; + case panda::compiler::AnyBaseType::UNDEFINED_TYPE: + return true; default: break; } + if (type == panda::compiler::AnyBaseType::UNDEFINED_TYPE) { + return std::nullopt; + } if (super_type == panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE || super_type == panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE || type == panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE || diff --git a/ecmastdlib/ecmastdlib.pa b/ecmastdlib/ecmastdlib.pa index 4b1fd16b4..56761698e 100644 --- a/ecmastdlib/ecmastdlib.pa +++ b/ecmastdlib/ecmastdlib.pa @@ -210,4 +210,6 @@ .function any Ecmascript.Intrinsics.allocDynObject(any a0, any a1) .function void Ecmascript.Intrinsics.throwDerivedCtorTypeError() -.function any Ecmascript.Intrinsics.resolveAllocResult(any a0, any a1, any a2) \ No newline at end of file +.function any Ecmascript.Intrinsics.resolveAllocResult(any a0, any a1, any a2) + +.function u1 Ecmascript.Intrinsics.StringEqualsIntrinsic(any a0, any a1) diff --git a/irtoc_scripts/common.irt b/irtoc_scripts/common.irt index 9d5357ee5..9d74887ab 100644 --- a/irtoc_scripts/common.irt +++ b/irtoc_scripts/common.irt @@ -170,6 +170,8 @@ module Constants THROW_DERIVED_CTOR_TYPE_ERROR = "cross_values::GetManagedThreadEntrypointOffset(GetArch(), EntrypointId::THROW_DERIVED_CTOR_TYPE_ERROR_SLOW_PATH)" ALLOC_DYN_OBJECT_SLOW_PATH = "cross_values::GetManagedThreadEntrypointOffset(GetArch(), EntrypointId::ALLOC_DYN_OBJECT_SLOW_PATH)" + STRING_EQUALS = "cross_values::GetManagedThreadEntrypointOffset(GetArch(), GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::ECMA_STRING_EQUALS_COMPRESSED : EntrypointId::ECMA_STRING_EQUALS)" + $VERBOSE = verbose end diff --git a/irtoc_scripts/interpreter_handlers.irt b/irtoc_scripts/interpreter_handlers.irt index a28633671..f2aafae6f 100644 --- a/irtoc_scripts/interpreter_handlers.irt +++ b/irtoc_scripts/interpreter_handlers.irt @@ -640,10 +640,91 @@ function(:"EcmaIstrue", params: {a: 'any'}, mode: [:Interpreter, :DynamicMethod, end #################################################################### -# Create macros for binary arithmetic +# COMPARE # -[:add, :sub, :and, :or, :xor, :mul, :div, :shl, :shr].each do |op| - macro(:"handle_ecma_#{op}2dyn") do |l, r| +macro(:"eq2dyn_string") do |l, r| + booltoany(Intrinsic(:ECMA_STRING_EQUALS, l, r).b).any +end + +macro(:"ne2dyn_string") do |l, r| + booltoany(Xor(Intrinsic(:ECMA_STRING_EQUALS, l, r).b, 1).b).any +end + +[[:eq, "Eq"], [:ne, "NotEq"], [:ge, "GreaterEq"], [:le, "LessEq"], [:gt, "Greater"], [:lt, "Less"]].each do |op, op_name| + 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).SrcType("DataType::FLOAT64").Fcmpg().i32, 0).CC(:"CC_#{op.upcase}").b) + when :gt, :ge + booltoany(Compare(Cmp(l, r).SrcType("DataType::FLOAT64").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| + send(doubles_macro, i32tof64(anytoi32(l.any)), anytof64(r.any)) + end + + macro(:"#{op}2dyn_double_smi") do |l, r| + send(doubles_macro, anytof64(l.any), i32tof64(anytoi32(r.any))) + end + + macro(:"#{op}2dyn_double_double") do |l, r| + send(doubles_macro, anytof64(l.any), anytof64(r.any)) + end + + i_i_macro = :"#{op}2dyn_smi_smi" + i_f_macro = :"#{op}2dyn_smi_double" + f_i_macro = :"#{op}2dyn_double_smi" + f_f_macro = :"#{op}2dyn_double_double" + + cpp_function(:"IrtocInlineCompare#{op.capitalize}Dyn") do + params type1: 'any', type2: 'any' + return_type 'bool' + variant(i_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(i_i_macro, type1, type2)) } + } + variant(i_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(i_f_macro, type1, type2)) } + } + variant(f_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(f_i_macro, type1, type2)) } + } + variant(f_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(f_f_macro, type1, type2)) } + } + if op == :eq || op == :ne + str_macro = :"#{op}2dyn_string" + variant(str_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_STRING_TYPE && type2 == AnyBaseType::ECMASCRIPT_STRING_TYPE" + code { Return(send(str_macro, type1, type2)) } + } + end + end +end + +#################################################################### +# Create macros for binary arithmetic and compare +# +[:add, :sub, :and, :or, :xor, :mul, :div, :shl, :shr, [:ge, "GreaterEq"], [:le, "LessEq"], [:gt, "Greater"], [:lt, "Less"]].each do |op| + if !op.is_a? Symbol + op, op_name = op + op_name2 = op_name + "2" + else + op_name = op.capitalize.to_s + "2" + op_name2 = op.capitalize.to_s + "2" + end + macro(:"handle_ecma_#{op_name.downcase}dyn") do |l, r| IfImm(cmpanyi32(l.any).b).Imm(0).NE.b { IfImm(cmpanyi32(r.any).b).Imm(0).NE.b { int_res := send(:"#{op}2dyn_smi_smi", l, r) @@ -670,18 +751,18 @@ end Label(:SlowPath) if self.mode.include? :FastPath - Intrinsic(:SLOW_PATH_ENTRY, l, r).Method("#{op.capitalize}2DynBridge", :AddImm).Relocate.Terminator.ptr + Intrinsic(:SLOW_PATH_ENTRY, l, r).Method("#{op_name}DynBridge", :AddImm).Relocate.Terminator.ptr Label(:Exit) Phi(int_res, fp1_res, fp2_res, fp3_res).any else - slow_res := ecma_intrinsic_invoke("#{op.capitalize}2DynSlow", l, r).any + slow_res := ecma_intrinsic_invoke("#{op_name}DynSlow", l, r).any Label(:Exit) Phi(int_res, fp1_res, fp2_res, fp3_res, slow_res).any end end - function(:"Ecma#{op.capitalize}2dyn", params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(send(:"handle_ecma_#{op}2dyn", a, b)) + function(:"Ecma#{op_name.capitalize}dyn", params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do + Return(send(:"handle_ecma_#{op_name.downcase}dyn", a, b)) end function(:"FastPathEcma#{op.capitalize}", @@ -694,7 +775,7 @@ end Intrinsic(:UNREACHABLE).Terminator.void next end - Return(send(:"handle_ecma_#{op}2dyn", a, b)).any + Return(send(:"handle_ecma_#{op_name.downcase}dyn", a, b)).any end end @@ -788,74 +869,27 @@ 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| - send(doubles_macro, i32tof64(anytoi32(l.any)), anytof64(r.any)) - end - - macro(:"#{op}2dyn_double_smi") do |l, r| - send(doubles_macro, anytof64(l.any), i32tof64(anytoi32(r.any))) - end - - macro(:"#{op}2dyn_double_double") do |l, r| - send(doubles_macro, anytof64(l.any), anytof64(r.any)) - end - - i_i_macro = :"#{op}2dyn_smi_smi" - i_f_macro = :"#{op}2dyn_smi_double" - f_i_macro = :"#{op}2dyn_double_smi" - f_f_macro = :"#{op}2dyn_double_double" - - cpp_function(:"IrtocInlineCompare#{op.capitalize}Dyn") do - params type1: 'any', type2: 'any' - return_type 'bool' - variant(i_i_macro) { - condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" - code { Return(send(i_i_macro, type1, type2)) } - } - variant(i_f_macro) { - condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" - code { Return(send(i_f_macro, type1, type2)) } - } - variant(f_i_macro) { - condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" - code { Return(send(f_i_macro, type1, type2)) } - } - variant(f_f_macro) { - condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" - code { Return(send(f_f_macro, type1, type2)) } - } - end -end -[:eq, :ne].each do |op| +[[:eq, "Eq"], [:ne, "NotEq"]].each do |op, op_name| i_i_macro = :"#{op}2dyn_smi_smi" i_f_macro = :"#{op}2dyn_smi_double" f_i_macro = :"#{op}2dyn_double_smi" f_f_macro = :"#{op}2dyn_double_double" + str_macro = :"#{op}2dyn_string" i_i_macro_strict = :"#{op}2dyn_smi_smi_strict" i_f_macro_strict = :"#{op}2dyn_smi_double_strict" f_i_macro_strict = :"#{op}2dyn_double_smi_strict" f_f_macro_strict = :"#{op}2dyn_double_double_strict" + str_macro_strict = :"#{op}2dyn_string_strict" - cpp_function(:"IrtocInlineCompareStrict#{op.capitalize}Dyn") do + if op == :eq + eq_result, ne_result = Constants::TAGGED_TRUE, Constants::TAGGED_FALSE + else + eq_result, ne_result = Constants::TAGGED_FALSE, Constants::TAGGED_TRUE + end + + cpp_function(:"IrtocInlineCompareStrict#{op_name}Dyn") do params type1: 'any', type2: 'any' return_type 'bool' variant(i_i_macro_strict) { @@ -874,170 +908,148 @@ end condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" code { Return(send(f_f_macro, type1, type2)) } } + variant(str_macro_strict) { + condition "type1 == AnyBaseType::ECMASCRIPT_STRING_TYPE" + code { Return(send(str_macro, type1, type2)) } + } end -end -# overlaps with slowpath -macro(:handle_ecma_toboolean) do |v| - booltoany(any_extractBoolean(v)) -end - -function(:EcmaToboolean, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_toboolean(a)) -end - -# overlaps with slowpath -macro(:handle_ecma_lessdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("LessDynSlow", l.u64, r.u64) - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_LT).b).any - } - Phi(slow_res, fp1_res).any -end - -function(:EcmaLessdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_lessdyn(a, b)) -end - -# overlaps with slowpath -macro(:handle_ecma_eqdyn) do |l, r| - If(l, r).CC(:CC_EQ).b { - IfImm(cmpanyf64(l)).Imm(0).CC(:CC_NE).b { - fp_eq_res := booltoany(Compare(anytof64(l), anytof64(l)).CC(:CC_EQ).b) - } Else { - nofp_eq_res := Constants::TAGGED_TRUE; + # overlaps with slowpath + macro(:"handle_ecma_strict#{op_name.downcase}dyn") do |l, r| + If(l, r).CC(:CC_EQ).b { + IfImm(cmpanyf64(l)).Imm(0).CC(:CC_NE).b { + fp_eq_res := booltoany(Compare(anytof64(l), anytof64(l)).CC(:"CC_#{op.upcase}").b) + Goto(:Exit) + } + nofp_eq_res := eq_result + Goto(:Exit) } - eq_res := Phi(fp_eq_res, nofp_eq_res).any - Goto(:Exit) - } - - IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { - int_neq_res := Constants::TAGGED_FALSE; - Goto(:Exit) - } - IfImm(Or(cmpanyundefined(r), cmpanynull(r)).b).Imm(0).CC(:CC_NE).b { - IfImm(cmpanyheapobj(l)).Imm(0).CC(:CC_NE).b { - spec_obj_neq_res := Constants::TAGGED_FALSE + IfImm(cmpanyi32(l).b).Imm(0).NE.b { + IfImm(cmpanyf64(r).b).Imm(0).NE.b { + fp1_res := send(:"#{op}2dyn_smi_double", l, r) + Goto(:Exit) + } + int_res := ne_result Goto(:Exit) } - IfImm(Or(cmpanyundefined(l), cmpanynull(l)).b).Imm(0).CC(:CC_NE).b { - spec_spec_neq_res := Constants::TAGGED_TRUE + IfImm(cmpanyf64(l).b).Imm(0).NE.b { + IfImm(cmpanyi32(r).b).Imm(0).NE.b { + fp2_res := send(:"#{op}2dyn_double_smi", l, r) + Goto(:Exit) + } + IfImm(cmpanyf64(r).b).Imm(0).NE.b { + fp3_res := send(:"#{op}2dyn_double_double", l, r) + Goto(:Exit) + } + fp4_res := ne_result Goto(:Exit) } - } - slow_res := ecma_intrinsic_invoke("EqDynSlow", l, r).any -Label(:Exit) - Phi(eq_res, int_neq_res, spec_obj_neq_res, spec_spec_neq_res, slow_res).any -end - -function(:EcmaEqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_eqdyn(a, b)) -end - -# overlaps with slowpath -macro(:handle_ecma_stricteqdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("StrictEqDynSlow", l.u64, r.u64).any - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_EQ).b).any - } - Phi(slow_res, fp1_res).any -end - -function(:EcmaStricteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_stricteqdyn(a, b)) -end - -# overlaps with slowpath -macro(:handle_ecma_strictnoteqdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("StrictNotEqDynSlow", l.u64, r.u64).any - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_NE).b).any - } - Phi(slow_res, fp1_res).any -end + IfImm(cmpanystring(l).b).Imm(0).CC(:CC_NE).b { + IfImm(cmpanystring(r).b).Imm(0).CC(:CC_NE).b { + if self.mode.include? :FastPath + if op == :eq && Options.arch != :arm32 + Intrinsic(:TAIL_CALL).AddImm(Constants::STRING_EQUALS).Terminator.void + else + Intrinsic(:SLOW_PATH_ENTRY, l, r).Method("Strict#{op_name}DynBridge", :AddImm).Relocate.Terminator.ptr + end + else + string_eq_res := send(:"#{op}2dyn_string", l, r) + Goto(:Exit) + end + } + string_neq_res := ne_result + Goto(:Exit) + } -function(:EcmaStrictnoteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_strictnoteqdyn(a, b)) -end + IfImm(cmpanybigint(l).b).Imm(0).CC(:CC_NE).b { + if self.mode.include? :FastPath + Intrinsic(:SLOW_PATH_ENTRY, l, r).Method("Strict#{op_name}DynBridge", :AddImm).Relocate.Terminator.ptr + else + bigint_res := ecma_intrinsic_invoke("Strict#{op_name}DynSlow", l, r).any + Goto(:Exit) + end + } -# overlaps with slowpath -macro(:handle_ecma_lesseqdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("LessEqDynSlow", l.u64, r.u64).any - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_LE).b).any - } - res := Phi(slow_res, fp1_res).any -end + neq_res := ne_result + Goto(:Exit) -function(:EcmaLesseqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_lesseqdyn(a, b)) -end + Label(:Exit) + Phi(*load_phi_inputs(fp_eq_res, nofp_eq_res, fp1_res, int_res, fp2_res, fp3_res, fp4_res, :string_eq_res, string_neq_res, :bigint_res, neq_res)).any + end -# overlaps with slowpath -macro(:handle_ecma_greaterdyn) do |l, r| - IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { - int_res := booltoany(Compare(anytoi32(l), anytoi32(r)).CC(:CC_GT).b) - Goto(:Exit) - } + function(:"EcmaStrict#{op_name.downcase}dyn", params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do + Return(send(:"handle_ecma_strict#{op_name.downcase}dyn", a, b)).any + end - IfImm(And(cmpanyi32(l), cmpanyf64(r)).b).Imm(0).CC(:CC_NE).b { - fp1_res := booltoany(Compare(i32tof64(anytoi32(l)), anytof64(r)).CC(:CC_GT).b) - Goto(:Exit) - } + function(:"FastPathEcmaStrict#{op_name}", + params: {a: 'any', b: 'any'}, + regmap: $full_regmap, + regalloc_set: RegMask.new($full_regmap, :arg0, :arg1) + $panda_callees_mask - $panda_regmap, + mode: [:FastPath, :DynamicMethod, :DynamicStub]) do + # Arm32 is not supported + if Options.arch == :arm32 + Intrinsic(:UNREACHABLE).Terminator.void + next + end + Return(send(:"handle_ecma_strict#{op_name.downcase}dyn", a, b)).any + end - IfImm(And(cmpanyf64(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { - fp2_res := booltoany(Compare(anytof64(l), i32tof64(anytoi32(r))).CC(:CC_GT).b) - Goto(:Exit) - } + # overlaps with slowpath + macro(:"handle_ecma_#{op_name.downcase}dyn") do |l, r| + If(l, r).CC(:CC_EQ).b { + IfImm(cmpanyf64(l)).Imm(0).CC(:CC_NE).b { + fp_eq_res := booltoany(Compare(anytof64(l), anytof64(l)).CC(:"CC_#{op.upcase}").b) + } Else { + nofp_eq_res := eq_result + } + eq_res := Phi(fp_eq_res, nofp_eq_res).any + Goto(:Exit) + } - IfImm(And(cmpanyf64(l), cmpanyf64(r)).b).Imm(0).CC(:CC_NE).b { - fp3_res := booltoany(Compare(anytof64(l), anytof64(r)).CC(:CC_GT).b) - Goto(:Exit) - } + IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { + int_neq_res := ne_result + Goto(:Exit) + } - slow_res := ecma_intrinsic_invoke("GreaterDynSlow", l, r).any + IfImm(Or(cmpanyundefined(r), cmpanynull(r)).b).Imm(0).CC(:CC_NE).b { + IfImm(cmpanyheapobj(l)).Imm(0).CC(:CC_NE).b { + spec_obj_neq_res := ne_result + Goto(:Exit) + } -Label(:Exit) - Phi(int_res, fp1_res, fp2_res, fp3_res, slow_res).any -end + IfImm(Or(cmpanyundefined(l), cmpanynull(l)).b).Imm(0).CC(:CC_NE).b { + spec_spec_neq_res := eq_result + Goto(:Exit) + } + } -function(:EcmaGreaterdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_greaterdyn(a, b)) -end + if Options.arch != :arm32 + IfImm(And(cmpanystring(l), cmpanystring(r)).b).Imm(0).CC(:CC_NE).b { + string_eq_res := send(:"#{op}2dyn_string", l, r) + Goto(:Exit) + } + end -# overlaps with slowpath -macro(:handle_ecma_greatereqdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("GreaterEqDynSlow", l.u64, r.u64).any - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_GE).b).any - } - Phi(slow_res, fp1_res).any -end + slow_res := ecma_intrinsic_invoke("#{op_name}DynSlow", l, r).any + Label(:Exit) + Phi(*load_phi_inputs(eq_res, int_neq_res, spec_obj_neq_res, spec_spec_neq_res, :string_eq_res, slow_res)).any + end -function(:EcmaGreatereqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_greatereqdyn(a, b)) + function(:"Ecma#{op_name.capitalize}dyn", params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do + Return(send(:"handle_ecma_#{op_name.downcase}dyn", a, b)) + end end # overlaps with slowpath -macro(:handle_ecma_noteqdyn) do |l, r| - IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { - slow_res := ecma_intrinsic_invoke("NotEqDynSlow", l.u64, r.u64) - } Else { # likely i32,i32 - fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_NE).b) - } - Phi(slow_res, fp1_res).any +macro(:handle_ecma_toboolean) do |v| + booltoany(any_extractBoolean(v)) end -function(:EcmaNoteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do - Return(handle_ecma_noteqdyn(a, b)) +function(:EcmaToboolean, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], enable_builder: true) do + Return(handle_ecma_toboolean(a)) end macro(:handle_ecma_ldlexenvdyn) do || diff --git a/irtoc_scripts/interpreter_main_loop.irt b/irtoc_scripts/interpreter_main_loop.irt index 9e50ebb88..1f0a87157 100644 --- a/irtoc_scripts/interpreter_main_loop.irt +++ b/irtoc_scripts/interpreter_main_loop.irt @@ -50,14 +50,14 @@ when "ECMA_ASHR2DYN_PREF_V8" ecma_intrinsic_setacc("Ashr2Dyn", vreg_value(op[0]).any, acc.any) when "ECMA_STRICTEQDYN_PREF_V8" - acc := handle_ecma_stricteqdyn(vreg_value(op[0]).any, acc) + acc := handle_ecma_stricteqdyn(vreg_value(op[0]).any, acc.any) when "ECMA_STRICTNOTEQDYN_PREF_V8" - acc := handle_ecma_strictnoteqdyn(vreg_value(op[0]).any, acc) + acc := handle_ecma_strictnoteqdyn(vreg_value(op[0]).any, acc.any) when "ECMA_EQDYN_PREF_V8" acc := handle_ecma_eqdyn(vreg_value(op[0]).any, acc.any) #ecma_intrinsic_setacc("EqDyn", vreg_value(op[0]).u64, acc.u64) when "ECMA_NOTEQDYN_PREF_V8" - acc := handle_ecma_noteqdyn(vreg_value(op[0]).any, acc) + acc := handle_ecma_noteqdyn(vreg_value(op[0]).any, acc.any) #ecma_intrinsic_setacc("NotEqDyn", vreg_value(op[0]).u64, acc.u64) when "ECMA_LESSDYN_PREF_V8" acc := handle_ecma_lessdyn(vreg_value(op[0]).any, acc) diff --git a/irtoc_scripts/object.irt b/irtoc_scripts/object.irt index 0735e197c..aeb3ca2a7 100644 --- a/irtoc_scripts/object.irt +++ b/irtoc_scripts/object.irt @@ -13,6 +13,7 @@ include_relative 'common.irt' include_relative '../../../irtoc/scripts/common.irt' +include_relative '../../../irtoc/scripts/strings.irt' include_relative 'string_helpers.irt' include_relative 'object_helpers.irt' @@ -661,4 +662,7 @@ end end, access_props)).any } -end \ No newline at end of file +end + +GenerateStringEquals(lang='Ecmascript', dynamic=true, compressed=true) +GenerateStringEquals(lang='Ecmascript', dynamic=true, compressed=false) \ No newline at end of file diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index d1860b45b..d544dbaf2 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -225,6 +225,9 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface case profiling::ProfilingKind::UNARY_ARITH: { UnaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); + if (type == ProfilingTypeBits::STRING) { + return compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE; + } if ((type & ProfilingTypeBits::HEAP_OBJECT) != 0) { return compiler::AnyBaseType::UNDEFINED_TYPE; } @@ -234,6 +237,9 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface BinaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); auto other_operand_type = p.GetOperandType(1 - index).GetType(); + if (type == ProfilingTypeBits::STRING && other_operand_type == ProfilingTypeBits::STRING) { + return compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE; + } auto object_count = ((type & ProfilingTypeBits::HEAP_OBJECT) != 0 ? 1 : 0) + ((other_operand_type & ProfilingTypeBits::HEAP_OBJECT) != 0 ? 1 : 0); // For opcodes like eq non-numeric types are common, do not inline them if object was seen as one of inputs diff --git a/runtime/ecma_entrypoints.yaml b/runtime/ecma_entrypoints.yaml index b7ec883db..a20e0017c 100644 --- a/runtime/ecma_entrypoints.yaml +++ b/runtime/ecma_entrypoints.yaml @@ -136,3 +136,48 @@ - panda::coretypes::String* - uint64_t - panda::FileEntityId + +- name: EcmaStringEquals + entrypoint: EcmascriptStringEquals + bridge: none + properties: [irtoc] + signature: + - bool + - void* + - void* + +- name: EcmaStringEqualsCompressed + entrypoint: EcmascriptStringEqualsCompressed + bridge: none + properties: [irtoc] + signature: + - bool + - void* + - void* + +- name: EcmascriptStringEqualsUnroll + entrypoint: EcmascriptStringEqualsUnroll + bridge: none + properties: [irtoc] + signature: + - bool + - void* + - void* + +- name: EcmascriptStringEqualsUnrollCompressed + entrypoint: EcmascriptStringEqualsUnrollCompressed + bridge: none + properties: [irtoc] + signature: + - bool + - void* + - void* + +- name: FastPathEcmaStrictEq + entrypoint: FastPathEcmaStrictEq + bridge: none + properties: [irtoc] + signature: + - uint64_t + - void* + - void* diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 1d9a7e0c3..1397e4b88 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -278,6 +278,7 @@ intrinsics: args: [any, acc] impl: panda::ecmascript::intrinsics::LessDyn set_flags: [heap_inv] + fast_path: FastPathEcmaLt - name: LessEqDyn space: ecmascript @@ -290,6 +291,7 @@ intrinsics: args: [any, acc] impl: panda::ecmascript::intrinsics::LessEqDyn set_flags: [heap_inv] + fast_path: FastPathEcmaLe - name: GreaterDyn space: ecmascript @@ -302,6 +304,7 @@ intrinsics: args: [any, acc] impl: panda::ecmascript::intrinsics::GreaterDyn set_flags: [heap_inv] + fast_path: FastPathEcmaGt - name: GreaterEqDyn space: ecmascript @@ -314,6 +317,7 @@ intrinsics: args: [any, acc] impl: panda::ecmascript::intrinsics::GreaterEqDyn set_flags: [heap_inv] + fast_path: FastPathEcmaGe - name: LdObjByValue space: ecmascript @@ -698,6 +702,7 @@ intrinsics: impl: panda::ecmascript::intrinsics::StrictNotEqDyn set_flags: [heap_inv] clear_flags: [no_dce] + codegen_func: CreateFastPathStrictNotEq - name: StrictEqDyn space: ecmascript @@ -709,6 +714,7 @@ intrinsics: args: [any, acc] impl: panda::ecmascript::intrinsics::StrictEqDyn set_flags: [heap_inv] + codegen_func: CreateFastPathStrictEq - name: NewobjspreadDyn space: ecmascript @@ -2267,3 +2273,16 @@ intrinsics: args: [any, any, any] codegen_func: CreateResolveAllocResult set_flags: [can_throw] + +- name: EcmaStringEquals + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StringEqualsIntrinsic + static: true + use_thread: false + signature: + ret: bool + args: [any, any] + codegen_func: CreateEcmaStringEquals + need_param_locations: true + clear_flags: [no_dce, no_hoist, no_cse, barrier, require_state, runtime_call] diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index d3bf71611..bfc5c06bd 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -114,6 +114,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ldlex.js SUPPORT_RELEASE true) 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) # 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/string_equals.js b/tests/checked/string_equals.js new file mode 100644 index 000000000..e01ae9d2f --- /dev/null +++ b/tests/checked/string_equals.js @@ -0,0 +1,76 @@ +/* + * 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. + */ + +//! CHECKER String equals with profiled type - call fastpath EcmaStringEquals +//! RUN options: "--no-async-jit --compiler-hotness-threshold=1 --compiler-regex _GLOBAL::test_.*", entry: "_GLOBAL::func_main_0" +//! EVENT_NOT /Deoptimization.*/ +//! METHOD "test_eq" +//! PASS_BEFORE "TypesResolving" +//! INST "Intrinsic.EqDyn" +//! INST_NEXT "Intrinsic.NotEqDyn" +//! INST_NEXT "Intrinsic.StrictEqDyn" +//! INST_NEXT "Intrinsic.StrictNotEqDyn" +//! PASS_AFTER "TypesResolving" +//! INST_NOT /Intrinsic.*EqDyn/ +//! INST_COUNT "Intrinsic.EcmaStringEquals", 4 +//! PASS_AFTER "Codegen" +//! INST_COUNT "Intrinsic.EcmaStringEquals", 1 + +//! CHECKER String equals without profiled type - call fastpath StrictEq/StrictNotEq +//! RUN options: "--no-async-jit --compiler-hotness-threshold=0 --compiler-regex _GLOBAL::test_.*", entry: "_GLOBAL::func_main_0" +//! METHOD "test_eq" +//! PASS_AFTER "Codegen" +//! INST_NOT "Intrinsic.EcmaStringEquals" +//! INST_COUNT /Intrinsic.*EqDyn/, 4 + +//! CHECKER String equals in interpreter +//! RUN options: "--interpreter-type irtoc --compiler-enable-jit=false", entry: "_GLOBAL::func_main_0" + + +function test_eq(a, b) { + if (!(a == b) || a != b || !(a === b) || a !== b) { + print(1); + throw "strings should be equal: " + a + " " + b; + } +} + +function test_ne(a, b) { + if (!(a != b) || a == b || !(a !== b) || a === b) { + throw "strings should be not equal: " + a + " " + b; + } +} + +test_eq("", ""); +test_ne("a", "b"); +let u1 = String.fromCharCode(1000) +let u2 = String.fromCharCode(1001) +test_eq(u1, String.fromCharCode(1000)); +test_ne(u1, u2); +print('a'); + +for (let len of [1, 2, 3, 4, 7, 8, 9, 16, 20, 32, 63, 64, 65, 128]) { + let a = 'a'.repeat(len); + let b = 'a'.repeat(len - 1) + 'b'; + let c = 'b' + 'a'.repeat(len - 1); + let l = Math.floor(len / 2); + let d = 'a'.repeat(l) + 'b' + 'a'.repeat(len - l - 1); + test_ne(a, a + 'a'); + test_ne(a, b); + test_ne(a, c); + test_ne(a, d); + test_ne(a + u1, a + u2); + test_eq(a + u1, a + u1); + test_eq(a + 'b', 'a' + b); +} diff --git a/tests/checked/type_resolving.js b/tests/checked/type_resolving.js index 56ef3d6d3..d101925c4 100644 --- a/tests/checked/type_resolving.js +++ b/tests/checked/type_resolving.js @@ -14,7 +14,7 @@ */ //! CHECKER Test arithmetic aot -//! RUN_PAOC options: "--compiler-regex '_GLOBAL::test_(\\w+_[if](_[if]|)|toboolean|ld.*)'" +//! RUN_PAOC options: "--compiler-regex '_GLOBAL::test_(\\w+_[if](_[if]|)|toboolean|ld.*|string|.*compare.*)'" //! check_aot = lambda do |op, intrinsic, form, inst| //! METHOD "test_#{op.downcase}_#{form}" //! PASS_BEFORE "TypesResolving" @@ -106,6 +106,35 @@ //! PASS_AFTER "TypesResolving" //! INST_COUNT "Intrinsic.Toboolean", 2 //! +//! METHOD "test_string" +//! PASS_BEFORE "TypesResolving" +//! INST "Intrinsic.EqDyn" +//! INST_NEXT "Intrinsic.NotEqDyn" +//! INST_NEXT "Intrinsic.StrictEqDyn" +//! INST_NEXT "Intrinsic.StrictNotEqDyn" +//! PASS_AFTER "TypesResolving" +//! INST_NOT /Intrinsic.*EqDyn/ +//! INST_COUNT "Intrinsic.EcmaStringEquals", 4 +//! +//! METHOD "test_strict_compare_diff_types" +//! PASS_BEFORE "TypesResolving" +//! INST_COUNT "Intrinsic.StrictNotEqDyn", 4 +//! INST_COUNT "Intrinsic.StrictEqDyn", 2 +//! INST_COUNT "Compare", 7 +//! PASS_AFTER "TypesResolving" +//! INST_NOT /Intrinsic.*EqDyn/ +//! INST_COUNT "Compare", 9 +//! +//! METHOD "test_compare_diff_types" +//! PASS_BEFORE "TypesResolving" +//! INST_COUNT "Intrinsic.EqDyn", 8 +//! INST_COUNT "Intrinsic.NotEqDyn", 3 +//! INST_COUNT "Compare", 11 +//! PASS_AFTER "TypesResolving" +//! INST_COUNT "Intrinsic.EqDyn", 1 +//! INST_NOT "Intrinsic.NotEqDyn" +//! INST_COUNT "Compare", 13 +//! //! RUN options: "--interpreter-type irtoc", entry: "_GLOBAL::func_main_0", force_jit: false //! //! EVENT_NOT /Deoptimization.*/ @@ -693,6 +722,58 @@ function NO_COMPILE_test_ldconst() { } } +function test_string() { + let a = "abcde"; + let b = "abcdf"; + let res = 0; + if (a == b) res += 1; + if (a != b) res += 2; + if (a === b) res += 4; + if (a !== b) res += 8; + if (res != 10) { + throw "test_string is failed" + } +} + +// Only two new Compare instructions should be created after types_resolving, +// other comparisons should be replaced with true/false based on input types +function test_strict_compare_diff_types() { + let a = "abcde"; + let b = 1.5; + let res = 0; + if (a !== b) res += 1; + if (a !== null) res += 2; + if (false !== null) res += 3; + if (a === undefined) res += 7; + if (b !== true) res += 8; + if (b === 0) res += 9; // Compare EQ after types_resolving + if (res != 14) { // Compare NE after types_resolving + throw "test_strict_compare_diff_types is failed" + } +} + +// Only one new Compare instruction should be created after types_resolving, +// other comparisons should be replaced with true/false or not inlined +function test_compare_diff_types() { + let a = "1"; + let b = 1.5; + let c = 1; + let res = 0; + if (null == null) res += 1; // true + if (undefined == undefined) res += 2; // true + if (null == undefined) res += 3; // true + if (a == undefined) res += 7; // false + if (undefined == b) res += 8; // false + if (a == c) res += 9; // true, leave as intrinsic + if (null != b) res += 10; // true + if (null == c) res += 11; // false + if (b == c) res += 12; // false, create Compare EQ + if (undefined != null) res += 13; // false + if (res != 25) { // Compare NE after types_resolving + throw "test_compare_diff_types is failed: " + res + } +} + test_add(); test_sub(); test_and(); @@ -709,4 +790,7 @@ test_inc(); test_dec(); test_cmp(); test_toboolean(); -NO_COMPILE_test_ldconst(); \ No newline at end of file +NO_COMPILE_test_ldconst(); +test_string(); +test_strict_compare_diff_types(); +test_compare_diff_types(); -- Gitee