diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 196cc3e386156..47ed1b959927a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -925,6 +925,9 @@ ERROR(cannot_import_embedded_module,none, ERROR(cannot_import_non_embedded_module,none, "module %0 cannot be imported in embedded Swift mode", (Identifier)) +ERROR(volatile_is_experimental,none, + "importing _Volatile module requires '-enable-experimental-feature Volatile'", ()) + ERROR(need_cxx_interop_to_import_module,none, "module %0 was built with C++ interoperability enabled, but " "current compilation does not enable C++ interoperability", diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 00d9cbe3b5029..82e9ac4273373 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -317,6 +317,9 @@ EXPERIMENTAL_FEATURE(RawLayout, true) /// Enables the "embedded" swift mode (no runtime). EXPERIMENTAL_FEATURE(Embedded, true) +/// Enables importing the Volatile module +EXPERIMENTAL_FEATURE(Volatile, true) + /// Enables noncopyable generics SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NoncopyableGenerics, true) diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index b2f6ac65c0806..eabcc42e76884 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -505,6 +505,7 @@ static bool usesFeatureRawLayout(Decl *decl) { } UNINTERESTING_FEATURE(Embedded) +UNINTERESTING_FEATURE(Volatile) UNINTERESTING_FEATURE(SuppressedAssociatedTypes) static bool usesFeatureNoncopyableGenerics(Decl *decl) { diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 9d2de9f61a47d..1d9eb66c38534 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -397,6 +397,15 @@ ImportResolver::getModule(ImportPath::Module modulePath) { } } + // Only allow importing "Volatile" with Feature::Volatile or Feature::Embedded + if (!ctx.LangOpts.hasFeature(Feature::Volatile) && + !ctx.LangOpts.hasFeature(Feature::Embedded)) { + if (ctx.getRealModuleName(moduleID.Item).str() == "_Volatile") { + ctx.Diags.diagnose(SourceLoc(), diag::volatile_is_experimental); + return nullptr; + } + } + // If the imported module name is the same as the current module, // skip the Swift module loader and use the Clang module loader instead. // This allows a Swift module to extend a Clang module of the same name. diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index eced0d1a99adf..a3c2ecd7b1769 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -280,6 +280,8 @@ if(SWIFT_BUILD_STDLIB AND NOT SWIFT_STDLIB_BUILD_ONLY_CORE_MODULES) if(SWIFT_ENABLE_SYNCHRONIZATION) add_subdirectory(Synchronization) endif() + + add_subdirectory(Volatile) endif() if(SWIFT_BUILD_REMOTE_MIRROR) diff --git a/stdlib/public/Volatile/CMakeLists.txt b/stdlib/public/Volatile/CMakeLists.txt new file mode 100644 index 0000000000000..f728b215ee302 --- /dev/null +++ b/stdlib/public/Volatile/CMakeLists.txt @@ -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) + 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() diff --git a/stdlib/public/Volatile/Volatile.swift b/stdlib/public/Volatile/Volatile.swift new file mode 100644 index 0000000000000..1ce38f835568c --- /dev/null +++ b/stdlib/public/Volatile/Volatile.swift @@ -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 +/// . +@frozen +public struct VolatileMappedRegister { + @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) + } +} diff --git a/test/Volatile/volatile-exec.swift b/test/Volatile/volatile-exec.swift new file mode 100644 index 0000000000000..8096d0fc85f23 --- /dev/null +++ b/test/Volatile/volatile-exec.swift @@ -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(unsafeBitPattern: UInt(bitPattern: p)) + print("byte = \(volatilePointer.load())") + // CHECK: byte = 42 + volatilePointer.store(77) + } + print("byte = \(byte)") + // CHECK: byte = 77 + + var heapPointer = UnsafeMutablePointer.allocate(capacity: 16) // uninitialized content + for i in 0 ..< 16 { + let pointerWithOffset = heapPointer.advanced(by: i) + let volatilePointer = VolatileMappedRegister(unsafeBitPattern: UInt(bitPattern: pointerWithOffset)) + volatilePointer.store(UInt8(0x61 + i)) + } + heapPointer[15] = 0 + print(String(cString: heapPointer)) + // CHECK: abcdefghijklmno + } +} diff --git a/test/Volatile/volatile-feature-flag.swift b/test/Volatile/volatile-feature-flag.swift new file mode 100644 index 0000000000000..0aae609b7fd53 --- /dev/null +++ b/test/Volatile/volatile-feature-flag.swift @@ -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' diff --git a/test/Volatile/volatile-ir.swift b/test/Volatile/volatile-ir.swift new file mode 100644 index 0000000000000..3c87e07e1ace9 --- /dev/null +++ b/test/Volatile/volatile-ir.swift @@ -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(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(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(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(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: } diff --git a/test/Volatile/volatile-null.swift b/test/Volatile/volatile-null.swift new file mode 100644 index 0000000000000..40a3505342f54 --- /dev/null +++ b/test/Volatile/volatile-null.swift @@ -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(unsafeBitPattern: 0) + p.store(42) +} + +// CHECK: define {{.*}}void @"$s4main20test_volatilepointeryyF"() +// CHECK: store atomic volatile i8 42, ptr null monotonic, align {{.*}} +// CHECK: ret void +// CHECK: } diff --git a/test/Volatile/volatile-repeat-loads.swift b/test/Volatile/volatile-repeat-loads.swift new file mode 100644 index 0000000000000..c8aa106df95b0 --- /dev/null +++ b/test/Volatile/volatile-repeat-loads.swift @@ -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(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: } diff --git a/test/embedded/volatile-exec.swift b/test/embedded/volatile-exec.swift new file mode 100644 index 0000000000000..f72a886fe8178 --- /dev/null +++ b/test/embedded/volatile-exec.swift @@ -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(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.allocate(capacity: 16) // uninitialized content + for i in 0 ..< 16 { + let pointerWithOffset = heapPointer.advanced(by: i) + let volatilePointer = VolatileMappedRegister(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 + } +} diff --git a/test/embedded/volatile-ir.swift b/test/embedded/volatile-ir.swift new file mode 100644 index 0000000000000..0b656f15e2dd5 --- /dev/null +++ b/test/embedded/volatile-ir.swift @@ -0,0 +1,57 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Embedded -Onone | %FileCheck %s +// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Embedded -O | %FileCheck %s +// RUN: %target-swift-emit-ir %s -module-name main -parse-as-library -enable-experimental-feature Embedded -Osize | %FileCheck %s + +// REQUIRES: swift_in_compiler +// REQUIRES: OS=macosx || OS=linux-gnu + +import _Volatile + +public func test_uint8() -> UInt8 { + let p = VolatileMappedRegister(unsafeBitPattern: 0xf000baa9) + p.store(42) + return p.load() +} + +// CHECK: define {{.*}}i8 @"$s4main10test_uint8s5UInt8VyF"() +// CHECK: store atomic volatile i8 42, ptr inttoptr (i64 4026579625 to ptr) monotonic, align 1 +// CHECK: [[RET:%.*]] = load atomic volatile i8, ptr inttoptr (i64 4026579625 to ptr) monotonic, align 1 +// CHECK: ret i8 [[RET]] +// CHECK: } + +public func test_uint16() -> UInt16 { + let p = VolatileMappedRegister(unsafeBitPattern: 0xf000baaa) + p.store(42) + return p.load() +} + +// CHECK: define {{.*}}i16 @"$s4main11test_uint16s6UInt16VyF"() +// CHECK: store atomic volatile i16 42, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 2 +// CHECK: [[RET:%.*]] = load atomic volatile i16, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 2 +// CHECK: ret i16 [[RET]] +// CHECK: } + +public func test_uint32() -> UInt32 { + let p = VolatileMappedRegister(unsafeBitPattern: 0xf000baaa) + p.store(42) + return p.load() +} + +// CHECK: define {{.*}}i32 @"$s4main11test_uint32s6UInt32VyF"() +// CHECK: store atomic volatile i32 42, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 4 +// CHECK: [[RET:%.*]] = load atomic volatile i32, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 4 +// CHECK: ret i32 [[RET]] +// CHECK: } + +public func test_uint64() -> UInt64 { + let p = VolatileMappedRegister(unsafeBitPattern: 0xf000baaa) + p.store(42) + return p.load() +} + +// CHECK: define {{.*}}i64 @"$s4main11test_uint64s6UInt64VyF"() +// CHECK: store atomic volatile i64 42, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 8 +// CHECK: [[RET:%.*]] = load atomic volatile i64, ptr inttoptr (i64 4026579626 to ptr) monotonic, align 8 +// CHECK: ret i64 [[RET]] +// CHECK: }