]> granicus.if.org Git - clang/commitdiff
Call out to GCC-compatible runtime functions for atomic ops that we can't use
authorDavid Chisnall <csdavec@swan.ac.uk>
Thu, 29 Mar 2012 18:01:11 +0000 (18:01 +0000)
committerDavid Chisnall <csdavec@swan.ac.uk>
Thu, 29 Mar 2012 18:01:11 +0000 (18:01 +0000)
LLVM intrinsics for.

I have an implementation of these functions, which wants to go in a libgcc_s
equivalent in compiler-rt.  It's currently here:

http://people.freebsd.org/~theraven/atomic.c

It will be committed to compiler-rt as soon as I work out where would be a
sensible place to put it...

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153666 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGExpr.cpp
test/CodeGen/atomic-ops.c

index 511914884f68bbc620a5275ae2fecdc133732129..0efb50884ccaacb19f97151d27ca19a1a7e5d7fe 100644 (file)
@@ -2798,7 +2798,6 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) {
     Val1 = EmitScalarExpr(E->getVal1());
     Val2 = EmitValToTemp(*this, E->getVal2());
     OrderFail = EmitScalarExpr(E->getOrderFail());
-    (void)OrderFail; // OrderFail is unused at the moment
   } else if ((E->getOp() == AtomicExpr::Add || E->getOp() == AtomicExpr::Sub) &&
              MemTy->isPointerType()) {
     // For pointers, we're required to do a bit of math: adding 1 to an int*
@@ -2817,50 +2816,78 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) {
   if (E->getOp() != AtomicExpr::Store && !Dest)
     Dest = CreateMemTemp(E->getType(), ".atomicdst");
 
+  // Use a library call.  See: http://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary .
   if (UseLibcall) {
-    // FIXME: Finalize what the libcalls are actually supposed to look like.
-    // See also http://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary .
-    return EmitUnsupportedRValue(E, "atomic library call");
-  }
-#if 0
-  if (UseLibcall) {
+
+    llvm::SmallVector<QualType, 5> Params;
+    CallArgList Args;
+    // Size is always the first parameter
+    Args.add(RValue::get(llvm::ConstantInt::get(SizeTy, Size)),
+             getContext().getSizeType());
+    // Atomic address is always the second parameter
+    Args.add(RValue::get(EmitCastToVoidPtr(Ptr)),
+             getContext().VoidPtrTy);
+
     const char* LibCallName;
+    QualType RetTy = getContext().VoidTy;
     switch (E->getOp()) {
+    // There is only one libcall for compare an exchange, because there is no
+    // optimisation benefit possible from a libcall version of a weak compare
+    // and exchange.
+    // bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
+     //                               void *desired, int success, int failure)
     case AtomicExpr::CmpXchgWeak:
-      LibCallName = "__atomic_compare_exchange_generic"; break;
     case AtomicExpr::CmpXchgStrong:
-      LibCallName = "__atomic_compare_exchange_generic"; break;
-    case AtomicExpr::Add:   LibCallName = "__atomic_fetch_add_generic"; break;
-    case AtomicExpr::Sub:   LibCallName = "__atomic_fetch_sub_generic"; break;
-    case AtomicExpr::And:   LibCallName = "__atomic_fetch_and_generic"; break;
-    case AtomicExpr::Or:    LibCallName = "__atomic_fetch_or_generic"; break;
-    case AtomicExpr::Xor:   LibCallName = "__atomic_fetch_xor_generic"; break;
-    case AtomicExpr::Xchg:  LibCallName = "__atomic_exchange_generic"; break;
-    case AtomicExpr::Store: LibCallName = "__atomic_store_generic"; break;
-    case AtomicExpr::Load:  LibCallName = "__atomic_load_generic"; break;
-    }
-    llvm::SmallVector<QualType, 4> Params;
-    CallArgList Args;
-    QualType RetTy = getContext().VoidTy;
-    if (E->getOp() != AtomicExpr::Store && !E->isCmpXChg())
+      LibCallName = "__atomic_compare_exchange";
+      RetTy = getContext().BoolTy;
+      Args.add(RValue::get(EmitCastToVoidPtr(Val1)),
+               getContext().VoidPtrTy);
+      Args.add(RValue::get(EmitCastToVoidPtr(Val2)),
+               getContext().VoidPtrTy);
+      Args.add(RValue::get(Order),
+               getContext().IntTy);
+      Order = OrderFail;
+      break;
+    // void __atomic_exchange(size_t size, void *mem, void *val, void *return,
+    //                        int order)
+    case AtomicExpr::Xchg:
+      LibCallName = "__atomic_exchange";
+      Args.add(RValue::get(EmitCastToVoidPtr(Val1)),
+               getContext().VoidPtrTy);
       Args.add(RValue::get(EmitCastToVoidPtr(Dest)),
                getContext().VoidPtrTy);
-    Args.add(RValue::get(EmitCastToVoidPtr(Ptr)),
-             getContext().VoidPtrTy);
-    if (E->getOp() != AtomicExpr::Load)
+      break;
+    // void __atomic_store(size_t size, void *mem, void *val, int order)
+    case AtomicExpr::Store:
+      LibCallName = "__atomic_store";
       Args.add(RValue::get(EmitCastToVoidPtr(Val1)),
                getContext().VoidPtrTy);
-    if (E->isCmpXChg()) {
-      Args.add(RValue::get(EmitCastToVoidPtr(Val2)),
+      break;
+    // void __atomic_load(size_t size, void *mem, void *return, int order)
+    case AtomicExpr::Load:
+      LibCallName = "__atomic_load";
+      Args.add(RValue::get(EmitCastToVoidPtr(Dest)),
                getContext().VoidPtrTy);
-      RetTy = getContext().IntTy;
+      break;
+#if 0
+    // These are only defined for 1-16 byte integers.  It is not clear what
+    // their semantics would be on anything else...
+    case AtomicExpr::Add:   LibCallName = "__atomic_fetch_add_generic"; break;
+    case AtomicExpr::Sub:   LibCallName = "__atomic_fetch_sub_generic"; break;
+    case AtomicExpr::And:   LibCallName = "__atomic_fetch_and_generic"; break;
+    case AtomicExpr::Or:    LibCallName = "__atomic_fetch_or_generic"; break;
+    case AtomicExpr::Xor:   LibCallName = "__atomic_fetch_xor_generic"; break;
+#endif
+    default: return EmitUnsupportedRValue(E, "atomic library call");
     }
-    Args.add(RValue::get(llvm::ConstantInt::get(SizeTy, Size)),
-             getContext().getSizeType());
+    // order is always the last parameter
+    Args.add(RValue::get(Order),
+             getContext().IntTy);
+
     const CGFunctionInfo &FuncInfo =
-        CGM.getTypes().arrangeFunctionCall(RetTy, Args, FunctionType::ExtInfo(),
-                                           /*variadic*/ false);
-    llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo, false);
+        CGM.getTypes().arrangeFunctionCall(RetTy, Args,
+            FunctionType::ExtInfo(), RequiredArgs::All);
+    llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo);
     llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName);
     RValue Res = EmitCall(FuncInfo, Func, ReturnValueSlot(), Args);
     if (E->isCmpXChg())
