From a184782a38406d2a04e717d0725f42d46258b422 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 23 Sep 2024 08:21:36 -0700 Subject: [PATCH] Introduce a `Builtin.FixedArray` type. `Builtin.FixedArray` has the layout of `N` elements of type `T` laid out sequentially in memory (with the tail padding of every element occupied by the array). This provides a primitive on which the standard library `Vector` type can be built. --- docs/ABI/Mangling.rst | 1 + include/swift/ABI/Metadata.h | 21 + include/swift/ABI/MetadataKind.def | 5 +- include/swift/AST/Builtins.def | 7 + include/swift/AST/DiagnosticsSema.def | 2 + include/swift/AST/TypeDifferenceVisitor.h | 13 + include/swift/AST/TypeMatcher.h | 2 + include/swift/AST/TypeNodes.def | 50 +- include/swift/AST/TypeTransform.h | 27 +- include/swift/AST/Types.h | 116 ++- include/swift/Demangling/DemangleNodes.def | 1 + include/swift/Runtime/Metadata.h | 8 + include/swift/Runtime/RuntimeFunctions.def | 11 + include/swift/SIL/SILInstruction.h | 1 + include/swift/Strings.h | 2 + lib/AST/ASTContext.cpp | 34 + lib/AST/ASTDumper.cpp | 15 + lib/AST/ASTMangler.cpp | 9 + lib/AST/ASTPrinter.cpp | 2 + lib/AST/Builtins.cpp | 159 ++++ lib/AST/ConformanceLookup.cpp | 13 +- lib/AST/Type.cpp | 25 + lib/AST/TypeWalker.cpp | 10 + lib/Demangling/Demangler.cpp | 12 + lib/Demangling/NodePrinter.cpp | 8 + lib/Demangling/OldRemangler.cpp | 4 + lib/Demangling/Remangler.cpp | 6 + lib/IRGen/CMakeLists.txt | 1 + lib/IRGen/GenArray.cpp | 702 ++++++++++++++++++ lib/IRGen/GenType.cpp | 6 + lib/IRGen/GenType.h | 4 +- lib/IRGen/IRGenDebugInfo.cpp | 10 + lib/IRGen/IRGenModule.cpp | 8 + lib/IRGen/MetadataRequest.cpp | 42 +- lib/IRGen/Outlining.cpp | 5 +- lib/SIL/IR/AbstractionPattern.cpp | 25 + lib/SIL/IR/TypeLowering.cpp | 93 ++- lib/SILGen/SILGenBuiltin.cpp | 66 ++ lib/Sema/TypeCheckType.cpp | 39 + lib/Serialization/DeclTypeRecordNodes.def | 4 +- lib/Serialization/Deserialization.cpp | 26 + lib/Serialization/ModuleFormat.h | 8 +- lib/Serialization/Serialization.cpp | 9 + stdlib/public/runtime/Metadata.cpp | 399 ++++++++-- .../public/runtime/MetadataAllocatorTags.def | 1 + stdlib/public/runtime/ProtocolConformance.cpp | 4 + test/IRGen/builtin_vector_fixed_array.sil | 295 ++++++++ test/Prototypes/Vector.swift | 107 +++ test/SILGen/builtin_vector.swift | 67 ++ .../definite_init_builtin_vector.swift | 16 + test/Sema/builtin_int.swift | 18 + test/Sema/builtin_vector.swift | 40 + test/abi/macOS/arm64/stdlib.swift | 1 + test/abi/macOS/x86_64/stdlib.swift | 1 + 54 files changed, 2489 insertions(+), 72 deletions(-) create mode 100644 lib/IRGen/GenArray.cpp create mode 100644 test/IRGen/builtin_vector_fixed_array.sil create mode 100644 test/Prototypes/Vector.swift create mode 100644 test/SILGen/builtin_vector.swift create mode 100644 test/SILOptimizer/definite_init_builtin_vector.swift create mode 100644 test/Sema/builtin_int.swift create mode 100644 test/Sema/builtin_vector.swift diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index b7638288ad4..fcb12b5a7ef 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -693,6 +693,7 @@ Types type ::= 'Bp' // Builtin.RawPointer type ::= 'Bt' // Builtin.SILToken type ::= type 'Bv' NATURAL '_' // Builtin.Vecx + type ::= type type 'BV' // Builtin.FixedArray type ::= 'Bw' // Builtin.Word type ::= function-signature 'c' // function type (escaping) type ::= function-signature 'X' FUNCTION-KIND // special function type diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 7cb7666935e..e9342135304 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -1665,6 +1665,27 @@ struct TargetMetatypeMetadata : public TargetMetadata { }; using MetatypeMetadata = TargetMetatypeMetadata; +/// The structure of `Builtin.FixedArray` type metadata. +template +struct TargetFixedArrayTypeMetadata : public TargetMetadata { + using StoredPointerDifference = typename Runtime::StoredPointerDifference; + + StoredPointerDifference Count; + ConstTargetMetadataPointer Element; + + // Returns the number of elements for which storage is reserved. + // A type that is instantiated with negative size cannot have values + // instantiated, so is laid out with zero size like an uninhabited type. + StoredPointerDifference getRealizedCount() const { + return Count < 0 ? 0 : Count; + } + + static bool classof(const TargetMetadata *metadata) { + return metadata->getKind() == MetadataKind::FixedArray; + } +}; +using FixedArrayTypeMetadata = TargetFixedArrayTypeMetadata; + /// The structure of tuple type metadata. template struct TargetTupleTypeMetadata : public TargetMetadata { diff --git a/include/swift/ABI/MetadataKind.def b/include/swift/ABI/MetadataKind.def index 2dbb56f94c7..c40744705dc 100644 --- a/include/swift/ABI/MetadataKind.def +++ b/include/swift/ABI/MetadataKind.def @@ -44,7 +44,6 @@ NOMINALTYPEMETADATAKIND(Class, 0) NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap) /// An enum type. -/// If we add reference enums, that needs to go here. NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap) /// An optional type. @@ -80,6 +79,10 @@ METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKin /// An extended existential type. METADATAKIND(ExtendedExistential, 7 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap) +/// A `Builtin.FixedArray`. +METADATAKIND(FixedArray, 8 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap) + + /// A heap-allocated local variable using statically-generated metadata. METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index edf123dcba8..5e7b3d09dc8 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -1126,6 +1126,13 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(DistributedActorAsAnyActor, "distributedActor /// lowered away at IRGen, no sooner. BUILTIN_MISC_OPERATION_WITH_SILGEN(AddressOfRawLayout, "addressOfRawLayout", "n", Special) +/// emplace((Builtin.RawPointer) -> Void) -> T +/// +/// Passes a pointer to an uninitialized result value to the given function, +/// which must initialize the memory before finishing execution. The value in +/// memory becomes the result of the call. +BUILTIN_SIL_OPERATION(Emplace, "emplace", Special) + /// Builtins for instrumentation added by sanitizers during SILGen. #ifndef BUILTIN_SANITIZER_OPERATION #define BUILTIN_SANITIZER_OPERATION(Id, Name, Attrs) BUILTIN(Id, Name, Attrs) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 67210759eea..3bdf543dfaf 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4641,6 +4641,8 @@ ERROR(cannot_explicitly_specialize_function,none, "cannot explicitly specialize %kind0", (ValueDecl *)) ERROR(not_a_generic_type,none, "cannot specialize non-generic type %0", (Type)) +ERROR(invalid_generic_builtin_type,none, + "invalid generic arguments to builtin type %0", (Type)) ERROR(not_a_generic_macro,none, "cannot specialize a non-generic external macro %0", (const ValueDecl *)) ERROR(protocol_declares_unknown_primary_assoc_type,none, diff --git a/include/swift/AST/TypeDifferenceVisitor.h b/include/swift/AST/TypeDifferenceVisitor.h index 60449c413fe..5dde346477d 100644 --- a/include/swift/AST/TypeDifferenceVisitor.h +++ b/include/swift/AST/TypeDifferenceVisitor.h @@ -129,6 +129,19 @@ public: return asImpl().visitDifferentTypeStructure(type1, type2); return asImpl().visit(type1.getElementType(), type2.getElementType()); } + + bool visitBuiltinUnboundGenericType(CanBuiltinUnboundGenericType type1, + CanBuiltinUnboundGenericType type2) { + return asImpl().visitDifferentTypeStructure(type1, type2); + } + + bool visitBuiltinFixedArrayType(CanBuiltinFixedArrayType type1, + CanBuiltinFixedArrayType type2) { + if (asImpl().visit(type1->getSize(), type2->getSize())) { + return true; + } + return asImpl().visit(type1->getElementType(), type2->getElementType()); + } bool visitPackType(CanPackType type1, CanPackType type2) { return visitComponentArray(type1, type2, diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index 6dc1d28a0d0..5b48c2dfb55 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -111,6 +111,8 @@ private: TRIVIAL_CASE(BuiltinIntegerType) TRIVIAL_CASE(BuiltinFloatType) TRIVIAL_CASE(BuiltinVectorType) + TRIVIAL_CASE(BuiltinUnboundGenericType) + TRIVIAL_CASE(BuiltinFixedArrayType) TRIVIAL_CASE(IntegerType) #define SINGLETON_TYPE(SHORT_ID, ID) TRIVIAL_CASE(ID##Type) #include "swift/AST/TypeNodes.def" diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index ef099ab27d5..0769efa7544 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -27,6 +27,14 @@ /// This type is a builtin type. The default behavior is /// ALWAYS_CANONICAL_TYPE(id, parent). +/// BUILTIN_CONCRETE_TYPE(id, parent) +/// This type is a builtin type without generic parameters. The default +/// behavior is BUILTIN_TYPE(id, parent). + +/// BUILTIN_GENERIC_TYPE(id, parent) +/// This type is a builtin type with generic parameters. The default +/// behavior is BUILTIN_TYPE(id, parent). + /// SUGARED_TYPE(id, parent) /// This type is never canonical. It provides an efficient accessor, /// getSinglyDesugaredType(), which removes one level of sugar. This @@ -72,6 +80,14 @@ #define BUILTIN_TYPE(id, parent) ALWAYS_CANONICAL_TYPE(id, parent) #endif +#ifndef BUILTIN_CONCRETE_TYPE +#define BUILTIN_CONCRETE_TYPE(id, parent) BUILTIN_TYPE(id, parent) +#endif + +#ifndef BUILTIN_GENERIC_TYPE +#define BUILTIN_GENERIC_TYPE(id, parent) BUILTIN_TYPE(id, parent) +#endif + #ifndef SUGARED_TYPE #define SUGARED_TYPE(id, parent) TYPE(id, parent) #endif @@ -111,22 +127,24 @@ UNCHECKED_TYPE(Unresolved, Type) UNCHECKED_TYPE(Placeholder, Type) ABSTRACT_TYPE(Builtin, Type) ABSTRACT_TYPE(AnyBuiltinInteger, BuiltinType) - BUILTIN_TYPE(BuiltinInteger, AnyBuiltinIntegerType) - BUILTIN_TYPE(BuiltinIntegerLiteral, AnyBuiltinIntegerType) + BUILTIN_CONCRETE_TYPE(BuiltinInteger, AnyBuiltinIntegerType) + BUILTIN_CONCRETE_TYPE(BuiltinIntegerLiteral, AnyBuiltinIntegerType) TYPE_RANGE(AnyBuiltinInteger, BuiltinInteger, BuiltinIntegerLiteral) - BUILTIN_TYPE(BuiltinExecutor, BuiltinType) - BUILTIN_TYPE(BuiltinFloat, BuiltinType) - BUILTIN_TYPE(BuiltinJob, BuiltinType) - BUILTIN_TYPE(BuiltinPackIndex, BuiltinType) - BUILTIN_TYPE(BuiltinRawPointer, BuiltinType) - BUILTIN_TYPE(BuiltinRawUnsafeContinuation, BuiltinType) - BUILTIN_TYPE(BuiltinNativeObject, BuiltinType) - BUILTIN_TYPE(BuiltinBridgeObject, BuiltinType) - BUILTIN_TYPE(BuiltinUnsafeValueBuffer, BuiltinType) - BUILTIN_TYPE(BuiltinDefaultActorStorage, BuiltinType) - BUILTIN_TYPE(BuiltinNonDefaultDistributedActorStorage, BuiltinType) - BUILTIN_TYPE(BuiltinVector, BuiltinType) - TYPE_RANGE(Builtin, BuiltinInteger, BuiltinVector) + BUILTIN_CONCRETE_TYPE(BuiltinExecutor, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinFloat, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinJob, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinPackIndex, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinRawPointer, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinRawUnsafeContinuation, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinNativeObject, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinBridgeObject, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinUnsafeValueBuffer, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinDefaultActorStorage, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinNonDefaultDistributedActorStorage, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinVector, BuiltinType) + BUILTIN_GENERIC_TYPE(BuiltinFixedArray, BuiltinType) + BUILTIN_CONCRETE_TYPE(BuiltinUnboundGeneric, BuiltinType) + TYPE_RANGE(Builtin, BuiltinInteger, BuiltinUnboundGeneric) TYPE(Tuple, Type) ABSTRACT_TYPE(ReferenceStorage, Type) #define REF_STORAGE(Name, ...) \ @@ -228,6 +246,8 @@ SINGLETON_TYPE(SILToken, SILToken) #undef UNCHECKED_TYPE #undef ARTIFICIAL_TYPE #undef SUGARED_TYPE +#undef BUILTIN_GENERIC_TYPE +#undef BUILTIN_CONCRETE_TYPE #undef BUILTIN_TYPE #undef ALWAYS_CANONICAL_TYPE #undef ALWAYS_CANONICAL_ARTIFICIAL_TYPE diff --git a/include/swift/AST/TypeTransform.h b/include/swift/AST/TypeTransform.h index e10d17a9c67..8b5bbd2d381 100644 --- a/include/swift/AST/TypeTransform.h +++ b/include/swift/AST/TypeTransform.h @@ -106,7 +106,7 @@ public: TypeBase *base = t.getPointer(); switch (base->getKind()) { -#define BUILTIN_TYPE(Id, Parent) \ +#define BUILTIN_CONCRETE_TYPE(Id, Parent) \ case TypeKind::Id: #define TYPE(Id, Parent) #include "swift/AST/TypeNodes.def" @@ -120,6 +120,31 @@ case TypeKind::Id: case TypeKind::Integer: return t; + case TypeKind::BuiltinFixedArray: { + auto bfaTy = cast(base); + + Type transSize = doIt(bfaTy->getSize(), + TypePosition::Invariant); + if (!transSize) { + return Type(); + } + + Type transElement = doIt(bfaTy->getElementType(), + TypePosition::Invariant); + if (!transElement) { + return Type(); + } + + CanType canTransSize = transSize->getCanonicalType(); + CanType canTransElement = transElement->getCanonicalType(); + if (canTransSize != bfaTy->getSize() + || canTransElement != bfaTy->getElementType()) { + return BuiltinFixedArrayType::get(canTransSize, canTransElement); + } + + return bfaTy; + } + case TypeKind::PrimaryArchetype: case TypeKind::PackArchetype: { auto *archetype = cast(base); diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 4e411c1a797..2ff5b6b1529 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -62,6 +62,7 @@ class ArgumentList; class AssociatedTypeDecl; class ASTContext; enum BufferPointerTypeKind : unsigned; +struct BuiltinNameStringLiteral; class BuiltinTupleDecl; class ClassDecl; class ClangModuleLoader; @@ -1508,6 +1509,9 @@ public: /// return `None`. std::optional getAutoDiffTangentSpace(LookupConformanceFn lookupConformance); + + /// Return the kind of generic parameter that this type can be matched to. + GenericTypeParamKind getMatchingParamKind(); }; /// AnyGenericType - This abstract class helps types ensure that fields @@ -1648,8 +1652,9 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(UnresolvedType, Type) /// BuiltinType - An abstract class for all the builtin types. class BuiltinType : public TypeBase { protected: - BuiltinType(TypeKind kind, const ASTContext &canTypeCtx) - : TypeBase(kind, &canTypeCtx, RecursiveTypeProperties()) {} + BuiltinType(TypeKind kind, const ASTContext &canTypeCtx, + RecursiveTypeProperties properties = {}) + : TypeBase(kind, &canTypeCtx, properties) {} public: static bool classof(const TypeBase *T) { return T->getKind() >= TypeKind::First_BuiltinType && @@ -1672,6 +1677,113 @@ public: }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinType, Type) +/// BuiltinUnboundGenericType - the base declaration of a generic builtin type +/// that has not yet had generic parameters applied. +/// +/// Referring to an unbound generic type by itself is invalid, but this +/// representation is used as an intermediate during type resolution when +/// resolving a type reference such as `Builtin.Int<31>`. Applying +/// the generic parameters produces the actual builtin type based on the +/// kind of the base. +class BuiltinUnboundGenericType : public BuiltinType { + friend class ASTContext; + TypeKind BoundGenericTypeKind; + + BuiltinUnboundGenericType(const ASTContext &C, + TypeKind genericTypeKind) + : BuiltinType(TypeKind::BuiltinUnboundGeneric, C), + BoundGenericTypeKind(genericTypeKind) + {} + +public: + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::BuiltinUnboundGeneric; + } + + /// Produce the unqualified name of the type. + BuiltinNameStringLiteral getBuiltinTypeName() const; + StringRef getBuiltinTypeNameString() const; + + static BuiltinUnboundGenericType *get(TypeKind genericTypeKind, + const ASTContext &C); + + /// Get the generic signature with which to substitute this type. + GenericSignature getGenericSignature() const; + + /// Get the type that results from binding the generic parameters of this + /// builtin to the given substitutions. + /// + /// Produces an ErrorType if the substitution is invalid. + Type getBound(SubstitutionMap subs) const; +}; +DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinUnboundGenericType, BuiltinType) + +/// BuiltinFixedArrayType - The builtin type representing N values stored +/// inline contiguously. +/// +/// All elements of a value of this type must be fully initialized any time the +/// value may be copied, moved, or destroyed. +class BuiltinFixedArrayType : public BuiltinType, public llvm::FoldingSetNode { + friend class ASTContext; + + CanType Size; + CanType ElementType; + + static RecursiveTypeProperties + getRecursiveTypeProperties(CanType Size, CanType Element) { + RecursiveTypeProperties properties; + properties |= Size->getRecursiveProperties(); + properties |= Element->getRecursiveProperties(); + return properties; + } + + BuiltinFixedArrayType(CanType Size, + CanType ElementType) + : BuiltinType(TypeKind::BuiltinFixedArray, ElementType->getASTContext(), + getRecursiveTypeProperties(Size, ElementType)), + Size(Size), + ElementType(ElementType) + {} + +public: + /// Arrays with more elements than this are always treated as in-memory values. + /// + /// (4096 is the hardcoded limit above which we refuse to import C arrays + /// as tuples. From first principles, a much lower threshold would probably + /// make sense, but we don't want to break the type lowering of C types + /// as they appear in existing Swift code.) + static constexpr const uint64_t MaximumLoadableSize = 4096; + + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::BuiltinFixedArray; + } + + static BuiltinFixedArrayType *get(CanType Size, + CanType ElementType); + + /// Get the integer generic parameter representing the number of elements. + CanType getSize() const { return Size; } + + /// Get the fixed integer number of elements if known and zero or greater. + std::optional getFixedInhabitedSize() const; + + /// True if the type is statically negative-sized (and therefore uninhabited). + bool isFixedNegativeSize() const; + + /// Get the element type. + CanType getElementType() const { return ElementType; } + + void Profile(llvm::FoldingSetNodeID &ID) const { + Profile(ID, getSize(), getElementType()); + } + static void Profile(llvm::FoldingSetNodeID &ID, + CanType Size, CanType ElementType) { + ID.AddPointer(Size.getPointer()); + ID.AddPointer(ElementType.getPointer()); + } +}; +DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinFixedArrayType, BuiltinType) + /// BuiltinRawPointerType - The builtin raw (and dangling) pointer type. This /// pointer is completely unmanaged and is equivalent to i8* in LLVM IR. class BuiltinRawPointerType : public BuiltinType { diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 9d8d0ade747..f45a19af894 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -46,6 +46,7 @@ NODE(BoundGenericTypeAlias) NODE(BoundGenericFunction) NODE(BuiltinTypeName) NODE(BuiltinTupleType) +NODE(BuiltinFixedArray) NODE(CFunctionPointer) NODE(ClangType) CONTEXT_NODE(Class) diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index c86e5145c59..a54baf27509 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -573,6 +573,14 @@ MetadataResponse swift_getForeignTypeMetadata(MetadataRequest request, ForeignTypeMetadata *nonUnique); +/// Fetch a metadata record representing a `Builtin.FixedArray` +/// of a given count and element type. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +MetadataResponse +swift_getFixedArrayTypeMetadata(MetadataRequest request, + intptr_t count, + const Metadata *element); + /// Fetch a uniqued metadata for a tuple type. /// /// The labels argument is null if and only if there are no element diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 21b706a947d..53d9cc3135d 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1313,6 +1313,17 @@ FUNCTION(GetTupleLayout3, swift_getTupleTypeLayout3, SwiftCC, AlwaysAvailable, EFFECT(NoEffect), UNKNOWN_MEMEFFECTS) +// MetadataResponse swift_getFixedArrayTypeMetadata(MetadataRequest request, +// intptr_t size, +// Metadata *elt); +FUNCTION(GetFixedArrayTypeMetadata, swift_getFixedArrayTypeMetadata, SwiftCC, + ValueGenericTypeAvailability, + RETURNS(TypeMetadataResponseTy), + ARGS(SizeTy, SizeTy, TypeMetadataPtrTy), + ATTRS(NoUnwind, WillReturn), + EFFECT(MetaData), + MEMEFFECTS(ReadOnly)) + // Metadata *swift_getExistentialTypeMetadata( // ProtocolClassConstraint classConstraint, // const Metadata *superclassConstraint, diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 81af3b733e2..5890eb32799 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -8580,6 +8580,7 @@ class StrongRetainInst StrongRetainInst(SILDebugLocation DebugLoc, SILValue Operand, Atomicity atomicity) : UnaryInstructionBase(DebugLoc, Operand) { + assert(!Operand->getType().getAs()); setAtomicity(atomicity); } }; diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 8538e6931a8..cf268f58b36 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -172,6 +172,8 @@ constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_UNKNOWNOBJECT = { /// The name of the Builtin type for Vector constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_VEC = { "Builtin.Vec"}; +constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_FIXEDARRAY = { + "Builtin.FixedArray"}; /// The name of the Builtin type for SILToken constexpr static BuiltinNameStringLiteral BUILTIN_TYPE_NAME_SILTOKEN = { "Builtin.SILToken"}; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 056f69c4b2c..deccbe80781 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -628,7 +628,9 @@ struct ASTContext::Implementation { llvm::FoldingSet SILBoxTypes; llvm::FoldingSet IntegerTypes; llvm::DenseMap BuiltinIntegerTypes; + llvm::DenseMap BuiltinUnboundGenericTypes; llvm::FoldingSet BuiltinVectorTypes; + llvm::FoldingSet BuiltinFixedArrayTypes; llvm::FoldingSet CompoundNames; llvm::DenseMap OpenedElementEnvironments; llvm::FoldingSet IndexSubsets; @@ -3571,6 +3573,38 @@ BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth, return Result; } +BuiltinUnboundGenericType * +BuiltinUnboundGenericType::get(TypeKind genericTypeKind, + const ASTContext &C) { + BuiltinUnboundGenericType *&Result + = C.getImpl().BuiltinUnboundGenericTypes[unsigned(genericTypeKind)]; + + if (Result == nullptr) { + Result = new (C, AllocationArena::Permanent) + BuiltinUnboundGenericType(C, genericTypeKind); + } + return Result; +} + +BuiltinFixedArrayType *BuiltinFixedArrayType::get(CanType Size, + CanType ElementType) { + llvm::FoldingSetNodeID id; + BuiltinFixedArrayType::Profile(id, Size, ElementType); + auto &context = Size->getASTContext(); + + void *insertPos; + if (BuiltinFixedArrayType *vecType + = context.getImpl().BuiltinFixedArrayTypes + .FindNodeOrInsertPos(id, insertPos)) + return vecType; + + BuiltinFixedArrayType *faTy + = new (context, AllocationArena::Permanent) + BuiltinFixedArrayType(Size, ElementType); + context.getImpl().BuiltinFixedArrayTypes.InsertNode(faTy, insertPos); + return faTy; +} + BuiltinVectorType *BuiltinVectorType::get(const ASTContext &context, Type elementType, unsigned numElements) { diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index c1b2b76028d..26ee7648a3d 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -4063,6 +4063,21 @@ namespace { printRec(T->getElementType()); printFoot(); } + + void visitBuiltinUnboundGenericType(BuiltinUnboundGenericType *T, + StringRef label) { + printCommon("builtin_unbound_generic_type", label); + printField(T->getBuiltinTypeNameString(), "name"); + printFoot(); + } + + void visitBuiltinFixedArrayType(BuiltinFixedArrayType *T, + StringRef label) { + printCommon("builtin_fixed_array_type", label); + printRec(T->getSize()); + printRec(T->getElementType()); + printFoot(); + } void visitTypeAliasType(TypeAliasType *T, StringRef label) { printCommon("type_alias_type", label); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index f9b1f7108c3..9ed9c778896 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1281,6 +1281,15 @@ void ASTMangler::appendType(Type type, GenericSignature sig, return appendOperator("Bb"); case TypeKind::BuiltinUnsafeValueBuffer: return appendOperator("BB"); + case TypeKind::BuiltinUnboundGeneric: + llvm_unreachable("not a real type"); + case TypeKind::BuiltinFixedArray: { + auto bfa = cast(tybase); + appendType(bfa->getSize(), sig, forDecl); + appendType(bfa->getElementType(), sig, forDecl); + return appendOperator("BV"); + } + case TypeKind::SILToken: return appendOperator("Bt"); case TypeKind::BuiltinVector: diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 9a829450c74..59cc5b52e90 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -6073,6 +6073,8 @@ public: ASTPRINTER_PRINT_BUILTINTYPE(BuiltinVectorType) ASTPRINTER_PRINT_BUILTINTYPE(BuiltinIntegerType) ASTPRINTER_PRINT_BUILTINTYPE(BuiltinFloatType) + ASTPRINTER_PRINT_BUILTINTYPE(BuiltinUnboundGenericType) + ASTPRINTER_PRINT_BUILTINTYPE(BuiltinFixedArrayType) #undef ASTPRINTER_PRINT_BUILTINTYPE void visitSILTokenType(SILTokenType *T) { diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 7b3c7a1566c..268cb4ac823 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Builtins.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTSynthesis.h" +#include "swift/AST/ConformanceLookup.h" #include "swift/AST/FileUnit.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" @@ -58,6 +59,10 @@ IntrinsicInfo::getOrCreateAttributes(ASTContext &Ctx) const { } Type swift::getBuiltinType(ASTContext &Context, StringRef Name) { + if (Name == "FixedArray") { + return BuiltinUnboundGenericType::get(TypeKind::BuiltinFixedArray, Context); + } + // Vectors are VecNxT, where "N" is the number of elements and // T is the element type. if (Name.starts_with("Vec")) { @@ -112,6 +117,10 @@ Type swift::getBuiltinType(ASTContext &Context, StringRef Name) { if (Name == "IntLiteral") return Context.TheIntegerLiteralType; + if (Name == "Int") { + return BuiltinUnboundGenericType::get(TypeKind::BuiltinInteger, Context); + } + // Handle 'int8' and friends. if (Name.substr(0, 3) == "Int") { unsigned BitWidth; @@ -2217,6 +2226,21 @@ static ValueDecl *getAddressOfRawLayout(ASTContext &ctx, Identifier id) { return builder.build(id); } +static ValueDecl *getEmplace(ASTContext &ctx, Identifier id) { + BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1); + + auto T = makeGenericParam(); + + auto fnParamTy = FunctionType::get(FunctionType::Param(ctx.TheRawPointerType), + ctx.TheEmptyTupleType, + ASTExtInfo().withNoEscape()); + + builder.addParameter(makeConcrete(fnParamTy), ParamSpecifier::Borrowing); + builder.setResult(T); + + return builder.build(id); +} + /// An array of the overloaded builtin kinds. static const OverloadedBuiltinKind OverloadedBuiltinKinds[] = { OverloadedBuiltinKind::None, @@ -3300,6 +3324,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::AddressOfRawLayout: return getAddressOfRawLayout(Context, Id); + + case BuiltinValueKind::Emplace: + return getEmplace(Context, Id); } llvm_unreachable("bad builtin value!"); @@ -3354,7 +3381,16 @@ bool BuiltinType::isBitwiseCopyable() const { case BuiltinTypeKind::BuiltinUnsafeValueBuffer: case BuiltinTypeKind::BuiltinDefaultActorStorage: case BuiltinTypeKind::BuiltinNonDefaultDistributedActorStorage: + case BuiltinTypeKind::BuiltinUnboundGeneric: return false; + + case BuiltinTypeKind::BuiltinFixedArray: { + // Bitwise-copyability depends on the element type. + auto bfa = cast(this); + auto &C = bfa->getASTContext(); + return (bool)lookupConformance(bfa->getElementType(), + C.getProtocol(KnownProtocolKind::BitwiseCopyable)); + } } } @@ -3462,8 +3498,131 @@ StringRef BuiltinType::getTypeName(SmallVectorImpl &result, } break; } + case BuiltinTypeKind::BuiltinFixedArray: { + auto bfa = cast(this); + printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FIXEDARRAY) + << '<'; + bfa->getSize()->print(printer); + printer << ", "; + bfa->getElementType()->print(printer); + printer << '>'; + break; + } + case BuiltinTypeKind::BuiltinUnboundGeneric: { + auto bug = cast(this); + printer << MAYBE_GET_NAMESPACED_BUILTIN(bug->getBuiltinTypeName()); + break; + } } #undef MAYBE_GET_NAMESPACED_BUILTIN return printer.str(); } + +BuiltinNameStringLiteral +BuiltinUnboundGenericType::getBuiltinTypeName() const { + switch (BoundGenericTypeKind) { + case TypeKind::BuiltinFixedArray: + return BUILTIN_TYPE_NAME_FIXEDARRAY; + case TypeKind::BuiltinInteger: + return BUILTIN_TYPE_NAME_INT; + + default: + llvm_unreachable("not a generic builtin kind"); + } +} + +StringRef +BuiltinUnboundGenericType::getBuiltinTypeNameString() const { + return getBuiltinTypeName(); +} + +GenericSignature +BuiltinUnboundGenericType::getGenericSignature() const { + auto &C = getASTContext(); + + switch (BoundGenericTypeKind) { + case TypeKind::BuiltinFixedArray: { + auto Count = GenericTypeParamType::get(C.getIdentifier("Count"), + GenericTypeParamKind::Value, + 0, 0, C.getIntType(), C); + auto Element = GenericTypeParamType::get(C.getIdentifier("Element"), + GenericTypeParamKind::Type, + 0, 1, Type(), C); + return GenericSignature::get({Count, Element}, {}); + } + + case TypeKind::BuiltinInteger: { + auto bits = GenericTypeParamType::get(C.getIdentifier("Bits"), + GenericTypeParamKind::Type, + 0, 0, C.getIntType(), C); + return GenericSignature::get(bits, {}); + } + default: + llvm_unreachable("not a generic builtin"); + } +} + +Type +BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const { + if (!subs.getGenericSignature()->isEqual(getGenericSignature())) { + return ErrorType::get(const_cast(this)); + } + + switch (BoundGenericTypeKind) { + case TypeKind::BuiltinFixedArray: { + auto types = subs.getReplacementTypes(); + + auto size = types[0]->getCanonicalType(); + if (size->getMatchingParamKind() != GenericTypeParamKind::Value) { + return ErrorType::get(const_cast(this)); + } + auto element = types[1]->getCanonicalType(); + if (element->getMatchingParamKind() != GenericTypeParamKind::Type) { + return ErrorType::get(const_cast(this)); + } + + return BuiltinFixedArrayType::get(size, element); + } + + case TypeKind::BuiltinInteger: { + auto size = subs.getReplacementTypes()[0]; + if (size->getMatchingParamKind() != GenericTypeParamKind::Value) { + return ErrorType::get(const_cast(this)); + } + + // TODO: support actual generic parameters + auto literalSize = size->getAs(); + + if (!literalSize) { + return ErrorType::get(const_cast(this)); + } + + return BuiltinIntegerType::get(literalSize->getValue().getLimitedValue(), + getASTContext()); + } + + default: + llvm_unreachable("not a generic builtin kind"); + } +} + +std::optional +BuiltinFixedArrayType::getFixedInhabitedSize() const { + if (auto intSize = getSize()->getAs()) { + if (intSize->getValue().isNegative()) { + return std::nullopt; + } + return intSize->getValue().getLimitedValue(); + } + + return std::nullopt; +} + +bool +BuiltinFixedArrayType::isFixedNegativeSize() const { + if (auto intSize = getSize()->getAs()) { + return intSize->getValue().isNegative(); + } + return false; +} diff --git a/lib/AST/ConformanceLookup.cpp b/lib/AST/ConformanceLookup.cpp index 02737fd65f4..abef5b62c8e 100644 --- a/lib/AST/ConformanceLookup.cpp +++ b/lib/AST/ConformanceLookup.cpp @@ -406,11 +406,22 @@ getBuiltinBuiltinTypeConformance(Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) { if (auto kp = protocol->getKnownProtocolKind()) { switch (*kp) { - // All builtin types are Sendable, Copyable, and Escapable. case KnownProtocolKind::Sendable: case KnownProtocolKind::Copyable: case KnownProtocolKind::Escapable: { ASTContext &ctx = protocol->getASTContext(); + + // FixedArray is Sendable, Copyable, or Escapable if its element type is. + if (auto bfa = dyn_cast(builtinType)) { + if (lookupConformance(bfa->getElementType(), protocol)) { + return ProtocolConformanceRef( + ctx.getBuiltinConformance(type, protocol, + BuiltinConformanceKind::Synthesized)); + } + break; + } + + // All other builtin types are Sendable, Copyable, and Escapable. return ProtocolConformanceRef( ctx.getBuiltinConformance(type, protocol, BuiltinConformanceKind::Synthesized)); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 346d295e1ab..558ffaeb6ef 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -258,6 +258,8 @@ bool CanType::isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig, case TypeKind::BuiltinTuple: case TypeKind::ErrorUnion: case TypeKind::Integer: + case TypeKind::BuiltinUnboundGeneric: + case TypeKind::BuiltinFixedArray: #define REF_STORAGE(Name, ...) \ case TypeKind::Name##Storage: #include "swift/AST/ReferenceStorage.def" @@ -4333,6 +4335,8 @@ ReferenceCounting TypeBase::getReferenceCounting() { case TypeKind::BuiltinTuple: case TypeKind::ErrorUnion: case TypeKind::Integer: + case TypeKind::BuiltinUnboundGeneric: + case TypeKind::BuiltinFixedArray: #define REF_STORAGE(Name, ...) \ case TypeKind::Name##Storage: #include "swift/AST/ReferenceStorage.def" @@ -4910,3 +4914,24 @@ TypeBase::getConcurrencyDiagnosticBehaviorLimit(DeclContext *declCtx) const { return {}; } + +GenericTypeParamKind +TypeBase::getMatchingParamKind() { + if (auto gtpt = dyn_cast(this)) { + return gtpt->getParamKind(); + } + + if (auto arch = dyn_cast(this)) { + return arch->mapTypeOutOfContext()->getMatchingParamKind(); + } + + if (isa(this)) { + return GenericTypeParamKind::Value; + } + + if (isa(this)) { + return GenericTypeParamKind::Pack; + } + + return GenericTypeParamKind::Type; +} diff --git a/lib/AST/TypeWalker.cpp b/lib/AST/TypeWalker.cpp index 9e890ecabb2..cbc366eff8b 100644 --- a/lib/AST/TypeWalker.cpp +++ b/lib/AST/TypeWalker.cpp @@ -297,6 +297,16 @@ class Traversal : public TypeVisitor } return false; } + + bool visitBuiltinFixedArrayType(BuiltinFixedArrayType *ty) { + if (ty->getSize() && doIt(ty->getSize())) { + return true; + } + if (ty->getElementType() && doIt(ty->getElementType())) { + return true; + } + return false; + } public: explicit Traversal(TypeWalker &walker) : Walker(walker) {} diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 52432d54079..2d53e9b980e 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1464,6 +1464,18 @@ NodePointer Demangler::demangleBuiltinType() { Ty = createNode(Node::Kind::BuiltinTypeName, name); break; } + case 'V': { + NodePointer element = popNode(Node::Kind::Type); + if (!element) + return nullptr; + NodePointer size = popNode(Node::Kind::Type); + if (!size) + return nullptr; + Ty = createNode(Node::Kind::BuiltinFixedArray); + Ty->addChild(size, *this); + Ty->addChild(element, *this); + break; + } case 'O': Ty = createNode(Node::Kind::BuiltinTypeName, BUILTIN_TYPE_NAME_UNKNOWNOBJECT); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 99d3e844456..9151049efc0 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -296,6 +296,7 @@ private: case Node::Kind::BoundGenericFunction: case Node::Kind::BuiltinTypeName: case Node::Kind::BuiltinTupleType: + case Node::Kind::BuiltinFixedArray: case Node::Kind::Class: case Node::Kind::DependentGenericType: case Node::Kind::DependentMemberType: @@ -1974,6 +1975,13 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, case Node::Kind::BuiltinTupleType: Printer << "Builtin.TheTupleType"; return nullptr; + case Node::Kind::BuiltinFixedArray: + Printer << "Builtin.FixedArray<"; + print(Node->getChild(0), depth + 1); + Printer << ", "; + print(Node->getChild(1), depth + 1); + Printer << ">"; + return nullptr; case Node::Kind::Number: Printer << Node->getIndex(); return nullptr; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index e38cf372417..b4b1a7a0be4 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1530,6 +1530,10 @@ static bool stripPrefix(StringRef &string, const char (&data)[N]) { return true; } +ManglingError Remangler::mangleBuiltinFixedArray(Node *node, unsigned depth) { + return MANGLING_ERROR(ManglingError::UnexpectedBuiltinType, node); +} + ManglingError Remangler::mangleBuiltinTypeName(Node *node, unsigned depth) { Buffer << 'B'; StringRef text = node->getText(); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 088cd672334..eea4ac5b66c 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -894,6 +894,12 @@ ManglingError Remangler::mangleBoundGenericFunction(Node *node, return ManglingError::Success; } +ManglingError Remangler::mangleBuiltinFixedArray(Node *node, unsigned depth) { + RETURN_IF_ERROR(mangleChildNodes(node, depth + 1)); + Buffer << "BV"; + return ManglingError::Success; +} + ManglingError Remangler::mangleBuiltinTypeName(Node *node, unsigned depth) { Buffer << 'B'; StringRef text = node->getText(); diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index f0f90c03c77..acb64ec8b49 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -8,6 +8,7 @@ add_swift_host_library(swiftIRGen STATIC ExtraInhabitants.cpp Fulfillment.cpp GenArchetype.cpp + GenArray.cpp GenBuiltin.cpp GenCall.cpp GenCast.cpp diff --git a/lib/IRGen/GenArray.cpp b/lib/IRGen/GenArray.cpp new file mode 100644 index 00000000000..b32ae7d62ab --- /dev/null +++ b/lib/IRGen/GenArray.cpp @@ -0,0 +1,702 @@ +//===--- GenArray.cpp - LLVM type lowering of fixed-size array types ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements TypeInfo subclasses for `Builtin.FixedArray`. +// +//===----------------------------------------------------------------------===// + +#include "FixedTypeInfo.h" +#include "GenType.h" +#include "IRGenModule.h" +#include "LoadableTypeInfo.h" +#include "NonFixedTypeInfo.h" + +using namespace swift; +using namespace irgen; + +template +class ArrayTypeInfoBase : public BaseTypeInfo { +protected: + const ElementTypeInfo ∈ + + template + ArrayTypeInfoBase(const ElementTypeInfo &elementTI, Args &&...args) + : BaseTypeInfo(std::forward(args)...), + Element(elementTI) + {} + + static SILType getElementSILType(IRGenModule &IGM, + SILType arrayType) { + return IGM.getLoweredType(AbstractionPattern::getOpaque(), + arrayType.castTo()->getElementType()); + } + + virtual llvm::Value *getArraySize(IRGenFunction &IGF, SILType T) const = 0; + virtual std::optional getFixedArraySize(SILType T) const = 0; + + void eachElementAddrLoop(IRGenFunction &IGF, + SILType T, + llvm::function_ref)> body, + ArrayRef
addrs) const { + auto fixedSize = getFixedArraySize(T); + if (fixedSize == 0) { + // empty type, nothing to do + return; + } + if (fixedSize == 1) { + // only one element to operate on + return body(addrs); + } + + auto arraySize = getArraySize(IGF, T); + auto predBB = IGF.Builder.GetInsertBlock(); + + auto loopBB = IGF.createBasicBlock("each_array_element"); + auto endBB = IGF.createBasicBlock("end_array_element"); + + auto one = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, 1); + auto zero = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, 0); + + if (!fixedSize.has_value()) { + // If the size isn't statically known, we have to dynamically check the + // zero case. + auto isEmptyArray = IGF.Builder.CreateICmpEQ(arraySize, zero); + IGF.Builder.CreateCondBr(isEmptyArray, endBB, loopBB); + } else { + // Otherwise, we statically handled the zero case above. + IGF.Builder.CreateBr(loopBB); + } + + IGF.Builder.emitBlock(loopBB); + + auto countPhi = IGF.Builder.CreatePHI(IGF.IGM.IntPtrTy, 2); + countPhi->addIncoming(arraySize, predBB); + + ConditionalDominanceScope scope(IGF); + + SmallVector addrPhis; + SmallVector eltAddrs; + for (auto a : addrs) { + auto *addrPhi = IGF.Builder.CreatePHI(a.getType(), 2); + addrPhi->addIncoming(a.getAddress(), predBB); + addrPhis.push_back(addrPhi); + eltAddrs.push_back(Address(addrPhi, Element.getStorageType(), + a.getAlignment())); + } + + body(eltAddrs); + + for (unsigned i : indices(addrPhis)) { + addrPhis[i]->addIncoming(Element.indexArray(IGF, eltAddrs[i], one, + getElementSILType(IGF.IGM, T)) + .getAddress(), + loopBB); + } + + auto nextCount = IGF.Builder.CreateSub(countPhi, one); + countPhi->addIncoming(nextCount, loopBB); + + auto done = IGF.Builder.CreateICmpEQ(nextCount, zero); + IGF.Builder.CreateCondBr(done, endBB, loopBB); + + IGF.Builder.emitBlock(endBB); + } + +public: + void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, + SILType T, bool isOutlined) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddrLoop(IGF, T, + [&](ArrayRef
destAndSrc) { + Element.assignWithCopy(IGF, destAndSrc[0], + destAndSrc[1], + eltTy, isOutlined); + }, {dest, src}); + } + + void assignWithTake(IRGenFunction &IGF, Address dest, Address src, + SILType T, bool isOutlined) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddrLoop(IGF, T, + [&](ArrayRef
destAndSrc) { + Element.assignWithTake(IGF, destAndSrc[0], + destAndSrc[1], + eltTy, isOutlined); + }, {dest, src}); + } + + void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, + SILType T, bool isOutlined) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddrLoop(IGF, T, + [&](ArrayRef
destAndSrc) { + Element.initializeWithCopy(IGF, destAndSrc[0], + destAndSrc[1], + eltTy, isOutlined); + }, {dest, src}); + } + + + void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, + SILType T, + bool isOutlined, + bool zeroizeIfSensitive) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddrLoop(IGF, T, + [&](ArrayRef
destAndSrc) { + Element.initializeWithTake(IGF, destAndSrc[0], + destAndSrc[1], + eltTy, isOutlined, + zeroizeIfSensitive); + }, {dest, src}); + } + + virtual void destroy(IRGenFunction &IGF, Address address, SILType T, + bool isOutlined) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddrLoop(IGF, T, + [&](ArrayRef
elt) { + Element.destroy(IGF, elt[0], eltTy, isOutlined); + }, {address}); + } +}; + +template +class FixedArrayTypeInfoBase : public ArrayTypeInfoBase { +protected: + using ArrayTypeInfoBase::Element; + const uint64_t ArraySize; + + using ArrayTypeInfoBase::getElementSILType; + + template + FixedArrayTypeInfoBase(unsigned arraySize, + const BaseTypeInfo &elementTI, Args &&...args) + : ArrayTypeInfoBase(elementTI, std::forward(args)...), + ArraySize(arraySize) + {} + + static Size getArraySize(uint64_t arraySize, + const FixedTypeInfo &elementTI) { + // We always pad out the stride, even for the final element. + return Size(arraySize * elementTI.getFixedStride().getValue()); + } + + static llvm::Type *getArrayType(uint64_t arraySize, + const FixedTypeInfo &elementTI) { + // Start with the element's storage type. + llvm::Type *elementTy = elementTI.getStorageType(); + auto &LLVMContext = elementTy->getContext(); + + if (arraySize == 0) { + return llvm::StructType::get(LLVMContext, {}); + } + + // If we need to, pad it to stride. + if (elementTI.getFixedSize() < elementTI.getFixedStride()) { + uint64_t paddingBytes = elementTI.getFixedStride().getValue() + - elementTI.getFixedSize().getValue(); + auto byteTy = llvm::IntegerType::get(LLVMContext, 8); + elementTy = llvm::StructType::get(LLVMContext, + {elementTy, + llvm::ArrayType::get(byteTy, paddingBytes)}, + /*packed*/ true); + } + + return llvm::ArrayType::get(elementTy, arraySize); + } + + static SpareBitVector getArraySpareBits(uint64_t arraySize, + const FixedTypeInfo &elementTI) { + if (arraySize == 0) { + return SpareBitVector(); + } + + // Take spare bits from the first element only. + SpareBitVector result = elementTI.getSpareBits(); + // We can use the padding to the next element as spare bits too. + result.appendSetBits(getArraySize(arraySize, elementTI).getValueInBits() + - result.size()); + return result; + } + + void eachElement(llvm::function_ref body) const { + for (uint64_t i = 0; i < ArraySize; ++i) { + body(); + } + } + + void eachElementAddr(IRGenFunction &IGF, Address addr, + llvm::function_ref body) const { + for (uint64_t i = 0; i < ArraySize; ++i) { + auto elementAddr = Element.indexArray(IGF, addr, + llvm::ConstantInt::get(IGF.IGM.IntPtrTy, i), + SILType()); + + body(elementAddr); + } + } + + std::optional getFixedArraySize(SILType T) const override { + return ArraySize; + } + + llvm::Value *getArraySize(IRGenFunction &IGF, SILType T) const override { + return llvm::ConstantInt::get(IGF.IGM.IntPtrTy, ArraySize); + } + +public: + void getSchema(ExplosionSchema &schema) const override { + eachElement([&]{ + Element.getSchema(schema); + }); + } + + TypeLayoutEntry * + buildTypeLayoutEntry(IRGenModule &IGM, + SILType T, + bool useStructLayouts) const override { + auto eltTy = getElementSILType(IGM, T); + auto elementLayout = Element.buildTypeLayoutEntry(IGM, eltTy, + useStructLayouts); + return IGM.typeLayoutCache.getOrCreateArrayEntry(elementLayout, eltTy, + T.castTo()->getSize()); + } + + void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, + Address src, SILType T, + bool isOutlined) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddr(IGF, src, + [&](Address elementAddr) { + Element.initializeFromParams(IGF, params, elementAddr, + eltTy, isOutlined); + }); + } + + // We take extra inhabitants from the first element, if any. + unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { + if (ArraySize == 0) + return 0; + return Element.getFixedExtraInhabitantCount(IGM); + } + + APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override { + if (ArraySize == 0) + return APInt::getAllOnes(0); + APInt elementMask = Element.getFixedExtraInhabitantMask(IGM); + return elementMask.zext(this->getFixedSize().getValueInBits()); + } + + /// Create a constant of the given bit width holding one of the extra + /// inhabitants of the type. + /// The index must be less than the value returned by + /// getFixedExtraInhabitantCount(). + APInt getFixedExtraInhabitantValue(IRGenModule &IGM, + unsigned bits, + unsigned index) const override { + return Element.getFixedExtraInhabitantValue(IGM, bits, index); + } + + llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, + Address src, SILType T, + bool isOutlined) const override { + if (ArraySize == 0) + return llvm::ConstantInt::get(IGF.IGM.Int32Ty, -1); + + auto firstElementAddr + = IGF.Builder.CreateElementBitCast(src, Element.getStorageType()); + + return Element.getExtraInhabitantIndex(IGF, firstElementAddr, + getElementSILType(IGF.IGM, T), + isOutlined); + } + + void storeExtraInhabitant(IRGenFunction &IGF, + llvm::Value *index, + Address dest, SILType T, + bool isOutlined) const override { + auto firstElementAddr + = IGF.Builder.CreateElementBitCast(dest, Element.getStorageType()); + + Element.storeExtraInhabitant(IGF, index, firstElementAddr, + getElementSILType(IGF.IGM, T), + isOutlined); + } +}; + +class LoadableArrayTypeInfo final + : public FixedArrayTypeInfoBase +{ +public: + LoadableArrayTypeInfo(uint64_t arraySize, + const LoadableTypeInfo &elementTI) + : FixedArrayTypeInfoBase(arraySize, elementTI, + getArrayType(arraySize, elementTI), + getArraySize(arraySize, elementTI), + getArraySpareBits(arraySize, elementTI), + elementTI.getFixedAlignment(), + elementTI.isTriviallyDestroyable(ResilienceExpansion::Maximal), + elementTI.isCopyable(ResilienceExpansion::Maximal), + elementTI.isFixedSize(ResilienceExpansion::Minimal), + elementTI.isABIAccessible()) + { + } + + unsigned getExplosionSize() const override { + return Element.getExplosionSize() * ArraySize; + } + + void loadAsCopy(IRGenFunction &IGF, Address addr, + Explosion &explosion) const override { + eachElementAddr(IGF, addr, + [&](Address elementAddr) { + Element.loadAsCopy(IGF, elementAddr, explosion); + }); + } + + void loadAsTake(IRGenFunction &IGF, Address addr, + Explosion &explosion) const override { + eachElementAddr(IGF, addr, + [&](Address elementAddr) { + Element.loadAsTake(IGF, elementAddr, explosion); + }); + } + + void assign(IRGenFunction &IGF, Explosion &explosion, Address addr, + bool isOutlined, SILType T) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElementAddr(IGF, addr, + [&](Address elementAddr) { + Element.assign(IGF, explosion, elementAddr, isOutlined, + eltTy); + }); + } + + void initialize(IRGenFunction &IGF, Explosion &explosion, Address addr, + bool isOutlined) const override { + eachElementAddr(IGF, addr, + [&](Address elementAddr) { + Element.initialize(IGF, explosion, elementAddr, isOutlined); + }); + } + + void reexplode(Explosion &sourceExplosion, + Explosion &targetExplosion) const override { + eachElement([&]{ + Element.reexplode(sourceExplosion, targetExplosion); + }); + } + + void copy(IRGenFunction &IGF, + Explosion &sourceExplosion, + Explosion &targetExplosion, + Atomicity atomicity) const override { + eachElement([&]{ + Element.copy(IGF, sourceExplosion, targetExplosion, atomicity); + }); + } + + void consume(IRGenFunction &IGF, Explosion &explosion, + Atomicity atomicity, + SILType T) const override { + auto eltTy = getElementSILType(IGF.IGM, T); + eachElement([&]{ + Element.consume(IGF, explosion, atomicity, eltTy); + }); + } + + void fixLifetime(IRGenFunction &IGF, + Explosion &explosion) const override { + eachElement([&]{ + Element.fixLifetime(IGF, explosion); + }); + } + + template + void eachElementOffset(Body &&body) const { + for (unsigned i = 0; i < ArraySize; ++i) { + body(i * Element.getFixedStride().getValue()); + } + } + + void packIntoEnumPayload(IRGenModule &IGM, + IRBuilder &builder, + EnumPayload &payload, + Explosion &sourceExplosion, + unsigned offset) const override { + eachElementOffset([&](unsigned eltByteOffset){ + Element.packIntoEnumPayload(IGM, builder, payload, + sourceExplosion, + offset + eltByteOffset * 8); + }); + } + + void unpackFromEnumPayload(IRGenFunction &IGF, + const EnumPayload &payload, + Explosion &targetExplosion, + unsigned offset) const override { + eachElementOffset([&](unsigned eltByteOffset){ + Element.unpackFromEnumPayload(IGF, payload, + targetExplosion, + offset + eltByteOffset * 8); + }); + } + + void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering, + Size offset) const override { + eachElementOffset([&](unsigned eltByteOffset){ + Element.addToAggLowering(IGM, lowering, + Size(offset.getValue() + eltByteOffset)); + }); + } +}; + +class FixedArrayTypeInfo final + : public FixedArrayTypeInfoBase +{ +public: + FixedArrayTypeInfo(uint64_t arraySize, + const FixedTypeInfo &elementTI) + : FixedArrayTypeInfoBase(arraySize, elementTI, + getArrayType(arraySize, elementTI), + getArraySize(arraySize, elementTI), + getArraySpareBits(arraySize, elementTI), + elementTI.getFixedAlignment(), + elementTI.isTriviallyDestroyable(ResilienceExpansion::Maximal), + elementTI.getBitwiseTakable(ResilienceExpansion::Maximal), + elementTI.isCopyable(ResilienceExpansion::Maximal), + elementTI.isFixedSize(ResilienceExpansion::Minimal), + elementTI.isABIAccessible()) + { + } +}; + +// NOTE: This does not simply use WitnessSizedTypeInfo in order to avoid +// dependency on a Swift runtime for handling fixed-size arrays that are +// unspecialized in their size parameter only, so that embedded Swift can +// work with unspecialized integer parameters. +class NonFixedArrayTypeInfo final + : public ArrayTypeInfoBase, + TypeInfo> { + using super = ArrayTypeInfoBase, + TypeInfo>; + + llvm::Value *getArraySize(IRGenFunction &IGF, SILType T) const override { + if (auto fixedSize = getFixedArraySize(T)) { + return llvm::ConstantInt::get(IGF.IGM.IntPtrTy, *fixedSize); + } + + CanType sizeParam = T.castTo()->getSize(); + + auto arg = IGF.emitValueGenericRef(sizeParam); + auto zero = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, 0); + auto isNegative = IGF.Builder.CreateICmpSLT(arg, zero); + return IGF.Builder.CreateSelect(isNegative, zero, arg); + } + + std::optional getFixedArraySize(SILType T) const override { + CanType sizeParam = T.castTo()->getSize(); + + if (auto integer = sizeParam->getAs()) { + if (integer->getValue().isNonNegative()) { + return integer->getValue().getLimitedValue(); + } + } + return std::nullopt; + } + +public: + NonFixedArrayTypeInfo(llvm::Type *opaqueTy, + const TypeInfo &Element) + : super(Element, + opaqueTy, Element.getBestKnownAlignment(), + Element.isTriviallyDestroyable(ResilienceExpansion::Maximal), + Element.getBitwiseTakable(ResilienceExpansion::Maximal), + Element.isCopyable(ResilienceExpansion::Maximal), + IsNotFixedSize, + Element.isABIAccessible(), + SpecialTypeInfoKind::None) + {} + + llvm::Value *getSize(IRGenFunction &IGF, SILType T) const override { + auto elementStride + = Element.getStride(IGF, getElementSILType(IGF.IGM, T)); + return IGF.Builder.CreateMul(elementStride, getArraySize(IGF, T)); + } + llvm::Value *getAlignmentMask(IRGenFunction &IGF, + SILType T) const override { + return Element.getAlignmentMask(IGF, getElementSILType(IGF.IGM, T)); + } + llvm::Value *getStride(IRGenFunction &IGF, SILType T) const override { + return getSize(IGF, T); + } + llvm::Value *getIsTriviallyDestroyable(IRGenFunction &IGF, + SILType T) const override { + return Element.getIsTriviallyDestroyable(IGF, + getElementSILType(IGF.IGM, T)); + } + llvm::Value *getIsBitwiseTakable(IRGenFunction &IGF, + SILType T) const override { + return Element.getIsBitwiseTakable(IGF, + getElementSILType(IGF.IGM, T)); + } + llvm::Value *isDynamicallyPackedInline(IRGenFunction &IGF, + SILType T) const override { + auto startBB = IGF.Builder.GetInsertBlock(); + auto no = llvm::ConstantInt::getBool(IGF.IGM.getLLVMContext(), + false); + // Prefetch the necessary info from the element type info. + auto isBT = getIsBitwiseTakable(IGF, T); + auto size = getSize(IGF, T); + auto align = getAlignmentMask(IGF, T); + + auto endBB = IGF.createBasicBlock("array_is_packed_inline"); + IGF.Builder.SetInsertPoint(endBB); + auto result = IGF.Builder.CreatePHI(IGF.IGM.Int1Ty, 3); + IGF.Builder.SetInsertPoint(startBB); + + // packed inline if the payload is bitwise-takable... + auto isBT_BB = IGF.createBasicBlock("array_is_bt"); + IGF.Builder.CreateCondBr(isBT, isBT_BB, endBB); + result->addIncoming(no, startBB); + + IGF.Builder.emitBlock(isBT_BB); + + // ...size fits the fixed-size buffer... + auto bufferSize = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, + getFixedBufferSize(IGF.IGM).getValue()); + auto sizeFits = IGF.Builder.CreateICmpULE(size, bufferSize); + auto sizeFitsBB = IGF.createBasicBlock("array_size_fits"); + IGF.Builder.CreateCondBr(sizeFits, sizeFitsBB, endBB); + result->addIncoming(no, isBT_BB); + + IGF.Builder.emitBlock(sizeFitsBB); + + // ...and so does alignment + auto bufferAlign = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, + getFixedBufferAlignment(IGF.IGM).getMaskValue()); + auto alignFits = IGF.Builder.CreateICmpULE(align, bufferAlign); + IGF.Builder.CreateBr(endBB); + result->addIncoming(alignFits, sizeFitsBB); + + IGF.Builder.emitBlock(endBB); + return result; + } + + bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { + return Element.mayHaveExtraInhabitants(IGM); + } + + llvm::Constant *getStaticSize(IRGenModule &IGM) const override { + return nullptr; + } + llvm::Constant *getStaticAlignmentMask(IRGenModule &IGM) const override { + return nullptr; + } + llvm::Constant *getStaticStride(IRGenModule &IGM) const override { + return nullptr; + } + + StackAddress allocateStack(IRGenFunction &IGF, SILType T, + const llvm::Twine &name) const override { + // Allocate memory on the stack. + auto alloca = IGF.emitDynamicAlloca(T, name); + IGF.Builder.CreateLifetimeStart(alloca.getAddressPointer()); + return alloca.withAddress(getAddressForPointer(alloca.getAddressPointer())); + } + + StackAddress allocateVector(IRGenFunction &IGF, SILType T, + llvm::Value *capacity, + const Twine &name) const override { + llvm_unreachable("not implemented, yet"); + } + + void deallocateStack(IRGenFunction &IGF, StackAddress stackAddress, + SILType T) const override { + IGF.Builder.CreateLifetimeEnd(stackAddress.getAddress().getAddress()); + IGF.emitDeallocateDynamicAlloca(stackAddress); + } + + void destroyStack(IRGenFunction &IGF, StackAddress stackAddress, SILType T, + bool isOutlined) const override { + emitDestroyCall(IGF, T, stackAddress.getAddress()); + deallocateStack(IGF, stackAddress, T); + } + + TypeLayoutEntry * + buildTypeLayoutEntry(IRGenModule &IGM, + SILType T, + bool useStructLayouts) const override { + return IGM.typeLayoutCache.getOrCreateResilientEntry(T); + } + + llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF, + llvm::Value *numEmptyCases, + Address arrayAddr, + SILType arrayType, + bool isOutlined) const override { + // take extra inhabitants from the first element + auto firstElementAddr + = IGF.Builder.CreateElementBitCast(arrayAddr, Element.getStorageType()); + return Element.getEnumTagSinglePayload(IGF, + numEmptyCases, + firstElementAddr, + getElementSILType(IGF.IGM, arrayType), + isOutlined); + } + + void storeEnumTagSinglePayload(IRGenFunction &IGF, + llvm::Value *index, + llvm::Value *numEmptyCases, + Address arrayAddr, + SILType arrayType, + bool isOutlined) const override { + // take extra inhabitants from the first element + auto firstElementAddr + = IGF.Builder.CreateElementBitCast(arrayAddr, Element.getStorageType()); + return Element.storeEnumTagSinglePayload(IGF, + index, + numEmptyCases, + firstElementAddr, + getElementSILType(IGF.IGM, arrayType), + isOutlined); + } +}; + +const TypeInfo * +TypeConverter::convertBuiltinFixedArrayType(BuiltinFixedArrayType *T) { + // Most of our layout properties come from the element type. + auto &elementTI = IGM.getTypeInfoForUnlowered(AbstractionPattern::getOpaque(), + T->getElementType()); + // ...unless the array size is not fixed, then the array layout is never + // fixed. + auto fixedSize = T->getFixedInhabitedSize(); + + // Statically zero or negative-sized array types are empty. + if (fixedSize == 0 || T->isFixedNegativeSize()) { + return &getEmptyTypeInfo(); + } + + if (!fixedSize.has_value() || !elementTI.isFixedSize()) { + return new NonFixedArrayTypeInfo(IGM.OpaqueTy, elementTI); + } + + if (*fixedSize <= BuiltinFixedArrayType::MaximumLoadableSize) { + if (auto *loadableTI = dyn_cast(&elementTI)) { + return new LoadableArrayTypeInfo(fixedSize.value(), *loadableTI); + } + } + + return new FixedArrayTypeInfo(fixedSize.value(), + *cast(&elementTI)); +} diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 84f341b40d5..2cccd858241 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -2313,6 +2313,12 @@ const TypeInfo *TypeConverter::convertType(CanType ty) { auto spareBits = SpareBitVector::getConstant(size.getValueInBits(), false); return new PrimitiveTypeInfo(ty, size, std::move(spareBits), align); } + case TypeKind::BuiltinUnboundGeneric: + llvm_unreachable("not a real type"); + + case TypeKind::BuiltinFixedArray: { + return convertBuiltinFixedArrayType(cast(ty)); + } case TypeKind::PrimaryArchetype: case TypeKind::OpenedArchetype: diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index c8ecf5d2b26..19d8eb5a5b9 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -183,7 +183,9 @@ private: #define REF_STORAGE(Name, ...) \ const TypeInfo *convert##Name##StorageType(Name##StorageType *T); #include "swift/AST/ReferenceStorage.def" - + const TypeInfo *convertBuiltinFixedArrayType(BuiltinFixedArrayType *T); + + public: TypeConverter(IRGenModule &IGM); ~TypeConverter(); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 44da9948bd5..7a7d951854f 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -1794,6 +1794,16 @@ createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type, // Here goes! switch (BaseTy->getKind()) { + case TypeKind::BuiltinUnboundGeneric: + llvm_unreachable("not a real type"); + + case TypeKind::BuiltinFixedArray: { + // TODO: provide proper array debug info + unsigned FwdDeclLine = 0; + return createOpaqueStruct(Scope, "Builtin.FixedArray", MainFile, FwdDeclLine, + SizeInBits, AlignInBits, Flags, MangledName); + } + case TypeKind::BuiltinPackIndex: case TypeKind::BuiltinInteger: { Encoding = llvm::dwarf::DW_ATE_unsigned; diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 92fcdf9f364..a7ccedab6c6 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1002,6 +1002,14 @@ namespace RuntimeConstants { return RuntimeAvailability::AlwaysAvailable; } + RuntimeAvailability ValueGenericTypeAvailability(ASTContext &Context) { + auto featureAvailability = Context.getValueGenericTypeAvailability(); + if (!isDeploymentAvailabilityContainedIn(Context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } + } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 62ab044adbe..414929bf19f 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1268,6 +1268,28 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF, return MetadataResponse::handle(IGF, request, result); } +static MetadataResponse emitFixedArrayMetadataRef(IRGenFunction &IGF, + CanBuiltinFixedArrayType type, + DynamicMetadataRequest request) { + if (type->isFixedNegativeSize() + || type->getFixedInhabitedSize() == 0) { + // Empty or negative-sized arrays are empty types. + return MetadataResponse::forComplete( + emitEmptyTupleTypeMetadataRef(IGF.IGM)); + } + + auto call = IGF.Builder.CreateCall( + IGF.IGM.getGetFixedArrayTypeMetadataFunctionPointer(), + {request.get(IGF), + IGF.emitValueGenericRef(type->getSize()), + IGF.emitTypeMetadataRef(type->getElementType(), MetadataState::Abstract) + .getMetadata()}); + call->setCallingConv(IGF.IGM.SwiftCC); + call->setDoesNotThrow(); + + return MetadataResponse::handle(IGF, request, call); +} + static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF, CanTupleType type, DynamicMetadataRequest request) { @@ -1288,8 +1310,7 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF, emitEmptyTupleTypeMetadataRef(IGF.IGM)); case 1: - // For metadata purposes, we consider a singleton tuple to be - // isomorphic to its element type. ??? + // A singleton tuple is isomorphic to its element type. return IGF.emitTypeMetadataRef(type.getElementType(0), request); case 2: { @@ -1861,7 +1882,24 @@ namespace { DynamicMetadataRequest request) { return emitDirectMetadataRef(type); } + + MetadataResponse + visitBuiltinUnboundGenericType(CanBuiltinUnboundGenericType type, + DynamicMetadataRequest request) { + llvm_unreachable("not a real type"); + } + MetadataResponse + visitBuiltinFixedArrayType(CanBuiltinFixedArrayType type, + DynamicMetadataRequest request) { + if (auto cached = tryGetLocal(type, request)) + return cached; + + auto response = emitFixedArrayMetadataRef(IGF, type, request); + + return setLocal(type, response); + } + MetadataResponse visitNominalType(CanNominalType type, DynamicMetadataRequest request) { assert(!type->isExistentialType()); diff --git a/lib/IRGen/Outlining.cpp b/lib/IRGen/Outlining.cpp index 1f882f898cd..f835e63ff95 100644 --- a/lib/IRGen/Outlining.cpp +++ b/lib/IRGen/Outlining.cpp @@ -245,7 +245,10 @@ void OutliningMetadataCollector::bindPolymorphicParameters( auto key = pair.first; assert(key.Kind.isAnyTypeMetadata()); setTypeMetadataName(IGF.IGM, arg, key.Type); - IGF.setUnscopedLocalTypeData(key, MetadataResponse::forComplete(arg)); + IGF.bindLocalTypeDataFromTypeMetadata(key.Type, + IsExact, + arg, + MetadataState::Complete); } return; } diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 7b9ddaad9f3..e4cd320e4b3 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -2585,6 +2585,31 @@ public: return nom; } + CanType visitBuiltinFixedArrayType(CanBuiltinFixedArrayType bfa, + AbstractionPattern pattern) { + auto orig = pattern.getAs(); + + // If there are no loose type parameters in the pattern here, we don't need + // to do a recursive visit at all. + if (!orig->hasTypeParameter() + && !orig->hasArchetype() + && !orig->hasOpaqueArchetype()) { + return bfa; + } + + CanType newSize = visit(bfa->getSize(), + AbstractionPattern(pattern.getGenericSubstitutions(), + pattern.getGenericSignatureOrNull(), + orig->getSize())); + CanType newElement = visit(bfa->getElementType(), + AbstractionPattern(pattern.getGenericSubstitutions(), + pattern.getGenericSignatureOrNull(), + orig->getElementType())); + + return BuiltinFixedArrayType::get(newSize, newElement) + ->getCanonicalType(); + } + CanType visitBoundGenericType(CanBoundGenericType bgt, AbstractionPattern pattern) { return handleGenericNominalType(pattern, bgt); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 78ecfe9c41c..d0be55efb4f 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -357,6 +357,51 @@ namespace { #undef IMPL + RetTy visitBuiltinUnboundGenericType(CanBuiltinUnboundGenericType type, + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive){ + llvm_unreachable("not a real type"); + } + + RecursiveProperties getBuiltinFixedArrayProperties( + CanBuiltinFixedArrayType type, + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + RecursiveProperties props; + + // We get most of the type properties from the element type. + AbstractionPattern origElementType = AbstractionPattern::getOpaque(); + if (auto bfaOrigTy = origType.getAs()) { + origElementType = AbstractionPattern( + origType.getGenericSignatureOrNull(), + bfaOrigTy->getElementType()); + } + + props.addSubobject(classifyType(origElementType, type->getElementType(), + TC, Expansion)); + + props = mergeIsTypeExpansionSensitive(isSensitive, props); + + // If the size isn't a known literal, then the layout is also dependent, + // so make it address-only. If the size is massive, also treat it as + // address-only since there's little practical benefit to passing around + // huge amounts of data "directly". + auto fixedSize = type->getFixedInhabitedSize(); + if (!type->isFixedNegativeSize() + && (!fixedSize.has_value() + || *fixedSize > BuiltinFixedArrayType::MaximumLoadableSize)) { + props.setAddressOnly(); + } + return props; + } + + RetTy visitBuiltinFixedArrayType(CanBuiltinFixedArrayType type, + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + return asImpl().handleAggregateByProperties(type, + getBuiltinFixedArrayProperties(type, origType, isSensitive)); + } + RetTy visitPackType(CanPackType type, AbstractionPattern origType, IsTypeExpansionSensitive_t isSensitive) { @@ -1359,7 +1404,7 @@ namespace { forEachNonTrivialChild(B, loc, aggValue, Fn); } }; - + /// A lowering for loadable but non-trivial tuple types. class LoadableTupleTypeLowering final : public LoadableAggTypeLowering { @@ -1765,6 +1810,44 @@ namespace { } }; + /// A class for nonspecific loadable nontrivial types. + class MiscNontrivialTypeLowering : public LeafLoadableTypeLowering { + public: + MiscNontrivialTypeLowering(SILType type, RecursiveProperties properties, + TypeExpansionContext forExpansion) + : LeafLoadableTypeLowering(type, properties, IsNotReferenceCounted, + forExpansion) {} + + MiscNontrivialTypeLowering(CanType type, RecursiveProperties properties, + TypeExpansionContext forExpansion) + : MiscNontrivialTypeLowering(SILType::getPrimitiveObjectType(type), + properties, forExpansion) + {} + + + SILValue emitCopyValue(SILBuilder &B, SILLocation loc, + SILValue value) const override { + if (isa(value) || isa(value) || + isa(value)) + return value; + + if (B.getFunction().hasOwnership()) + return B.createCopyValue(loc, value); + + B.createRetainValue(loc, value, B.getDefaultAtomicity()); + return value; + } + + void emitDestroyValue(SILBuilder &B, SILLocation loc, + SILValue value) const override { + if (B.getFunction().hasOwnership()) { + B.createDestroyValue(loc, value); + return; + } + B.createReleaseValue(loc, value, B.getDefaultAtomicity()); + } + }; + /// A class for reference types, which are all non-trivial but still /// loadable. class ReferenceTypeLowering : public LeafLoadableTypeLowering { @@ -2342,6 +2425,14 @@ namespace { return handleAggregateByProperties(tupleType, properties); } + + TypeLowering *visitBuiltinFixedArrayType(CanBuiltinFixedArrayType faType, + AbstractionPattern origType, + IsTypeExpansionSensitive_t isSensitive) { + + return handleAggregateByProperties(faType, + getBuiltinFixedArrayProperties(faType, origType, isSensitive)); + } bool handleResilience(CanType type, NominalTypeDecl *D, RecursiveProperties &properties) { diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 488bf559e5a..3b073e663c5 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -2077,6 +2077,72 @@ static ManagedValue emitBuiltinAddressOfRawLayout(SILGenFunction &SGF, return ManagedValue::forObjectRValueWithoutOwnership(bi); } +static ManagedValue emitBuiltinEmplace(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap subs, + ArrayRef args, + SGFContext C) { + // TODO: deal with reabstraction of the result type + + auto &Ctx = SGF.getASTContext(); + auto resultASTTy = subs.getReplacementTypes()[0]; + auto &loweredBufferTy = SGF.getTypeLowering(AbstractionPattern::getOpaque(), + resultASTTy); + bool didEmitInto; + Initialization *dest; + std::unique_ptr destOwner; + + // Use the context destination if available. + if (C.getEmitInto() + && C.getEmitInto()->canPerformInPlaceInitialization()) { + didEmitInto = true; + dest = C.getEmitInto(); + } else { + didEmitInto = false; + destOwner = SGF.emitTemporary(loc, loweredBufferTy); + dest = destOwner.get(); + } + + auto buffer = dest->getAddressForInPlaceInitialization(SGF, loc); + + // Zero-initialize the buffer. + // Aside from providing a modicum of predictability if the memory isn't + // actually initialized, this also serves to communicate to DI that the memory + // is considered initialized from this point. + auto zeroInit = getBuiltinValueDecl(Ctx, + Ctx.getIdentifier("zeroInitializer")); + SGF.B.createBuiltin(loc, zeroInit->getBaseIdentifier(), + SILType::getEmptyTupleType(Ctx), + SubstitutionMap::get(zeroInit->getInnermostDeclContext() + ->getGenericSignatureOfContext(), + {resultASTTy}, + LookUpConformanceInModule()), + buffer); + + SILValue bufferPtr = SGF.B.createAddressToPointer(loc, buffer, + SILType::getPrimitiveObjectType(SGF.getASTContext().TheRawPointerType), + /*needs stack protection*/ true); + SGF.B.createApply(loc, args[0].getValue(), {}, bufferPtr); + + dest->finishInitialization(SGF); + + if (didEmitInto) { + C.getEmitInto()->finishInitialization(SGF); + return ManagedValue::forInContext(); + } + + auto resultTy = SGF.getLoweredType(subs.getReplacementTypes()[0]); + + // If the result is naturally address-only, then we can adopt the stack slot + // as the value directly. + if (resultTy == loweredBufferTy.getLoweredType().getAddressType()) { + return SGF.emitManagedRValueWithCleanup(buffer); + } + + // If the result is loadable, load it. + return SGF.B.createLoadTake(loc, ManagedValue::forLValue(buffer)); +} + std::optional SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) { // Only consider standalone declarations in the Builtin module. diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index e012046aea6..d619f03e54e 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -907,6 +907,45 @@ static Type applyGenericArguments(Type type, return parameterized; } + + // Builtins have special handling. + if (auto bug = type->getAs()) { + // We don't have any variadic builtins yet, but we do have value arguments. + auto argOptions = options.withoutContext() + .withContext(TypeResolverContext::ValueGenericArgument); + auto genericResolution = resolution.withOptions(argOptions); + + // Resolve the types of the generic arguments. + SmallVector args; + for (auto tyR : genericArgs) { + // Propagate failure. + Type substTy = genericResolution.resolveType(tyR, silContext); + if (!substTy || substTy->hasError()) + return ErrorType::get(ctx); + + args.push_back(substTy); + } + + // Try to form a substitution map. + auto subs = SubstitutionMap::get(bug->getGenericSignature(), + args, + [&](CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) + -> ProtocolConformanceRef { + // no generic builtins have conformance + // requirements yet. + llvm_unreachable("not implemented yet"); + }); + + auto bound = bug->getBound(subs); + + if (bound->hasError()) { + diags.diagnose(loc, diag::invalid_generic_builtin_type, type); + return ErrorType::get(ctx); + } + return bound; + } // We must either have an unbound generic type, or a generic type alias. if (!type->is()) { diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index a4a0aa77e9c..a9b94c9d48b 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -122,7 +122,9 @@ TYPE(ERROR) TYPE(INTEGER) -FIRST_DECL(TYPE_ALIAS, 40) +TYPE(BUILTIN_FIXED_ARRAY) + +FIRST_DECL(TYPE_ALIAS, 50) DECL(GENERIC_TYPE_PARAM) DECL(ASSOCIATED_TYPE) DECL(STRUCT) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index b354195cda3..e91f6229bbd 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6827,6 +6827,32 @@ DESERIALIZE_TYPE(BUILTIN_ALIAS_TYPE)( return alias->getDeclaredInterfaceType(); } +Expected +DESERIALIZE_TYPE(BUILTIN_FIXED_ARRAY_TYPE)( + ModuleFile &MF, SmallVectorImpl &scratch, StringRef blobData) +{ + TypeID sizeID; + TypeID elementTypeID; + decls_block::BuiltinFixedArrayTypeLayout::readRecord(scratch, sizeID, + elementTypeID); + + + auto sizeOrError = MF.getTypeChecked(sizeID); + if (!sizeOrError) { + return sizeOrError.takeError(); + } + auto size = sizeOrError.get()->getCanonicalType(); + + auto elementTypeOrError = MF.getTypeChecked(elementTypeID); + if (!elementTypeOrError) { + return elementTypeOrError.takeError(); + } + auto elementType = elementTypeOrError.get()->getCanonicalType(); + + return BuiltinFixedArrayType::get(size, elementType); +} + + Expected DESERIALIZE_TYPE(NAME_ALIAS_TYPE)( ModuleFile &MF, SmallVectorImpl &scratch, StringRef blobData) { DeclID typealiasID; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9b7a48376c6..3dafb9f62aa 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 896; // @yield_once_2 produces address +const uint16_t SWIFTMODULE_VERSION_MINOR = 897; // Builtin.FixedArray /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1230,6 +1230,12 @@ namespace decls_block { TypeIDField // canonical type (a fallback) ); + TYPE_LAYOUT(BuiltinFixedArrayTypeLayout, + BUILTIN_FIXED_ARRAY_TYPE, + TypeIDField, // count + TypeIDField // element type + ); + TYPE_LAYOUT(TypeAliasTypeLayout, NAME_ALIAS_TYPE, DeclIDField, // typealias decl diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index cc7c8b737af..f26cd982d20 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -5437,6 +5437,15 @@ public: void visitBuiltinType(BuiltinType *ty) { visitBuiltinTypeImpl(ty); } + + void visitBuiltinFixedArrayType(BuiltinFixedArrayType *ty) { + using namespace decls_block; + unsigned abbrCode = S.DeclTypeAbbrCodes[BuiltinFixedArrayTypeLayout::Code]; + BuiltinFixedArrayTypeLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, + S.addTypeRef(ty->getSize()), + S.addTypeRef(ty->getElementType())); + } void visitSILTokenType(SILTokenType *ty) { // This is serialized like a BuiltinType, even though it isn't one. diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 5206b798ec6..9989e956cc3 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -1709,6 +1709,343 @@ FunctionCacheEntry::FunctionCacheEntry(const Key &key) { } } +/***************************************************************************/ +/*** Vectors ****************************************************************/ +/***************************************************************************/ + +namespace { + +class FixedArrayCacheEntry + : public MetadataCacheEntryBase +{ +public: + // We have to give MetadataCacheEntryBase a non-empty list of trailing + // objects or else it gets annoyed. + template + static size_t numTrailingObjects(OverloadToken, Etc &&...) { return 0; } + + AllocationResult allocate() { + swift_unreachable("allocated during construction"); + } + + ValueWitnessTable Witnesses; + FullMetadata Data; + + struct Key { + intptr_t Count; + const Metadata *Element; + + + static llvm::hash_code hash_value(intptr_t count, const Metadata *elt) { + return llvm::hash_combine(count, elt); + } + + friend llvm::hash_code hash_value(const Key &key) { + return hash_value(key.Count, key.Element); + } + }; + + static const char *getName() { return "FixedArrayCache"; } + + ValueType getValue() { + return &Data; + } + void setValue(ValueType value) { + assert(value == &Data); + } + + FixedArrayCacheEntry(const Key &key, MetadataWaitQueue::Worker &worker, + MetadataRequest request); + + MetadataStateWithDependency tryInitialize(Metadata *metadata, + PrivateMetadataState state, + PrivateMetadataCompletionContext *context); + + MetadataStateWithDependency checkTransitiveCompleteness() { + auto dependency = ::checkTransitiveCompleteness(&Data); + return { dependency ? PrivateMetadataState::NonTransitiveComplete + : PrivateMetadataState::Complete, + dependency }; + } + + intptr_t getKeyIntValueForDump() { + return 0; // No single meaningful value + } + + friend llvm::hash_code hash_value(const FixedArrayCacheEntry &value) { + return Key::hash_value(value.Data.Count, value.Data.Element); + } + + bool matchesKey(const Key &key) { + return Data.Count == key.Count && Data.Element == key.Element; + } + +}; + +class FixedArrayCacheStorage : + public LockingConcurrentMapStorage { +public: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-offsetof" + static FixedArrayCacheEntry * + resolveExistingEntry(const FixedArrayTypeMetadata *metadata) { + // The correctness of this arithmetic is verified by an assertion in + // the TupleCacheEntry constructor. + auto bytes = reinterpret_cast(asFullMetadata(metadata)); + bytes -= offsetof(FixedArrayCacheEntry, Data); + auto entry = reinterpret_cast(bytes); + return const_cast(entry); + } +#pragma clang diagnostic pop +}; + +class FixedArrayCache : + public LockingConcurrentMap { +}; + +static Lazy FixedArrayTypes; + +} // end anonymous namespace + +MetadataResponse +swift::swift_getFixedArrayTypeMetadata(MetadataRequest request, + intptr_t count, + const Metadata *element) { + // An empty array is laid out like an empty tuple. + // Since Builtin.FixedArray is a builtin type, we don't try to guarantee it a + // unique runtime identity. + if (count <= 0) { + return MetadataResponse{&METADATA_SYM(EMPTY_TUPLE_MANGLING), + MetadataState::Complete}; + } + + // If the element type has no tail padding, then its metadata is good enough + // to hold space for the vector. + if (count == 1 + && element->getValueWitnesses()->size == element->getValueWitnesses()->stride) { + return MetadataResponse{element, MetadataState::Complete}; + } + + auto &cache = FixedArrayTypes.get(); + FixedArrayCacheEntry::Key key{count, element}; + return cache.getOrInsert(key, request).second; +} + +FixedArrayCacheEntry::FixedArrayCacheEntry(const Key &key, + MetadataWaitQueue::Worker &worker, + MetadataRequest request) + : MetadataCacheEntryBase(worker, PrivateMetadataState::Abstract) { + Data.setKind(MetadataKind::FixedArray); + Data.Count = key.Count; + Data.Element = key.Element; + + assert(FixedArrayCacheStorage::resolveExistingEntry(&Data) == this); +} + +/// Given a metatype pointer, produce the value-witness table for it. +static const ValueWitnessTable *generic_getValueWitnesses(const Metadata *metatype) { + return asFullMetadata(metatype)->ValueWitnesses; +} + +/// Generic value witness for 'projectBuffer'. +template +static OpaqueValue *generic_projectBuffer(ValueBuffer *buffer, + const Metadata *metatype) { + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); + + if (IsInline) + return reinterpret_cast(buffer); + + auto wtable = generic_getValueWitnesses(metatype); + unsigned alignMask = wtable->getAlignmentMask(); + // Compute the byte offset of the object in the box. + unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask; + auto *bytePtr = + reinterpret_cast(*reinterpret_cast(buffer)); + return reinterpret_cast(bytePtr + byteOffset); +} + +static void +vector_destroy(OpaqueValue *dest, const Metadata *metatype) { + if (metatype->getValueWitnesses()->isPOD()) { + return; + } + + auto vectorType = cast(metatype); + auto destBytes = (char*)dest; + + for (unsigned i = 0, end = vectorType->getRealizedCount(), + stride = vectorType->Element->vw_stride(); + i < end; + ++i, destBytes += stride) { + vectorType->Element->vw_destroy((OpaqueValue*)destBytes); + } +} + +namespace { +template +OpaqueValue * +vector_elementwise_transfer(OpaqueValue *dest, OpaqueValue *src, + const Metadata *metatype, + ElementFn &&elementFn) { + if (metatype->getValueWitnesses()->isPOD()) { + memcpy(dest, src, metatype->vw_size()); + return dest; + } + + auto vectorType = cast(metatype); + auto destBytes = (char*)dest; + auto srcBytes = (char*)src; + + for (unsigned i = 0, end = vectorType->getRealizedCount(), + stride = vectorType->Element->vw_stride(); + i < end; + ++i, destBytes += stride, srcBytes += stride) { + elementFn( + (OpaqueValue*)destBytes, + (OpaqueValue*)srcBytes, + vectorType->Element); + } + return dest; +} +} +static OpaqueValue * +vector_initializeWithCopy(OpaqueValue *dest, OpaqueValue *src, + const Metadata *metatype) { + return vector_elementwise_transfer(dest, src, metatype, + [&](OpaqueValue *destElt, OpaqueValue *srcElt, const Metadata *eltType) { + eltType->vw_initializeWithCopy(destElt, srcElt); + }); +} +static OpaqueValue * +vector_assignWithCopy(OpaqueValue *dest, OpaqueValue *src, + const Metadata *metatype) { + return vector_elementwise_transfer(dest, src, metatype, + [&](OpaqueValue *destElt, OpaqueValue *srcElt, const Metadata *eltType) { + eltType->vw_assignWithCopy(destElt, srcElt); + }); +} +static OpaqueValue * +vector_initializeWithTake(OpaqueValue *dest, OpaqueValue *src, + const Metadata *metatype) { + return vector_elementwise_transfer(dest, src, metatype, + [&](OpaqueValue *destElt, OpaqueValue *srcElt, const Metadata *eltType) { + eltType->vw_initializeWithTake(destElt, srcElt); + }); +} +static OpaqueValue * +vector_assignWithTake(OpaqueValue *dest, OpaqueValue *src, + const Metadata *metatype) { + return vector_elementwise_transfer(dest, src, metatype, + [&](OpaqueValue *destElt, OpaqueValue *srcElt, const Metadata *eltType) { + eltType->vw_assignWithTake(destElt, srcElt); + }); +} + +static OpaqueValue *vector_initializeBufferWithCopyOfBuffer(ValueBuffer *dest, + ValueBuffer *src, + const Metadata *metatype) { + if (metatype->getValueWitnesses()->isValueInline()) { + return vector_initializeWithCopy( + generic_projectBuffer(dest, metatype), + generic_projectBuffer(src, metatype), + metatype); + } + + auto *srcReference = *reinterpret_cast(src); + *reinterpret_cast(dest) = srcReference; + swift_retain(srcReference); + return generic_projectBuffer(dest, metatype); +} + +static unsigned +vector_getEnumTagSinglePayload(const OpaqueValue *theEnum, + unsigned numEmptyCases, + const Metadata *metatype) { + auto vectorType = cast(metatype); + return vectorType->Element->vw_getEnumTagSinglePayload(theEnum, + numEmptyCases); +} + +static void +vector_storeEnumTagSinglePayload(OpaqueValue *theEnum, + unsigned whichCase, + unsigned numEmptyCases, + const Metadata *metatype) { + auto vectorType = cast(metatype); + vectorType->Element->vw_storeEnumTagSinglePayload(theEnum, + whichCase, + numEmptyCases); +} + +MetadataStateWithDependency +FixedArrayCacheEntry::tryInitialize(Metadata *metadata, + PrivateMetadataState state, + PrivateMetadataCompletionContext *context) { + // If we've already reached non-transitive completeness, just check that. + if (state == PrivateMetadataState::NonTransitiveComplete) { + return checkTransitiveCompleteness(); + } + + // Otherwise, we must still be abstract, because vectors don't have an + // intermediate state between that and non-transitive completeness. + assert(state == PrivateMetadataState::Abstract); + + // Require the element type to be layout-complete. + const Metadata *element = Data.Element; + auto eltRequest = MetadataRequest(MetadataState::LayoutComplete, + /*nonblocking*/ true); + auto eltResponse = swift_checkMetadataState(eltRequest, element); + + // If the element is not layout-complete, we have to suspend. + if (!isAtLeast(eltResponse.State, MetadataState::LayoutComplete)) { + return {PrivateMetadataState::Abstract, + MetadataDependency(element, MetadataState::LayoutComplete)}; + } + + // We can derive the array's layout from the element's. + intptr_t count = Data.Count; + // We should have checked for empty and uninhabited cases before getting this + // far. + assert(count > 0); + Data.ValueWitnesses = &Witnesses; + auto eltWitnesses = element->getValueWitnesses(); + auto arraySize + = Witnesses.size = Witnesses.stride = eltWitnesses->stride * count; + // We take on most of the properties of the element type, except that an array + // of elements might end up larger than an inline buffer. + Witnesses.flags = eltWitnesses->flags + .withInlineStorage( + ValueWitnessTable::isValueInline(eltWitnesses->isBitwiseTakable(), + arraySize, + eltWitnesses->getAlignment())); + // We get extra inhabitants from the first element. + Witnesses.extraInhabitantCount = eltWitnesses->extraInhabitantCount; + + // Copy in the value witnesses. + // TODO: Specialize witnesses for POD etc.? +#define WANT_ONLY_REQUIRED_VALUE_WITNESSES +#define VALUE_WITNESS(LOWER_ID, UPPER_ID) \ + Witnesses.LOWER_ID = vector_##LOWER_ID; +#define DATA_VALUE_WITNESS(LOWER_ID, UPPER_ID, TYPE) +#include "swift/ABI/ValueWitness.def" + + // If the element type is complete, so are we. + if (eltResponse.State == MetadataState::Complete) { + return {PrivateMetadataState::Complete, MetadataDependency()}; + } + + // If it isn't at least non-transitively complete, wait for it to be. + if (!isAtLeast(eltResponse.State, MetadataState::NonTransitiveComplete)) { + return {PrivateMetadataState::NonTransitiveComplete, + MetadataDependency(element, + MetadataState::NonTransitiveComplete) }; + } + + // Otherwise, do a full completeness check. + return checkTransitiveCompleteness(); +} + /***************************************************************************/ /*** Tuples ****************************************************************/ /***************************************************************************/ @@ -1721,8 +2058,6 @@ class TupleCacheEntry public: static const char *getName() { return "TupleCache"; } - // NOTE: if you change the layout of this type, you'll also need - // to update tuple_getValueWitnesses(). unsigned ExtraInhabitantProvidingElement; ValueWitnessTable Witnesses; FullMetadata Data; @@ -1824,7 +2159,6 @@ public: class TupleCacheStorage : public LockingConcurrentMapStorage { public: -// FIXME: https://github.com/apple/swift/issues/43763. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winvalid-offsetof" static TupleCacheEntry * @@ -1848,37 +2182,12 @@ class TupleCache : /// The uniquing structure for tuple type metadata. static Lazy TupleTypes; -/// Given a metatype pointer, produce the value-witness table for it. -/// This is equivalent to metatype->ValueWitnesses but more efficient. -static const ValueWitnessTable *tuple_getValueWitnesses(const Metadata *metatype) { - return asFullMetadata(metatype)->ValueWitnesses; -} - -/// Generic tuple value witness for 'projectBuffer'. -template -static OpaqueValue *tuple_projectBuffer(ValueBuffer *buffer, - const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); - - if (IsInline) - return reinterpret_cast(buffer); - - auto wtable = tuple_getValueWitnesses(metatype); - unsigned alignMask = wtable->getAlignmentMask(); - // Compute the byte offset of the object in the box. - unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask; - auto *bytePtr = - reinterpret_cast(*reinterpret_cast(buffer)); - return reinterpret_cast(bytePtr + byteOffset); -} - /// Generic tuple value witness for 'allocateBuffer' template static OpaqueValue *tuple_allocateBuffer(ValueBuffer *buffer, const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(metatype)->isPOD()); + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); if (IsInline) return reinterpret_cast(buffer); @@ -1891,8 +2200,8 @@ static OpaqueValue *tuple_allocateBuffer(ValueBuffer *buffer, template static void tuple_destroy(OpaqueValue *tuple, const Metadata *_metadata) { auto &metadata = *(const TupleTypeMetadata*) _metadata; - assert(IsPOD == tuple_getValueWitnesses(&metadata)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(&metadata)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(&metadata)->isPOD()); + assert(IsInline == generic_getValueWitnesses(&metadata)->isValueInline()); if (IsPOD) return; @@ -1938,8 +2247,8 @@ template static OpaqueValue *tuple_initializeWithCopy(OpaqueValue *dest, OpaqueValue *src, const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(metatype)->isPOD()); + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); if (IsPOD) return tuple_memcpy(dest, src, metatype); return tuple_forEachField(dest, src, metatype, @@ -1953,8 +2262,8 @@ template static OpaqueValue *tuple_initializeWithTake(OpaqueValue *dest, OpaqueValue *src, const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(metatype)->isPOD()); + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); if (IsPOD) return tuple_memcpy(dest, src, metatype); return tuple_forEachField(dest, src, metatype, @@ -1968,8 +2277,8 @@ template static OpaqueValue *tuple_assignWithCopy(OpaqueValue *dest, OpaqueValue *src, const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(metatype)->isPOD()); + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); if (IsPOD) return tuple_memcpy(dest, src, metatype); return tuple_forEachField(dest, src, metatype, @@ -1995,18 +2304,18 @@ template static OpaqueValue *tuple_initializeBufferWithCopyOfBuffer(ValueBuffer *dest, ValueBuffer *src, const Metadata *metatype) { - assert(IsPOD == tuple_getValueWitnesses(metatype)->isPOD()); - assert(IsInline == tuple_getValueWitnesses(metatype)->isValueInline()); + assert(IsPOD == generic_getValueWitnesses(metatype)->isPOD()); + assert(IsInline == generic_getValueWitnesses(metatype)->isValueInline()); if (IsInline) { return tuple_initializeWithCopy( - tuple_projectBuffer(dest, metatype), - tuple_projectBuffer(src, metatype), metatype); + generic_projectBuffer(dest, metatype), + generic_projectBuffer(src, metatype), metatype); } auto *srcReference = *reinterpret_cast(src); *reinterpret_cast(dest) = srcReference; swift_retain(srcReference); - return tuple_projectBuffer(dest, metatype); + return generic_projectBuffer(dest, metatype); } SWIFT_CC(swift) @@ -2047,7 +2356,7 @@ static unsigned tuple_getEnumTagSinglePayload(const OpaqueValue *enumAddr, unsigned numEmptyCases, const Metadata *self) { - auto *witnesses = tuple_getValueWitnesses(self); + auto *witnesses = generic_getValueWitnesses(self); auto size = witnesses->getSize(); auto numExtraInhabitants = witnesses->getNumExtraInhabitants(); auto getExtraInhabitantTag = tuple_getExtraInhabitantTag; @@ -2061,7 +2370,7 @@ template static void tuple_storeEnumTagSinglePayload(OpaqueValue *enumAddr, unsigned whichCase, unsigned numEmptyCases, const Metadata *self) { - auto *witnesses = tuple_getValueWitnesses(self); + auto *witnesses = generic_getValueWitnesses(self); auto size = witnesses->getSize(); auto numExtraInhabitants = witnesses->getNumExtraInhabitants(); auto storeExtraInhabitantTag = tuple_storeExtraInhabitantTag; diff --git a/stdlib/public/runtime/MetadataAllocatorTags.def b/stdlib/public/runtime/MetadataAllocatorTags.def index 0184d1ce7fb..94a826d5225 100644 --- a/stdlib/public/runtime/MetadataAllocatorTags.def +++ b/stdlib/public/runtime/MetadataAllocatorTags.def @@ -49,5 +49,6 @@ TAG(ExtendedExistentialTypeShapes, 23) TAG(MetadataPack, 24) TAG(WitnessTablePack, 25) TAG(LayoutString, 26) +TAG(FixedArrayCache, 27) #undef TAG diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index d1ebc57cd30..1ae20f838b8 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1811,6 +1811,10 @@ checkInvertibleRequirementsStructural(const Metadata *type, // The existential representation has no room for specifying any // suppressed requirements, so it always succeeds. return std::nullopt; + + case MetadataKind::FixedArray: + // Builtin.FixedArray has no conformances of its own. + return std::nullopt; case MetadataKind::LastEnumerated: break; diff --git a/test/IRGen/builtin_vector_fixed_array.sil b/test/IRGen/builtin_vector_fixed_array.sil new file mode 100644 index 00000000000..a3027144465 --- /dev/null +++ b/test/IRGen/builtin_vector_fixed_array.sil @@ -0,0 +1,295 @@ +// RUN: %target-swift-frontend -emit-ir -disable-availability-checking -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule %s | %FileCheck %s +import Builtin +import Swift + +// CHECK-LABEL: define{{.*}} @trivial_fixed +sil @trivial_fixed: $@convention(thin) (Builtin.FixedArray<2, Int>) -> (Builtin.FixedArray<2, Int>, Builtin.FixedArray<2, Int>) { +entry(%v : $Builtin.FixedArray<2, Int>): + // CHECK: [[T0:%.*]] = insertvalue {{.*}} undef, {{i[0-9]+}} %0, 0 + // CHECK: [[T1:%.*]] = insertvalue {{.*}} [[T0]], {{i[0-9]+}} %1, 1 + // CHECK: [[T2:%.*]] = insertvalue {{.*}} [[T1]], {{i[0-9]+}} %0, 2 + // CHECK: [[T3:%.*]] = insertvalue {{.*}} [[T2]], {{i[0-9]+}} %1, 3 + // CHECK: ret {{.*}} [[T3]] + %r = tuple (%v : $Builtin.FixedArray<2, Int>, %v : $Builtin.FixedArray<2, Int>) + return %r : $(Builtin.FixedArray<2, Int>, Builtin.FixedArray<2, Int>) +} + +// CHECK-LABEL: define{{.*}} @loadable_fixed +sil @loadable_fixed: $@convention(thin) (@owned Builtin.FixedArray<2, AnyObject>) -> @owned (Builtin.FixedArray<2, AnyObject>, Builtin.FixedArray<2, AnyObject>) { +entry(%v : $Builtin.FixedArray<2, AnyObject>): + // CHECK: call {{.*}} @swift_{{.*}}etain({{.*}} %0) + // CHECK: call {{.*}} @swift_{{.*}}etain({{.*}} %1) + retain_value %v : $Builtin.FixedArray<2, AnyObject> + // CHECK: [[T0:%.*]] = insertvalue {{.*}} undef, ptr %0, 0 + // CHECK: [[T1:%.*]] = insertvalue {{.*}} [[T0]], ptr %1, 1 + // CHECK: [[T2:%.*]] = insertvalue {{.*}} [[T1]], ptr %0, 2 + // CHECK: [[T3:%.*]] = insertvalue {{.*}} [[T2]], ptr %1, 3 + // CHECK: ret {{.*}} [[T3]] + %r = tuple (%v : $Builtin.FixedArray<2, AnyObject>, %v : $Builtin.FixedArray<2, AnyObject>) + return %r : $(Builtin.FixedArray<2, AnyObject>, Builtin.FixedArray<2, AnyObject>) +} + +// CHECK-LABEL: define{{.*}} @loadable_fixed_indirect +sil @loadable_fixed_indirect: $@convention(thin) (@in Builtin.FixedArray<420, AnyObject>) -> @out (Builtin.FixedArray<420, AnyObject>, Builtin.FixedArray<420, AnyObject>) { +entry(%r : $*(Builtin.FixedArray<420, AnyObject>, Builtin.FixedArray<420, AnyObject>), %v : $*Builtin.FixedArray<420, AnyObject>): + // CHECK: [[A:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0, {{i[0-9]+}} 0 + %a = tuple_element_addr %r : $*(Builtin.FixedArray<420, AnyObject>, Builtin.FixedArray<420, AnyObject>), 0 + + // CHECK: br label %[[COPY_LOOP_ENTRY:.*]] + // CHECK: [[COPY_LOOP_ENTRY]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ 420, %entry ], [ [[DEC_COUNT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[DEST_ELT:%.*]] = phi ptr [ [[A]], %entry ], [ [[INC_DEST_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT:%.*]] = phi ptr [ %1, %entry ], [ [[INC_SRC_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT0:%.*]] = getelementptr {{.*}} ptr [[SRC_ELT]], + // CHECK: [[SRC_ELT_VAL:%.*]] = load ptr, ptr [[SRC_ELT0]] + // CHECK: call {{.*}} @swift_{{.*}}etain({{.*}} [[SRC_ELT_VAL]]) + // CHECK: [[DEST_ELT0:%.*]] = getelementptr {{.*}} ptr [[DEST_ELT]], + // CHECK: store ptr [[SRC_ELT_VAL]], ptr [[DEST_ELT0]] + // CHECK: [[INC_DEST_ELT]] = getelementptr inbounds %AnyObject, ptr [[DEST_ELT]], {{i[0-9]+}} 1 + // CHECK: [[INC_SRC_ELT]] = getelementptr inbounds %AnyObject, ptr [[SRC_ELT]], {{i[0-9]+}} 1 + // CHECK: [[DEC_COUNT]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[DEC_COUNT]], 0 + // CHECK: br i1 [[DONE]], label %[[COPY_LOOP_END:.*]], label %[[COPY_LOOP_ENTRY]] + // CHECK: [[COPY_LOOP_END]]: + copy_addr %v to [init] %a: $*Builtin.FixedArray<420, AnyObject> + + // CHECK: [[B:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0, {{i[0-9]+}} 1 + %b = tuple_element_addr %r : $*(Builtin.FixedArray<420, AnyObject>, Builtin.FixedArray<420, AnyObject>), 1 + + // CHECK: br label %[[TAKE_LOOP_ENTRY:.*]] + // CHECK: [[TAKE_LOOP_ENTRY]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ 420, %[[COPY_LOOP_END]] ], [ [[DEC_COUNT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: [[DEST_ELT:%.*]] = phi ptr [ [[B]], %[[COPY_LOOP_END]] ], [ [[INC_DEST_ELT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT:%.*]] = phi ptr [ %1, %[[COPY_LOOP_END]] ], [ [[INC_SRC_ELT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT0:%.*]] = getelementptr {{.*}} ptr [[SRC_ELT]], + // CHECK: [[SRC_ELT_VAL:%.*]] = load ptr, ptr [[SRC_ELT0]] + // CHECK: [[DEST_ELT0:%.*]] = getelementptr {{.*}} ptr [[DEST_ELT]], + // CHECK: store ptr [[SRC_ELT_VAL]], ptr [[DEST_ELT0]] + // CHECK: [[INC_DEST_ELT]] = getelementptr inbounds %AnyObject, ptr [[DEST_ELT]], {{i[0-9]+}} 1 + // CHECK: [[INC_SRC_ELT]] = getelementptr inbounds %AnyObject, ptr [[SRC_ELT]], {{i[0-9]+}} 1 + // CHECK: [[DEC_COUNT]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[DEC_COUNT]], 0 + // CHECK: br i1 [[DONE]], label %[[TAKE_LOOP_END:.*]], label %[[TAKE_LOOP_ENTRY]] + // CHECK: [[TAKE_LOOP_END]]: + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray<420, AnyObject> + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @loadable_fixed_indirect_to_direct +sil @loadable_fixed_indirect_to_direct: $@convention(thin) (@in Builtin.FixedArray<2, AnyObject>) -> @owned Builtin.FixedArray<2, AnyObject> { +entry(%v : $*Builtin.FixedArray<2, AnyObject>): + // CHECK: [[V0:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0 + // CHECK: [[V0_0:%.*]] = getelementptr {{.*}} [[V0]], {{i[0-9]+}} 0, {{i[0-9]+}} 0 + // CHECK: [[V0_VALUE:%.*]] = load {{.*}} [[V0_0]], align 8 + + // CHECK: [[V1:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 1 + // CHECK: [[V1_0:%.*]] = getelementptr {{.*}} [[V1]], {{i[0-9]+}} 0, {{i[0-9]+}} 0 + // CHECK: [[V1_VALUE:%.*]] = load {{.*}} [[V1_0]], align 8 + %a = load %v : $*Builtin.FixedArray<2, AnyObject> + + // CHECK: [[R0:%.*]] = insertvalue {{.*}} undef, ptr [[V0_VALUE]], 0 + // CHECK: [[R1:%.*]] = insertvalue {{.*}} [[R0]], ptr [[V1_VALUE]], 1 + // CHECK: ret {{.*}} [[R1]] + return %a : $Builtin.FixedArray<2, AnyObject> +} + +// CHECK-LABEL: define{{.*}} @loadable_fixed_direct_to_indirect +sil @loadable_fixed_direct_to_indirect: $@convention(thin) (@owned Builtin.FixedArray<2, AnyObject>) -> @out Builtin.FixedArray<2, AnyObject> { +entry(%r : $*Builtin.FixedArray<2, AnyObject>, %v : $Builtin.FixedArray<2, AnyObject>): + // CHECK: [[R0:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0 + // CHECK: [[R0_0:%.*]] = getelementptr {{.*}} [[R0]], {{i[0-9]+}} 0, {{i[0-9]+}} 0 + // CHECK: store {{.*}}, ptr [[R0_0]], + + // CHECK: [[R1:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 1 + // CHECK: [[R1_0:%.*]] = getelementptr {{.*}} [[R1]], {{i[0-9]+}} 0, {{i[0-9]+}} 0 + // CHECK: store {{.*}}, ptr [[R1_0]], + store %v to %r : $*Builtin.FixedArray<2, AnyObject> + return undef : $() +} + +struct Padded { + var a: Int16 + var b: Int8 +} + +// CHECK-LABEL: define{{.*}} @loadable_padded_fixed_indirect_to_direct +sil @loadable_padded_fixed_indirect_to_direct: $@convention(thin) (@in Builtin.FixedArray<2, Padded>) -> @owned Builtin.FixedArray<2, Padded> { +entry(%v : $*Builtin.FixedArray<2, Padded>): + // ensure the elements are indexed using the byte stride rather than the + // packed struct representation + // CHECK: getelementptr inbounds i8, ptr %0, {{i[0-9]+}} 0 + // CHECK: getelementptr inbounds i8, ptr %0, {{i[0-9]+}} 4 + %a = load %v : $*Builtin.FixedArray<2, Padded> + return %a : $Builtin.FixedArray<2, Padded> +} + +// CHECK-LABEL: define{{.*}} @loadable_padded_fixed_direct_to_indirect +sil @loadable_padded_fixed_direct_to_indirect: $@convention(thin) (@owned Builtin.FixedArray<2, Padded>) -> @out Builtin.FixedArray<2, Padded> { +entry(%r : $*Builtin.FixedArray<2, Padded>, %v : $Builtin.FixedArray<2, Padded>): + // ensure the elements are indexed using the byte stride rather than the + // packed struct representation + // CHECK: getelementptr inbounds i8, ptr %0, {{i[0-9]+}} 0 + // CHECK: getelementptr inbounds i8, ptr %0, {{i[0-9]+}} 4 + store %v to %r : $*Builtin.FixedArray<2, Padded> + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @ao_fixed +sil @ao_fixed: $@convention(thin) (@in Builtin.FixedArray<2, Any>) -> @out (Builtin.FixedArray<2, Any>, Builtin.FixedArray<2, Any>) { +entry(%r : $*(Builtin.FixedArray<2, Any>, Builtin.FixedArray<2, Any>), %v : $*Builtin.FixedArray<2, Any>): + // CHECK: [[A:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0, {{i[0-9]+}} 0 + %a = tuple_element_addr %r : $*(Builtin.FixedArray<2, Any>, Builtin.FixedArray<2, Any>), 0 + + // CHECK: br label %[[COPY_LOOP_ENTRY:.*]] + // CHECK: [[COPY_LOOP_ENTRY]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ 2, %entry ], [ [[DEC_COUNT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[DEST_ELT:%.*]] = phi ptr [ [[A]], %entry ], [ [[INC_DEST_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT:%.*]] = phi ptr [ %1, %entry ], [ [[INC_SRC_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: call ptr @"$sypWOc"(ptr [[SRC_ELT]], ptr [[DEST_ELT]]) + // CHECK: [[INC_DEST_ELT]] = getelementptr inbounds %Any, ptr [[DEST_ELT]], {{i[0-9]+}} 1 + // CHECK: [[INC_SRC_ELT]] = getelementptr inbounds %Any, ptr [[SRC_ELT]], {{i[0-9]+}} 1 + // CHECK: [[DEC_COUNT]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[DEC_COUNT]], 0 + // CHECK: br i1 [[DONE]], label %[[COPY_LOOP_END:.*]], label %[[COPY_LOOP_ENTRY]] + // CHECK: [[COPY_LOOP_END]]: + copy_addr %v to [init] %a: $*Builtin.FixedArray<2, Any> + + // CHECK: [[B:%.*]] = getelementptr {{.*}} %0, {{i[0-9]+}} 0, {{i[0-9]+}} 1 + %b = tuple_element_addr %r : $*(Builtin.FixedArray<2, Any>, Builtin.FixedArray<2, Any>), 1 + + // CHECK: br label %[[TAKE_LOOP_ENTRY:.*]] + // CHECK: [[TAKE_LOOP_ENTRY]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ 2, %[[COPY_LOOP_END]] ], [ [[DEC_COUNT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: [[DEST_ELT:%.*]] = phi ptr [ [[B]], %[[COPY_LOOP_END]] ], [ [[INC_DEST_ELT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT:%.*]] = phi ptr [ %1, %[[COPY_LOOP_END]] ], [ [[INC_SRC_ELT:%.*]], %[[TAKE_LOOP_ENTRY]] ] + // CHECK: call ptr @"$sypWOb"(ptr [[SRC_ELT]], ptr [[DEST_ELT]]) + // CHECK: [[INC_DEST_ELT]] = getelementptr inbounds %Any, ptr [[DEST_ELT]], {{i[0-9]+}} 1 + // CHECK: [[INC_SRC_ELT]] = getelementptr inbounds %Any, ptr [[SRC_ELT]], {{i[0-9]+}} 1 + // CHECK: [[DEC_COUNT]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[DEC_COUNT]], 0 + // CHECK: br i1 [[DONE]], label %[[TAKE_LOOP_END:.*]], label %[[TAKE_LOOP_ENTRY]] + // CHECK: [[TAKE_LOOP_END]]: + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray<2, Any> + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @dep_fixed +sil @dep_fixed: $@convention(thin) (@in Builtin.FixedArray<2, T>) -> @out (Builtin.FixedArray<2, T>, Builtin.FixedArray<2, T>) { +entry(%r : $*(Builtin.FixedArray<2, T>, Builtin.FixedArray<2, T>), %v : $*Builtin.FixedArray<2, T>): + %a = tuple_element_addr %r : $*(Builtin.FixedArray<2, T>, Builtin.FixedArray<2, T>), 0 + + // CHECK: br label %[[COPY_LOOP_ENTRY:.*]] + // CHECK: [[COPY_LOOP_ENTRY]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ 2, %entry ], [ [[DEC_COUNT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[DEST_ELT:%.*]] = phi ptr [ %0, %entry ], [ [[INC_DEST_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: [[SRC_ELT:%.*]] = phi ptr [ %1, %entry ], [ [[INC_SRC_ELT:%.*]], %[[COPY_LOOP_ENTRY]] ] + // CHECK: call ptr %InitializeWithCopy(ptr noalias [[DEST_ELT]], ptr noalias [[SRC_ELT]], ptr %T) + // CHECK: [[STRIDE:%.*]] = mul nsw {{i[0-9]+}} 1, %stride + // CHECK: [[INC_DEST_ELT]] = getelementptr inbounds i8, ptr [[DEST_ELT]], {{i[0-9]+}} [[STRIDE]] + // CHECK: [[STRIDE:%.*]] = mul nsw {{i[0-9]+}} 1, %stride + // CHECK: [[INC_SRC_ELT]] = getelementptr inbounds i8, ptr [[SRC_ELT]], {{i[0-9]+}} [[STRIDE]] + // CHECK: [[DEC_COUNT]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[DEC_COUNT]], 0 + // CHECK: br i1 [[DONE]], label %[[COPY_LOOP_END:.*]], label %[[COPY_LOOP_ENTRY]] + // CHECK: [[COPY_LOOP_END]]: + copy_addr %v to [init] %a: $*Builtin.FixedArray<2, T> + + %b = tuple_element_addr %r : $*(Builtin.FixedArray<2, T>, Builtin.FixedArray<2, T>), 1 + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray<2, T> + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @trivial_dep +sil @trivial_dep: $@convention(thin) (@in Builtin.FixedArray) -> @out (Builtin.FixedArray, Builtin.FixedArray) { +entry(%r : $*(Builtin.FixedArray, Builtin.FixedArray), %v : $*Builtin.FixedArray): + %a = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 0 + // CHECK: [[SIZE_NEG:%.*]] = icmp slt {{i[0-9]+}} %N, 0 + // CHECK: [[SIZE:%.*]] = select i1 [[SIZE_NEG]], {{i[0-9]+}} 0, {{i[0-9]+}} %N + // CHECK: [[IS_ZERO:%.*]] = icmp eq i64 [[SIZE]], 0 + // CHECK: br i1 [[IS_ZERO]], label %[[LOOP_END:.*]], label %[[LOOP:.*]] + // CHECK: [[LOOP]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ [[SIZE]], %entry ], [ [[SIZE_DEC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST:%.*]] = phi ptr [ %0, %entry ], [ [[DEST_INC:%.*]], %[[LOOP]] ] + // CHECK: [[SRC:%.*]] = phi ptr [ %1, %entry ], [ [[SRC_INC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST_INC]] = getelementptr inbounds %TSi, ptr [[DEST]], i64 1 + // CHECK: [[SRC_INC]] = getelementptr inbounds %TSi, ptr [[SRC]], i64 1 + // CHECK: [[SIZE_DEC]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[SIZE_DEC]], 0 + // CHECK: br i1 [[DONE]], label %[[LOOP_END]], label %[[LOOP]] + copy_addr %v to [init] %a: $*Builtin.FixedArray + %b = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 1 + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @loadable_dep +sil @loadable_dep: $@convention(thin) (@in Builtin.FixedArray) -> @out (Builtin.FixedArray, Builtin.FixedArray) { +entry(%r : $*(Builtin.FixedArray, Builtin.FixedArray), %v : $*Builtin.FixedArray): + %a = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 0 + // CHECK: [[SIZE_NEG:%.*]] = icmp slt {{i[0-9]+}} %N, 0 + // CHECK: [[SIZE:%.*]] = select i1 [[SIZE_NEG]], {{i[0-9]+}} 0, {{i[0-9]+}} %N + // CHECK: [[IS_ZERO:%.*]] = icmp eq i64 [[SIZE]], 0 + // CHECK: br i1 [[IS_ZERO]], label %[[LOOP_END:.*]], label %[[LOOP:.*]] + // CHECK: [[LOOP]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ [[SIZE]], %entry ], [ [[SIZE_DEC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST:%.*]] = phi ptr [ %0, %entry ], [ [[DEST_INC:%.*]], %[[LOOP]] ] + // CHECK: [[SRC:%.*]] = phi ptr [ %1, %entry ], [ [[SRC_INC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST_INC]] = getelementptr inbounds %AnyObject, ptr [[DEST]], i64 1 + // CHECK: [[SRC_INC]] = getelementptr inbounds %AnyObject, ptr [[SRC]], i64 1 + // CHECK: [[SIZE_DEC]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[SIZE_DEC]], 0 + // CHECK: br i1 [[DONE]], label %[[LOOP_END]], label %[[LOOP]] + copy_addr %v to [init] %a: $*Builtin.FixedArray + %b = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 1 + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @ao_dep +sil @ao_dep: $@convention(thin) (@in Builtin.FixedArray) -> @out (Builtin.FixedArray, Builtin.FixedArray) { +entry(%r : $*(Builtin.FixedArray, Builtin.FixedArray), %v : $*Builtin.FixedArray): + %a = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 0 + // CHECK: [[SIZE_NEG:%.*]] = icmp slt {{i[0-9]+}} %N, 0 + // CHECK: [[SIZE:%.*]] = select i1 [[SIZE_NEG]], {{i[0-9]+}} 0, {{i[0-9]+}} %N + // CHECK: [[IS_ZERO:%.*]] = icmp eq i64 [[SIZE]], 0 + // CHECK: br i1 [[IS_ZERO]], label %[[LOOP_END:.*]], label %[[LOOP:.*]] + // CHECK: [[LOOP]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ [[SIZE]], %entry ], [ [[SIZE_DEC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST:%.*]] = phi ptr [ %0, %entry ], [ [[DEST_INC:%.*]], %[[LOOP]] ] + // CHECK: [[SRC:%.*]] = phi ptr [ %1, %entry ], [ [[SRC_INC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST_INC]] = getelementptr inbounds %Any, ptr [[DEST]], i64 1 + // CHECK: [[SRC_INC]] = getelementptr inbounds %Any, ptr [[SRC]], i64 1 + // CHECK: [[SIZE_DEC]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[SIZE_DEC]], 0 + // CHECK: br i1 [[DONE]], label %[[LOOP_END]], label %[[LOOP]] + copy_addr %v to [init] %a: $*Builtin.FixedArray + %b = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 1 + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray + return undef : $() +} + +// CHECK-LABEL: define{{.*}} @dep_dep +sil @dep_dep: $@convention(thin) (@in Builtin.FixedArray) -> @out (Builtin.FixedArray, Builtin.FixedArray) { +entry(%r : $*(Builtin.FixedArray, Builtin.FixedArray), %v : $*Builtin.FixedArray): + %a = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 0 + // CHECK: [[SIZE_NEG:%.*]] = icmp slt {{i[0-9]+}} %N, 0 + // CHECK: [[SIZE:%.*]] = select i1 [[SIZE_NEG]], {{i[0-9]+}} 0, {{i[0-9]+}} %N + // CHECK: [[IS_ZERO:%.*]] = icmp eq i64 [[SIZE]], 0 + // CHECK: br i1 [[IS_ZERO]], label %[[LOOP_END:.*]], label %[[LOOP:.*]] + // CHECK: [[LOOP]]: + // CHECK: [[COUNT:%.*]] = phi {{i[0-9]+}} [ [[SIZE]], %entry ], [ [[SIZE_DEC:%.*]], %[[LOOP]] ] + // CHECK: [[DEST:%.*]] = phi ptr [ %0, %entry ], [ [[DEST_INC:%.*]], %[[LOOP]] ] + // CHECK: [[SRC:%.*]] = phi ptr [ %1, %entry ], [ [[SRC_INC:%.*]], %[[LOOP]] ] + // CHECK: %stride = load + // CHECK: [[DIST:%.*]] = mul nsw {{i[0-9]+}} 1, %stride + // CHECK: [[DEST_INC]] = getelementptr inbounds i8, ptr [[DEST]], i{{[0-9]+}} [[DIST]] + // CHECK: [[DIST:%.*]] = mul nsw {{i[0-9]+}} 1, %stride + // CHECK: [[SRC_INC]] = getelementptr inbounds i8, ptr [[SRC]], i{{[0-9]+}} [[DIST]] + // CHECK: [[SIZE_DEC]] = sub {{i[0-9]+}} [[COUNT]], 1 + // CHECK: [[DONE:%.*]] = icmp eq {{i[0-9]+}} [[SIZE_DEC]], 0 + // CHECK: br i1 [[DONE]], label %[[LOOP_END]], label %[[LOOP]] + copy_addr %v to [init] %a: $*Builtin.FixedArray + %b = tuple_element_addr %r : $*(Builtin.FixedArray, Builtin.FixedArray), 1 + copy_addr [take] %v to [init] %b: $*Builtin.FixedArray + return undef : $() +} diff --git a/test/Prototypes/Vector.swift b/test/Prototypes/Vector.swift new file mode 100644 index 00000000000..768e3f471d3 --- /dev/null +++ b/test/Prototypes/Vector.swift @@ -0,0 +1,107 @@ +// RUN: %target-run-simple-swift(-target %module-target-future -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule -Xfrontend -disable-experimental-parser-round-trip) | %FileCheck %s + +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// FIXME: miscompile/verifier error after optimization + +import Builtin + +@frozen +public struct Vector: ~Copyable { + private var storage: Builtin.FixedArray + + public init(_ valueForIndex: (Int) -> Element) { + storage = Builtin.emplace { rawPointer in + let base = UnsafeMutablePointer(rawPointer) + for i in 0.. Element { + _read { + assert(i >= 0 && i < Count) + let rawPointer = Builtin.addressOfBorrow(self) + let base = UnsafePointer(rawPointer) + yield ((base + i).pointee) + } + + _modify { + assert(i >= 0 && i < Count) + let rawPointer = Builtin.addressof(&self) + let base = UnsafeMutablePointer(rawPointer) + yield (&(base + i).pointee) + } + } +} +extension Vector: Copyable where Element: Copyable { + public init(repeating value: Element) { + self.init { _ in value } + } +} +extension Vector: BitwiseCopyable where Element: BitwiseCopyable {} + +extension Vector: CustomDebugStringConvertible { + public var debugDescription: String { + var result = "[" + if Count > 0 { + result += String(reflecting: self[0]) + + for i in 1.." + } + + deinit { print("deinit <\(self.value)>") } +} + +func main() { + var v = Vector<4, Canary>(repeating: Canary(value: "")) + + // CHECK: [<>, <>, <>, <>] + print(v) + + v[0] = Canary(value: "hello") + v[1] = Canary(value: "world") + v[2] = Canary(value: "goodbye") + v[3] = Canary(value: "moon") + // CHECK-NEXT: deinit <> + + // CHECK-NEXT: [, , , ] + print(v) + + do { + var w = v + w[1] = Canary(value: "sun") + w[3] = Canary(value: "earth") + // CHECK-NEXT: [, , , ] + print(w) + } + // CHECK-NEXT: deinit + // CHECK-NEXT: deinit + + // CHECK-NEXT: [, , , ] + print(v) + + // CHECK-NEXT: deinit + // CHECK-NEXT: deinit + // CHECK-NEXT: deinit + // CHECK-NEXT: deinit +} +main() diff --git a/test/SILGen/builtin_vector.swift b/test/SILGen/builtin_vector.swift new file mode 100644 index 00000000000..56bd6bcc27a --- /dev/null +++ b/test/SILGen/builtin_vector.swift @@ -0,0 +1,67 @@ +// RUN: %target-swift-emit-silgen -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule %s | %FileCheck %s + +import Builtin + +struct MyVector: ~Copyable { + var storage: Builtin.FixedArray +} +extension MyVector: Copyable where T: Copyable {} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}13trivial_fixed +// CHECK: bb0(%0 : $MyVector<4, Int>): +// CHECK: tuple (%0 : {{.*}}, %0 : {{.*}}) +func trivial_fixed(a: MyVector<4, Int>) -> (MyVector<4, Int>, MyVector<4, Int>){ + return (a, a) +} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}14loadable_fixed +// CHECK: bb0(%0 : @guaranteed $MyVector<4, AnyObject>): +// CHECK: [[COPY1:%.*]] = copy_value %0 +// CHECK: [[COPY2:%.*]] = copy_value %0 +// CHECK: tuple ([[COPY1]] : {{.*}}, [[COPY2]] : {{.*}}) +func loadable_fixed(a: MyVector<4, AnyObject>) + -> (MyVector<4, AnyObject>, MyVector<4, AnyObject>) +{ + return (a, a) +} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}8ao_fixed +// CHECK: bb0(%0 : $*MyVector<4, Any>, %1 : $*MyVector<4, Any>, %2 : $*MyVector<4, Any>): +// CHECK: copy_addr %2 to [init] %0 +// CHECK: copy_addr %2 to [init] %1 +func ao_fixed(a: MyVector<4, Any>) + -> (MyVector<4, Any>, MyVector<4, Any>) +{ + return (a, a) +} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}11trivial_dep +// CHECK: bb0(%0 : $*MyVector, %1 : $*MyVector, %2 : $*MyVector): +// CHECK: copy_addr %2 to [init] %0 +// CHECK: copy_addr %2 to [init] %1 +func trivial_dep(a: MyVector) + -> (MyVector, MyVector) +{ + return (a, a) +} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}12loadable_dep +// CHECK: bb0(%0 : $*MyVector, %1 : $*MyVector, %2 : $*MyVector): +// CHECK: copy_addr %2 to [init] %0 +// CHECK: copy_addr %2 to [init] %1 +func loadable_dep(a: MyVector) + -> (MyVector, MyVector) +{ + return (a, a) +} + +// CHECK-LABEL: sil{{.*}} @$s{{.*}}6ao_dep +// CHECK: bb0(%0 : $*MyVector, %1 : $*MyVector, %2 : $*MyVector): +// CHECK: copy_addr %2 to [init] %0 +// CHECK: copy_addr %2 to [init] %1 +func ao_dep(a: MyVector) + -> (MyVector, MyVector) +{ + return (a, a) +} + diff --git a/test/SILOptimizer/definite_init_builtin_vector.swift b/test/SILOptimizer/definite_init_builtin_vector.swift new file mode 100644 index 00000000000..7a5ded4a4b1 --- /dev/null +++ b/test/SILOptimizer/definite_init_builtin_vector.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -emit-sil -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule %s + +import Builtin + +struct MyVector: ~Copyable { + var storage: Builtin.FixedArray + + init() { + // make sure that `Builtin.emplace` considers the result storage + // initialized, even if the closure doesn't actually do anything + // with the pointer + self.storage = Builtin.emplace { ptr in + } + } +} + diff --git a/test/Sema/builtin_int.swift b/test/Sema/builtin_int.swift new file mode 100644 index 00000000000..cf409c2dd23 --- /dev/null +++ b/test/Sema/builtin_int.swift @@ -0,0 +1,18 @@ +// RUN: %target-swift-frontend -disable-experimental-parser-round-trip -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule -typecheck -verify %s + +import Builtin + +func a(x: Builtin.Int<64>) -> Builtin.Int64 { + return x +} + +func b(x: Builtin.Int<42>) -> Builtin.Int43 { + return x // expected-error{{}} +} + +func c(x: Builtin.Int) {} // expected-error{{}} + +// TODO: implement support for dependent-width integer types +func d(x: Builtin.Int) -> Builtin.Int { // expected-error 2 {{}} + return x +} diff --git a/test/Sema/builtin_vector.swift b/test/Sema/builtin_vector.swift new file mode 100644 index 00000000000..2db1d8795c7 --- /dev/null +++ b/test/Sema/builtin_vector.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-frontend -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature ValueGenerics -enable-experimental-feature BuiltinModule -typecheck -verify %s + +import Builtin + +func a(x: Builtin.FixedArray) -> Builtin.FixedArray { + return x +} + +func b(x: Builtin.FixedArray) -> Builtin.FixedArray { // expected-error 2 {{}} + return x +} + +func c(x: Builtin.FixedArray<4, Int>) {} + +func d(x: Builtin.FixedArray<4, 4>) {} // expected-error{{}} +func e(x: Builtin.FixedArray) {} // expected-error{{}} +func f(x: Builtin.FixedArray) {} // expected-error{{}} + +struct CopyableContainingNoncopyableVector: Copyable { + var x: Builtin.FixedArray<4, T> // expected-error{{}} +} + +struct CopyableContainingCopyableVector: Copyable { + var x: Builtin.FixedArray<4, T> +} + +struct MyVector: ~Copyable { + var storage: Builtin.FixedArray +} +extension MyVector: Copyable where T: Copyable {} +extension MyVector: BitwiseCopyable where T: BitwiseCopyable {} + +struct BitwiseCopyableVector: BitwiseCopyable { + var x: Builtin.FixedArray<4, T> +} + +struct NonBitwiseCopyableVector: BitwiseCopyable { + var x: Builtin.FixedArray<4, T> // expected-error{{}} +} + diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index c25a739dd26..71585081bbc 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -570,6 +570,7 @@ Added: __swift_enableSwizzlingOfAllocationAndRefCountingFunctions_forInstruments Added: _swift_clearSensitive Added: _swift_updatePureObjCClassMetadata Added: _swift_initRawStructMetadata2 +Added: _swift_getFixedArrayTypeMetadata // Runtime bincompat functions for Concurrency runtime to detect legacy mode Added: _swift_bincompat_useLegacyNonCrashingExecutorChecks diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index e041ee84104..cff7502457b 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -570,6 +570,7 @@ Added: __swift_enableSwizzlingOfAllocationAndRefCountingFunctions_forInstruments Added: _swift_clearSensitive Added: _swift_updatePureObjCClassMetadata Added: _swift_initRawStructMetadata2 +Added: _swift_getFixedArrayTypeMetadata // Runtime bincompat functions for Concurrency runtime to detect legacy mode Added: _swift_bincompat_useLegacyNonCrashingExecutorChecks