-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add experimental _Volatile module providing low-level primitives for MMIO #70944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
90c8a8e
81f8b2a
e722fc7
7d44c2b
1c66aea
4b7c678
62f1e3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#===--- CMakeLists.txt ---------------------------------------------------===# | ||
# | ||
# This source file is part of the Swift.org open source project | ||
# | ||
# Copyright (c) 2019 - 2022 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 | ||
# | ||
#===----------------------------------------------------------------------===# | ||
|
||
add_swift_target_library(swift_Volatile ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB | ||
Volatile.swift | ||
|
||
SWIFT_COMPILE_FLAGS | ||
${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} | ||
-parse-stdlib | ||
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" | ||
|
||
INSTALL_IN_COMPONENT stdlib | ||
) | ||
|
||
if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if I want to try out volatile thingies in regular desktop swift? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the small block above, that's defining and building Volatile for regular platforms. |
||
add_custom_target(embedded-volatile ALL) | ||
foreach(entry ${EMBEDDED_STDLIB_TARGET_TRIPLES}) | ||
string(REGEX REPLACE "[ \t]+" ";" list "${entry}") | ||
list(GET list 0 arch) | ||
list(GET list 1 mod) | ||
list(GET list 2 triple) | ||
|
||
set(SWIFT_SDK_embedded_ARCH_${arch}_MODULE "${mod}") | ||
set(SWIFT_SDK_embedded_LIB_SUBDIR "embedded") | ||
set(SWIFT_SDK_embedded_ARCH_${arch}_TRIPLE "${triple}") | ||
add_swift_target_library_single( | ||
embedded-volatile-${mod} | ||
swift_Volatile | ||
ONLY_SWIFTMODULE | ||
IS_SDK_OVERLAY IS_FRAGILE | ||
|
||
Volatile.swift | ||
|
||
SWIFT_COMPILE_FLAGS | ||
-Xcc -D__MACH__ -Xcc -D__APPLE__ -Xcc -ffreestanding -enable-experimental-feature Embedded | ||
-parse-stdlib | ||
C_COMPILE_FLAGS | ||
-D__MACH__ -D__APPLE__ -ffreestanding | ||
MODULE_DIR "${CMAKE_BINARY_DIR}/lib/swift/embedded" | ||
SDK "embedded" | ||
ARCHITECTURE "${arch}" | ||
DEPENDS embedded-stdlib-${mod} | ||
INSTALL_IN_COMPONENT stdlib | ||
) | ||
add_dependencies(embedded-volatile embedded-volatile-${mod}) | ||
endforeach() | ||
endif() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2020 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Swift | ||
|
||
/// A pointer for accessing "volatile" memory, e.g. memory-mapped I/O registers. | ||
/// | ||
/// Do not use for inter-thread synchronization. This is only meaningful for | ||
/// low-level operations on special memory addresses performed from OS kernels, | ||
/// embedded firmware, and similar environments. | ||
/// | ||
/// The semantics of volatile load and volatile store operations match the LLVM | ||
/// volatile semantics. Notably, a volatile operation cannot be added, removed, | ||
/// or reordered with other volatile operations by the compiler. They may be | ||
/// reordered with non-volatile operations. For details, see | ||
/// <https://llvm.org/docs/LangRef.html#volatile-memory-accesses>. | ||
@frozen | ||
public struct VolatileMappedRegister<Pointee> { | ||
@usableFromInline | ||
let _rawPointer: Builtin.RawPointer | ||
|
||
@_transparent | ||
public init(unsafeBitPattern: UInt) { | ||
self._rawPointer = Builtin.inttoptr_Word(unsafeBitPattern._builtinWordValue) | ||
} | ||
} | ||
|
||
extension VolatileMappedRegister where Pointee == UInt8 { | ||
/// Perform an 8-bit volatile load operation from the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func load() -> Pointee { | ||
UInt8(Builtin.atomicload_monotonic_volatile_Int8(_rawPointer)) | ||
} | ||
|
||
/// Perform an 8-bit volatile store operation on the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func store(_ value: Pointee) { | ||
Builtin.atomicstore_monotonic_volatile_Int8(_rawPointer, value._value) | ||
} | ||
} | ||
|
||
extension VolatileMappedRegister where Pointee == UInt16 { | ||
/// Perform a 16-bit volatile load operation from the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func load() -> Pointee { | ||
UInt16(Builtin.atomicload_monotonic_volatile_Int16(_rawPointer)) | ||
} | ||
|
||
/// Perform a 16-bit volatile store operation on the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func store(_ value: Pointee) { | ||
Builtin.atomicstore_monotonic_volatile_Int16(_rawPointer, value._value) | ||
} | ||
} | ||
|
||
extension VolatileMappedRegister where Pointee == UInt32 { | ||
/// Perform a 32-bit volatile load operation from the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func load() -> Pointee { | ||
UInt32(Builtin.atomicload_monotonic_volatile_Int32(_rawPointer)) | ||
} | ||
|
||
/// Perform a 32-bit volatile store operation on the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func store(_ value: Pointee) { | ||
Builtin.atomicstore_monotonic_volatile_Int32(_rawPointer, value._value) | ||
} | ||
} | ||
|
||
extension VolatileMappedRegister where Pointee == UInt64 { | ||
/// Perform a 64-bit volatile load operation from the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func load() -> Pointee { | ||
UInt64(Builtin.atomicload_monotonic_volatile_Int64(_rawPointer)) | ||
} | ||
|
||
/// Perform a 64-bit volatile store operation on the target pointer. | ||
/// | ||
/// Do not use for inter-thread synchronization. | ||
@_transparent | ||
public func store(_ value: Pointee) { | ||
Builtin.atomicstore_monotonic_volatile_Int64(_rawPointer, value._value) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// RUN: %target-run-simple-swift(-parse-as-library -enable-experimental-feature Volatile) | %FileCheck %s | ||
|
||
// REQUIRES: executable_test | ||
|
||
import _Volatile | ||
|
||
@main | ||
struct Main { | ||
static func main() { | ||
var byte: UInt8 = 42 | ||
withUnsafePointer(to: &byte) { p in | ||
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: p)) | ||
print("byte = \(volatilePointer.load())") | ||
// CHECK: byte = 42 | ||
volatilePointer.store(77) | ||
} | ||
print("byte = \(byte)") | ||
// CHECK: byte = 77 | ||
|
||
var heapPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 16) // uninitialized content | ||
for i in 0 ..< 16 { | ||
let pointerWithOffset = heapPointer.advanced(by: i) | ||
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: pointerWithOffset)) | ||
volatilePointer.store(UInt8(0x61 + i)) | ||
} | ||
heapPointer[15] = 0 | ||
print(String(cString: heapPointer)) | ||
// CHECK: abcdefghijklmno | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: not %target-swift-emit-ir %s -module-name main -parse-as-library 2>&1 | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile | ||
|
||
import _Volatile | ||
|
||
// CHECK: importing _Volatile module requires '-enable-experimental-feature Volatile' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s | ||
|
||
import _Volatile | ||
|
||
public func test_uint8() -> UInt8 { | ||
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0xf000baa9) | ||
p.store(42) | ||
return p.load() | ||
} | ||
|
||
// CHECK: define {{.*}}i8 @"$s4main10test_uint8s5UInt8VyF"() | ||
// CHECK: store atomic volatile i8 42, ptr {{.*}} monotonic, align 1 | ||
// CHECK: [[RET:%.*]] = load atomic volatile i8, ptr {{.*}} monotonic, align 1 | ||
// CHECK: ret i8 [[RET]] | ||
// CHECK: } | ||
|
||
public func test_uint16() -> UInt16 { | ||
let p = VolatileMappedRegister<UInt16>(unsafeBitPattern: 0xf000baaa) | ||
p.store(42) | ||
return p.load() | ||
} | ||
|
||
// CHECK: define {{.*}}i16 @"$s4main11test_uint16s6UInt16VyF"() | ||
// CHECK: store atomic volatile i16 42, ptr {{.*}} monotonic, align 2 | ||
// CHECK: [[RET:%.*]] = load atomic volatile i16, ptr {{.*}} monotonic, align 2 | ||
// CHECK: ret i16 [[RET]] | ||
// CHECK: } | ||
|
||
public func test_uint32() -> UInt32 { | ||
let p = VolatileMappedRegister<UInt32>(unsafeBitPattern: 0xf000baaa) | ||
p.store(42) | ||
return p.load() | ||
} | ||
|
||
// CHECK: define {{.*}}i32 @"$s4main11test_uint32s6UInt32VyF"() | ||
// CHECK: store atomic volatile i32 42, ptr {{.*}} monotonic, align 4 | ||
// CHECK: [[RET:%.*]] = load atomic volatile i32, ptr {{.*}} monotonic, align 4 | ||
// CHECK: ret i32 [[RET]] | ||
// CHECK: } | ||
|
||
public func test_uint64() -> UInt64 { | ||
let p = VolatileMappedRegister<UInt64>(unsafeBitPattern: 0xf000baaa) | ||
p.store(42) | ||
return p.load() | ||
} | ||
|
||
// CHECK: define {{.*}}i64 @"$s4main11test_uint64s6UInt64VyF"() | ||
// CHECK: store atomic volatile i64 42, ptr {{.*}} monotonic, align 8 | ||
// CHECK: [[RET:%.*]] = load atomic volatile i64, ptr {{.*}} monotonic, align 8 | ||
// CHECK: ret i64 [[RET]] | ||
// CHECK: } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s | ||
|
||
import _Volatile | ||
|
||
public func test_volatilepointer() { | ||
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0) | ||
p.store(42) | ||
} | ||
|
||
// CHECK: define {{.*}}void @"$s4main20test_volatilepointeryyF"() | ||
// CHECK: store atomic volatile i8 42, ptr null monotonic, align {{.*}} | ||
// CHECK: ret void | ||
// CHECK: } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s | ||
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s | ||
|
||
import _Volatile | ||
|
||
public func test_volatilepointer() -> UInt8 { | ||
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0xf000baa9) | ||
p.store(42) | ||
let a = p.load() | ||
let b = p.load() | ||
let c = p.load() | ||
return c | ||
} | ||
|
||
// CHECK: define {{.*}}i8 @"$s4main20test_volatilepointers5UInt8VyF"() | ||
// CHECK: store atomic volatile i8 42, ptr {{.*}} monotonic, align 1 | ||
// CHECK: load atomic volatile i8, ptr {{.*}} monotonic, align 1 | ||
// CHECK: load atomic volatile i8, ptr {{.*}} monotonic, align 1 | ||
// CHECK: [[RET:%.*]] = load atomic volatile i8, ptr {{.*}} monotonic, align 1 | ||
// CHECK: ret i8 [[RET]] | ||
// CHECK: } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// RUN: %target-run-simple-swift(-parse-as-library -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none) | %FileCheck %s | ||
|
||
// REQUIRES: swift_in_compiler | ||
// REQUIRES: executable_test | ||
// REQUIRES: optimized_stdlib | ||
// REQUIRES: OS=macosx || OS=linux-gnu | ||
|
||
import _Volatile | ||
|
||
@_silgen_name("putchar") | ||
@discardableResult | ||
func putchar(_: CInt) -> CInt | ||
|
||
@main | ||
struct Main { | ||
static func main() { | ||
var byte: UInt8 = 42 | ||
withUnsafePointer(to: &byte) { p in | ||
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: p)) | ||
print("byte = ", terminator: "") | ||
print(volatilePointer.load()) | ||
// CHECK: byte = 42 | ||
volatilePointer.store(77) | ||
} | ||
print("byte = ", terminator: "") | ||
print(byte) | ||
// CHECK: byte = 77 | ||
|
||
var heapPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 16) // uninitialized content | ||
for i in 0 ..< 16 { | ||
let pointerWithOffset = heapPointer.advanced(by: i) | ||
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: pointerWithOffset)) | ||
volatilePointer.store(UInt8(0x61 + i)) | ||
} | ||
for i in 0 ..< 16 { putchar(CInt(heapPointer[i])) } | ||
putchar(CInt(("\n" as Unicode.Scalar).value)) | ||
// CHECK: abcdefghijklmnop | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should allow this in non-embedded no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes -- it's allowed in the current implementation in this PR