Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

Volatile read/write intrinsics

Version 2
Created 2012-10-10
StatusImplemented
Last modified 2012-10-11
Author Alex Rønne Petersen (alex (AT) lycus.org)

Abstract

This document describes a couple of simple compiler intrinsics that D compilers should implement. These are necessary for low-level, embedded, kernel, and driver developers. These intrinsics will ensure that a compiler cannot reorder volatile loads and stores with regards to each other.

Rationale

D currently has no way to do safe memory-mapped I/O. The reason for that is that the language has no well-defined means to do volatile memory loads and stores. This means that a compiler is free to reorder memory operations as it sees fit and even erase some loads/stores that it thinks are dead (but which have actual impact on program semantics).

These intrinsics will be essential for low-level development in D. D cannot truly replace C and/or C++ until it can perform the same low-level operations that developers who use those languages are accustomed to.

Description

Two intrinsics shall be declared in the core.bitop module.

The first one, volatileLoad is used to perform volatile read operations from memory:

   T volatileLoad(T)(T* ptr);

A call to this function, such as:

   T* p = ...;    T i = volatileLoad(p);

Shall be equivalent to:

   T* p = ...;    T i = *p;

However, with the exception that the compiler is not allowed to optimize out seemingly dead calls to volatileLoad, nor reorder calls to volatileLoad with respect to other calls to it or volatileStore. The second, volatileStore is used to perform volatile write operations to memory:

   void volatileStore(T)(T* ptr, T val);

A call to this function, such as:

   T* p = ...;    T i = ...;    volatileStore(p, i);

Shall be equivalent to:

   T* p = ...;    T i = ...;    *p = i;

However, with the exception that the compiler is not allowed to optimize out seemingly dead calls to volatileStore, nor reorder calls to volatileStore with respect to other calls to it or volatileLoad.

Detection

Compilers that support these intrinsics should define the D_Volatile version identifier. Compilers are free to support the intrinsics without defining this version identifier, but programmers should not rely on the presence of the intrinsics if it is not defined.

Implementation

DMD

DMD does not currently reorder loads and stores, so no particular change needs to happen in this area of the compiler. However, it is quite likely that the back end eliminates dead loads and stores, so calls to the intrinsics must be flagged as volatile in whatever way DMD’s back end allows it.

GDC

GCC’s internal code representation allows volatile statements in the C sense, which is sufficient.

LDC

LLVM trivially allows marking loads and stores as volatile

Other compilers

Other compilers presumably have similar means to flag loads and stores as volatile.

Alternatives

A number of alternatives to volatile intrinsics have been suggested. They are, however, not good enough to actually replace a volatile intrinsics for the reasons outlined below.

Shared qualifier

The shared type qualifier has been suggested as a solution to the problems volatile intrinsics try to solve. However:

Inline assembly

It was suggested to use inline assembly to perform volatile memory operations. While a correct solution, it is not reasonable:

This document has been placed in the Public Domain.