diff --git a/stdlib/public/core/SIMDVector.swift b/stdlib/public/core/SIMDVector.swift index dce9b3355b674..2d8aa80ecb23f 100644 --- a/stdlib/public/core/SIMDVector.swift +++ b/stdlib/public/core/SIMDVector.swift @@ -33,7 +33,7 @@ prefix operator .! /// conform to `SIMD`. public protocol SIMDStorage { /// The type of scalars in the vector space. - associatedtype Scalar : Codable, Hashable + associatedtype Scalar: Codable, Hashable /// The number of scalars, or elements, in the vector. var scalarCount: Int { get } @@ -48,26 +48,37 @@ public protocol SIMDStorage { subscript(index: Int) -> Scalar { get set } } +extension SIMDStorage { + /// The number of scalars, or elements, in a vector of this type. + @_alwaysEmitIntoClient + public static var scalarCount: Int { + // Wouldn't it make more sense to define the instance var in terms of the + // static var? Yes, probably, but by doing it this way we make the static + // var backdeployable. + return Self().scalarCount + } +} + /// A type that can be used as an element in a SIMD vector. public protocol SIMDScalar { - associatedtype SIMDMaskScalar : SIMDScalar & FixedWidthInteger & SignedInteger - associatedtype SIMD2Storage : SIMDStorage where SIMD2Storage.Scalar == Self - associatedtype SIMD4Storage : SIMDStorage where SIMD4Storage.Scalar == Self - associatedtype SIMD8Storage : SIMDStorage where SIMD8Storage.Scalar == Self - associatedtype SIMD16Storage : SIMDStorage where SIMD16Storage.Scalar == Self - associatedtype SIMD32Storage : SIMDStorage where SIMD32Storage.Scalar == Self - associatedtype SIMD64Storage : SIMDStorage where SIMD64Storage.Scalar == Self + associatedtype SIMDMaskScalar: SIMDScalar & FixedWidthInteger & SignedInteger + associatedtype SIMD2Storage: SIMDStorage where SIMD2Storage.Scalar == Self + associatedtype SIMD4Storage: SIMDStorage where SIMD4Storage.Scalar == Self + associatedtype SIMD8Storage: SIMDStorage where SIMD8Storage.Scalar == Self + associatedtype SIMD16Storage: SIMDStorage where SIMD16Storage.Scalar == Self + associatedtype SIMD32Storage: SIMDStorage where SIMD32Storage.Scalar == Self + associatedtype SIMD64Storage: SIMDStorage where SIMD64Storage.Scalar == Self } /// A SIMD vector of a fixed number of elements. -public protocol SIMD : SIMDStorage, - Codable, - Hashable, - CustomStringConvertible, - ExpressibleByArrayLiteral { +public protocol SIMD: SIMDStorage, + Codable, + Hashable, + CustomStringConvertible, + ExpressibleByArrayLiteral { /// The mask type resulting from pointwise comparisons of this vector type. - associatedtype MaskStorage : SIMD - where MaskStorage.Scalar : FixedWidthInteger & SignedInteger + associatedtype MaskStorage: SIMD + where MaskStorage.Scalar: FixedWidthInteger & SignedInteger } extension SIMD { @@ -176,8 +187,10 @@ extension SIMD { /// Creates a vector from the given sequence. /// - /// - Parameter scalars: The elements to use in the vector. `scalars` must - /// have the same number of elements as the vector type. + /// - Precondition: `scalars` must have the same number of elements as the + /// vector type. + /// + /// - Parameter scalars: The elements to use in the vector. @inlinable public init(_ scalars: S) where S.Element == Scalar { self.init() @@ -193,11 +206,116 @@ extension SIMD { _preconditionFailure("Not enough elements in sequence.") } } + + /// Extracts the scalars at specified indices to form a SIMD2. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD2) -> SIMD2 + where Index: FixedWidthInteger { + var result = SIMD2() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD3. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD3) -> SIMD3 + where Index: FixedWidthInteger { + var result = SIMD3() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD4. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD4) -> SIMD4 + where Index: FixedWidthInteger { + var result = SIMD4() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD8. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD8) -> SIMD8 + where Index: FixedWidthInteger { + var result = SIMD8() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD16. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD16) -> SIMD16 + where Index: FixedWidthInteger { + var result = SIMD16() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD32. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD32) -> SIMD32 + where Index: FixedWidthInteger { + var result = SIMD32() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } + + /// Extracts the scalars at specified indices to form a SIMD64. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. + @_alwaysEmitIntoClient + public subscript(index: SIMD64) -> SIMD64 + where Index: FixedWidthInteger { + var result = SIMD64() + for i in result.indices { + result[i] = self[Int(index[i]) % scalarCount] + } + return result + } } // Implementations of comparison operations. These should eventually all // be replaced with @_semantics to lower directly to vector IR nodes. -extension SIMD where Scalar : Comparable { +extension SIMD where Scalar: Comparable { /// Returns a vector mask with the result of a pointwise less than /// comparison. @_transparent @@ -215,6 +333,18 @@ extension SIMD where Scalar : Comparable { for i in result.indices { result[i] = lhs[i] <= rhs[i] } return result } + + /// The least element in the vector. + @_alwaysEmitIntoClient + public func min() -> Scalar { + return indices.reduce(into: self[0]) { $0 = Swift.min($0, self[$1]) } + } + + /// The greatest element in the vector. + @_alwaysEmitIntoClient + public func max() -> Scalar { + return indices.reduce(into: self[0]) { $0 = Swift.max($0, self[$1]) } + } } // These operations should never need @_semantics; they should be trivial @@ -268,7 +398,7 @@ extension SIMD { } } -extension SIMD where Scalar : Comparable { +extension SIMD where Scalar: Comparable { /// Returns a vector mask with the result of a pointwise greater than or /// equal comparison. @_transparent @@ -336,15 +466,31 @@ extension SIMD where Scalar : Comparable { public static func .>(lhs: Self, rhs: Scalar) -> SIMDMask { return lhs .> Self(repeating: rhs) } + + @_alwaysEmitIntoClient + public mutating func clamp(lowerBound: Self, upperBound: Self) { + self = self.clamped(lowerBound: lowerBound, upperBound: upperBound) + } + + @_alwaysEmitIntoClient + public func clamped(lowerBound: Self, upperBound: Self) -> Self { + return Swift.min(upperBound, Swift.max(lowerBound, self)) + } } -extension SIMD where Scalar : FixedWidthInteger { +extension SIMD where Scalar: FixedWidthInteger { /// A vector with zero in all lanes. @_transparent public static var zero: Self { return Self() } + /// A vector with one in all lanes. + @_alwaysEmitIntoClient + public static var one: Self { + return Self(repeating: 1) + } + /// Returns a vector with random values from within the specified range in /// all lanes, using the given generator as a source for randomness. @inlinable @@ -390,16 +536,22 @@ extension SIMD where Scalar : FixedWidthInteger { } } -extension SIMD where Scalar : FloatingPoint { +extension SIMD where Scalar: FloatingPoint { /// A vector with zero in all lanes. @_transparent public static var zero: Self { return Self() } + + /// A vector with one in all lanes. + @_alwaysEmitIntoClient + public static var one: Self { + return Self(repeating: 1) + } } extension SIMD -where Scalar : BinaryFloatingPoint, Scalar.RawSignificand : FixedWidthInteger { +where Scalar: BinaryFloatingPoint, Scalar.RawSignificand: FixedWidthInteger { /// Returns a vector with random values from within the specified range in /// all lanes, using the given generator as a source for randomness. @inlinable @@ -446,11 +598,11 @@ where Scalar : BinaryFloatingPoint, Scalar.RawSignificand : FixedWidthInteger { } @_fixed_layout -public struct SIMDMask : SIMD - where Storage : SIMD, - Storage.Scalar : FixedWidthInteger & SignedInteger { +public struct SIMDMask: SIMD + where Storage: SIMD, + Storage.Scalar: FixedWidthInteger & SignedInteger { - public var _storage : Storage + public var _storage: Storage public typealias MaskStorage = Storage @@ -506,7 +658,7 @@ extension SIMDMask { // Implementations of integer operations. These should eventually all // be replaced with @_semantics to lower directly to vector IR nodes. -extension SIMD where Scalar : FixedWidthInteger { +extension SIMD where Scalar: FixedWidthInteger { @_transparent public var leadingZeroBitCount: Self { var result = Self() @@ -604,11 +756,20 @@ extension SIMD where Scalar : FixedWidthInteger { for i in result.indices { result[i] = lhs[i] % rhs[i] } return result } + + /// Returns the sum of the scalars in the vector, computed with wrapping + /// addition. + /// + /// Equivalent to indices.reduce(into: 0) { $0 &+= self[$1] }. + @_alwaysEmitIntoClient + public func wrappedSum() -> Scalar { + return indices.reduce(into: 0) { $0 &+= self[$1] } + } } // Implementations of floating-point operations. These should eventually all // be replaced with @_semantics to lower directly to vector IR nodes. -extension SIMD where Scalar : FloatingPoint { +extension SIMD where Scalar: FloatingPoint { @_transparent public static func +(lhs: Self, rhs: Self) -> Self { var result = Self() @@ -657,6 +818,28 @@ extension SIMD where Scalar : FloatingPoint { for i in result.indices { result[i] = self[i].rounded(rule) } return result } + + /// Returns the least scalar in the vector. + @_alwaysEmitIntoClient + public func min() -> Scalar { + return indices.reduce(into: self[0]) { $0 = Scalar.minimum($0, self[$1]) } + } + + /// Returns the greatest scalar in the vector. + @_alwaysEmitIntoClient + public func max() -> Scalar { + return indices.reduce(into: self[0]) { $0 = Scalar.maximum($0, self[$1]) } + } + + /// Returns the sum of the scalars in the vector. + @_alwaysEmitIntoClient + public func sum() -> Scalar { + // Implementation note: this eventually be defined to lower to either + // llvm.experimental.vector.reduce.fadd or an explicit tree-sum. Open- + // coding the tree sum is problematic, we probably need to define a + // Swift Builtin to support it. + return indices.reduce(into: 0) { $0 += self[$1] } + } } extension SIMDMask { @@ -683,7 +866,7 @@ extension SIMDMask { // These operations should never need @_semantics; they should be trivial // wrappers around the core operations defined above. -extension SIMD where Scalar : FixedWidthInteger { +extension SIMD where Scalar: FixedWidthInteger { @_transparent public static func &(lhs: Scalar, rhs: Self) -> Self { @@ -961,7 +1144,7 @@ extension SIMD where Scalar : FixedWidthInteger { } } -extension SIMD where Scalar : FloatingPoint { +extension SIMD where Scalar: FloatingPoint { @_transparent public static prefix func -(rhs: Self) -> Self { @@ -1085,7 +1268,6 @@ extension SIMD where Scalar : FloatingPoint { } extension SIMDMask { - @_transparent public static func .&(lhs: Bool, rhs: SIMDMask) -> SIMDMask { return SIMDMask(repeating: lhs) .& rhs @@ -1146,3 +1328,72 @@ extension SIMDMask { lhs = lhs .| rhs } } + +/// True if any lane of mask is true. +@_alwaysEmitIntoClient +public func any(_ mask: SIMDMask) -> Bool { + return mask._storage.min() < 0 +} + +/// True if every lane of mask is true. +@_alwaysEmitIntoClient +public func all(_ mask: SIMDMask) -> Bool { + return mask._storage.max() < 0 +} + +/// The lanewise minimum of two vectors. +/// +/// Each element of the result is the minimum of the corresponding elements +/// of the inputs. +@_alwaysEmitIntoClient +public func min(_ lhs: V, _ rhs: V) -> V +where V: SIMD, V.Scalar: Comparable { + var result = V() + for i in result.indices { + result[i] = min(lhs[i], rhs[i]) + } + return result +} + +/// The lanewise maximum of two vectors. +/// +/// Each element of the result is the maximum of the corresponding elements +/// of the inputs. +@_alwaysEmitIntoClient +public func max(_ lhs: V, _ rhs: V) -> V +where V: SIMD, V.Scalar: Comparable { + var result = V() + for i in result.indices { + result[i] = max(lhs[i], rhs[i]) + } + return result +} + + +/// The lanewise minimum of two vectors. +/// +/// Each element of the result is the minimum of the corresponding elements +/// of the inputs. +@_alwaysEmitIntoClient +public func min(_ lhs: V, _ rhs: V) -> V +where V: SIMD, V.Scalar: FloatingPoint { + var result = V() + for i in result.indices { + result[i] = V.Scalar.minimum(lhs[i], rhs[i]) + } + return result +} + +/// The lanewise maximum of two vectors. +/// +/// Each element of the result is the maximum of the corresponding elements +/// of the inputs. +@_alwaysEmitIntoClient +public func max(_ lhs: V, _ rhs: V) -> V +where V: SIMD, V.Scalar: FloatingPoint { + var result = V() + for i in result.indices { + result[i] = V.Scalar.maximum(lhs[i], rhs[i]) + } + return result +} diff --git a/stdlib/public/core/SIMDVectorTypes.swift.gyb b/stdlib/public/core/SIMDVectorTypes.swift.gyb index 76cb5795b454a..c24556106fe1f 100644 --- a/stdlib/public/core/SIMDVectorTypes.swift.gyb +++ b/stdlib/public/core/SIMDVectorTypes.swift.gyb @@ -26,7 +26,7 @@ ordinalPositions = ['first', 'second', 'third', 'fourth'] % storageN = 4 if n == 3 else n /// A vector of ${spelledNumbers[n]} scalar values. @_fixed_layout -public struct SIMD${n} : SIMD where Scalar: SIMDScalar { +public struct SIMD${n}: SIMD where Scalar: SIMDScalar { public var _storage: Scalar.SIMD${storageN}Storage @@ -69,9 +69,9 @@ public struct SIMD${n} : SIMD where Scalar: SIMDScalar { /// Creates a new vector from the given elements. /// /// - Parameters: - % for i in range(n): +% for i in range(n): /// - ${'xyzw'[i]}: The ${ordinalPositions[i]} element of the vector. - % end +% end @_transparent public init(${', '.join([c + ': Scalar' for c in 'xyzw'[:n]])}) { self.init(${', '.join('xyzw'[:n])}) @@ -113,14 +113,14 @@ public struct SIMD${n} : SIMD where Scalar: SIMDScalar { % end } -extension SIMD${n} where Scalar : FixedWidthInteger { +extension SIMD${n} where Scalar: FixedWidthInteger { /// Creates a new vector from the given vector, truncating the bit patterns /// of the given vector's elements if necessary. /// /// - Parameter other: The vector to convert. @inlinable public init(truncatingIfNeeded other: SIMD${n}) - where Other : FixedWidthInteger { + where Other: FixedWidthInteger { self.init() for i in indices { self[i] = Scalar(truncatingIfNeeded: other[i]) } } @@ -131,7 +131,7 @@ extension SIMD${n} where Scalar : FixedWidthInteger { /// - Parameter other: The vector to convert. @inlinable public init(clamping other: SIMD${n}) - where Other : FixedWidthInteger { + where Other: FixedWidthInteger { self.init() for i in indices { self[i] = Scalar(clamping: other[i]) } } @@ -148,14 +148,14 @@ extension SIMD${n} where Scalar : FixedWidthInteger { _ other: SIMD${n}, rounding rule: FloatingPointRoundingRule = .towardZero ) - where Other : BinaryFloatingPoint { + where Other: BinaryFloatingPoint { self.init() // TODO: this should clamp for i in indices { self[i] = Scalar(other[i].rounded(rule)) } } } -extension SIMD${n} : CustomDebugStringConvertible { +extension SIMD${n}: CustomDebugStringConvertible { public var debugDescription: String { return "SIMD${n}<\(Scalar.self)>(${', '.join(map(lambda c: '\\(self['+ str(c) + '])', @@ -163,13 +163,13 @@ extension SIMD${n} : CustomDebugStringConvertible { } } -extension SIMD${n} where Scalar : BinaryFloatingPoint { +extension SIMD${n} where Scalar: BinaryFloatingPoint { /// Creates a new vector from the given vector of integers. /// /// - Parameter other: The vector to convert. @inlinable public init(_ other: SIMD${n}) - where Other : FixedWidthInteger { + where Other: FixedWidthInteger { self.init() for i in indices { self[i] = Scalar(other[i]) } } @@ -179,7 +179,7 @@ extension SIMD${n} where Scalar : BinaryFloatingPoint { /// - Parameter other: The vector to convert. @inlinable public init(_ other: SIMD${n}) - where Other : BinaryFloatingPoint { + where Other: BinaryFloatingPoint { self.init() for i in indices { self[i] = Scalar(other[i]) } } @@ -187,11 +187,27 @@ extension SIMD${n} where Scalar : BinaryFloatingPoint { %end +extension SIMD3 { + /// A three-element vector created by appending a scalar to a two-element vector. + @_alwaysEmitIntoClient + public init(_ xy: SIMD2, _ z: Scalar) { + self.init(xy.x, xy.y, z) + } +} + +extension SIMD4 { + /// A four-element vector created by appending a scalar to a three-element vector. + @_alwaysEmitIntoClient + public init(_ xyz: SIMD3, _ w: Scalar) { + self.init(xyz.x, xyz.y, xyz.z, w) + } +} + %for self_type in all_integer_types(word_bits): % Self = self_type.stdlib_name % BuiltinName = self_type.builtin_name % Mask = Self if self_type.is_signed else self_type.get_opposite_signedness().stdlib_name -extension ${Self} : SIMDScalar { +extension ${Self}: SIMDScalar { public typealias SIMDMaskScalar = ${Mask} @@ -200,7 +216,7 @@ extension ${Self} : SIMDScalar { /// Storage for a vector of ${spelledNumbers[n]} integers. @_fixed_layout @_alignment(${bytes if bytes <= 16 else 16}) - public struct SIMD${n}Storage : SIMDStorage { + public struct SIMD${n}Storage: SIMDStorage { public var _value: Builtin.Vec${n}x${BuiltinName} @@ -236,7 +252,7 @@ extension ${Self} : SIMDScalar { %end %for (Self, bits) in [('Float',32), ('Double',64)]: -extension ${Self} : SIMDScalar { +extension ${Self}: SIMDScalar { public typealias SIMDMaskScalar = Int${bits} @@ -245,7 +261,7 @@ extension ${Self} : SIMDScalar { /// Storage for a vector of ${spelledNumbers[n]} floating-point values. @_fixed_layout @_alignment(${bytes if bytes <= 16 else 16}) - public struct SIMD${n}Storage : SIMDStorage { + public struct SIMD${n}Storage: SIMDStorage { public var _value: Builtin.Vec${n}xFPIEEE${bits}