From 661db901e07ee50ddbf6d5602dba9080a93ea88a Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Wed, 22 Mar 2023 21:06:18 +0300 Subject: [PATCH] Support special types in profiling Signed-off-by: Andrey Efremov --- .../code_generator/compiler_base_types.cpp | 158 ++++++++++++------ compiler/optimizer/ir/dyn_datatype.h | 60 ++++++- .../ecmascript_inst_builder_gen.cpp.erb | 12 +- .../compiler/ecmascript_runtime_interface.cpp | 85 ++++++---- .../compiler/ecmascript_runtime_interface.h | 3 +- runtime/ecma_profiling.h | 17 +- runtime/interpreter/ecma-interpreter-inl.h | 4 + tests/checked/CMakeLists.txt | 1 + tests/checked/ecma_profiling.js | 157 ++++++++++++++++- tests/checked/recompile_not_number.js | 4 +- tests/checked/recompile_special.js | 53 ++++++ tests/checked/type_resolving.js | 32 ++-- .../compiler/checks_elimination_ecma_test.cpp | 79 +++++++++ 13 files changed, 561 insertions(+), 104 deletions(-) create mode 100644 tests/checked/recompile_special.js diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index 937a7a689..7e5f562ed 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -23,7 +23,26 @@ namespace panda::compiler { -static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, bool is_int_possible, +static void IsHasTagMaskGen(Encoder *enc, Reg dst, Reg src, Imm val_tag, Imm val_mask, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + ScopedTmpReg reg_val_tag(enc, TypeInfo(TypeInfo::TypeId::INT64)); + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + + enc->EncodeMov(reg_val_tag, val_tag); + if (val_tag != val_mask) { + enc->EncodeAnd(dst_ext, src, val_mask); + } else { + enc->EncodeAnd(dst_ext, src, reg_val_tag); + } + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeCompare(dst, dst_ext, reg_val_tag, Condition::EQ); + } else { + enc->EncodeJump(id, dst_ext, reg_val_tag, Condition::NE); + } +} + +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) { @@ -32,13 +51,27 @@ static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, bool is_int_ auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); - 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::SPECIAL_INT) { + constexpr auto min_object = coretypes::TaggedValue::VALUE_UNDEFINED + 1; + constexpr auto min_number = 1ULL << coretypes::TaggedValue::TAG_BITS_SHIFT; + static_assert(coretypes::TaggedValue::VALUE_TRUE < min_object); + static_assert(coretypes::TaggedValue::TAG_EXCEPTION >= min_object); + enc->EncodeSub(dst_ext, src, Imm(min_object)); + enc->EncodeJump(not_number_label, dst_ext, Imm(min_number - min_object), Condition::LO); + return; + } + if (allowed_input_type == profiling::AnyInputType::INTEGER) { 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); + enc->EncodeJump(not_number_label, dst_ext, Imm(0), Condition::EQ); + return; + } + LabelHolder::LabelId end_label = enc->CreateLabel(); + if (allowed_input_type == profiling::AnyInputType::SPECIAL) { + enc->EncodeJump(end_label, src, Imm(coretypes::TaggedValue::VALUE_UNDEFINED), Condition::EQ); + } else { + ASSERT(allowed_input_type == profiling::AnyInputType::DEFAULT); } // (u16) tag + 0xffff is [0xffff for Object, 0xfffe for Int, other - Double] enc->EncodeAdd(dst_ext, src, Imm(panda::coretypes::TaggedValue::TAG_MASK)); @@ -60,6 +93,28 @@ static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, bool is_int_ enc->BindLabel(end_label); } +static void CompareAnyTypeGenInt(Encoder *enc, Reg dst, Reg src, profiling::AnyInputType allowed_input_type, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + if (allowed_input_type == profiling::AnyInputType::DEFAULT) { + IsHasTagMaskGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::TAG_INT), + Imm(panda::coretypes::TaggedValue::TAG_MASK), id); + return; + } + ASSERT(allowed_input_type == profiling::AnyInputType::SPECIAL); + constexpr auto min_fail = coretypes::TaggedValue::VALUE_UNDEFINED; + // accept values < VALUE_UNDEFINED or >= TAG_INT + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeSub(dst_ext, src, Imm(min_fail)); + if (id == LabelHolder::INVALID_LABEL) { + ScopedTmpReg reg_cmp(enc, TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeMov(reg_cmp, Imm(coretypes::TaggedValue::TAG_INT - min_fail)); + enc->EncodeCompare(dst_ext, dst_ext, reg_cmp, Condition::HS); + } else { + enc->EncodeJump(id, dst_ext, Imm(coretypes::TaggedValue::TAG_INT - min_fail), Condition::LO); + } +} + static void CompareAnyTypeGenBool(Encoder *enc, Reg dst, Reg src, LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) { static constexpr uint64_t BOOL_DIFF_MASK = @@ -79,34 +134,15 @@ static void CompareAnyTypeGenBool(Encoder *enc, Reg dst, Reg src, LabelHolder::L } } -static void IsHasTagMaskGen(Encoder *enc, Reg dst, Reg src, Imm val_tag, Imm val_mask, - LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) -{ - ScopedTmpReg reg_val_tag(enc, TypeInfo(TypeInfo::TypeId::INT64)); - auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); - - enc->EncodeMov(reg_val_tag, val_tag); - if (val_tag != val_mask) { - enc->EncodeAnd(dst_ext, src, val_mask); - } else { - enc->EncodeAnd(dst_ext, src, reg_val_tag); - } - if (id == LabelHolder::INVALID_LABEL) { - enc->EncodeCompare(dst, dst_ext, reg_val_tag, Condition::EQ); - } else { - enc->EncodeJump(id, dst_ext, reg_val_tag, Condition::NE); - } -} - 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); } } @@ -237,8 +273,7 @@ bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor IsEqualToValGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::VALUE_UNDEFINED)); return true; case AnyBaseType::ECMASCRIPT_INT_TYPE: - IsHasTagMaskGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::TAG_INT), - Imm(panda::coretypes::TaggedValue::TAG_MASK)); + CompareAnyTypeGenInt(enc, dst, src, cati->GetAllowedInputType()); return true; case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: IsHasTagMaskGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::TAG_OBJECT), @@ -248,7 +283,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())); @@ -316,36 +351,65 @@ bool ecmascript::CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVis enc->EncodeAnd(dst, src, Imm(1)); return true; case AnyBaseType::ECMASCRIPT_INT_TYPE: { - auto src_reg_type = dst_reg_type; if (DataType::GetTypeSize(dst_reg_type, RUNTIME_ARCH) > DataType::GetTypeSize(DataType::INT32, RUNTIME_ARCH)) { ASSERT(false && "sign-extension by dst type is not supported"); - src_reg_type = DataType::INT32; } - src = enc_v->GetCodegen()->ConvertRegister(cati->GetSrcReg(0), src_reg_type); - if (dst.GetId() == src.GetId() && !dst.IsFloat() && !src.IsFloat()) { - enc->EncodeCast(dst, false, src, false); - } else { - enc->EncodeMov(dst, src); + Reg res_reg = src; + ScopedTmpRegLazy tmp_dst(enc); + if (cati->GetAllowedInputType() == profiling::AnyInputType::SPECIAL) { + if (dst.GetId() == src.GetId()) { + tmp_dst.AcquireIfInvalid(); + res_reg = tmp_dst; + } else { + res_reg = dst.As(INT64_TYPE); + } + enc->EncodeAShr(res_reg, src, Imm(BitNumbers() - 1)); + enc->EncodeOr(res_reg, res_reg, Imm(1)); + // here res_reg contains 1 for special values and -1 for tagged ints + enc->EncodeAnd(res_reg, res_reg, src); } + ASSERT(res_reg.GetSize() == DOUBLE_WORD_SIZE); + ASSERT(!dst.IsFloat() && !src.IsFloat()); + enc->EncodeCast(dst, false, res_reg, false); return true; } case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: { 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::SPECIAL) != 0) { + // We allow Hole value in AnyTypeCheck with SPECIAL flag, but assume it cannot be + // CastValueToAnyType input, so it is cast to 0 instead of NaN here + 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); + } + if (cati->GetAllowedInputType() == profiling::AnyInputType::SPECIAL_INT) { + SCOPED_DISASM_STR(enc_v->GetCodegen(), "Try cast from Boolean or Null"); + auto int_double_label = enc->CreateLabel(); + enc->EncodeShr(reg_tmp, src, Imm(coretypes::TaggedValue::TAG_BITS_SHIFT)); + enc->EncodeJump(int_double_label, reg_tmp, Imm(0), Condition::NE); + enc->EncodeAnd(dst.As(INT32_TYPE), src, Imm(1)); + enc->EncodeCast(dst.As(FLOAT64_TYPE), true, dst.As(INT32_TYPE), true); + enc->EncodeJump(end_label); + enc->BindLabel(int_double_label); + } + 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); } - 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; @@ -466,8 +530,7 @@ static bool AnyTypeCheckGenCustomDeoptimization(Encoder *enc, Codegen *codegen, case AnyBaseType::ECMASCRIPT_INT_TYPE: { auto id = codegen->CreateSlowPath(check_inst, DeoptimizeType::NOT_SMALL_INT)->GetLabel(); - IsHasTagMaskGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_INT), - Imm(panda::coretypes::TaggedValue::TAG_MASK), id); + CompareAnyTypeGenInt(enc, tmp_reg, src, check_inst->GetAllowedInputType(), id); return true; } case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: { @@ -475,7 +538,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: @@ -506,8 +569,7 @@ bool ecmascript::AnyTypeCheckGen(AnyTypeCheckInst *check_inst, EncodeVisitor *en IsEqualToValGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::VALUE_UNDEFINED), id); return true; case AnyBaseType::ECMASCRIPT_INT_TYPE: { - IsHasTagMaskGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_INT), - Imm(panda::coretypes::TaggedValue::TAG_MASK), id); + CompareAnyTypeGenInt(enc, tmp_reg, src, check_inst->GetAllowedInputType(), id); return true; } case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: @@ -518,7 +580,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/optimizer/ir/dyn_datatype.h b/compiler/optimizer/ir/dyn_datatype.h index 292d31498..53a80e077 100644 --- a/compiler/optimizer/ir/dyn_datatype.h +++ b/compiler/optimizer/ir/dyn_datatype.h @@ -19,6 +19,7 @@ #include #include "compiler/optimizer/ir/datatype.h" #include "ir-dyn-base-types.h" +#include "profiling/profiling.h" namespace panda::compiler::ecmascript { static inline panda::compiler::AnyBaseType NumericDataTypeToAnyType(panda::compiler::DataType::Type type) @@ -56,11 +57,52 @@ static inline panda::compiler::AnyBaseType GetAnyBigintType() return panda::compiler::AnyBaseType::ECMASCRIPT_BIGINT_TYPE; } +// For two types (at least one of which is int or double) inclusion relation on +// masks returned by this function is eqivalent to this relation on types +static inline uint8_t GetPossibleTypeMask(panda::compiler::AnyBaseType type, profiling::AnyInputType allowed_types) +{ + enum Type : uint8_t { + NONE = 0, + INTEGER = (1U << 0U), + DOUBLE = (1U << 1U), + BOOLEAN = (1U << 2U), + SPECIAL = (1U << 3U), + OTHER = (1U << 5U), + SPECIAL_INT = SPECIAL | INTEGER, + SPECIAL_DOUBLE = SPECIAL | DOUBLE, + }; + uint8_t result = 0; + if ((allowed_types & profiling::AnyInputType::SPECIAL) != 0) { + result = Type::SPECIAL; + } + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE: + return Type::SPECIAL_INT; + case panda::compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + return Type::SPECIAL_DOUBLE; + case panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE: + result |= Type::INTEGER; + break; + case panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + result |= Type::DOUBLE; + if ((allowed_types & profiling::AnyInputType::INTEGER) != 0) { + result |= Type::INTEGER; + } + break; + default: + return Type::OTHER; + } + return result; +} + static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBaseType super_type, - panda::compiler::AnyBaseType type) + panda::compiler::AnyBaseType type, + [[maybe_unused]] profiling::AnyInputType super_allowed_types, + [[maybe_unused]] profiling::AnyInputType allowed_types) { if (super_type == type) { - return true; + return (super_allowed_types & allowed_types) == allowed_types ? std::optional {true} : std::nullopt; } switch (super_type) { @@ -141,7 +183,19 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa default: break; } - + if (super_type == panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE || + super_type == panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE || + type == panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE || + type == panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + auto possible_mask_super = GetPossibleTypeMask(super_type, super_allowed_types); + auto possible_mask = GetPossibleTypeMask(type, allowed_types); + if ((possible_mask_super & possible_mask) == possible_mask) { + return true; + } + if ((possible_mask_super & possible_mask) != 0) { + return std::nullopt; + } + } return false; } } // namespace panda::compiler::ecmascript diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb index 698f2712f..8bb59f742 100644 --- a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -169,10 +169,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); @@ -230,11 +230,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 bae954248..0bcce5312 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -165,8 +165,8 @@ Expected EcmaRuntimeInterface::AddProfile(std::string_view f return true; } -inline compiler::AnyBaseType GetTypeOfType(panda::ecmascript::ProfilingTypeOfBits::Type type, bool *is_integer_seen, - bool *is_type_profiled) +inline compiler::AnyBaseType GetTypeOfType(panda::ecmascript::ProfilingTypeOfBits::Type type, + profiling::AnyInputType *allowed_input_type, bool *is_type_profiled) { if (type == panda::ecmascript::ProfilingTypeOfBits::NONE) { return compiler::AnyBaseType::UNDEFINED_TYPE; @@ -175,7 +175,7 @@ inline compiler::AnyBaseType GetTypeOfType(panda::ecmascript::ProfilingTypeOfBit *is_type_profiled = true; switch (type) { case panda::ecmascript::ProfilingTypeOfBits::NUMBER: { - *is_integer_seen = true; + *allowed_input_type = profiling::AnyInputType::INTEGER; return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; } case panda::ecmascript::ProfilingTypeOfBits::SYMBOL: @@ -196,30 +196,50 @@ inline compiler::AnyBaseType GetTypeOfType(panda::ecmascript::ProfilingTypeOfBit return compiler::AnyBaseType::UNDEFINED_TYPE; } +static bool IsEqualsInst(const BytecodeInstruction *bc_inst) +{ + switch (bc_inst->GetOpcode()) { + 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 true; + default: + return false; + } +} + 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::TYPE_OF: { panda::ecmascript::TypeOfOperationProfile p(ecma_prof); - return GetTypeOfType(p.GetOperandType(index).GetType(), is_integer_seen, is_type_profiled); + return GetTypeOfType(p.GetOperandType(index).GetType(), allowed_input_type, is_type_profiled); } case profiling::ProfilingKind::UNARY_ARITH: { - panda::ecmascript::UnaryOperationProfile p(ecma_prof); + UnaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); + if ((type & ProfilingTypeBits::HEAP_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) { + 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 + // For arithmetic opcodes if we saw double and object, assume double type and deoptimize without method + // destruction in case of object + if (object_count == 2 || (object_count == 1 && IsEqualsInst(bc_inst))) { *is_type_profiled = true; return compiler::AnyBaseType::UNDEFINED_TYPE; } @@ -228,34 +248,41 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface default: 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::BOOLEAN) { + return compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_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; + auto accept_special = (type & ProfilingTypeBits::SPECIAL) != 0 || (type & ProfilingTypeBits::BOOLEAN) != 0; + if (accept_special && IsEqualsInst(bc_inst)) { + // some instructions treat NaN and undefined or true and 1 differently + return compiler::AnyBaseType::UNDEFINED_TYPE; + } + if ((type & ProfilingTypeBits::SPECIAL_INT) == type) { + if (accept_special) { + *allowed_input_type = profiling::AnyInputType::SPECIAL; } - return compiler::AnyBaseType::ECMASCRIPT_INT_TYPE; } - if (type == panda::ecmascript::ProfilingTypeBits::BOOLEAN) { - return compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE; + // If object was seen only in one of inputs and we have seen a number as input, + // build AnyTypeCheck with Double type for this input + if ((type & ProfilingTypeBits::DOUBLE_INTEGER) != 0) { + uint8_t allowed_type_mask = 0; + if (accept_special) { + allowed_type_mask |= profiling::AnyInputType::SPECIAL; + } + if ((type & ProfilingTypeBits::INTEGER) != 0) { + allowed_type_mask |= profiling::AnyInputType::INTEGER; + } + *allowed_input_type = static_cast(allowed_type_mask); + return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; } + return compiler::AnyBaseType::UNDEFINED_TYPE; } diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 4c04ed62a..205d06449 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 617b4a510..ce07cf5ae 100644 --- a/runtime/ecma_profiling.h +++ b/runtime/ecma_profiling.h @@ -31,10 +31,17 @@ public: DOUBLE = (1U << 1U), BOOLEAN = (1U << 2U), STRING = (1U << 3U), - OBJECT = (1U << 4U), + SPECIAL = (1U << 4U), + OBJECT = (1U << 5U), LAST = OBJECT, - DOUBLE_INTEGER = DOUBLE | INTEGER + DOUBLE_INTEGER = DOUBLE | INTEGER, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + SPECIAL_INT = SPECIAL | BOOLEAN | INTEGER, // boolean can be cast to integer + SPECIAL_DOUBLE = SPECIAL | DOUBLE, + SPECIAL_NUMBER = SPECIAL_INT | DOUBLE, + HEAP_OBJECT = STRING | OBJECT }; + template static void Dump(S &stream, Type type) { @@ -227,6 +234,12 @@ inline ProfilingTypeBits::Type GetTypeFromValue(JSTaggedValue value) if (value.IsString()) { return ProfilingTypeBits::STRING; } + if (value.IsUndefined()) { + return ProfilingTypeBits::SPECIAL_DOUBLE; + } + if (value.IsNull()) { + return ProfilingTypeBits::SPECIAL_INT; + } return ProfilingTypeBits::OBJECT; } diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index 4da0b2361..1329a5a44 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -1617,6 +1617,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(); } @@ -1630,6 +1632,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/CMakeLists.txt b/tests/checked/CMakeLists.txt index 2ea87098f..18196bd63 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -101,6 +101,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_not_number.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_undefined.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/destroy_method.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_special.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/donot_recompile_not_profiled.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_index.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_name.js SUPPORT_RELEASE true) diff --git a/tests/checked/ecma_profiling.js b/tests/checked/ecma_profiling.js index aba4bd51e..d8214757e 100644 --- a/tests/checked/ecma_profiling.js +++ b/tests/checked/ecma_profiling.js @@ -14,7 +14,9 @@ */ //! 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 +24,52 @@ //! 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 s" +//! INST_NEXT "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i s" +//! INST_NEXT "Cast" +//! INST_NEXT "Or" +//! +//! METHOD "test_eq" +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.EqDyn.*/ +//! +//! METHOD "test_strict_eq" +//! PASS_AFTER "Codegen" +//! INST /Intrinsic.StrictEqDyn.*/ +//! +//! METHOD "test_strict_eq_number" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.StrictEq.*/ +//! PASS_AFTER "Codegen" +//! INST_NOT /Intrinsic.StrictEq.*/ +//! INST "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i p" +//! INST_NEXT "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i" +//! INST_NEXT "Cast" +//! INST_NEXT "Compare EQ f64" +//! +//! METHOD "test_special_int" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.Add.*/ +//! PASS_AFTER "Codegen" +//! INST_NOT /Intrinsic.Add.*/ +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_INT_TYPE s", 2 +//! INST_COUNT "CastAnyTypeValue ECMASCRIPT_INT_TYPE s", 2 +//! INST "Add" +//! +//! METHOD "test_special_all" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.Sub.*/ +//! PASS_AFTER "Codegen" +//! INST_NOT /Intrinsic.Sub.*/ +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i s", 2 +//! INST_COUNT "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i s", 2 +//! INST "Sub" function test_profile(a, b) { a = a + b; @@ -36,3 +84,110 @@ 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; +} + +// Profiled input types can be cast to double, but don't inline because +// undefined == undefined and NaN != NaN +function test_eq(x, y) { + return x == y; +} + +sum = 0; +for (var i = 0; i < 5; i++) { + sum += test_eq(undefined, undefined); + sum += test_eq(NaN, NaN); + sum += test_eq(NaN, undefined); + sum += test_eq(1.1, 1.1); +} +if (sum != 10) { + throw "Wrong sum for equals: " + sum; +} + +// Profiled input types can be cast to int, but dont inline because i. e. false !== 0 +function test_strict_eq(x, y) { + return x === y; +} + +sum = 0; +for (var i = 0; i < 5; i++) { + sum += test_strict_eq(false, null); + sum += test_strict_eq(true, 1); + sum += test_strict_eq(0, false); + sum += test_strict_eq(1, 1); +} +if (sum != 5) { + throw "Wrong sum for strict equals: " + sum; +} + +// Profiled input types are int or double, inline +function test_strict_eq_number(x, y) { + return x === y; +} + +sum = 0; +for (var i = 1; i <= 5; i++) { + sum += test_strict_eq_number(i, i / 3); + sum += test_strict_eq_number(i, i); + sum += test_strict_eq_number(1 / i, 1 / i); + sum += test_strict_eq_number(1 / i * i, 1); +} +if (sum != 15) { + throw "Wrong sum for strict equals with number inputs: " + sum; +} + +function test_special_int(x, y) { + return x + y; +} + +for (var i = 0; i < 5; i++) { + let result = test_special_int(i, i < 10); + if (result != i + 1) { + throw "Wrong result for int + true: " + result; + } + result = test_special_int(10, i < 0); + if (result != 10) { + throw "Wrong result for int + false: " + result; + } + result = test_special_int(null, i); + if (result != i) { + throw "Wrong result for null + int: " + result; + } +} + +function test_special_all(x, y) { + return x - y; +} + +for (var i = 0; i < 5; i++) { + let result = test_special_all(undefined, 1.1); + if (result == result) { + throw "Wrong result for undefined + double: " + result; + } + result = test_special_all(2.5, true); + if (result != 1.5) { + throw "Wrong result for double + true: " + result; + } + result = test_special_all(i * 2, i); + if (result != i) { + throw "Wrong result for int + int profiled as double:" + result; + } + result = test_special_all(null, false); + if (result != 0) { + throw "Wrong result for null + false profiled as double:" + result; + } +} \ No newline at end of file diff --git a/tests/checked/recompile_not_number.js b/tests/checked/recompile_not_number.js index 3923a2013..5a3a4f264 100644 --- a/tests/checked/recompile_not_number.js +++ b/tests/checked/recompile_not_number.js @@ -37,13 +37,13 @@ sum(1, 2); sum(1, 3); sum(1, 3); // sum was compiled -sum(null, 3); // not_smi deoptimization +sum(undefined, 3); // not_smi deoptimization sum(1, 3); sum(1, 3); sum(1, 3); // sum was compiled again sum(1.1, 3); -sum(null, 3); // not_number deoptimization +sum("0", 3); // not_number deoptimization sum(1, 3); sum(1, 3); sum(1, 3); diff --git a/tests/checked/recompile_special.js b/tests/checked/recompile_special.js new file mode 100644 index 000000000..33ba5d485 --- /dev/null +++ b/tests/checked/recompile_special.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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 Test deoptimization/recompilation for special values +//! RUN options: "--compiler-regex='_GLOBAL::sum' --no-async-jit=true --compiler-hotness-threshold=2", entry: "_GLOBAL::func_main_0" +//! EVENT /Compilation,_GLOBAL::sum/ +//! EVENT_NEXT /DeoptimizationReason,_GLOBAL::func_sum_1,NOT_SMALL_INT/ +//! EVENT_NEXT /Deoptimization,_GLOBAL::func_sum_1/ +//! EVENT_NEXT /Compilation,_GLOBAL::sum/ +//! EVENT_NEXT /DeoptimizationReason,_GLOBAL::func_sum_1,NOT_NUMBER/ +//! EVENT_NEXT /Deoptimization,_GLOBAL::func_sum_1/ +//! EVENT_NEXT_NOT /Deoptimization,_GLOBAL::func_sum_1/ +//! EVENT_NEXT_NOT /Compilation,_GLOBAL::sum/ +//! METHOD "_GLOBAL::func_sum_1" +//! PASS_AFTER "IrBuilder" +//! INST "AnyTypeCheck ECMASCRIPT_INT_TYPE s" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" +//! PASS_AFTER_NEXT "IrBuilder" +//! INST "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i s" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" + +function sum(a, b) { + return a + b; +} + +sum(1, 2); +sum(null, 1); +sum(1, 3); +// sum was compiled +sum(1.1, 3); // not_smi deoptimization +sum(1, 3); +sum(1, 3); +sum(1, 3); +// sum was compiled again +sum(1, 3); +sum(true, 3); +sum(1.1, 3); +sum(undefined, 1); +sum(false, 1); +sum(null, 1); +sum("1", 1); // not_number deoptimization only here diff --git a/tests/checked/type_resolving.js b/tests/checked/type_resolving.js index 7979ee64c..56ef3d6d3 100644 --- a/tests/checked/type_resolving.js +++ b/tests/checked/type_resolving.js @@ -80,7 +80,7 @@ //! check_aot.call(:Ldnan, 'Ldnan', '', /f64 +Constant +nan/) //! //! forms = [['i_i', 'i32'], ['i_f', 'f64'], ['f_i', 'f64'], ['f_f', 'f64']] -//! insts = [['eq', 'Eq'], ['ne', 'NotEq'], ['ge', 'GreaterEq'], ['le', 'LessEq'], ['gt', 'Greater'], ['lt', 'Less']] +//! insts = [['eq', 'Eq'], ['ne', 'NotEq'], ['ge', 'GreaterEq'], ['le', 'LessEq'], ['gt', 'Greater'], ['lt', 'Less'], ['eq', 'StrictEq'], ['ne', 'StrictNotEq']] //! forms.each do |form, type| //! METHOD "test_cmp_#{form}" //! PASS_BEFORE "TypesResolving" @@ -575,7 +575,9 @@ function test_cmp_i_i() { if (a <= b) res += 4; if (a > b) res += 5; if (a < b) res += 6; - return res; // 12 + if (a === b) res += 7; + if (a !== b) res += 8; + return res; // 20 } function test_cmp_i_f() { @@ -588,12 +590,14 @@ function test_cmp_i_f() { if (a <= b) res += 14; if (a > b) res += 15; if (a < b) res += 16; + if (a === b) res += 17; + if (a !== b) res += 18; // 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 + if (a <= NaN) res += 19; + if (a >= NaN) res += 20; + if (a < NaN) res += 21; + if (a > NaN) res += 22; + return res; // 58 } function test_cmp_f_i() { @@ -606,7 +610,9 @@ function test_cmp_f_i() { if (a <= b) res += 24; if (a > b) res += 25; if (a < b) res += 26; - return res; // 72 + if (a === b) res += 27; + if (a !== b) res += 28; + return res; // 100 } function test_cmp_f_f() { @@ -619,9 +625,11 @@ function test_cmp_f_f() { if (a <= b) res += 34; if (a > b) res += 35; if (a < b) res += 36; - if (NaN == NaN) res += 37; // false - if (NaN != NaN) res += 38; // true - return res; // 140 + if (a === b) res += 37; + if (a !== b) res += 38; + if (NaN == NaN) res += 39; // false + if (NaN != NaN) res += 40; // true + return res; // 180 } function test_cmp() { @@ -630,7 +638,7 @@ function test_cmp() { res += test_cmp_i_f(); res += test_cmp_f_i(); res += test_cmp_f_f(); - if (res != 264) { + if (res != 358) { throw "test_cmp is failed"; } } diff --git a/tests/compiler/checks_elimination_ecma_test.cpp b/tests/compiler/checks_elimination_ecma_test.cpp index 5f86e7a91..626e9e101 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,39 @@ TEST_F(CheckEliminationEcmaTest, NotEliminateAnyTypeCheckIntWasSeen) EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); } +TEST_F(CheckEliminationEcmaTest, TestAnyTypeCheckDoubleSpecialTypes) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL_INT); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL_INT); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL, + AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, profiling::AnyInputType::SPECIAL, + 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::SPECIAL_INT, + 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, TestAnyTypeCheckIntSpecialTypes) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::SPECIAL); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::SPECIAL, + AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::SPECIAL); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT, + AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT); + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::SPECIAL, + AnyBaseType::ECMASCRIPT_INT_TYPE, profiling::AnyInputType::DEFAULT); +} + TEST_F(CheckEliminationEcmaTest, NotEliminateAnyTypeCheck) { auto graph = CreateGraphDynWithDefaultRuntime(); -- Gitee