1218885Sdim//===-- Atomic.cpp - Atomic Operations --------------------------*- C++ -*-===//
2218885Sdim//
3218885Sdim//                     The LLVM Compiler Infrastructure
4218885Sdim//
5218885Sdim// This file is distributed under the University of Illinois Open Source
6218885Sdim// License. See LICENSE.TXT for details.
7218885Sdim//
8218885Sdim//===----------------------------------------------------------------------===//
9218885Sdim//
10276479Sdim//  This file implements atomic operations.
11218885Sdim//
12218885Sdim//===----------------------------------------------------------------------===//
13218885Sdim
14218885Sdim#include "llvm/Support/Atomic.h"
15234353Sdim#include "llvm/Config/llvm-config.h"
16218885Sdim
17218885Sdimusing namespace llvm;
18218885Sdim
19218885Sdim#if defined(_MSC_VER)
20276479Sdim#include <Intrin.h>
21218885Sdim#include <windows.h>
22218885Sdim#undef MemoryFence
23218885Sdim#endif
24218885Sdim
25243830Sdim#if defined(__GNUC__) || (defined(__IBMCPP__) && __IBMCPP__ >= 1210)
26243830Sdim#define GNU_ATOMICS
27243830Sdim#endif
28243830Sdim
29218885Sdimvoid sys::MemoryFence() {
30226633Sdim#if LLVM_HAS_ATOMICS == 0
31218885Sdim  return;
32218885Sdim#else
33243830Sdim#  if defined(GNU_ATOMICS)
34218885Sdim  __sync_synchronize();
35218885Sdim#  elif defined(_MSC_VER)
36218885Sdim  MemoryBarrier();
37218885Sdim#  else
38218885Sdim# error No memory fence implementation for your platform!
39218885Sdim#  endif
40218885Sdim#endif
41218885Sdim}
42218885Sdim
43218885Sdimsys::cas_flag sys::CompareAndSwap(volatile sys::cas_flag* ptr,
44218885Sdim                                  sys::cas_flag new_value,
45218885Sdim                                  sys::cas_flag old_value) {
46226633Sdim#if LLVM_HAS_ATOMICS == 0
47218885Sdim  sys::cas_flag result = *ptr;
48218885Sdim  if (result == old_value)
49218885Sdim    *ptr = new_value;
50218885Sdim  return result;
51243830Sdim#elif defined(GNU_ATOMICS)
52218885Sdim  return __sync_val_compare_and_swap(ptr, old_value, new_value);
53218885Sdim#elif defined(_MSC_VER)
54218885Sdim  return InterlockedCompareExchange(ptr, new_value, old_value);
55218885Sdim#else
56218885Sdim#  error No compare-and-swap implementation for your platform!
57218885Sdim#endif
58218885Sdim}
59218885Sdim
60218885Sdimsys::cas_flag sys::AtomicIncrement(volatile sys::cas_flag* ptr) {
61226633Sdim#if LLVM_HAS_ATOMICS == 0
62218885Sdim  ++(*ptr);
63218885Sdim  return *ptr;
64243830Sdim#elif defined(GNU_ATOMICS)
65218885Sdim  return __sync_add_and_fetch(ptr, 1);
66218885Sdim#elif defined(_MSC_VER)
67218885Sdim  return InterlockedIncrement(ptr);
68218885Sdim#else
69218885Sdim#  error No atomic increment implementation for your platform!
70218885Sdim#endif
71218885Sdim}
72218885Sdim
73218885Sdimsys::cas_flag sys::AtomicDecrement(volatile sys::cas_flag* ptr) {
74226633Sdim#if LLVM_HAS_ATOMICS == 0
75218885Sdim  --(*ptr);
76218885Sdim  return *ptr;
77243830Sdim#elif defined(GNU_ATOMICS)
78218885Sdim  return __sync_sub_and_fetch(ptr, 1);
79218885Sdim#elif defined(_MSC_VER)
80218885Sdim  return InterlockedDecrement(ptr);
81218885Sdim#else
82218885Sdim#  error No atomic decrement implementation for your platform!
83218885Sdim#endif
84218885Sdim}
85218885Sdim
86218885Sdimsys::cas_flag sys::AtomicAdd(volatile sys::cas_flag* ptr, sys::cas_flag val) {
87226633Sdim#if LLVM_HAS_ATOMICS == 0
88218885Sdim  *ptr += val;
89218885Sdim  return *ptr;
90243830Sdim#elif defined(GNU_ATOMICS)
91218885Sdim  return __sync_add_and_fetch(ptr, val);
92218885Sdim#elif defined(_MSC_VER)
93218885Sdim  return InterlockedExchangeAdd(ptr, val) + val;
94218885Sdim#else
95218885Sdim#  error No atomic add implementation for your platform!
96218885Sdim#endif
97218885Sdim}
98218885Sdim
99218885Sdimsys::cas_flag sys::AtomicMul(volatile sys::cas_flag* ptr, sys::cas_flag val) {
100218885Sdim  sys::cas_flag original, result;
101218885Sdim  do {
102218885Sdim    original = *ptr;
103218885Sdim    result = original * val;
104218885Sdim  } while (sys::CompareAndSwap(ptr, result, original) != original);
105218885Sdim
106218885Sdim  return result;
107218885Sdim}
108218885Sdim
109218885Sdimsys::cas_flag sys::AtomicDiv(volatile sys::cas_flag* ptr, sys::cas_flag val) {
110218885Sdim  sys::cas_flag original, result;
111218885Sdim  do {
112218885Sdim    original = *ptr;
113218885Sdim    result = original / val;
114218885Sdim  } while (sys::CompareAndSwap(ptr, result, original) != original);
115218885Sdim
116218885Sdim  return result;
117218885Sdim}
118