Skip to content

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

Merged
merged 7 commits into from
Apr 6, 2024

Conversation

kubamracek
Copy link
Contributor

@kubamracek kubamracek commented Jan 16, 2024

See the full pitch at https://forums.swift.org/t/pitch-low-level-operations-for-volatile-memory-accesses/69483. Summary:

This proposal:

  • adds APIs for the most basic load + store volatile operations, on 8, 16, 32 and 64 bit integers,
    assumes the actual volatile semantics are defined by Clang and LLVM, which match the commonly understood behavior (no removal or reordering by the compiler), see the definition at https://llvm.org/docs/LangRef.html#volatile-memory-accesses,
  • packages the new APIs into a separate module which must be explicitly imported by the user (import Volatile) -- this is intended to prevent accidental usage of volatile operations in e.g. userspace code for inter-thread synchronization (a common misuse of volatile in C).

This proposal:

  • doesn't try to provide any safety or structured access to these operations, as that is left for libraries built on top of the low-level APIs,
  • doesn't try to make it possible to mark struct members or variables as "volatile" (like C allows), instead only a pointer to storage can be volatile and a load/store using an eligible pointer can be volatile -- this sidesteps many design issues around volatile that C has,
  • doesn't try to provide an abstraction over making more types volatile (e.g. how the AtomicValue and AtomicRepresentation protocols in swift-atomics provide an abstraction for making custom types atomic) -- the reasoning is that unlike atomics, volatile operations are only intended for MMIO HW register access and similar usage, which is only meaningful on machine level integer types and allowing custom types to become volatile would encourage misuse for inter-thread synchronization.

Added APIs in a new "Volatile" module (underscored name, _Volatile, for now):

struct UnsafeVolatilePointer<Pointee> {
  init(bitPattern: UInt)
}

extension UnsafeVolatilePointer<UInt8> { // also 16, 32, 64
  func load() -> UInt8 // with LLVM volatile load semantics, assume natural alignment
  func store(_ value: UInt8) // with LLVM volatile store semantics, assume natural alignment
}

extension UnsafeMutablePointer<UInt8> { // also 16, 32, 64
  func volatileLoad() -> UInt8 // with LLVM volatile load semantics, assume natural alignment
  func volatileStore(_ value: UInt8) // with LLVM volatile store semantics, assume natural alignment
}

Copy link
Member

@rauhul rauhul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great start, we will obviously update this to match wherever the pitch ends up

@kubamracek
Copy link
Contributor Author

@swift-ci please test

@kubamracek kubamracek added the embedded Embedded Swift label Jan 17, 2024
public struct UnsafeVolatilePointer<Pointee> {
public var _rawPointer: Builtin.RawPointer
public init(bitPattern: UInt) {
_rawPointer = UnsafeRawPointer(bitPattern: bitPattern)!._rawValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens when someone passes 0? this will assert - are there ever memory mapped registers at 0x0?

Should this not use self.init(Builtin.inttoptr_Word(bitPattern._builtinWordValue))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should now handle 0 correctly.




extension UnsafeMutablePointer where Pointee == UInt8 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say drop these extensions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped

Copy link
Contributor

@phausler phausler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To land this we should definitely do the following

  1. gate it by a flag
  2. After consideration this should not conform to _Pointer
  3. permit this for all build types not just embedded - I could see the use for more than just embedded.
  4. the initializer should mention unsafe in it since that is the only spot that truly is unsafe; the type and the load/store are perfectly safe to hold
  5. it should probably be named VolatileRegister or something like that (or VolatileMappedRegister) to avoid confusion about general volatile things that are NOT register bound
  6. after validating this is the design we want we should then move it from a module down into the stdlib asap

Copy link
Contributor

@phausler phausler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be good to add functional tests to verify the initialization of values etc (and maybe even a test store/load on a heap region initialized unsafely)

The rest of the changes other than the tests and restriction to embedded seem on point.

@@ -397,6 +397,15 @@ ImportResolver::getModule(ImportPath::Module modulePath) {
}
}

// Only allow importing "Volatile" with Feature::Volatile or Feature::Embedded
Copy link
Contributor

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?

Copy link
Contributor Author

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

INSTALL_IN_COMPONENT stdlib
)

if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB)
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

@kubamracek
Copy link
Contributor Author

it might be good to add functional tests to verify the initialization of values etc (and maybe even a test store/load on a heap region initialized unsafely)

Added! Any more ideas what to try to cover? (The tests btw show that the Volatile module is available outside of embedded.)

@kubamracek
Copy link
Contributor Author

@swift-ci please test

@kubamracek
Copy link
Contributor Author

@swift-ci please test

@kubamracek kubamracek enabled auto-merge April 6, 2024 03:54
@kubamracek kubamracek merged commit 70afeef into swiftlang:main Apr 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
embedded Embedded Swift
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants