Skip to content
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

remove confusing remarks about mixed volatile and non-volatile accesses #60972

Merged
merged 1 commit into from May 21, 2019

Conversation

@RalfJung
Copy link
Member

RalfJung commented May 20, 2019

These comments were originally added by @ecstatic-morse in 911d35f and then later edited by me. The intention, I think, was to make sure people do both their reads and writes with these methods if the affected memory really is used for communication with external devices.

However, people read this as saying that mixed volatile/non-volatile accesses are UB, which -- to my knowledge -- they are not. So better remove this.

Cc @rkruppe @rust-lang/wg-unsafe-code-guidelines

@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented May 20, 2019

r? @alexcrichton

(rust_highfive has picked a reviewer for you, use r? to override)

@petrochenkov

This comment has been minimized.

Copy link
Contributor

petrochenkov commented May 20, 2019

For reference: C++ paper proposing volatile_load/volatile_store has some more detailed discussion of mixed volatile/non-volatile operations.

@rkruppe

This comment has been minimized.

Copy link
Member

rkruppe commented May 20, 2019

I couldn't for the life of me describe how volatile and non-volatile accesses to the same memory location interact, but that's more of a consequence of the fact that the rules of volatile accesses in general are enormously under-specified in LLVM and the languages LLVM mostly takes cues from. There doesn't seem to be anything in LLVM docs explicitly saying that such mixed-volatile accesses are UB, though. Plus, intuitively it seems like such mixed accesses can be made sense of -- with the caveat that the non-volatile ones are just as subject to the optimizers' whims as any other non-volatile accesses.

I have sympathies for the position that in such cases (some behavior is not specified and there's not even much of a shared informal understanding of what it should be), it's almost as bad as UB to rely on. But we can't very well make volatile go away until it's all figured out, so we have to be more pragmatic and try to guide people to implement the operations they need in the way least likely to be broken.

Certainly there is some truth to this warning and it would be nice to keep it in some form (in particular I feel confident saying it must be adhered to for correct MMIO), but as you say it causes some confusion. That confusion is perhaps warranted (see above) but a blanket avoidance of volatile for the use case that kicked this off (memory zeroing) seems a net loss. Perhaps we can weaken this wording, throw in some weasel words about how avoiding non-volatile accesses for "common use cases" and perhaps name MMIO as a clear-cut example?

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented May 20, 2019

@bors: r+ rollup

@bors

This comment has been minimized.

Copy link
Contributor

bors commented May 20, 2019

📌 Commit b9be68c has been approved by alexcrichton

Centril added a commit to Centril/rust that referenced this pull request May 20, 2019
remove confusing remarks about mixed volatile and non-volatile accesses

These comments were originally added by @ecstatic-morse in rust-lang@911d35f and then later edited by me. The intention, I think, was to make sure people do both their reads and writes with these methods if the affected memory really is used for communication with external devices.

However, [people read this as saying that mixed volatile/non-volatile accesses are UB](rust-lang#58599 (comment)), which -- to my knowledge -- they are not. So better remove this.

Cc @rkruppe @rust-lang/wg-unsafe-code-guidelines
bors added a commit that referenced this pull request May 20, 2019
Rollup of 11 pull requests

Successful merges:

 - #60383 (Fix position source code files toggle)
 - #60453 (Fall back to `/dev/urandom` on `EPERM` for `getrandom`)
 - #60487 (Fix search sidebar width when no crate select is present)
 - #60511 (Fix intra-doc link resolution failure on re-exporting libstd)
 - #60823 (Fix incremental compilation of cdylib emitting spurious unused_attributes lint)
 - #60915 (stable hashing: Remove unused field and add documentation.)
 - #60942 (Misc changes to rustc_metadata)
 - #60952 (Document BinaryHeap time complexity)
 - #60959 (rustc: Improve type size assertions)
 - #60972 (remove confusing remarks about mixed volatile and non-volatile accesses)
 - #60983 (Set -funwind-tables and -fno-exceptions unconditionally for LLVM's libunwind)

Failed merges:

r? @ghost
@bors bors merged commit b9be68c into rust-lang:master May 21, 2019
1 check passed
1 check passed
Travis CI - Pull Request Build Passed
Details
@RalfJung

This comment has been minimized.

Copy link
Member Author

RalfJung commented May 21, 2019

I have sympathies for the position that in such cases (some behavior is not specified and there's not even much of a shared informal understanding of what it should be), it's almost as bad as UB to rely on.

TBH I think the shared confusion is mostly about how to express the guarantee, while the intuitive understanding of it seems to be fairly universally shared. At least so far I have not encountered a version of their semantics that is in conflict with the one in my head. ;)

Personally, I'd specify volatile accesses as follows (volatile objects in the C sense are then simply objects whose accesses are implicitly volatile, and we could encode that as a user library in Rust similar to the Atomic* types): they are basically like syscalls, except that we know the only effect they have in memory "the abstract machine cares about" (memory allocated via malloc, stack memory, and maybe the memory backing global statics) is the read from / write to the given location. However, they can have an arbitrary effect on some not-directly-observable state. The part about not affecting unrelated memory "the abstract machine cares about" explains why volatile accesses can be reordered with non-volatile accesses to different locations, and the part where they can have additional side-effects that are not directly observable explains why volatile accesses themselves cannot be reordered and removed.

Things get more subtle when it comes to tearing of large volatile accesses, but that's mostly a matter of underspecification: beyond some size, a volatile write is not actually one write but a sequence of writes. The details of how that happens should ideally be implementation-defined, but in practice seem to be unspecified except that accesses that are no larger than a machine word (?) definitely do not get teared. But then the semantics above apply after tearing was done.

Certainly there is some truth to this warning and it would be nice to keep it in some form (in particular I feel confident saying it must be adhered to for correct MMIO),

The problem is that MMIO has some additional problems due to dereferencable. So if we decide to talk about MMIO we should really have more of a guide listing all the things you need to be aware of. Seems like more of a case for the embedded book or the nomicon.

@rkruppe

This comment has been minimized.

Copy link
Member

rkruppe commented May 24, 2019

I think there are tricky details where there's potential for disagreement about the end result, for example:

  • can volatile accesses trap (diverge)? For compiler optimizations it's very useful to assume they can't and indeed LLVM does that, but the "just lower this to an actual load/store instruction and don't mess with it" camp, which e.g. includes those who want to intentionally cause a segfault with write_volatile(0 as *mut u8, 42), would probably object.
  • wrt tearing, does alignment matter? You didn't mention it, but some architectures don't have unaligned word-sized loads/stores. Yet on other architectures, non-tearing unaligned accesses might be supported and useful.

But your point that writing a guide for MMIO specifically will open a huge can of worms that goes beyond just volatile accesses is a good one.

tarcieri pushed a commit to iqlusioninc/crates that referenced this pull request Jun 4, 2019
The documentation previously included a lengthy essay about potential
caveats in what it can guarantee as mixed volatile/non-volatile writes
were previously assumed to be undefined behavior due to language to that
effect in the official documentation for `write_volatile`.

That language has subsequently been removed after discussion about the
safety implications:

rust-lang/rust#60972

Though generally it's not a good idea to mix volatile and non-volatile
writes when using volatile accesses to communicate with e.g. memory
mapped external devices, for the particular usage pattern in this crate,
i.e. writing a zero-value, it can be considered well-defined.
@tarcieri

This comment has been minimized.

Copy link

tarcieri commented Jun 4, 2019

FYI, I've updated the documentation of the zeroize crate accordingly, in case anyone cares to double check what I'm saying about this PR is accurate: iqlusioninc/crates#214

@RalfJung RalfJung deleted the RalfJung:volatile branch Jun 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.