]> granicus.if.org Git - clang/commitdiff
CodeGen: Unify handling guard variables in the Itanium C++ ABI
authorJustin Bogner <mail@justinbogner.com>
Wed, 23 Apr 2014 01:50:10 +0000 (01:50 +0000)
committerJustin Bogner <mail@justinbogner.com>
Wed, 23 Apr 2014 01:50:10 +0000 (01:50 +0000)
We previously treated ARM separately from the generic Itanium ABI for
initializing guard variables. This code duplication led to things like
the ARM path missing the memory barrier for threadsafe handling, and a
highly misleading comment about how we were (mis)using the generic ABI
for ARM64 when really it went through the ARM codepath.

This unifies the two code paths. Functionally, this changes the ARM
and ARM64 codepath to use one byte loads instead of 4 and 8,
respectively, and adds the missing atomic acquire to these loads.
Other architectures are unchanged.

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

lib/CodeGen/ItaniumCXXABI.cpp
test/CodeGenCXX/aarch64-cxxabi.cpp
test/CodeGenCXX/arm.cpp

index 27825ab85d64894a72571ead527c561a52819b63..4b1c1c255af0e51bab46284cb4fb8b1f7bacee99 100644 (file)
@@ -1393,25 +1393,7 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
   }
 
   // Test whether the variable has completed initialization.
-  llvm::Value *isInitialized;
-
-  // ARM C++ ABI 3.2.3.1:
-  //   To support the potential use of initialization guard variables
-  //   as semaphores that are the target of ARM SWP and LDREX/STREX
-  //   synchronizing instructions we define a static initialization
-  //   guard variable to be a 4-byte aligned, 4- byte word with the
-  //   following inline access protocol.
-  //     #define INITIALIZED 1
-  //     if ((obj_guard & INITIALIZED) != INITIALIZED) {
-  //       if (__cxa_guard_acquire(&obj_guard))
-  //         ...
-  //     }
-  if (UseARMGuardVarABI && !useInt8GuardVariable) {
-    llvm::Value *V = Builder.CreateLoad(guard);
-    llvm::Value *Test1 = llvm::ConstantInt::get(guardTy, 1);
-    V = Builder.CreateAnd(V, Test1);
-    isInitialized = Builder.CreateIsNull(V, "guard.uninitialized");
-
+  //
   // Itanium C++ ABI 3.3.2:
   //   The following is pseudo-code showing how these functions can be used:
   //     if (obj_guard.first_byte == 0) {
@@ -1427,29 +1409,45 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
   //       }
   //     }
 
-    // ARM64 C++ ABI 3.2.2:
-    // This ABI instead only specifies the value bit 0 of the static guard
-    // variable; all other bits are platform defined. Bit 0 shall be 0 when the
-    // variable is not initialized and 1 when it is.
-    // FIXME: Reading one bit is no more efficient than reading one byte so
-    // the codegen is same as generic Itanium ABI.
-  } else {
-    // Load the first byte of the guard variable.
-    llvm::LoadInst *LI = 
+  // Load the first byte of the guard variable.
+  llvm::LoadInst *LI =
       Builder.CreateLoad(Builder.CreateBitCast(guard, CGM.Int8PtrTy));
-    LI->setAlignment(1);
-
-    // Itanium ABI:
-    //   An implementation supporting thread-safety on multiprocessor
-    //   systems must also guarantee that references to the initialized
-    //   object do not occur before the load of the initialization flag.
-    //
-    // In LLVM, we do this by marking the load Acquire.
-    if (threadsafe)
-      LI->setAtomic(llvm::Acquire);
-
-    isInitialized = Builder.CreateIsNull(LI, "guard.uninitialized");
-  }
+  LI->setAlignment(1);
+
+  // Itanium ABI:
+  //   An implementation supporting thread-safety on multiprocessor
+  //   systems must also guarantee that references to the initialized
+  //   object do not occur before the load of the initialization flag.
+  //
+  // In LLVM, we do this by marking the load Acquire.
+  if (threadsafe)
+    LI->setAtomic(llvm::Acquire);
+
+  // For ARM, we should only check the first bit, rather than the entire byte:
+  //
+  // ARM C++ ABI 3.2.3.1:
+  //   To support the potential use of initialization guard variables
+  //   as semaphores that are the target of ARM SWP and LDREX/STREX
+  //   synchronizing instructions we define a static initialization
+  //   guard variable to be a 4-byte aligned, 4-byte word with the
+  //   following inline access protocol.
+  //     #define INITIALIZED 1
+  //     if ((obj_guard & INITIALIZED) != INITIALIZED) {
+  //       if (__cxa_guard_acquire(&obj_guard))
+  //         ...
+  //     }
+  //
+  // and similarly for ARM64:
+  //
+  // ARM64 C++ ABI 3.2.2:
+  //   This ABI instead only specifies the value bit 0 of the static guard
+  //   variable; all other bits are platform defined. Bit 0 shall be 0 when the
+  //   variable is not initialized and 1 when it is.
+  llvm::Value *V =
+      (UseARMGuardVarABI && !useInt8GuardVariable)
+          ? Builder.CreateAnd(LI, llvm::ConstantInt::get(CGM.Int8Ty, 1))
+          : LI;
+  llvm::Value *isInitialized = Builder.CreateIsNull(V, "guard.uninitialized");
 
   llvm::BasicBlock *InitCheckBlock = CGF.createBasicBlock("init.check");
   llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end");
