From ffef3789ede3a835acf9718868698d2d16333936 Mon Sep 17 00:00:00 2001 From: Anna Antipina Date: Wed, 28 May 2025 11:35:29 +0300 Subject: [PATCH] Provide new type descriptors for union types - Implement canonicalization for union subtypes. - Emit a new descriptor for the union type instead of LUB type. - Transfer semi-LUB reduction for union descriptor type from FE to Runtime. - Add codegen tests for unions Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICFV1A Testing: ninja all tests Change-Id: I2908cd7233a1ecacd55f9089ba2a7721330b00d8 Signed-off-by: Anna Antipina Signed-off-by: Redkin Mikhail --- ets2panda/checker/ETSchecker.cpp | 5 + ets2panda/checker/ETSchecker.h | 5 + ets2panda/checker/ets/typeCreation.cpp | 5 +- ets2panda/checker/types/ets/etsUnionType.cpp | 95 +++++++++++- ets2panda/checker/types/ets/etsUnionType.h | 14 ++ ets2panda/compiler/core/ETSemitter.cpp | 11 +- ets2panda/evaluate/debugInfoStorage.cpp | 2 +- ets2panda/test/unit/CMakeLists.txt | 4 + ets2panda/test/unit/union_emit_test.cpp | 146 +++++++++++++++++++ 9 files changed, 280 insertions(+), 7 deletions(-) create mode 100644 ets2panda/test/unit/union_emit_test.cpp diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index ce5dd58750..fa504c4e41 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 7de58a472e..383840caa3 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/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 1c8c6c9a98..c4ef7ae5d8 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 cb112121c8..9dc06ad586 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -34,19 +34,108 @@ 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); + 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 << "}"; +} + +static std::string GetAssemblerTypeString(Type *type) +{ + std::stringstream ss; + type->ToAssemblerTypeWithRank(ss); + return ss.str(); +} + +void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) +{ + 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(); +} + +void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) +{ + auto *const apparent = checker->GetApparentType(this); + if (!apparent->IsETSUnionType()) { + assemblerConstituentTypes_.push_back(apparent); + return; + } + if (apparent != this) { + const auto &types = apparent->AsETSUnionType()->GetAssemblerTypes(); + assemblerConstituentTypes_.insert(assemblerConstituentTypes_.begin(), types.begin(), types.end()); + return; + } + + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + for (auto *type : constituentTypes_) { + ES2PANDA_ASSERT(!type->IsETSUnionType()); + if (type->IsETSUndefinedType() || type->IsETSVoidType()) { + continue; + } + if (type->IsETSNullType()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; + } + if (type->IsTypeError()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalTypeError()); + return; + } + 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); + } + } + 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)) + : Type(TypeFlag::ETS_UNION), + constituentTypes_(std::move(constituentTypes)), + assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) { ES2PANDA_ASSERT(constituentTypes_.size() > 1); assemblerLub_ = ComputeAssemblerLUB(checker, this); + CanonicalizedAssemblerType(checker); + InitAssemblerTypeCache(checker); } bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index 54c15370d8..be1d6c3b29 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -60,6 +60,16 @@ public: return assemblerLub_; } + const util::StringView &GetAssemblerType() const + { + return assemblerTypeCache_; + } + + const ArenaVector &GetAssemblerTypes() const + { + return assemblerConstituentTypes_; + } + template [[nodiscard]] bool AllOfConstituentTypes(UnaryPredicate p) const noexcept { @@ -106,8 +116,12 @@ private: std::map &numericTypes) const noexcept; static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); + void CanonicalizedAssemblerType(ETSChecker *checker); + void InitAssemblerTypeCache(ETSChecker *checker); ArenaVector const constituentTypes_; + ArenaVector assemblerConstituentTypes_; + util::StringView assemblerTypeCache_; Type *assemblerLub_ {nullptr}; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/compiler/core/ETSemitter.cpp b/ets2panda/compiler/core/ETSemitter.cpp index 8895c5009a..50c9693ec9 100644 --- a/ets2panda/compiler/core/ETSemitter.cpp +++ b/ets2panda/compiler/core/ETSemitter.cpp @@ -104,7 +104,9 @@ static pandasm::Type PandasmTypeWithRank(checker::Type const *type, uint32_t ran return PandasmTypeWithRank(type->AsETSPartialTypeParameter()->GetUnderlying()); } if (type->IsETSUnionType()) { - return PandasmTypeWithRank(type->AsETSUnionType()->GetAssemblerLUB()); + if (type->AsETSUnionType()->GetAssemblerTypes().size() == 1) { + return pandasm::Type::FromName(type->AsETSUnionType()->GetAssemblerType().Mutf8()); + } } std::stringstream ss; @@ -306,6 +308,13 @@ void ETSEmitter::GenAnnotation() for (auto [arrType, signature] : checker->GlobalArrayTypes()) { GenGlobalArrayRecord(arrType, signature); } + for (auto unionType : checker->UnionAssemblerTypes()) { + if (unionType->GetAssemblerTypes().size() > 1) { + auto unionRecord = pandasm::Record(unionType->GetAssemblerType().Mutf8(), Program()->lang); + unionRecord.metadata->SetAttribute(Signatures::EXTERNAL); + Program()->recordTable.emplace(unionRecord.name, std::move(unionRecord)); + } + } if (Context()->config->options->WasSetWithExportTable()) { auto result = StoreExportNodes(Context()->parserProgram->DeclGenExportNodes(), Program()); Program()->exportStrMap = std::move(result); diff --git a/ets2panda/evaluate/debugInfoStorage.cpp b/ets2panda/evaluate/debugInfoStorage.cpp index 1aa286225d..20163df765 100644 --- a/ets2panda/evaluate/debugInfoStorage.cpp +++ b/ets2panda/evaluate/debugInfoStorage.cpp @@ -30,7 +30,7 @@ std::string GetFullRecordName(const panda_file::File &pf, const panda_file::File std::string name = utf::Mutf8AsCString(pf.GetStringData(classId).data); auto type = pandasm::Type::FromDescriptor(name); - type = pandasm::Type(type.GetComponentName(), type.GetRank()); + type = pandasm::Type(type.GetNameWithoutRank(), type.GetRank()); return type.GetPandasmName(); } diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index 07663a9a29..509f7de8a4 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -41,6 +41,10 @@ ets2panda_add_gtest(es2panda_globalETSObjectType_tests CPP_SOURCES globalETSObjectType_test.cpp ) +ets2panda_add_gtest(es2panda_union_emit_standard_test + CPP_SOURCES union_emit_test.cpp +) + if (PANDA_TARGET_LINUX AND PANDA_TARGET_64) ets2panda_add_gtest(sizeof_node_tests CPP_SOURCES sizeof_node_test.cpp diff --git a/ets2panda/test/unit/union_emit_test.cpp b/ets2panda/test/unit/union_emit_test.cpp new file mode 100644 index 0000000000..d63214db56 --- /dev/null +++ b/ets2panda/test/unit/union_emit_test.cpp @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2025 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. + */ + +#include +#include "test/utils/asm_test.h" + +namespace ark::es2panda::compiler::test { + +class UnionAsmTest : public ::test::utils::AsmTest { +public: + UnionAsmTest() = default; + + ~UnionAsmTest() override = default; + + void CheckUnionType(std::string_view recordName) + { + pandasm::Record *rec = GetRecord(recordName, program_); + ASSERT_TRUE(rec != nullptr) << "Type '" << recordName << "' not found"; + } + +private: + NO_COPY_SEMANTIC(UnionAsmTest); + NO_MOVE_SEMANTIC(UnionAsmTest); +}; + +TEST_F(UnionAsmTest, union_test) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:A|B) {} + function test2(v:A|B|C) {} + function test3(v:A|B|FixedArray) {} + function test4(v:B|C|D|int) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C[]}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C}"); + CheckUnionType("{Udummy.B,dummy.C,dummy.D,std.core.Int}"); +} + +TEST_F(UnionAsmTest, union_test_extends) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C extends B {} + class D extends B {} + function test1(v:A|B) {} + function test2(v:A|B|C) {} + function test3(v:A|B|FixedArray) {} + function test4(v:B|C|D|int) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C[]}"); + CheckUnionType("{Udummy.B,std.core.Int}"); +} + +TEST_F(UnionAsmTest, union_test_2) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:C|C|B|C|B) {} + function test2(v:D|C|B|A) {} + function test3(v:A|A|A|B) {} + function test4(v:A|double) {} + function test5(v:double|double|long) {} + function test6(v:double|int|long) {} + function test7(v:B|C|B|A) {} + function test8(v:A|D|D|D) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B,dummy.C,dummy.D}"); + CheckUnionType("{Udummy.A,dummy.B,dummy.C}"); + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.D}"); + CheckUnionType("{Udummy.A,std.core.Double}"); + CheckUnionType("{Udummy.B,dummy.C}"); +} + +TEST_F(UnionAsmTest, union_test_arrays) +{ + SetCurrentProgram(R"( + class A {} + class B {} + class C {} + class D {} + function test1(v:B|C|FixedArray) {} + function test1(v:FixedArray|FixedArray|FixedArray) {} + function test1(v:B|C|FixedArray) {} + function test1(v:B|FixedArray|FixedArray|FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray|FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray) {} + function test1(v:FixedArray|FixedArray) {} + function test1(v:FixedArray|C|B) {} + function test1(v:FixedArray|long|double) {} + function test1(v:FixedArray|FixedArray>|FixedArray|FixedArray) {} + )"); + + CheckUnionType("{Udummy.A,dummy.B}"); + CheckUnionType("{Udummy.A,dummy.B}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Double}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Double}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Int}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Int}[]"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Long}"); + CheckUnionType("{Udummy.A,dummy.D,std.core.Long}[]"); + CheckUnionType("{Udummy.A,dummy.D}"); + CheckUnionType("{Udummy.A,dummy.D}[]"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.A,dummy.B}[]}"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.A,dummy.D,std.core.Double}[]}"); + CheckUnionType("{Udummy.B,dummy.C,{Udummy.C,dummy.D}[]}"); + CheckUnionType("{Udummy.B,dummy.C}"); + CheckUnionType("{Udummy.B,dummy.C}[]"); + CheckUnionType("{Udummy.B,dummy.D}"); + CheckUnionType("{Udummy.B,dummy.D}[]"); + CheckUnionType("{Udummy.B,{Udummy.A,dummy.D}[],{Udummy.B,dummy.C}[],{Udummy.B,dummy.D}[],{Udummy.C,dummy.D}[]}"); + CheckUnionType("{Udummy.C,dummy.D}"); + CheckUnionType("{Udummy.C,dummy.D}[]"); + CheckUnionType( + "{U{Udummy.A,dummy.D,std.core.Double}[],{Udummy.A,dummy.D,std.core.Int}[],{Udummy.A,dummy.D,std.core.Long}[]}"); + CheckUnionType("{U{Udummy.A,dummy.D}[],{Udummy.B,dummy.C}[],{Udummy.B,dummy.D}[],{Udummy.C,dummy.D}[]}"); + CheckUnionType("{U{Udummy.A,dummy.D}[],{Udummy.B,dummy.D}[]}"); + CheckUnionType("{U{Udummy.B,dummy.C}[],{Udummy.C,dummy.D}[]}"); +} + +} // namespace ark::es2panda::compiler::test -- Gitee