Skip to content

Commit

Permalink
Add Projection associated type to BitField (#130)
Browse files Browse the repository at this point in the history
Updates the `BitField` associated type to require a `Projection`
associated type which conforms to `BitFieldProjectable`. Renames
existing `insert` and `extract` methods to `insertBits` and
`extractBits` respectively. Adds new typed variants of `insert` and
`extract` which operate on `Projection`.

The result of this change is that `preconditionMatchingBitWidth` can be
hidden from the MMIO module's public API and the macro expansion code is
simpler.
  • Loading branch information
rauhul authored Oct 8, 2024
1 parent 1f84beb commit c24e034
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 101 deletions.
55 changes: 49 additions & 6 deletions Sources/MMIO/BitField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,53 @@ extension FixedWidthInteger {

public protocol BitField {
associatedtype Storage: FixedWidthInteger & UnsignedInteger
associatedtype Projection: BitFieldProjectable

static var bitWidth: Int { get }

static func insert(_ value: Storage, into storage: inout Storage)
static func extract(from storage: Storage) -> Storage
static func insertBits(_ value: Storage, into storage: inout Storage)
static func extractBits(from storage: Storage) -> Storage

static func insert(_ value: Projection, into storage: inout Storage)
static func extract(from storage: Storage) -> Projection
}

extension BitField {
@inlinable @inline(__always)
static func preconditionMatchingBitWidth(
file: StaticString = #file,
line: UInt = #line
) {
#if hasFeature(Embedded)
// FIXME: Embedded doesn't have static interpolated strings yet
precondition(
Self.bitWidth == Projection.bitWidth,
"Illegal projection of bit-field as type of differing bit-width",
file: file,
line: line)
#else
precondition(
Self.bitWidth == Projection.bitWidth,
"""
Illegal projection of \(Self.bitWidth) bit bit-field '\(Self.self)' \
as \(Projection.bitWidth) bit type '\(Projection.self)'
""",
file: file,
line: line)
#endif
}

@inlinable @inline(__always)
public static func insert(_ value: Projection, into storage: inout Storage) {
Self.preconditionMatchingBitWidth()
Self.insertBits(value.storage(Storage.self), into: &storage)
}

@inlinable @inline(__always)
public static func extract(from storage: Self.Storage) -> Projection {
Self.preconditionMatchingBitWidth()
return Projection(storage: Self.extractBits(from: storage))
}
}

public protocol ContiguousBitField: BitField {
Expand All @@ -125,12 +168,12 @@ extension ContiguousBitField {

// FIXME: value.bitWidth <= Self.bitWidth <= Storage.bitWidth
@inlinable @inline(__always)
public static func insert(_ value: Storage, into storage: inout Storage) {
public static func insertBits(_ value: Storage, into storage: inout Storage) {
storage[bits: Self.bitRange] = value
}

@inlinable @inline(__always)
public static func extract(from storage: Storage) -> Storage {
public static func extractBits(from storage: Storage) -> Storage {
storage[bits: Self.bitRange]
}
}
Expand All @@ -152,12 +195,12 @@ extension DiscontiguousBitField {
}

@inlinable @inline(__always)
public static func insert(_ value: Storage, into storage: inout Storage) {
public static func insertBits(_ value: Storage, into storage: inout Storage) {
storage[bits: Self.bitRanges] = value
}

@inlinable @inline(__always)
public static func extract(from storage: Storage) -> Storage {
public static func extractBits(from storage: Storage) -> Storage {
storage[bits: Self.bitRanges]
}
}
26 changes: 0 additions & 26 deletions Sources/MMIO/BitFieldProjectable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,29 +186,3 @@ where Self: RawRepresentable, RawValue: FixedWidthInteger {
return Storage(rawValue)
}
}

@inlinable @inline(__always)
public func preconditionMatchingBitWidth(
_ fieldType: (some BitField).Type,
_ projectedType: (some BitFieldProjectable).Type,
file: StaticString = #file,
line: UInt = #line
) {
#if hasFeature(Embedded)
// FIXME: Embedded doesn't have static interpolated strings yet
precondition(
fieldType.bitWidth == projectedType.bitWidth,
"Illegal projection of bit-field as type of differing bit-width",
file: file,
line: line)
#else
precondition(
fieldType.bitWidth == projectedType.bitWidth,
"""
Illegal projection of \(fieldType.bitWidth) bit bit-field '\(fieldType)' \
as \(projectedType.bitWidth) bit type '\(projectedType)'
""",
file: file,
line: line)
#endif
}
21 changes: 9 additions & 12 deletions Sources/MMIOMacros/Macros/Descriptions/BitFieldDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ extension BitFieldDescription {
"""
\(self.accessLevel)enum \(self.fieldType): ContiguousBitField {
\(self.accessLevel)typealias Storage = \(self.storageType())
\(self.accessLevel)typealias Projection = \(self.projectedType ?? "Never")
\(self.accessLevel)static let bitRange = \(bitRangeExpression)
}
"""
Expand All @@ -71,6 +72,7 @@ extension BitFieldDescription {
"""
\(self.accessLevel)enum \(self.fieldType): DiscontiguousBitField {
\(self.accessLevel)typealias Storage = \(self.storageType())
\(self.accessLevel)typealias Projection = \(self.projectedType ?? "Never")
\(self.accessLevel)static let bitRanges = \(ArrayExprSyntax(expressions: bitRangeExpressions))
}
"""
Expand All @@ -84,10 +86,10 @@ extension BitFieldDescription {
"""
\(self.accessLevel)var \(self.fieldName): \(self.storageType()) {
@inlinable @inline(__always) get {
\(self.fieldType).extract(from: self.storage)
\(self.fieldType).extractBits(from: self.storage)
}
@inlinable @inline(__always) set {
\(self.fieldType).insert(newValue, into: &self.storage)
\(self.fieldType).insertBits(newValue, into: &self.storage)
}
}
"""
Expand All @@ -105,12 +107,10 @@ extension BitFieldDescription {
return """
\(self.accessLevel)var \(self.fieldName): \(projectedType) {
@inlinable @inline(__always) get {
preconditionMatchingBitWidth(\(self.fieldType).self, \(projectedType).self)
return \(projectedType)(storage: self.raw.\(self.fieldName))
\(self.fieldType).extract(from: self.storage)
}
@inlinable @inline(__always) set {
preconditionMatchingBitWidth(\(self.fieldType).self, \(projectedType).self)
self.raw.\(self.fieldName) = newValue.storage(Self.Value.Raw.Storage.self)
\(self.fieldType).insert(newValue, into: &self.storage)
}
}
"""
Expand All @@ -127,8 +127,7 @@ extension BitFieldDescription {
return """
\(self.accessLevel)var \(self.fieldName): \(projectedType) {
@inlinable @inline(__always) get {
preconditionMatchingBitWidth(\(self.fieldType).self, \(projectedType).self)
return \(projectedType)(storage: self.raw.\(self.fieldName))
\(self.fieldType).extract(from: self.storage)
}
}
"""
Expand All @@ -149,12 +148,10 @@ extension BitFieldDescription {
\(self.accessLevel)var \(self.fieldName): \(projectedType) {
@available(*, deprecated, message: "API misuse; read from write view returns the value to be written, not the value initially read.")
@inlinable @inline(__always) get {
preconditionMatchingBitWidth(\(self.fieldType).self, \(projectedType).self)
return \(projectedType)(storage: self.raw.\(self.fieldName))
\(self.fieldType).extract(from: self.storage)
}
@inlinable @inline(__always) set {
preconditionMatchingBitWidth(\(self.fieldType).self, \(projectedType).self)
self.raw.\(self.fieldName) = newValue.storage(Self.Value.Raw.Storage.self)
\(self.fieldType).insert(newValue, into: &self.storage)
}
}
"""
Expand Down
19 changes: 12 additions & 7 deletions Tests/MMIOFileCheckTests/MMIOFileCheckTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,18 @@ final class MMIOFileCheckTests: XCTestCase, @unchecked Sendable {
print("TOOLCHAINS not set.")
#if os(macOS)
print("Searching for swift-latest toolchain")
toolchainID = try sh(
"""
plutil \
-extract CFBundleIdentifier raw \
-o - \
/Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist
""")
do {
toolchainID = try sh(
"""
plutil \
-extract CFBundleIdentifier raw \
-o - \
/Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist
""")
} catch {
print("Failed to locate toolchain by plist: \(error)")
toolchainID = ""
}
#else
toolchainID = ""
#endif
Expand Down
Loading

0 comments on commit c24e034

Please sign in to comment.