From 4b94685d57ceb3993e2263a75fab04ac79308c55 Mon Sep 17 00:00:00 2001 From: "Steve (Numerics) Canon" Date: Thu, 18 Apr 2019 15:55:28 -0400 Subject: [PATCH] Restore elementwise min/max on SIMD, but as statics instead of free functions. Having these as free functions causes expression too complex issues due to overload vis-a-vis min. There are (at least) three plausible solutions: 1. Fix the typechecker. This is infeasable in the short term; or more precisely, we do not know how much work is involved. 2. Give these operations different names. Candidates discussed with core team include "pointwiseMin", "elementwiseMin", "lanewiseMin"; these all suffer from the flaw that when someone writes "min" in a SIMD context, they are essentially always looking for either the horizontal minimum (reduction) on a single vector or--more often--the lanewise minimum of two vectors (this operation). It would be odd to give the operation that people actually want the unnecessarily verbose name. 3. Make these operations static; this is, in effect, a different name, but it's one which frequently allows eliding the qualifier: let x = v + .min(v, w) This isn't perfect; you will still need to spell out SIMD4.min( ) fairly often, but that's more concise than any of the proposed alternatives, and at least allows elision some of the time. Also, if you squint, you can pretend that the "." prefix is like ".<" and ".&" and indicates lanewise operation. --- stdlib/public/core/SIMDVector.swift | 128 ++++++++++++++-------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/stdlib/public/core/SIMDVector.swift b/stdlib/public/core/SIMDVector.swift index 89eba56605bfa..87ed522050e00 100644 --- a/stdlib/public/core/SIMDVector.swift +++ b/stdlib/public/core/SIMDVector.swift @@ -345,6 +345,32 @@ extension SIMD where Scalar: Comparable { public func max() -> Scalar { return indices.reduce(into: self[0]) { $0 = Swift.max($0, self[$1]) } } + + /// The lanewise minimum of two vectors. + /// + /// Each element of the result is the minimum of the corresponding elements + /// of the inputs. + @_alwaysEmitIntoClient + public static func min(_ a: Self, _ b: Self) -> Self { + var result = Self() + for i in result.indices { + result[i] = Swift.min(a[i], b[i]) + } + return result + } + + /// The lanewise maximum of two vectors. + /// + /// Each element of the result is the minimum of the corresponding elements + /// of the inputs. + @_alwaysEmitIntoClient + public static func max(_ a: Self, _ b: Self) -> Self { + var result = Self() + for i in result.indices { + result[i] = Swift.max(a[i], b[i]) + } + return result + } } // These operations should never need @_semantics; they should be trivial @@ -467,7 +493,6 @@ extension SIMD where Scalar: Comparable { return lhs .> Self(repeating: rhs) } - /* Temporarily removed pending plan for Swift.min / Swift.max @_alwaysEmitIntoClient public mutating func clamp(lowerBound: Self, upperBound: Self) { self = self.clamped(lowerBound: lowerBound, upperBound: upperBound) @@ -475,9 +500,8 @@ extension SIMD where Scalar: Comparable { @_alwaysEmitIntoClient public func clamped(lowerBound: Self, upperBound: Self) -> Self { - return Swift.min(upperBound, Swift.max(lowerBound, self)) + return Self.min(upperBound, Self.max(lowerBound, self)) } - */ } extension SIMD where Scalar: FixedWidthInteger { @@ -550,6 +574,42 @@ extension SIMD where Scalar: FloatingPoint { public static var one: Self { return Self(repeating: 1) } + + /// The lanewise minimum of two vectors. + /// + /// Each element of the result is the minimum of the corresponding elements + /// of the inputs. + @_alwaysEmitIntoClient + public static func min(_ a: Self, _ b: Self) -> Self { + var result = Self() + for i in result.indices { + result[i] = Scalar.minimum(a[i], b[i]) + } + return result + } + + /// The lanewise maximum of two vectors. + /// + /// Each element of the result is the minimum of the corresponding elements + /// of the inputs. + @_alwaysEmitIntoClient + public static func max(_ a: Self, _ b: Self) -> Self { + var result = Self() + for i in result.indices { + result[i] = Scalar.maximum(a[i], b[i]) + } + return result + } + + @_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 Self.min(upperBound, Self.max(lowerBound, self)) + } } extension SIMD @@ -1342,65 +1402,3 @@ public func any(_ mask: SIMDMask) -> Bool { public func all(_ mask: SIMDMask) -> Bool { return mask._storage.max() < 0 } - -/* -Temporarily removed while we investigate compile-time regressions caused by -introducing these global functions. - -/// 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 -} -*/