diff --git a/llvm/include/llvm/Analysis/MemorySSA.h b/llvm/include/llvm/Analysis/MemorySSA.h --- a/llvm/include/llvm/Analysis/MemorySSA.h +++ b/llvm/include/llvm/Analysis/MemorySSA.h @@ -735,6 +735,15 @@ void dump() const; void print(raw_ostream &) const; + // Return true if the *approximate* number of accesses seen by MemorySSA for + // the current function is prohibitively large. + // + // This information can be used to limit optimizations that need to update + // MemorySSA for IRs with pathological patterns. + // FIXME: A better informative number would be the number of Defs, but this + // is currently only targetting extreme cases. Revisit if the usecases change. + bool prohibitivelyLargeNumberOfAccesses() const; + /// Return true if \p MA represents the live on entry value /// /// Loads and stores from pointer arguments and other global values may be diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp --- a/llvm/lib/Analysis/MemorySSA.cpp +++ b/llvm/lib/Analysis/MemorySSA.cpp @@ -95,6 +95,12 @@ "enable-mssa-loop-dependency", cl::Hidden, cl::init(true), cl::desc("Enable MemorySSA dependency for loop pass manager")); +static cl::opt ProhibitivelyLargeAccessNo( + "memssa-prohibitively-large-access-no", cl::Hidden, cl::init(100000), + cl::desc("The number of accesses seen in a function by MemorySSA, past" + "which optimizations should consider updates to MemorySSA to be" + "prohibitively expensive (default = 100000)")); + static cl::opt VerifyMemorySSAX("verify-memoryssa", cl::location(VerifyMemorySSA), cl::Hidden, cl::desc("Enable verification of MemorySSA.")); @@ -1864,7 +1870,16 @@ LLVM_DUMP_METHOD void MemorySSA::dump() const { print(dbgs()); } #endif +bool MemorySSA::prohibitivelyLargeNumberOfAccesses() const { + return NextID > ProhibitivelyLargeAccessNo; +} + void MemorySSA::verifyMemorySSA() const { + if (prohibitivelyLargeNumberOfAccesses()) { + LLVM_DEBUG(dbgs() << "MemorySSA found a function with prohibitevely large " + "number of accesses: " + << F.getName() << "\n";); + } verifyOrderingDominationAndDefUses(F); verifyDominationNumbers(F); verifyPrevDefInPhis(F); diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -1760,6 +1760,14 @@ if (!TLI->has(LibFunc_memset) || !TLI->has(LibFunc_memcpy)) return false; + // If the number of memory accesses is prohibitively large, skip the pass. + if (MSSA && MSSA->prohibitivelyLargeNumberOfAccesses()) { + LLVM_DEBUG( + dbgs() << "Number of memoryaccesses prohibitively large in function " + << F.getName() << ". Skipping MemCpyOpt.\n"); + return false; + } + while (true) { if (!iterateOnFunction(F)) break; diff --git a/llvm/test/Transforms/MemCpyOpt/too-many-accesses.ll b/llvm/test/Transforms/MemCpyOpt/too-many-accesses.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/MemCpyOpt/too-many-accesses.ll @@ -0,0 +1,54 @@ +; RUN: opt < %s -memssa-prohibitively-large-access-no=10 -passes=memcpyopt -S -enable-memcpyopt-memoryssa=1 | FileCheck %s +; RUN: opt < %s -memssa-prohibitively-large-access-no=5 -passes=memcpyopt -S -enable-memcpyopt-memoryssa=1 | FileCheck %s --check-prefix=LARGE + +define void @test1(i8 signext %c) nounwind { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[X:%.*]] = alloca [6 x i8], align 1 +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 0 +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 1 +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 2 +; CHECK-NEXT: [[TMP13:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 3 +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 4 +; CHECK-NEXT: [[TMP21:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 5 +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 1 [[TMP]], i8 [[C:%.*]], i64 6, i1 false) +; CHECK-NEXT: [[TMP25:%.*]] = call i32 (...) @bar([6 x i8]* [[X]]) [[ATTR0:#.*]] +; CHECK-NEXT: ret void + +; LARGE-LABEL: @test1( +; LARGE-NEXT: entry: +; LARGE-NEXT: [[X:%.*]] = alloca [6 x i8], align 1 +; LARGE-NEXT: [[TMP:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 0 +; LARGE-NEXT: store i8 %c, i8* [[TMP]], align 1 +; LARGE-NEXT: [[TMP5:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 1 +; LARGE-NEXT: store i8 %c, i8* [[TMP5]], align 1 +; LARGE-NEXT: [[TMP9:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 2 +; LARGE-NEXT: store i8 %c, i8* [[TMP9]], align 1 +; LARGE-NEXT: [[TMP13:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 3 +; LARGE-NEXT: store i8 %c, i8* [[TMP13]], align 1 +; LARGE-NEXT: [[TMP17:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 4 +; LARGE-NEXT: store i8 %c, i8* [[TMP17]], align 1 +; LARGE-NEXT: [[TMP21:%.*]] = getelementptr [6 x i8], [6 x i8]* [[X]], i32 0, i32 5 +; LARGE-NEXT: store i8 %c, i8* [[TMP21]], align 1 +; LARGE-NEXT: [[TMP25:%.*]] = call i32 (...) @bar([6 x i8]* [[X]]) [[ATTR0:#.*]] +; LARGE-NEXT: ret void + +entry: + %x = alloca [6 x i8] + %tmp = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 0 + store i8 %c, i8* %tmp, align 1 + %tmp5 = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 1 + store i8 %c, i8* %tmp5, align 1 + %tmp9 = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 2 + store i8 %c, i8* %tmp9, align 1 + %tmp13 = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 3 + store i8 %c, i8* %tmp13, align 1 + %tmp17 = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 4 + store i8 %c, i8* %tmp17, align 1 + %tmp21 = getelementptr [6 x i8], [6 x i8]* %x, i32 0, i32 5 + store i8 %c, i8* %tmp21, align 1 + %tmp25 = call i32 (...) @bar( [6 x i8]* %x ) nounwind + ret void +} + +declare i32 @bar(...)