Skip to content
This repository has been archived by the owner on Feb 8, 2024. It is now read-only.

Commit

Permalink
Implemented atomic intrinsics for llvm 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexeyProkhin committed Oct 25, 2011
1 parent fba10fa commit 2f53629
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 59 deletions.
76 changes: 48 additions & 28 deletions import/ldc/intrinsics.di
Original file line number Diff line number Diff line change
Expand Up @@ -245,65 +245,85 @@ pragma(intrinsic, "llvm.part.select.i#")
// ATOMIC OPERATIONS AND SYNCHRONIZATION INTRINSICS
//

// The llvm.memory.barrier intrinsic guarantees ordering between specific
// pairs of memory access types.
enum AtomicOrdering {
NotAtomic = 0,
Unordered = 1,
Monotonic = 2,
Consume = 3,
Acquire = 4,
Release = 5,
AcquireRelease = 6,
SequentiallyConsistent = 7
};
alias AtomicOrdering.SequentiallyConsistent DefaultOrdering;

// The 'fence' intrinsic is used to introduce happens-before edges between operations.
pragma(fence)
void llvm_memory_fence(AtomicOrdering ordering = DefaultOrdering);

// This intrinsic loads a value stored in memory at ptr.
pragma(atomic_load)
T llvm_atomic_load(T)(T *ptr, AtomicOrdering ordering = DefaultOrdering);

