From 3386d252579ea00d0fc26a3ba7874bec25ce4516 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Wed, 16 Oct 2013 14:06:14 +0000 Subject: [PATCH] [asan] Optimize accesses to global arrays with constant index Summary: Given a global array G[N], which is declared in this CU and has static initializer avoid instrumenting accesses like G[i], where 'i' is a constant and 0<=i ClDebugMin("asan-debug-min", cl::desc("Debug min inst"), static cl::opt ClDebugMax("asan-debug-max", cl::desc("Debug man inst"), cl::Hidden, cl::init(-1)); +STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); +STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); +STATISTIC(NumOptimizedAccessesToGlobalArray, + "Number of optimized accesses to global arrays"); +STATISTIC(NumOptimizedAccessesToGlobalVar, + "Number of optimized accesses to global vars"); + namespace { /// A set of dynamically initialized globals extracted from metadata. class SetOfDynamicallyInitializedGlobals { @@ -315,6 +323,7 @@ struct AddressSanitizer : public FunctionPass { bool ShouldInstrumentGlobal(GlobalVariable *G); bool LooksLikeCodeInBug11395(Instruction *I); void FindDynamicInitializers(Module &M); + bool GlobalIsLinkerInitialized(GlobalVariable *G); bool CheckInitOrder; bool CheckUseAfterReturn; @@ -655,6 +664,13 @@ static Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite) { return NULL; } +bool AddressSanitizer::GlobalIsLinkerInitialized(GlobalVariable *G) { + // If a global variable does not have dynamic initialization we don't + // have to instrument it. However, if a global does not have initializer + // at all, we assume it has dynamic initializer (in other TU). + return G->hasInitializer() && !DynamicallyInitializedGlobals.Contains(G); +} + void AddressSanitizer::instrumentMop(Instruction *I) { bool IsWrite = false; Value *Addr = isInterestingMemoryAccess(I, &IsWrite); @@ -663,13 +679,19 @@ void AddressSanitizer::instrumentMop(Instruction *I) { if (GlobalVariable *G = dyn_cast(Addr)) { // If initialization order checking is disabled, a simple access to a // dynamically initialized global is always valid. - if (!CheckInitOrder) - return; - // If a global variable does not have dynamic initialization we don't - // have to instrument it. However, if a global does not have initailizer - // at all, we assume it has dynamic initializer (in other TU). - if (G->hasInitializer() && !DynamicallyInitializedGlobals.Contains(G)) + if (!CheckInitOrder || GlobalIsLinkerInitialized(G)) { + NumOptimizedAccessesToGlobalVar++; return; + } + } + ConstantExpr *CE = dyn_cast(Addr); + if (CE && CE->isGEPWithNoNotionalOverIndexing()) { + if (GlobalVariable *G = dyn_cast(CE->getOperand(0))) { + if (CE->getOperand(1)->isNullValue() && GlobalIsLinkerInitialized(G)) { + NumOptimizedAccessesToGlobalArray++; + return; + } + } } } @@ -681,6 +703,11 @@ void AddressSanitizer::instrumentMop(Instruction *I) { assert((TypeSize % 8) == 0); + if (IsWrite) + NumInstrumentedWrites++; + else + NumInstrumentedReads++; + // Instrument a 1-, 2-, 4-, 8-, or 16- byte access with one check. if (TypeSize == 8 || TypeSize == 16 || TypeSize == 32 || TypeSize == 64 || TypeSize == 128) diff --git a/test/Instrumentation/AddressSanitizer/asan-vs-gvn.ll b/test/Instrumentation/AddressSanitizer/asan-vs-gvn.ll index da8f5413759..1087c9a58ff 100644 --- a/test/Instrumentation/AddressSanitizer/asan-vs-gvn.ll +++ b/test/Instrumentation/AddressSanitizer/asan-vs-gvn.ll @@ -9,7 +9,7 @@ target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f3 %struct_of_7_bytes_4_aligned = type { i32, i8, i8, i8} -@f = global %struct_of_7_bytes_4_aligned zeroinitializer, align 4 +@f = external global %struct_of_7_bytes_4_aligned , align 4 ; Accessing bytes 4 and 6, not ok to widen to i32 if sanitize_address is set. diff --git a/test/Instrumentation/AddressSanitizer/instrument_global.ll b/test/Instrumentation/AddressSanitizer/instrument_global.ll index 2c183f523fe..4717277b9af 100644 --- a/test/Instrumentation/AddressSanitizer/instrument_global.ll +++ b/test/Instrumentation/AddressSanitizer/instrument_global.ll @@ -9,12 +9,73 @@ target triple = "x86_64-unknown-linux-gnu" ; CHECK: llvm.global_ctors ; CHECK: llvm.global_dtors -; CHECK: define internal void @asan.module_ctor +; Test that we don't instrument global arrays with static initializer +; indexed with constants in-bounds. But instrument all other cases. + +@GlobSt = global [10 x i32] zeroinitializer, align 16 ; static initializer +@GlobDy = global [10 x i32] zeroinitializer, align 16 ; dynamic initializer +@GlobEx = external global [10 x i32] , align 16 ; extern initializer + +; GlobSt is declared here, and has static initializer -- ok to optimize. +define i32 @AccessGlobSt_0_2() sanitize_address { +entry: + %0 = load i32* getelementptr inbounds ([10 x i32]* @GlobSt, i64 0, i64 2), align 8 + ret i32 %0 +; CHECK-LABEL: define i32 @AccessGlobSt_0_2 +; CHECK-NOT: __asan_report +; CHECK: ret i32 %0 +} + +; GlobSt is accessed out of bounds -- can't optimize +define i32 @AccessGlobSt_0_12() sanitize_address { +entry: + %0 = load i32* getelementptr inbounds ([10 x i32]* @GlobSt, i64 0, i64 12), align 8 + ret i32 %0 +; CHECK-LABEL: define i32 @AccessGlobSt_0_12 +; CHECK: __asan_report +; CHECK: ret i32 +} + +; GlobSt is accessed with Gep that has non-0 first index -- can't optimize. +define i32 @AccessGlobSt_1_2() sanitize_address { +entry: + %0 = load i32* getelementptr inbounds ([10 x i32]* @GlobSt, i64 1, i64 2), align 8 + ret i32 %0 +; CHECK-LABEL: define i32 @AccessGlobSt_1_2 +; CHECK: __asan_report +; CHECK: ret i32 +} + +; GlobDy is declared with dynamic initializer -- can't optimize. +define i32 @AccessGlobDy_0_2() sanitize_address { +entry: + %0 = load i32* getelementptr inbounds ([10 x i32]* @GlobDy, i64 0, i64 2), align 8 + ret i32 %0 +; CHECK-LABEL: define i32 @AccessGlobDy_0_2 +; CHECK: __asan_report +; CHECK: ret i32 +} + +; GlobEx is an external global -- can't optimize. +define i32 @AccessGlobEx_0_2() sanitize_address { +entry: + %0 = load i32* getelementptr inbounds ([10 x i32]* @GlobEx, i64 0, i64 2), align 8 + ret i32 %0 +; CHECK-LABEL: define i32 @AccessGlobEx_0_2 +; CHECK: __asan_report +; CHECK: ret i32 +} + + +!llvm.asan.dynamically_initialized_globals = !{!0} +!0 = metadata !{[10 x i32]* @GlobDy} + +; CHECK-LABEL: define internal void @asan.module_ctor ; CHECK-NOT: ret ; CHECK: call void @__asan_register_globals ; CHECK: ret -; CHECK: define internal void @asan.module_dtor +; CHECK-LABEL: define internal void @asan.module_dtor ; CHECK-NOT: ret ; CHECK: call void @__asan_unregister_globals ; CHECK: ret