Difference between revisions of "DIP20"

From D Wiki
Jump to: navigation, search
m (syntax)
m (Use syntaxhighlight blocks instead of plain ones)
 
(One intermediate revision by one other user not shown)
Line 10: Line 10:
 
|-
 
|-
 
|Status:
 
|Status:
|Draft
+
|Implemented
 
|-
 
|-
 
|Created:
 
|Created:
Line 38: Line 38:
 
The first one, volatileLoad is used to perform volatile read operations from memory:
 
The first one, volatileLoad is used to perform volatile read operations from memory:
  
 +
<syntaxhighlight lang=D>
 
     T volatileLoad(T)(T* ptr);
 
     T volatileLoad(T)(T* ptr);
 +
</syntaxhighlight>
  
 +
A call to this function, such as:
  
A call to this function, such as:
+
<syntaxhighlight lang=D>
 
     T* p = ...;
 
     T* p = ...;
 
     T i = volatileLoad(p);
 
     T i = volatileLoad(p);
 +
</syntaxhighlight>
  
 +
Shall be equivalent to:
  
Shall be equivalent to:
+
<syntaxhighlight lang=D>
 
     T* p = ...;
 
     T* p = ...;
 
     T i = *p;
 
     T i = *p;
 
+
</syntaxhighlight>
  
 
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.
 
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:
 
The second, volatileStore is used to perform volatile write operations to memory:
  
 +
<syntaxhighlight lang=D>
 
     void volatileStore(T)(T* ptr, T val);
 
     void volatileStore(T)(T* ptr, T val);
 +
</syntaxhighlight>
  
 +
A call to this function, such as:
  
A call to this function, such as:
+
<syntaxhighlight lang=D>
 
     T* p = ...;
 
     T* p = ...;
 
     T i = ...;
 
     T i = ...;
 
     volatileStore(p, i);
 
     volatileStore(p, i);
 +
</syntaxhighlight>
  
 +
Shall be equivalent to:
  
Shall be equivalent to:
+
<syntaxhighlight lang=D>
 
     T* p = ...;
 
     T* p = ...;
 
     T i = ...;
 
     T i = ...;
 
     *p = i;
 
     *p = i;
 +
</syntaxhighlight>
  
 
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.
 
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.

Latest revision as of 10:23, 12 June 2016

Title: Volatile read/write intrinsics
DIP: 20
Version: 2
Status: Implemented
Created: 2012-10-10
Last Modified: 2012-10-11
Author: Alex Rønne Petersen (alex (AT) lycus.org)
Links: proposed implementation, pull

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:

  • It is not implemented in any compiler, so practically using it now is not possible at all.
  • It does not have any well-defined semantics yet.
  • It will most likely not be portable because it's designed for the x86 memory model.
  • If ever implemented, it will result in memory fences and/or atomic operations, which is **not** what volatile memory operations * are about. This will severely affect pipelining and performance in general.

Inline assembly

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

  • It leads to unportable programs.
  • It leads to a dependency on the compiler's inline assembly syntax.
  • Some compilers may even decide to optimize the assembly itself.
  • Memory-mapped I/O is too common in low-level programming for a systems language to require the programmer to drop to assembly.

Copyright

This document has been placed in the Public Domain.