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