@@ -2869,7 +2896,7 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) {
       return RValue::get(0);
     return ConvertTempToRValue(*this, E->getType(), Dest);
   }
-#endif
+
   llvm::Type *IPtrTy =
       llvm::IntegerType::get(getLLVMContext(), Size * 8)->getPointerTo();
   llvm::Value *OrigDest = Dest;
index 3885d76b6deb4b308df824d90f67968b4598508e..a3608de6e0838def86838bf70d171a45c7ee18d8 100644 (file)
@@ -81,3 +81,39 @@ int lock_free() {
   // CHECK: ret i32 1
   return __atomic_is_lock_free(sizeof(_Atomic(int)));
 }
+
+// Tests for atomic operations on big values.  These should call the functions
+// defined here:
+// http://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary#The_Library_interface
+
+struct foo {
+  int big[128];
+};
+
+_Atomic(struct foo) bigAtomic;
+
+void structAtomicStore() {
+  // CHECK: @structAtomicStore
+  struct foo f = {0};
+  __atomic_store(&bigAtomic, f, 5);
+  // CHECK: call void @__atomic_store(i32 512, i8* bitcast (%struct.foo* @bigAtomic to i8*), i8* %3, i32 5)
+}
+void structAtomicLoad() {
+  // CHECK: @structAtomicLoad
+  struct foo f = __atomic_load(&bigAtomic, 5);
+  // CHECK: call void @__atomic_load(i32 512, i8* bitcast (%struct.foo* @bigAtomic to i8*), i8* %0, i32 5)
+}
+struct foo structAtomicExchange() {
+  // CHECK: @structAtomicExchange
+  struct foo f = {0};
+  return __atomic_exchange(&bigAtomic, f, 5);
+  // CHECK: call void @__atomic_exchange(i32 512, i8* bitcast (%struct.foo* @bigAtomic to i8*), i8* %3, i8* %4, i32 5)
+}
+int structAtomicCmpExchange() {
+  // CHECK: @structAtomicCmpExchange
+  struct foo f = {0};
+  struct foo g = {0};
+  g.big[12] = 12;
+  return __atomic_compare_exchange_strong(&bigAtomic, &f, g, 5, 5);
+  // CHECK: call zeroext i1 @__atomic_compare_exchange(i32 512, i8* bitcast (%struct.foo* @bigAtomic to i8*), i8* %4, i8* %5, i32 5, i32 5)
+}