/**
 * This module declares intrinsics for volatile operations.
 *
 * Copyright: Copyright © 2019, The D Language Foundation
 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Authors:   Walter Bright, Emily Castellotti
 * Source:    $(DRUNTIMESRC core/volatile.d)
 */

module core.volatile;

nothrow:
@safe:
@nogc:

/*************************************
 * Read/write value from/to the memory location indicated by ptr.
 *
 * These functions are recognized by the compiler, and calls to them are guaranteed
 * to not be removed (as dead assignment elimination or presumed to have no effect)
 * or reordered in the same thread.
 *
 * These reordering guarantees are only made with regards to other
 * operations done through these functions; the compiler is free to reorder regular
 * loads/stores with regards to loads/stores done through these functions.
 *
 * This is useful when dealing with memory-mapped I/O (MMIO) where a store can
 * have an effect other than just writing a value, or where sequential loads
 * with no intervening stores can retrieve
 * different values from the same location due to external stores to the location.
 *
 * These functions will, when possible, do the load/store as a single operation. In
 * general, this is possible when the size of the operation is less than or equal to
 * $(D (void*).sizeof), although some targets may support larger operations. If the
 * load/store cannot be done as a single operation, multiple smaller operations will be used.
 *
 * These are not to be conflated with atomic operations. They do not guarantee any
 * atomicity. This may be provided by coincidence as a result of the instructions
 * used on the target, but this should not be relied on for portable programs.
 * Further, no memory fences are implied by these functions.
 * They should not be used for communication between threads.
 * They may be used to guarantee a write or read cycle occurs at a specified address.
 */

version (LDC)
{
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ubyte volatileLoad(ubyte* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ushort volatileLoad(ushort* ptr);  /// ditto
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        uint volatileLoad(uint* ptr);      /// ditto
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ulong volatileLoad(ulong* ptr);    /// ditto

    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ubyte* ptr, ubyte value);   /// ditto
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ushort* ptr, ushort value); /// ditto
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(uint* ptr, uint value);     /// ditto
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ulong* ptr, ulong value);   /// ditto
}
else
{

ubyte  volatileLoad(ubyte * ptr);
ushort volatileLoad(ushort* ptr);  /// ditto
uint   volatileLoad(uint  * ptr);  /// ditto
ulong  volatileLoad(ulong * ptr);  /// ditto

void volatileStore(ubyte * ptr, ubyte  value);   /// ditto
void volatileStore(ushort* ptr, ushort value);   /// ditto
void volatileStore(uint  * ptr, uint   value);   /// ditto
void volatileStore(ulong * ptr, ulong  value);   /// ditto

} // !LDC

@system unittest
{
    alias TT(T...) = T;

    foreach (T; TT!(ubyte, ushort, uint, ulong))
    {
        T u;
        T* p = &u;
        volatileStore(p, 1);
        T r = volatileLoad(p);
        assert(r == u);
    }
}
