diff --git a/include/llvm/Analysis/AliasAnalysis.h b/include/llvm/Analysis/AliasAnalysis.h index 2d43bddf7e0..9f411350a79 100644 --- a/include/llvm/Analysis/AliasAnalysis.h +++ b/include/llvm/Analysis/AliasAnalysis.h @@ -197,6 +197,10 @@ public: virtual ModRefBehavior getModRefBehavior(Function *F, std::vector *Info = 0); + /// getModRefBehavior - Return the modref behavior of the intrinsic with the + /// given id. + static ModRefBehavior getModRefBehavior(unsigned iid); + /// doesNotAccessMemory - If the specified call is known to never read or /// write memory, return true. If the call only reads from known-constant /// memory, it is also legal to return true. Calls that unwind the stack diff --git a/lib/Analysis/AliasAnalysis.cpp b/lib/Analysis/AliasAnalysis.cpp index dee9b535871..371dcafa9f3 100644 --- a/lib/Analysis/AliasAnalysis.cpp +++ b/lib/Analysis/AliasAnalysis.cpp @@ -116,13 +116,16 @@ AliasAnalysis::getModRefBehavior(Function *F, return DoesNotAccessMemory; if (F->onlyReadsMemory()) return OnlyReadsMemory; - if (unsigned id = F->getIntrinsicID()) { + if (unsigned id = F->getIntrinsicID()) + return getModRefBehavior(id); + } + return UnknownModRefBehavior; +} + +AliasAnalysis::ModRefBehavior AliasAnalysis::getModRefBehavior(unsigned iid) { #define GET_INTRINSIC_MODREF_BEHAVIOR #include "llvm/Intrinsics.gen" #undef GET_INTRINSIC_MODREF_BEHAVIOR - } - } - return UnknownModRefBehavior; } AliasAnalysis::ModRefResult diff --git a/lib/Target/README.txt b/lib/Target/README.txt index 22dadfeb3dd..38c3daa9383 100644 --- a/lib/Target/README.txt +++ b/lib/Target/README.txt @@ -1678,8 +1678,8 @@ And functionattrs doesn't realize that the p.0 load points to function local memory. Also, functionattrs doesn't know about memcpy/memset. This function should be -marked readnone, since it only twiddles local memory, but functionattrs doesn't -handle memset/memcpy/memmove aggressively: +marked readnone rather than readonly, since it only twiddles local memory, but +functionattrs doesn't handle memset/memcpy/memmove aggressively: struct X { int *p; int *q; }; int foo() { diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp index a16d335ef50..0bff2b94e9d 100644 --- a/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/lib/Transforms/IPO/FunctionAttrs.cpp @@ -136,6 +136,21 @@ bool FunctionAttrs::AddReadAttrs(const std::vector &SCC) { // Ignore calls to functions in the same SCC. if (SCCNodes.count(CS.getCalledFunction())) continue; + // Ignore intrinsics that only access local memory. + if (unsigned id = CS.getCalledFunction()->getIntrinsicID()) + if (AliasAnalysis::getModRefBehavior(id) == + AliasAnalysis::AccessesArguments) { + // Check that all pointer arguments point to local memory. + for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end(); + CI != CE; ++CI) { + Value *Arg = *CI; + if (isa(Arg->getType()) && !PointsToLocalMemory(Arg)) + // Writes memory. Just give up. + return false; + } + // Only reads and writes local memory. + continue; + } } else if (LoadInst *LI = dyn_cast(I)) { // Ignore loads from local memory. if (PointsToLocalMemory(LI->getPointerOperand())) diff --git a/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll b/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll index 09eb4687ac2..460780d8ea5 100644 --- a/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll +++ b/test/Transforms/FunctionAttrs/2008-10-04-LocalMemory.ll @@ -1,6 +1,8 @@ -; RUN: opt < %s -functionattrs -S | grep readnone | count 2 +; RUN: opt < %s -functionattrs -S | grep readonly | count 3 -declare i32 @g(i32*) readnone +%struct.X = type { i32*, i32* } + +declare i32 @g(i32*) readonly define i32 @f() { %x = alloca i32 ; [#uses=2] @@ -8,3 +10,30 @@ define i32 @f() { %y = call i32 @g(i32* %x) ; [#uses=1] ret i32 %y } + +define i32 @foo() nounwind { +entry: + %y = alloca %struct.X ; <%struct.X*> [#uses=2] + %x = alloca %struct.X ; <%struct.X*> [#uses=2] + %j = alloca i32 ; [#uses=2] + %i = alloca i32 ; [#uses=2] + %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] + store i32 0, i32* %i, align 4 + store i32 1, i32* %j, align 4 + %0 = getelementptr inbounds %struct.X* %y, i32 0, i32 0 ; [#uses=1] + store i32* %i, i32** %0, align 8 + %1 = getelementptr inbounds %struct.X* %x, i32 0, i32 1 ; [#uses=1] + store i32* %j, i32** %1, align 8 + %x1 = bitcast %struct.X* %x to i8* ; [#uses=2] + %y2 = bitcast %struct.X* %y to i8* ; [#uses=1] + call void @llvm.memcpy.i64(i8* %x1, i8* %y2, i64 8, i32 1) + %2 = bitcast i8* %x1 to i32** ; [#uses=1] + %3 = load i32** %2, align 8 ; [#uses=1] + %4 = load i32* %3, align 4 ; [#uses=1] + br label %return + +return: ; preds = %entry + ret i32 %4 +} + +declare void @llvm.memcpy.i64(i8* nocapture, i8* nocapture, i64, i32) nounwind diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index 23919d97f2b..c5df9e411c6 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -522,7 +522,7 @@ void IntrinsicEmitter:: EmitModRefBehavior(const std::vector &Ints, raw_ostream &OS){ OS << "// Determine intrinsic alias analysis mod/ref behavior.\n"; OS << "#ifdef GET_INTRINSIC_MODREF_BEHAVIOR\n"; - OS << "switch (id) {\n"; + OS << "switch (iid) {\n"; OS << "default:\n return UnknownModRefBehavior;\n"; for (unsigned i = 0, e = Ints.size(); i != e; ++i) { if (Ints[i].ModRef == CodeGenIntrinsic::WriteMem)