// This intrinsic stores a value in val in the memory at ptr.
pragma(atomic_store)
void llvm_atomic_store(T)(T val, T *ptr, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.memory.barrier")
void llvm_memory_barrier(bool ll, bool ls, bool sl, bool ss, bool device);

// This loads a value in memory and compares it to a given value. If they are
// equal, it stores a new value into the memory.

pragma(intrinsic, "llvm.atomic.cmp.swap.i#.p0i#")
T llvm_atomic_cmp_swap(T)(shared T* ptr, T cmp, T val);
pragma(atomic_cmp_xchg)
T llvm_atomic_cmp_swap(T)(shared T* ptr, T cmp, T val, AtomicOrdering ordering = DefaultOrdering);


// This intrinsic loads the value stored in memory at ptr and yields the value
// from memory. It then stores the value in val in the memory at ptr.

pragma(intrinsic, "llvm.atomic.swap.i#.p0i#")
T llvm_atomic_swap(T)(T* ptr, T val);
pragma(atomic_rmw, "xchg")
T llvm_atomic_swap(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

// This intrinsic adds delta to the value stored in memory at ptr. It yields
// the original value at ptr.

pragma(intrinsic, "llvm.atomic.load.add.i#.p0i#")
T llvm_atomic_load_add(T)(T* ptr, T val);
pragma(atomic_rmw, "add")
T llvm_atomic_load_add(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

// This intrinsic subtracts delta to the value stored in memory at ptr. It
// yields the original value at ptr.

pragma(intrinsic, "llvm.atomic.load.sub.i#.p0i#")
T llvm_atomic_load_sub(T)(T* ptr, T val);
pragma(atomic_rmw, "sub")
T llvm_atomic_load_sub(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

// These intrinsics bitwise the operation (and, nand, or, xor) delta to the
// value stored in memory at ptr. It yields the original value at ptr.

pragma(intrinsic, "llvm.atomic.load.and.i#.p0i#")
T llvm_atomic_load_and(T)(T* ptr, T val);
pragma(atomic_rmw, "and")
T llvm_atomic_load_and(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.nand.i#.p0i#")
T llvm_atomic_load_nand(T)(T* ptr, T val);
pragma(atomic_rmw, "nand")
T llvm_atomic_load_nand(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.or.i#.p0i#")
T llvm_atomic_load_or(T)(T* ptr, T val);
pragma(atomic_rmw, "or")
T llvm_atomic_load_or(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.xor.i#.p0i#")
T llvm_atomic_load_xor(T)(T* ptr, T val);
pragma(atomic_rmw, "xor")
T llvm_atomic_load_xor(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

// These intrinsics takes the signed or unsigned minimum or maximum of delta
// and the value stored in memory at ptr. It yields the original value at ptr.

pragma(intrinsic, "llvm.atomic.load.max.i#.p0i#")
T llvm_atomic_load_max(T)(T* ptr, T val);
pragma(atomic_rmw, "max")
T llvm_atomic_load_max(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.min.i#.p0i#")
T llvm_atomic_load_min(T)(T* ptr, T val);
pragma(atomic_rmw, "min")
T llvm_atomic_load_min(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.umax.i#.p0i#")
T llvm_atomic_load_umax(T)(T* ptr, T val);
pragma(atomic_rmw, "umax")
T llvm_atomic_load_umax(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);

pragma(intrinsic, "llvm.atomic.load.umin.i#.p0i#")
T llvm_atomic_load_umin(T)(T* ptr, T val);
pragma(atomic_rmw, "umin")
T llvm_atomic_load_umin(T)(T* ptr, T val, AtomicOrdering ordering = DefaultOrdering);


//
Expand Down
65 changes: 34 additions & 31 deletions src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -236,50 +236,35 @@ else version( LDC )
seq, /// fully sequenced (acq + rel)
}

HeadUnshared!(T) atomicLoad(msync ms = msync.seq, T)( ref const shared T val )
if(!__traits(isFloating, T))
private AtomicOrdering getOrdering(msync ms) pure
{
llvm_memory_barrier(
ms == msync.acq || ms == msync.seq,
ms == msync.acq || ms == msync.seq,
ms == msync.rel || ms == msync.seq,
ms == msync.rel || ms == msync.seq,
false);
static if (is(T P == U*, U)) // pointer
{
llvm_atomic_load_add!(size_t)(cast(size_t*)&val, 0);
}
else static if (T.sizeof == bool.sizeof)
{
llvm_atomic_load_add!(ubyte)(cast(ubyte*)&val, cast(ubyte)0);
}
if (ms == msync.acq)
return AtomicOrdering.Acquire;
else if (ms == msync.rel)
return AtomicOrdering.Release;
else if (ms == msync.seq)
return AtomicOrdering.SequentiallyConsistent;
else if (ms == msync.raw)
return AtomicOrdering.NotAtomic;
else
{
llvm_atomic_load_add!(T)(cast(T*)&val, cast(T)0);
}
return cast(HeadUnshared!(T))val;
assert(0);
}

void atomicStore(msync ms = msync.seq, T, V1)( ref shared T val, V1 newval )
if(!__traits(isFloating, T) && __traits(compiles, mixin("val = newval")))
HeadUnshared!(T) atomicLoad(msync ms = msync.seq, T)( ref const shared T val )
if(!__traits(isFloating, T))
{
llvm_memory_barrier(
ms == msync.acq || ms == msync.seq,
ms == msync.acq || ms == msync.seq,
ms == msync.rel || ms == msync.seq,
ms == msync.rel || ms == msync.seq,
false);
enum ordering = getOrdering(ms == msync.acq ? msync.seq : ms);
static if (is(T P == U*, U)) // pointer
{
llvm_atomic_swap!(size_t)(cast(size_t*)&val, cast(size_t)newval);
return cast(HeadUnshared!(T))llvm_atomic_load!(size_t)(cast(size_t*)&val, ordering);
}
else static if (T.sizeof == bool.sizeof)
{
llvm_atomic_swap!(ubyte)(cast(ubyte*)&val, newval);
return cast(HeadUnshared!(T))llvm_atomic_load!(ubyte)(cast(ubyte*)&val, ordering);
}
else
{
llvm_atomic_swap!(T)(cast(T*)&val, cast(T)newval);
return cast(HeadUnshared!(T))llvm_atomic_load!(T)(cast(T*)&val, ordering);
}
}

Expand All @@ -305,6 +290,24 @@ else version( LDC )
static assert(0, "Cannot atomically store 80-bit reals.");
}
}

void atomicStore(msync ms = msync.seq, T, V1)( ref shared T val, V1 newval )
if(!__traits(isFloating, T) && __traits(compiles, mixin("val = newval")))
{
enum ordering = getOrdering(ms == msync.rel ? msync.seq : ms);
static if (is(T P == U*, U)) // pointer
{
llvm_atomic_store!(size_t)(cast(size_t)newval, cast(size_t*)&val, ordering);
}
else static if (T.sizeof == bool.sizeof)
{
llvm_atomic_store!(ubyte)(newval, cast(ubyte*)&val, ordering);
}
else
{
llvm_atomic_store!(T)(cast(T)newval, cast(T*)&val, ordering);
}
}
}
else version( AsmX86_32 )
{
Expand Down

0 comments on commit 2f53629

Please sign in to comment.