diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index ce5dd58750cf2af73e27ba167dafc524205d481a..fa504c4e41dae37fdd9b966a8361ea5d0f9b0d4a 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -696,6 +696,11 @@ const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const return globalArraySignatures_; } +const ArenaSet &ETSChecker::UnionAssemblerTypes() const +{ + return unionAssemblerTypes_; +} + Type *ETSChecker::GlobalTypeError() const { return GetGlobalTypesHolder()->GlobalTypeError(); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 7de58a472eae05ef500ffe336b287abbca60f4b6..383840caa39dee4193c1b870a84eb0bb258244c7 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -92,6 +92,7 @@ public: invokeToArrowSignatures_(Allocator()->Adapter()), arrowToFuncInterfaces_(Allocator()->Adapter()), globalArraySignatures_(Allocator()->Adapter()), + unionAssemblerTypes_(Allocator()->Adapter()), dynamicIntrinsics_ {DynamicCallIntrinsicsMap {Allocator()->Adapter()}, DynamicCallIntrinsicsMap {Allocator()->Adapter()}}, dynamicClasses_ {DynamicClassIntrinsicsMap(Allocator()->Adapter()), @@ -171,6 +172,8 @@ public: GlobalArraySignatureMap &GlobalArrayTypes(); const GlobalArraySignatureMap &GlobalArrayTypes() const; + const ArenaSet &UnionAssemblerTypes() const; + Type *GlobalTypeError() const; [[nodiscard]] Type *InvalidateType(ir::Typed *node); [[nodiscard]] Type *TypeError(ir::Typed *node, const diagnostic::DiagnosticKind &diagKind, @@ -900,6 +903,7 @@ public: pendingConstraintCheckRecords_.clear(); constraintCheckScopesCount_ = 0; globalArraySignatures_.clear(); + unionAssemblerTypes_.clear(); GetCachedComputedAbstracts()->clear(); for (auto &dynamicCallIntrinsicsMap : dynamicIntrinsics_) { dynamicCallIntrinsicsMap.clear(); @@ -1058,6 +1062,7 @@ private: FunctionInterfaceMap arrowToFuncInterfaces_; size_t constraintCheckScopesCount_ {0}; GlobalArraySignatureMap globalArraySignatures_; + ArenaSet unionAssemblerTypes_; ComputedAbstracts *cachedComputedAbstracts_ {nullptr}; // NOTE(aleksisch): Extract dynamic from checker to separate class std::array dynamicIntrinsics_; diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index dbb75b798a849302374059a73d7fa529b303e345..eace7633ab5af3d8b557d3b29f09aa9c763560e7 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -649,8 +649,7 @@ Type *ETSChecker::ResolveUnionUncheckedType(ArenaVector &&appar } auto *unionType = CreateETSUnionType(std::move(apparentTypes)); if (unionType->IsETSUnionType()) { - checker::Type *typeLUB = unionType->AsETSUnionType()->GetAssemblerLUB(); - return typeLUB; + return unionType->AsETSUnionType(); } // Is case of single apparent type, just return itself return unionType; diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 1c8c6c9a98910a1e00c65c7f932c68aaca91f26f..c4ef7ae5d8f3dd69e699f49c9b5aa2c091cfe2f6 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -137,8 +137,9 @@ Type *ETSChecker::CreateETSUnionType(Span constituentTypes) if (newConstituentTypes.size() == 1) { return newConstituentTypes[0]; } - - return ProgramAllocator()->New(this, std::move(newConstituentTypes)); + auto *un = ProgramAllocator()->New(this, std::move(newConstituentTypes)); + unionAssemblerTypes_.insert(un); + return un; } ETSTypeAliasType *ETSChecker::CreateETSTypeAliasType(util::StringView name, const ir::AstNode *declNode, diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index cb112121c82df4692d0fcbafdbee36da4bc05c13..b701f412e4d510b03e98cb98dffb31c46fcf9850 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -34,74 +34,119 @@ void ETSUnionType::ToString(std::stringstream &ss, bool precise) const void ETSUnionType::ToAssemblerType(std::stringstream &ss) const { - assemblerLub_->ToAssemblerTypeWithRank(ss); + ss << GetAssemblerType(); } void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const { - assemblerLub_->ToDebugInfoType(ss); -} - -ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) - : Type(TypeFlag::ETS_UNION), constituentTypes_(std::move(constituentTypes)) -{ - ES2PANDA_ASSERT(constituentTypes_.size() > 1); - assemblerLub_ = ComputeAssemblerLUB(checker, this); + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToDebugInfoType(ss); + return; + } + ss << "{U"; + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToDebugInfoType(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; } -bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +static std::string GetAssemblerTypeString(Type *type) { - return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), - [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); + std::stringstream ss; + type->ToAssemblerTypeWithRank(ss); + return ss.str(); } -bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) { - return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), - [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); + ES2PANDA_ASSERT(!assemblerConstituentTypes_.empty()); + std::stringstream ss; + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToAssemblerTypeWithRank(ss); + } else { + ss << "{U"; + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; + } + assemblerTypeCache_ = util::UString(ss.str(), checker->ProgramAllocator()).View(); } -// This function computes effective runtime representation of union type -Type *ETSUnionType::ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un) +void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) { - auto *const apparent = checker->GetApparentType(un); + auto *const apparent = checker->GetApparentType(this); if (!apparent->IsETSUnionType()) { - return apparent; + assemblerConstituentTypes_.push_back(apparent); + return; } - if (apparent != un) { - return apparent->AsETSUnionType()->assemblerLub_; + if (apparent != this) { + const auto &types = apparent->AsETSUnionType()->GetAssemblerTypes(); + assemblerConstituentTypes_.insert(assemblerConstituentTypes_.begin(), types.begin(), types.end()); + return; } - un = apparent->AsETSUnionType(); - - Type *lub = nullptr; - for (auto *t : un->ConstituentTypes()) { - if (t->IsTypeError()) { - return checker->GlobalTypeError(); - } - // NOTE(vpukhov): #19701 void refactoring - ES2PANDA_ASSERT(t->IsETSReferenceType() || t->IsETSVoidType()); - t = t->IsETSVoidType() ? checker->GlobalETSUndefinedType() : t; - if (lub == nullptr || lub->IsETSUndefinedType()) { - lub = t; + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + for (auto *type : constituentTypes_) { + ES2PANDA_ASSERT(!type->IsETSUnionType()); + if (type->IsETSUndefinedType() || type->IsETSVoidType()) { continue; } - if (lub == t || t->IsETSUndefinedType()) { - continue; + if (type->IsETSNullType()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; } - if (t->IsETSNullType()) { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + if (type->IsTypeError()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalTypeError()); + return; } - if (t->IsETSObjectType() && lub->IsETSObjectType()) { - lub = checker->GetClosestCommonAncestor(lub->AsETSObjectType(), t->AsETSObjectType()); - } else if (t->IsETSArrayType() && lub->IsETSArrayType()) { - // NOTE: can compute "common(lub, t)[]" - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); - } else { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + auto found = + std::find_if(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [&type](Type *t) { return GetAssemblerTypeString(type) == GetAssemblerTypeString(t); }); + if (found == assemblerConstituentTypes_.end()) { + assemblerConstituentTypes_.push_back(type); } } - return checker->GetNonConstantType(lub); + if (assemblerConstituentTypes_.empty()) { + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; + } + if (assemblerConstituentTypes_.size() == 1) { + return; + } + + std::sort(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [](Type *a, Type *b) { return GetAssemblerTypeString(a) < GetAssemblerTypeString(b); }); +} + +ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) + : Type(TypeFlag::ETS_UNION), + constituentTypes_(std::move(constituentTypes)), + assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) +{ + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + CanonicalizedAssemblerType(checker); + InitAssemblerTypeCache(checker); +} + +bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +{ + return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), + [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); +} + +bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +{ + return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), + [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); } void ETSUnionType::Identical(TypeRelation *relation, Type *other) diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index 54c15370d85c9d453a8183ea0844b4a576856743..dc1fed610fde415d2965eecf4b17893b9bbd96c2 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -54,10 +54,14 @@ public: [[nodiscard]] ArenaVector GetNonConstantTypes(ETSChecker *checker) const noexcept; - // Do not use it anywhere except codegen - Type *GetAssemblerLUB() const noexcept + const util::StringView &GetAssemblerType() const { - return assemblerLub_; + return assemblerTypeCache_; + } + + const ArenaVector &GetAssemblerTypes() const + { + return assemblerConstituentTypes_; } template @@ -105,10 +109,12 @@ private: checker::ETSChecker *checker, checker::ETSObjectType *sourceType, std::map &numericTypes) const noexcept; - static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); + void CanonicalizedAssemblerType(ETSChecker *checker); + void InitAssemblerTypeCache(ETSChecker *checker); ArenaVector const constituentTypes_; - Type *assemblerLub_ {nullptr}; + ArenaVector assemblerConstituentTypes_; + util::StringView assemblerTypeCache_; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 149c12d4d57963671b7f818166bbcc4ac098b78d..9d5095d8d34b0d0000143f6113d2672b88b32e72 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -556,7 +556,8 @@ void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple break; case checker::TypeFlag::ETS_ARRAY: case checker::TypeFlag::ETS_TUPLE: - case checker::TypeFlag::FUNCTION: { + case checker::TypeFlag::FUNCTION: + case checker::TypeFlag::ETS_UNION: { EmitIsInstance(node, ToAssemblerType(target)); BranchIfTrue(node, ifTrue); break; @@ -587,11 +588,7 @@ void ETSGen::BranchIfIsInstance(const ir::AstNode *const node, const VReg srcReg TestIsInstanceConstituent(n, std::tie(ifTrue, ifFalse), t, acceptNull); }; - if (target->IsETSUnionType()) { - for (auto *ct : target->AsETSUnionType()->ConstituentTypes()) { - checkType(node, ct); - } - } else if (!target->IsETSNeverType()) { + if (!target->IsETSNeverType()) { checkType(node, target); } @@ -686,6 +683,29 @@ void ETSGen::CheckedReferenceNarrowingObject(const ir::AstNode *node, const chec } } +// optimized specialization for union +void ETSGen::CheckedReferenceNarrowingUnion(const ir::AstNode *node, const checker::Type *target) +{ + ES2PANDA_ASSERT(target->IsETSUnionType()); + const RegScope rs(this); + const auto srcReg = AllocReg(); + StoreAccumulator(node, srcReg); + + auto isUndefined = AllocLabel(); + + if (target->PossiblyETSUndefined()) { + BranchIfUndefined(node, isUndefined); + LoadAccumulator(node, srcReg); + EmitCheckCast(node, ToAssemblerType(target)); + SetLabel(node, isUndefined); + SetAccumulatorType(target); + } else { + EmitNullcheck(node); + EmitCheckCast(node, ToAssemblerType(target)); + SetAccumulatorType(target); + } +} + // Implemented on top of the runtime type system, do not relax checks, do not introduce new types void ETSGen::CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target) { @@ -702,6 +722,10 @@ void ETSGen::CheckedReferenceNarrowing(const ir::AstNode *node, const checker::T SetAccumulatorType(target); return; } + if (target->IsETSUnionType()) { + CheckedReferenceNarrowingUnion(node, target); + return; + } if (target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT | checker::TypeFlag::ETS_TUPLE) && !target->IsConstantType()) { CheckedReferenceNarrowingObject(node, target); diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 6712e10b91f0bc63dc89d21b7b79f458c137d21b..10b30e0f73f893d1eee96e55f7b1e0c6f4188497 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -449,6 +449,7 @@ private: void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple