Skip to content

Commit 70afeef

Browse files
authored
Merge pull request #70944 from kubamracek/volatile
Add experimental _Volatile module providing low-level primitives for MMIO
2 parents 512ac6c + 62f1e3b commit 70afeef

14 files changed

+407
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,9 @@ ERROR(cannot_import_embedded_module,none,
925925
ERROR(cannot_import_non_embedded_module,none,
926926
"module %0 cannot be imported in embedded Swift mode",
927927
(Identifier))
928+
ERROR(volatile_is_experimental,none,
929+
"importing _Volatile module requires '-enable-experimental-feature Volatile'", ())
930+
928931
ERROR(need_cxx_interop_to_import_module,none,
929932
"module %0 was built with C++ interoperability enabled, but "
930933
"current compilation does not enable C++ interoperability",

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ EXPERIMENTAL_FEATURE(RawLayout, true)
317317
/// Enables the "embedded" swift mode (no runtime).
318318
EXPERIMENTAL_FEATURE(Embedded, true)
319319

320+
/// Enables importing the Volatile module
321+
EXPERIMENTAL_FEATURE(Volatile, true)
322+
320323
/// Enables noncopyable generics
321324
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NoncopyableGenerics, true)
322325

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ static bool usesFeatureRawLayout(Decl *decl) {
505505
}
506506

507507
UNINTERESTING_FEATURE(Embedded)
508+
UNINTERESTING_FEATURE(Volatile)
508509
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
509510

510511
static bool usesFeatureNoncopyableGenerics(Decl *decl) {

lib/Sema/ImportResolution.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,15 @@ ImportResolver::getModule(ImportPath::Module modulePath) {
397397
}
398398
}
399399

400+
// Only allow importing "Volatile" with Feature::Volatile or Feature::Embedded
401+
if (!ctx.LangOpts.hasFeature(Feature::Volatile) &&
402+
!ctx.LangOpts.hasFeature(Feature::Embedded)) {
403+
if (ctx.getRealModuleName(moduleID.Item).str() == "_Volatile") {
404+
ctx.Diags.diagnose(SourceLoc(), diag::volatile_is_experimental);
405+
return nullptr;
406+
}
407+
}
408+
400409
// If the imported module name is the same as the current module,
401410
// skip the Swift module loader and use the Clang module loader instead.
402411
// This allows a Swift module to extend a Clang module of the same name.

stdlib/public/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ if(SWIFT_BUILD_STDLIB AND NOT SWIFT_STDLIB_BUILD_ONLY_CORE_MODULES)
280280
if(SWIFT_ENABLE_SYNCHRONIZATION)
281281
add_subdirectory(Synchronization)
282282
endif()
283+
284+
add_subdirectory(Volatile)
283285
endif()
284286

285287
if(SWIFT_BUILD_REMOTE_MIRROR)

stdlib/public/Volatile/CMakeLists.txt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#===--- CMakeLists.txt ---------------------------------------------------===#
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2019 - 2022 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See https://swift.org/LICENSE.txt for license information
9+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
#===----------------------------------------------------------------------===#
12+
13+
add_swift_target_library(swift_Volatile ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
14+
Volatile.swift
15+
16+
SWIFT_COMPILE_FLAGS
17+
${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
18+
-parse-stdlib
19+
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
20+
21+
INSTALL_IN_COMPONENT stdlib
22+
)
23+
24+
if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB)
25+
add_custom_target(embedded-volatile ALL)
26+
foreach(entry ${EMBEDDED_STDLIB_TARGET_TRIPLES})
27+
string(REGEX REPLACE "[ \t]+" ";" list "${entry}")
28+
list(GET list 0 arch)
29+
list(GET list 1 mod)
30+
list(GET list 2 triple)
31+
32+
set(SWIFT_SDK_embedded_ARCH_${arch}_MODULE "${mod}")
33+
set(SWIFT_SDK_embedded_LIB_SUBDIR "embedded")
34+
set(SWIFT_SDK_embedded_ARCH_${arch}_TRIPLE "${triple}")
35+
add_swift_target_library_single(
36+
embedded-volatile-${mod}
37+
swift_Volatile
38+
ONLY_SWIFTMODULE
39+
IS_SDK_OVERLAY IS_FRAGILE
40+
41+
Volatile.swift
42+
43+
SWIFT_COMPILE_FLAGS
44+
-Xcc -D__MACH__ -Xcc -D__APPLE__ -Xcc -ffreestanding -enable-experimental-feature Embedded
45+
-parse-stdlib
46+
C_COMPILE_FLAGS
47+
-D__MACH__ -D__APPLE__ -ffreestanding
48+
MODULE_DIR "${CMAKE_BINARY_DIR}/lib/swift/embedded"
49+
SDK "embedded"
50+
ARCHITECTURE "${arch}"
51+
DEPENDS embedded-stdlib-${mod}
52+
INSTALL_IN_COMPONENT stdlib
53+
)
54+
add_dependencies(embedded-volatile embedded-volatile-${mod})
55+
endforeach()
56+
endif()

stdlib/public/Volatile/Volatile.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
15+
/// A pointer for accessing "volatile" memory, e.g. memory-mapped I/O registers.
16+
///
17+
/// Do not use for inter-thread synchronization. This is only meaningful for
18+
/// low-level operations on special memory addresses performed from OS kernels,
19+
/// embedded firmware, and similar environments.
20+
///
21+
/// The semantics of volatile load and volatile store operations match the LLVM
22+
/// volatile semantics. Notably, a volatile operation cannot be added, removed,
23+
/// or reordered with other volatile operations by the compiler. They may be
24+
/// reordered with non-volatile operations. For details, see
25+
/// <https://llvm.org/docs/LangRef.html#volatile-memory-accesses>.
26+
@frozen
27+
public struct VolatileMappedRegister<Pointee> {
28+
@usableFromInline
29+
let _rawPointer: Builtin.RawPointer
30+
31+
@_transparent
32+
public init(unsafeBitPattern: UInt) {
33+
self._rawPointer = Builtin.inttoptr_Word(unsafeBitPattern._builtinWordValue)
34+
}
35+
}
36+
37+
extension VolatileMappedRegister where Pointee == UInt8 {
38+
/// Perform an 8-bit volatile load operation from the target pointer.
39+
///
40+
/// Do not use for inter-thread synchronization.
41+
@_transparent
42+
public func load() -> Pointee {
43+
UInt8(Builtin.atomicload_monotonic_volatile_Int8(_rawPointer))
44+
}
45+
46+
/// Perform an 8-bit volatile store operation on the target pointer.
47+
///
48+
/// Do not use for inter-thread synchronization.
49+
@_transparent
50+
public func store(_ value: Pointee) {
51+
Builtin.atomicstore_monotonic_volatile_Int8(_rawPointer, value._value)
52+
}
53+
}
54+
55+
extension VolatileMappedRegister where Pointee == UInt16 {
56+
/// Perform a 16-bit volatile load operation from the target pointer.
57+
///
58+
/// Do not use for inter-thread synchronization.
59+
@_transparent
60+
public func load() -> Pointee {
61+
UInt16(Builtin.atomicload_monotonic_volatile_Int16(_rawPointer))
62+
}
63+
64+
/// Perform a 16-bit volatile store operation on the target pointer.
65+
///
66+
/// Do not use for inter-thread synchronization.
67+
@_transparent
68+
public func store(_ value: Pointee) {
69+
Builtin.atomicstore_monotonic_volatile_Int16(_rawPointer, value._value)
70+
}
71+
}
72+
73+
extension VolatileMappedRegister where Pointee == UInt32 {
74+
/// Perform a 32-bit volatile load operation from the target pointer.
75+
///
76+
/// Do not use for inter-thread synchronization.
77+
@_transparent
78+
public func load() -> Pointee {
79+
UInt32(Builtin.atomicload_monotonic_volatile_Int32(_rawPointer))
80+
}
81+
82+
/// Perform a 32-bit volatile store operation on the target pointer.
83+
///
84+
/// Do not use for inter-thread synchronization.
85+
@_transparent
86+
public func store(_ value: Pointee) {
87+
Builtin.atomicstore_monotonic_volatile_Int32(_rawPointer, value._value)
88+
}
89+
}
90+
91+
extension VolatileMappedRegister where Pointee == UInt64 {
92+
/// Perform a 64-bit volatile load operation from the target pointer.
93+
///
94+
/// Do not use for inter-thread synchronization.
95+
@_transparent
96+
public func load() -> Pointee {
97+
UInt64(Builtin.atomicload_monotonic_volatile_Int64(_rawPointer))
98+
}
99+
100+
/// Perform a 64-bit volatile store operation on the target pointer.
101+
///
102+
/// Do not use for inter-thread synchronization.
103+
@_transparent
104+
public func store(_ value: Pointee) {
105+
Builtin.atomicstore_monotonic_volatile_Int64(_rawPointer, value._value)
106+
}
107+
}

test/Volatile/volatile-exec.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library -enable-experimental-feature Volatile) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
import _Volatile
6+
7+
@main
8+
struct Main {
9+
static func main() {
10+
var byte: UInt8 = 42
11+
withUnsafePointer(to: &byte) { p in
12+
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: p))
13+
print("byte = \(volatilePointer.load())")
14+
// CHECK: byte = 42
15+
volatilePointer.store(77)
16+
}
17+
print("byte = \(byte)")
18+
// CHECK: byte = 77
19+
20+
var heapPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 16) // uninitialized content
21+
for i in 0 ..< 16 {
22+
let pointerWithOffset = heapPointer.advanced(by: i)
23+
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: pointerWithOffset))
24+
volatilePointer.store(UInt8(0x61 + i))
25+
}
26+
heapPointer[15] = 0
27+
print(String(cString: heapPointer))
28+
// CHECK: abcdefghijklmno
29+
}
30+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: not %target-swift-emit-ir %s -module-name main -parse-as-library 2>&1 | %FileCheck %s
3+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile
4+
5+
import _Volatile
6+
7+
// CHECK: importing _Volatile module requires '-enable-experimental-feature Volatile'

test/Volatile/volatile-ir.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s
3+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s
4+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s
5+
6+
import _Volatile
7+
8+
public func test_uint8() -> UInt8 {
9+
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0xf000baa9)
10+
p.store(42)
11+
return p.load()
12+
}
13+
14+
// CHECK: define {{.*}}i8 @"$s4main10test_uint8s5UInt8VyF"()
15+
// CHECK: store atomic volatile i8 42, ptr {{.*}} monotonic, align 1
16+
// CHECK: [[RET:%.*]] = load atomic volatile i8, ptr {{.*}} monotonic, align 1
17+
// CHECK: ret i8 [[RET]]
18+
// CHECK: }
19+
20+
public func test_uint16() -> UInt16 {
21+
let p = VolatileMappedRegister<UInt16>(unsafeBitPattern: 0xf000baaa)
22+
p.store(42)
23+
return p.load()
24+
}
25+
26+
// CHECK: define {{.*}}i16 @"$s4main11test_uint16s6UInt16VyF"()
27+
// CHECK: store atomic volatile i16 42, ptr {{.*}} monotonic, align 2
28+
// CHECK: [[RET:%.*]] = load atomic volatile i16, ptr {{.*}} monotonic, align 2
29+
// CHECK: ret i16 [[RET]]
30+
// CHECK: }
31+
32+
public func test_uint32() -> UInt32 {
33+
let p = VolatileMappedRegister<UInt32>(unsafeBitPattern: 0xf000baaa)
34+
p.store(42)
35+
return p.load()
36+
}
37+
38+
// CHECK: define {{.*}}i32 @"$s4main11test_uint32s6UInt32VyF"()
39+
// CHECK: store atomic volatile i32 42, ptr {{.*}} monotonic, align 4
40+
// CHECK: [[RET:%.*]] = load atomic volatile i32, ptr {{.*}} monotonic, align 4
41+
// CHECK: ret i32 [[RET]]
42+
// CHECK: }
43+
44+
public func test_uint64() -> UInt64 {
45+
let p = VolatileMappedRegister<UInt64>(unsafeBitPattern: 0xf000baaa)
46+
p.store(42)
47+
return p.load()
48+
}
49+
50+
// CHECK: define {{.*}}i64 @"$s4main11test_uint64s6UInt64VyF"()
51+
// CHECK: store atomic volatile i64 42, ptr {{.*}} monotonic, align 8
52+
// CHECK: [[RET:%.*]] = load atomic volatile i64, ptr {{.*}} monotonic, align 8
53+
// CHECK: ret i64 [[RET]]
54+
// CHECK: }

