Skip to content

Commit 3501568

Browse files
Make SIMD types Codable. (#22092)
* Make SIMD types codable. We're considering this a bugfix. This is a very tiny ABI change, in that user-defined SIMD types compiled with an earlier version of 5.0 will be missing the necessary conformance to Codable. Discussed with Ben, and we're OK with this because we don't think there are such types yet, and it can be fixed with a recompile. * Add basic tests
1 parent d07513b commit 3501568

File tree

4 files changed

+118
-1
lines changed

4 files changed

+118
-1
lines changed

stdlib/public/core/SIMDVector.swift

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ prefix operator .!
2323
/// conform to `SIMD`.
2424
public protocol SIMDStorage {
2525
/// The type of scalars in the vector space.
26-
associatedtype Scalar : Hashable
26+
associatedtype Scalar : Codable, Hashable
2727

2828
/// The number of scalars, or elements, in the vector.
2929
var scalarCount: Int { get }
@@ -51,6 +51,7 @@ public protocol SIMDScalar {
5151

5252
/// A SIMD vector of a fixed number of elements.
5353
public protocol SIMD : SIMDStorage,
54+
Codable,
5455
Hashable,
5556
CustomStringConvertible,
5657
ExpressibleByArrayLiteral {
@@ -85,6 +86,42 @@ public extension SIMD {
8586
for i in indices { hasher.combine(self[i]) }
8687
}
8788

89+
/// Encodes the scalars of this vector into the given encoder in an unkeyed
90+
/// container.
91+
///
92+
/// This function throws an error if any values are invalid for the given
93+
/// encoder's format.
94+
///
95+
/// - Parameter encoder: The encoder to write data to.
96+
func encode(to encoder: Encoder) throws {
97+
var container = encoder.unkeyedContainer()
98+
for i in indices {
99+
try container.encode(self[i])
100+
}
101+
}
102+
103+
/// Creates a new vector by decoding scalars from the given decoder.
104+
///
105+
/// This initializer throws an error if reading from the decoder fails, or
106+
/// if the data read is corrupted or otherwise invalid.
107+
///
108+
/// - Parameter decoder: The decoder to read data from.
109+
init(from decoder: Decoder) throws {
110+
self.init()
111+
var container = try decoder.unkeyedContainer()
112+
guard container.count == scalarCount else {
113+
throw DecodingError.dataCorrupted(
114+
DecodingError.Context(
115+
codingPath: decoder.codingPath,
116+
debugDescription: "Expected vector with exactly \(scalarCount) elements."
117+
)
118+
)
119+
}
120+
for i in indices {
121+
self[i] = try container.decode(Scalar.self)
122+
}
123+
}
124+
88125
/// A textual description of the vector.
89126
var description: String {
90127
get {

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,8 @@ Var _RawDictionaryStorage.empty has declared type change from _EmptyDictionarySi
7373
Var _RawSetStorage.empty has declared type change from _EmptySetSingleton to __EmptySetSingleton
7474

7575
Func tryReallocateUniquelyReferenced(buffer:newMinimumCapacity:) has been removed
76+
77+
Protocol SIMD has added inherited protocol Decodable
78+
Protocol SIMD has added inherited protocol Encodable
79+
Protocol SIMD has generic signature change from <τ_0_0 : CustomStringConvertible, τ_0_0 : ExpressibleByArrayLiteral, τ_0_0 : Hashable, τ_0_0 : SIMDStorage, τ_0_0.MaskStorage : SIMD, τ_0_0.MaskStorage.Scalar : FixedWidthInteger, τ_0_0.MaskStorage.Scalar : SignedInteger> to <τ_0_0 : CustomStringConvertible, τ_0_0 : Decodable, τ_0_0 : Encodable, τ_0_0 : ExpressibleByArrayLiteral, τ_0_0 : Hashable, τ_0_0 : SIMDStorage, τ_0_0.MaskStorage : SIMD, τ_0_0.MaskStorage.Scalar : FixedWidthInteger, τ_0_0.MaskStorage.Scalar : SignedInteger>
80+
Protocol SIMDStorage has generic signature change from <τ_0_0.Scalar : Hashable> to <τ_0_0.Scalar : Decodable, τ_0_0.Scalar : Encodable, τ_0_0.Scalar : Hashable>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
Func tryReallocateUniquelyReferenced(buffer:newMinimumCapacity:) has been removed
2+
Protocol SIMD has added inherited protocol Decodable
3+
Protocol SIMD has added inherited protocol Encodable
4+
Protocol SIMD has generic signature change from <Self : CustomStringConvertible, Self : ExpressibleByArrayLiteral, Self : Hashable, Self : SIMDStorage, Self.MaskStorage : SIMD, Self.MaskStorage.Scalar : FixedWidthInteger, Self.MaskStorage.Scalar : SignedInteger> to <Self : CustomStringConvertible, Self : Decodable, Self : Encodable, Self : ExpressibleByArrayLiteral, Self : Hashable, Self : SIMDStorage, Self.MaskStorage : SIMD, Self.MaskStorage.Scalar : FixedWidthInteger, Self.MaskStorage.Scalar : SignedInteger>
5+
Protocol SIMDStorage has generic signature change from <Self.Scalar : Hashable> to <Self.Scalar : Decodable, Self.Scalar : Encodable, Self.Scalar : Hashable>

test/stdlib/SIMD.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
// REQUIRES: objc_interop
4+
5+
import Foundation
6+
import StdlibUnittest
7+
8+
let SIMDCodableTests = TestSuite("SIMDCodable")
9+
10+
func testRoundTrip<T>(_ for: T.Type)
11+
where T : SIMD, T.Scalar : FixedWidthInteger {
12+
let input = T.random(in: T.Scalar.min ... T.Scalar.max)
13+
let encoder = JSONEncoder()
14+
let decoder = JSONDecoder()
15+
do {
16+
let data = try encoder.encode(input)
17+
let output = try decoder.decode(T.self, from: data)
18+
expectEqual(input, output)
19+
}
20+
catch {
21+
expectUnreachableCatch(error)
22+
}
23+
}
24+
25+
func testRoundTrip<T>(_ for: T.Type)
26+
where T : SIMD, T.Scalar : BinaryFloatingPoint,
27+
T.Scalar.RawSignificand : FixedWidthInteger {
28+
let input = T.random(in: -16 ..< 16)
29+
let encoder = JSONEncoder()
30+
let decoder = JSONDecoder()
31+
do {
32+
let data = try encoder.encode(input)
33+
let output = try decoder.decode(T.self, from: data)
34+
expectEqual(input, output)
35+
}
36+
catch {
37+
expectUnreachableCatch(error)
38+
}
39+
}
40+
41+
// Very basic round-trip test. We can be much more sophisticated in the future,
42+
// but we want to at least exercise the API. Also need to add some negative
43+
// tests for the error paths, and test a more substantial set of types.
44+
SIMDCodableTests.test("roundTrip") {
45+
testRoundTrip(SIMD2<Int8>.self)
46+
testRoundTrip(SIMD3<Int8>.self)
47+
testRoundTrip(SIMD4<Int8>.self)
48+
testRoundTrip(SIMD2<UInt8>.self)
49+
testRoundTrip(SIMD3<UInt8>.self)
50+
testRoundTrip(SIMD4<UInt8>.self)
51+
testRoundTrip(SIMD2<Int32>.self)
52+
testRoundTrip(SIMD3<Int32>.self)
53+
testRoundTrip(SIMD4<Int32>.self)
54+
testRoundTrip(SIMD2<UInt32>.self)
55+
testRoundTrip(SIMD3<UInt32>.self)
56+
testRoundTrip(SIMD4<UInt32>.self)
57+
testRoundTrip(SIMD2<Int>.self)
58+
testRoundTrip(SIMD3<Int>.self)
59+
testRoundTrip(SIMD4<Int>.self)
60+
testRoundTrip(SIMD2<UInt>.self)
61+
testRoundTrip(SIMD3<UInt>.self)
62+
testRoundTrip(SIMD4<UInt>.self)
63+
testRoundTrip(SIMD2<Float>.self)
64+
testRoundTrip(SIMD3<Float>.self)
65+
testRoundTrip(SIMD4<Float>.self)
66+
testRoundTrip(SIMD2<Double>.self)
67+
testRoundTrip(SIMD3<Double>.self)
68+
testRoundTrip(SIMD4<Double>.self)
69+
}
70+
71+
runAllTests()

0 commit comments

Comments
 (0)