From: Justin Bogner Date: Wed, 23 Apr 2014 01:50:10 +0000 (+0000) Subject: CodeGen: Unify handling guard variables in the Itanium C++ ABI X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8e180fea6b697173ff706a77fff5bf5ed54cac36;p=clang CodeGen: Unify handling guard variables in the Itanium C++ ABI 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 --- diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 27825ab85d..4b1c1c255a 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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"); diff --git a/test/CodeGenCXX/aarch64-cxxabi.cpp b/test/CodeGenCXX/aarch64-cxxabi.cpp index 04d9493ae6..c7d910d5f0 100644 --- a/test/CodeGenCXX/aarch64-cxxabi.cpp +++ b/test/CodeGenCXX/aarch64-cxxabi.cpp @@ -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* diff --git a/test/CodeGenCXX/arm.cpp b/test/CodeGenCXX/arm.cpp index 2790199446..b8d95991e2 100644 --- a/test/CodeGenCXX/arm.cpp +++ b/test/CodeGenCXX/arm.cpp @@ -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)