index 04d9493ae6b0867f3a4903acb453626f0d3b257b..c7d910d5f00d3ce82fd34f824e9171a5b721dbb7 100644 (file)
@@ -40,8 +40,8 @@ public:
 
 void guard_variables(int a) {
   static Guarded mine(a);
-// CHECK: [[GUARDBIT:%[0-9]+]] = and i64 {{%[0-9]+}}, 1
-// CHECK: icmp eq i64 [[GUARDBIT]], 0
+// CHECK: [[GUARDBIT:%[0-9]+]] = and i8 {{%[0-9]+}}, 1
+// CHECK: icmp eq i8 [[GUARDBIT]], 0
 
   // As guards are 64-bit, these helpers should take 64-bit pointers.
 // CHECK: call i32 @__cxa_guard_acquire(i64*
index 2790199446d7ec09bb8c5eab5da221dd23e6d17d..b8d95991e2d6a8d7e904d65732b181a3fe6a31b4 100644 (file)
@@ -292,9 +292,9 @@ namespace test7 {
 
   // CHECK-LABEL: define void @_ZN5test74testEv()
   void test() {
-    // CHECK:      [[T0:%.*]] = load i32* @_ZGVZN5test74testEvE1x
-    // CHECK-NEXT: [[T1:%.*]] = and i32 [[T0]], 1
-    // CHECK-NEXT: [[T2:%.*]] = icmp eq i32 [[T1]], 0
+    // CHECK:      [[T0:%.*]] = load atomic i8* bitcast (i32* @_ZGVZN5test74testEvE1x to i8*) acquire, align 1
+    // CHECK-NEXT: [[T1:%.*]] = and i8 [[T0]], 1
+    // CHECK-NEXT: [[T2:%.*]] = icmp eq i8 [[T1]], 0
     // CHECK-NEXT: br i1 [[T2]]
     //   -> fallthrough, end
     // CHECK:      [[T3:%.*]] = call i32 @__cxa_guard_acquire(i32* @_ZGVZN5test74testEvE1x)
@@ -327,9 +327,9 @@ namespace test8 {
 
   // CHECK-LABEL: define void @_ZN5test84testEv()
   void test() {
-    // CHECK:      [[T0:%.*]] = load i32* @_ZGVZN5test84testEvE1x
-    // CHECK-NEXT: [[T1:%.*]] = and i32 [[T0]], 1
-    // CHECK-NEXT: [[T2:%.*]] = icmp eq i32 [[T1]], 0
+    // CHECK:      [[T0:%.*]] = load atomic i8* bitcast (i32* @_ZGVZN5test84testEvE1x to i8*) acquire, align 1
+    // CHECK-NEXT: [[T1:%.*]] = and i8 [[T0]], 1
+    // CHECK-NEXT: [[T2:%.*]] = icmp eq i8 [[T1]], 0
     // CHECK-NEXT: br i1 [[T2]]
     //   -> fallthrough, end
     // CHECK:      [[T3:%.*]] = call i32 @__cxa_guard_acquire(i32* @_ZGVZN5test84testEvE1x)