test/Volatile/volatile-null.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s
3+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s
4+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s
5+
6+
import _Volatile
7+
8+
public func test_volatilepointer() {
9+
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0)
10+
p.store(42)
11+
}
12+
13+
// CHECK: define {{.*}}void @"$s4main20test_volatilepointeryyF"()
14+
// CHECK: store atomic volatile i8 42, ptr null monotonic, align {{.*}}
15+
// CHECK: ret void
16+
// CHECK: }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Onone | %FileCheck %s
3+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -O | %FileCheck %s
4+
// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Volatile -Osize | %FileCheck %s
5+
6+
import _Volatile
7+
8+
public func test_volatilepointer() -> UInt8 {
9+
let p = VolatileMappedRegister<UInt8>(unsafeBitPattern: 0xf000baa9)
10+
p.store(42)
11+
let a = p.load()
12+
let b = p.load()
13+
let c = p.load()
14+
return c
15+
}
16+
17+
// CHECK: define {{.*}}i8 @"$s4main20test_volatilepointers5UInt8VyF"()
18+
// CHECK: store atomic volatile i8 42, ptr {{.*}} monotonic, align 1
19+
// CHECK: load atomic volatile i8, ptr {{.*}} monotonic, align 1
20+
// CHECK: load atomic volatile i8, ptr {{.*}} monotonic, align 1
21+
// CHECK: [[RET:%.*]] = load atomic volatile i8, ptr {{.*}} monotonic, align 1
22+
// CHECK: ret i8 [[RET]]
23+
// CHECK: }

test/embedded/volatile-exec.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none) | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
// REQUIRES: executable_test
5+
// REQUIRES: optimized_stdlib
6+
// REQUIRES: OS=macosx || OS=linux-gnu
7+
8+
import _Volatile
9+
10+
@_silgen_name("putchar")
11+
@discardableResult
12+
func putchar(_: CInt) -> CInt
13+
14+
@main
15+
struct Main {
16+
static func main() {
17+
var byte: UInt8 = 42
18+
withUnsafePointer(to: &byte) { p in
19+
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: p))
20+
print("byte = ", terminator: "")
21+
print(volatilePointer.load())
22+
// CHECK: byte = 42
23+
volatilePointer.store(77)
24+
}
25+
print("byte = ", terminator: "")
26+
print(byte)
27+
// CHECK: byte = 77
28+
29+
var heapPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 16) // uninitialized content
30+
for i in 0 ..< 16 {
31+
let pointerWithOffset = heapPointer.advanced(by: i)
32+
let volatilePointer = VolatileMappedRegister<UInt8>(unsafeBitPattern: UInt(bitPattern: pointerWithOffset))
33+
volatilePointer.store(UInt8(0x61 + i))
34+
}
35+
for i in 0 ..< 16 { putchar(CInt(heapPointer[i])) }
36+
putchar(CInt(("\n" as Unicode.Scalar).value))
37+
// CHECK: abcdefghijklmnop
38+
}
39+
}

0 commit comments

Comments
 (0)