Index: include/polly/TempScopInfo.h =================================================================== --- include/polly/TempScopInfo.h +++ include/polly/TempScopInfo.h @@ -23,6 +23,7 @@ namespace llvm { class DataLayout; +class IntrinsicInst; } using namespace llvm; @@ -258,6 +259,9 @@ // Access function of statements (currently BasicBlocks) . AccFuncMapType AccFuncMap; + /// @brief All base addresses of arrays in this SCoP. + SmallPtrSet BaseAddresses; + // Pre-created zero for the scalar accesses, with it we do not need create a // zero scev every time when we need it. const SCEV *ZeroOffset; @@ -265,6 +269,14 @@ // Mapping regions to the corresponding Scop in current function. TempScopMapType TempScops; + /// @brief Set of intstructions which may access __all__ memory locations. + /// + /// Elements in this set have unknown access behaviours we have to over + /// approximate. To do so we pretend all memory locations known in the SCoP + /// are accessed by the instruction. The access kind read/write is stored too. + using GlobalAccessInstruction = std::pair; + SmallVector GlobalAccessInstructions; + // Clear the context. void clear(); @@ -296,14 +308,30 @@ /// instruction. IRAccess buildIRAccess(Instruction *Inst, Loop *L, Region *R); - /// @brief Build an instance of IRAccess if the call can access the memory. + /// @brief Build instances of IRAccess if the call can access the memory. /// /// @param CI The call instruction that might access the memory /// @param L The parent loop of the instruction - /// @param R The region on which we are going to build a TempScop /// @param Accs The set of accesses in which we accumulate IRAccesses - void buildIRCallAccess(CallInst *CI, Loop *L, Region *R, - AccFuncSetType &Accs); + void buildIRCallAccess(CallInst *CI, Loop *L, AccFuncSetType &Accs); + + /// @brief Build instances of IRAccess if the intrinsic can access the memory. + /// + /// @param IT The intrinsic call instruction that might access the memory + /// @param L The parent loop of the instruction + /// @param Accs The set of accesses in which we accumulate IRAccesses + /// + /// @return True iff the intrinsic was handled here, false otherwise. + /// + /// @note If the intrinsic is not handled here it should be treated as a call. + bool buildIRIntrinsicAccess(IntrinsicInst *IT, Loop *L, AccFuncSetType &Accs); + + /// @brief Build all IRAccesses representing global accesses. + /// + /// Global accesses are instructions which have effects we cannot model + /// precicely but is purely on the memory level. For these instructions + /// we create overaproximated accesses to __all__ arrays in this SCoP. + void buildGlobalAccesses(); /// @brief Analyze and extract the cross-BB scalar dependences (or, /// dataflow dependencies) of an instruction. Index: lib/Analysis/ScopDetection.cpp =================================================================== --- lib/Analysis/ScopDetection.cpp +++ lib/Analysis/ScopDetection.cpp @@ -360,6 +360,28 @@ } } + switch (AA->getModRefBehavior(CalledFunction)) { + case AliasAnalysis::DoesNotAccessMemory: + case AliasAnalysis::OnlyReadsMemory: + return true; + case AliasAnalysis::OnlyReadsArgumentPointees: + case AliasAnalysis::OnlyAccessesArgumentPointees: + for (const auto &ArgUse : CI.arg_operands()) { + Value *Arg = ArgUse.get(); + if (!Arg->getType()->isPointerTy()) + continue; + + AF = SE->getSCEVAtScope(Arg, L); + // Bail if a pointer argument has a base address not known to + // ScalarEvolution. Note that a zero pointer is acceptable. + if (!AF->isZero() && !isa(SE->getPointerBase(AF))) + return false; + } + return true; + default: + break; + } + return false; } Index: lib/Analysis/ScopInfo.cpp =================================================================== --- lib/Analysis/ScopInfo.cpp +++ lib/Analysis/ScopInfo.cpp @@ -443,7 +443,7 @@ // Non affine accesses are either real memory accesses with access functions // we can not analyze or special instructions (e.g., memset intrinsics) which - // write and/or read memory. The former will be overaproximatied (e.g., we + // write and/or read memory. The former will be overaproximated (e.g., we // model the access to the whole array) while the later comes with bounds. // Since the overaproximated accesses might not write all array cells they // are marked as MAY_WRITE. Index: lib/Analysis/TempScopInfo.cpp =================================================================== --- lib/Analysis/TempScopInfo.cpp +++ lib/Analysis/TempScopInfo.cpp @@ -171,6 +171,8 @@ dyn_cast(SE->getPointerBase(AccessFunction)); assert(BasePointer && "Could not find base pointer"); + BaseAddresses.insert(BasePointer->getValue()); + AccessFunction = SE->getMinusSCEV(AccessFunction, BasePointer); SmallVector Subscripts, Sizes; @@ -189,13 +191,79 @@ Subscripts, Sizes); } -void TempScopInfo::buildIRCallAccess(CallInst *CI, Loop *L, Region *R, - AccFuncSetType &Accs) { - // Trivial calls can be ignored. - if (CI->doesNotAccessMemory()) +void TempScopInfo::buildGlobalAccesses() { + // Determine if we have read/write/both or no global accesses. + bool HasRead = false, HasWrite = false; + for (auto &GlobalAccessPair : GlobalAccessInstructions) { + if (GlobalAccessPair.second == IRAccess::READ) + HasRead = true; + else + HasWrite = true; + } + + // If there are no global accesses in this SCoP we are done. + if (!HasRead && !HasWrite) return; SmallVector Subscripts, Sizes; + SmallVector ReadAccesses, WriteAccesses; + + // Create read/write IRAccesses for all base addresses in the SCoP. + for (Value *BaseAddr : BaseAddresses) { + if (HasRead) + ReadAccesses.push_back(IRAccess(IRAccess::READ, BaseAddr, ZeroOffset, 1, + /* isAffine? */ false, Subscripts, + Sizes)); + if (HasWrite) + WriteAccesses.push_back( + IRAccess(IRAccess::MAY_WRITE, BaseAddr, ZeroOffset, 1, + /* isAffine? */ false, Subscripts, Sizes)); + } + + // For each global access create read/write accesses based on the IRAccesses. + for (auto &GlobalAccessPair : GlobalAccessInstructions) { + Instruction *I = GlobalAccessPair.first; + BasicBlock *BB = I->getParent(); + AccFuncSetType &Accs = AccFuncMap[BB]; + if (GlobalAccessPair.second == IRAccess::READ) + for (const IRAccess &Acc : ReadAccesses) + Accs.push_back(std::make_pair(Acc, I)); + else + for (const IRAccess &Acc : WriteAccesses) + Accs.push_back(std::make_pair(Acc, I)); + } + + // Reset the set of global accesses. + GlobalAccessInstructions.clear(); +} + +bool TempScopInfo::buildIRIntrinsicAccess(IntrinsicInst *IT, Loop *L, + AccFuncSetType &Accs) { + + // First check if we treat this intrinsic special or as a general call. + // In the latter case we return false and are done. + switch (IT->getIntrinsicID()) { + // Ignore misc intrinsics without any "real" memory effect for now. + case llvm::Intrinsic::lifetime_start: + case llvm::Intrinsic::lifetime_end: + case llvm::Intrinsic::invariant_start: + case llvm::Intrinsic::invariant_end: + case llvm::Intrinsic::var_annotation: + case llvm::Intrinsic::ptr_annotation: + case llvm::Intrinsic::annotation: + case llvm::Intrinsic::donothing: + case llvm::Intrinsic::assume: + case llvm::Intrinsic::expect: + return true; + // Check for intrinsics with "real" memory effects. + case llvm::Intrinsic::memmove: + case llvm::Intrinsic::memcpy: + case llvm::Intrinsic::memset: + break; + // All other intrinsics should be handled as regular call statements. + default: + return false; + } const SCEVUnknown *WriteBasePtr = nullptr, *ReadBasePtr = nullptr; const SCEV *Len = nullptr, *WriteLen = nullptr, *ReadLen = nullptr; @@ -204,70 +272,132 @@ const SCEV *WriteUB = nullptr, *ReadUB = nullptr; unsigned RSize, WSize; + SmallVector Subscripts, Sizes; + // Handle memory intrinsics with well defined read and write effects. - if (auto *IT = dyn_cast(CI)) { - switch (IT->getIntrinsicID()) { - // Ignore misc intrinsics without any "real" memory effect for now. - case llvm::Intrinsic::lifetime_start: - case llvm::Intrinsic::lifetime_end: - case llvm::Intrinsic::invariant_start: - case llvm::Intrinsic::invariant_end: - case llvm::Intrinsic::var_annotation: - case llvm::Intrinsic::ptr_annotation: - case llvm::Intrinsic::annotation: - case llvm::Intrinsic::donothing: - case llvm::Intrinsic::assume: - case llvm::Intrinsic::expect: - return; - // Check for intrinsics with "real" memory effects. - case llvm::Intrinsic::memmove: - case llvm::Intrinsic::memcpy: - ReadAF = SE->getSCEVAtScope(IT->getOperand(1), L); - ReadBasePtr = dyn_cast(SE->getPointerBase(ReadAF)); - assert(ReadBasePtr && "Could not find base pointer"); - ReadAF = SE->getMinusSCEV(ReadAF, ReadBasePtr); - RSize = - TD->getTypeStoreSize(ReadBasePtr->getType()->getPointerElementType()); - ReadSize = SE->getConstant(ZeroOffset->getType(), RSize); - - // Fall through - case llvm::Intrinsic::memset: - WriteAF = SE->getSCEVAtScope(IT->getOperand(0), L); - WriteBasePtr = dyn_cast(SE->getPointerBase(WriteAF)); - assert(WriteBasePtr && "Could not find base pointer"); - WriteAF = SE->getMinusSCEV(WriteAF, WriteBasePtr); - - WSize = TD->getTypeStoreSize( - WriteBasePtr->getType()->getPointerElementType()); - WriteSize = SE->getConstant(ZeroOffset->getType(), WSize); - - Len = SE->getSCEVAtScope(IT->getOperand(2), L); - WriteLen = - SE->getAddExpr(Len, SE->getConstant(Len->getType(), WSize - 1)); - WriteLen = SE->getUDivExpr(WriteLen, WriteSize); - WriteUB = SE->getAddExpr(WriteLen, WriteAF); + switch (IT->getIntrinsicID()) { + case llvm::Intrinsic::memmove: + case llvm::Intrinsic::memcpy: + ReadAF = SE->getSCEVAtScope(IT->getOperand(1), L); + ReadBasePtr = dyn_cast(SE->getPointerBase(ReadAF)); + assert(ReadBasePtr && "Could not find base pointer"); + BaseAddresses.insert(ReadBasePtr->getValue()); + ReadAF = SE->getMinusSCEV(ReadAF, ReadBasePtr); + RSize = + TD->getTypeStoreSize(ReadBasePtr->getType()->getPointerElementType()); + ReadSize = SE->getConstant(ZeroOffset->getType(), RSize); + + // Fall through + case llvm::Intrinsic::memset: + WriteAF = SE->getSCEVAtScope(IT->getOperand(0), L); + WriteBasePtr = dyn_cast(SE->getPointerBase(WriteAF)); + assert(WriteBasePtr && "Could not find base pointer"); + BaseAddresses.insert(WriteBasePtr->getValue()); + WriteAF = SE->getMinusSCEV(WriteAF, WriteBasePtr); + + WSize = + TD->getTypeStoreSize(WriteBasePtr->getType()->getPointerElementType()); + WriteSize = SE->getConstant(ZeroOffset->getType(), WSize); + + Len = SE->getSCEVAtScope(IT->getOperand(2), L); + WriteLen = SE->getAddExpr(Len, SE->getConstant(Len->getType(), WSize - 1)); + WriteLen = SE->getUDivExpr(WriteLen, WriteSize); + WriteUB = SE->getAddExpr(WriteLen, WriteAF); + + Accs.push_back(std::make_pair( + IRAccess(IRAccess::MUST_WRITE, WriteBasePtr->getValue(), WriteAF, WSize, + false, Subscripts, Sizes, WriteAF, WriteUB), + IT)); + + if (ReadBasePtr) { + ReadLen = SE->getAddExpr(Len, SE->getConstant(Len->getType(), RSize - 1)); + ReadLen = SE->getUDivExpr(ReadLen, ReadSize); + ReadUB = SE->getAddExpr(ReadLen, ReadAF); Accs.push_back(std::make_pair( - IRAccess(IRAccess::MUST_WRITE, WriteBasePtr->getValue(), WriteAF, - WSize, false, Subscripts, Sizes, WriteAF, WriteUB), - CI)); - - if (ReadBasePtr) { - ReadLen = - SE->getAddExpr(Len, SE->getConstant(Len->getType(), RSize - 1)); - ReadLen = SE->getUDivExpr(ReadLen, ReadSize); - ReadUB = SE->getAddExpr(ReadLen, ReadAF); - - Accs.push_back(std::make_pair( - IRAccess(IRAccess::READ, ReadBasePtr->getValue(), ReadAF, RSize, - false, Subscripts, Sizes, ReadAF, ReadUB), - CI)); - } + IRAccess(IRAccess::READ, ReadBasePtr->getValue(), ReadAF, RSize, + false, Subscripts, Sizes, ReadAF, ReadUB), + IT)); + } + return true; + default: + IT->dump(); + llvm_unreachable("Unhandled intrinsic call!"); + } + + return false; +} + +void TempScopInfo::buildIRCallAccess(CallInst *CI, Loop *L, + AccFuncSetType &Accs) { + // Trivial calls can be ignored. + if (CI->doesNotAccessMemory()) + return; + + // Check intriniscis explicitly, some are treated special. + if (auto *IT = dyn_cast(CI)) + if (buildIRIntrinsicAccess(IT, L, Accs)) return; - default: - break; + + Function *CalledFunction = CI->getCalledFunction(); + assert(CalledFunction && "Found indirect function call"); + + SmallVector Subscripts, Sizes; + const SCEVUnknown *BasePtrSCEV; + const SCEV *AccessFunction; + bool OnlyReads; + Value *BasePtr; + + OnlyReads = false; + switch (AA->getModRefBehavior(CalledFunction)) { + case AliasAnalysis::DoesNotAccessMemory: + return; + case AliasAnalysis::OnlyReadsArgumentPointees: + OnlyReads = true; + // Fall through + case AliasAnalysis::OnlyAccessesArgumentPointees: + + // Iterate over all arguments and filter those with pointer type + for (const auto &ArgUse : CI->arg_operands()) { + Value *Arg = ArgUse.get(); + if (!Arg->getType()->isPointerTy()) + continue; + + // Get the ScalarEvolution representation of the pointer. + AccessFunction = SE->getSCEVAtScope(Arg, L); + + // Skip zero pointers as they can't be used to access any memory. + if (AccessFunction->isZero()) + continue; + + // Compute the actual base address and offset. + BasePtrSCEV = dyn_cast(SE->getPointerBase(AccessFunction)); + assert(BasePtr && "Could not find base pointer"); + AccessFunction = SE->getMinusSCEV(AccessFunction, BasePtrSCEV); + BasePtr = BasePtrSCEV->getValue(); + BaseAddresses.insert(BasePtr); + + Accs.push_back( + std::make_pair(IRAccess(IRAccess::READ, BasePtr, AccessFunction, 1, + false, Subscripts, Sizes), + CI)); + + if (!OnlyReads) + Accs.push_back(std::make_pair(IRAccess(IRAccess::MAY_WRITE, BasePtr, + AccessFunction, 1, false, + Subscripts, Sizes), + CI)); } + return; + + case AliasAnalysis::OnlyReadsMemory: + // Remember the call for later when we know all arrays in the SCoP. Only + // then is it possible to overestimate the effect of this call. + GlobalAccessInstructions.push_back(std::make_pair(CI, IRAccess::READ)); + return; + default: + break; } llvm_unreachable("Unknown call instruction, cannot model the effect!"); @@ -285,7 +415,7 @@ // Some call accesses and intrinsics might access the memory. If so we // build over approximated IRAccesses for them. if (CallInst *CI = dyn_cast(Inst)) - buildIRCallAccess(CI, L, &R, Functions); + buildIRCallAccess(CI, L, Functions); if (!isa(Inst) && buildScalarDependences(Inst, &R)) { // If the Instruction is used outside the statement, we need to build the @@ -293,6 +423,7 @@ SmallVector Subscripts, Sizes; IRAccess ScalarAccess(IRAccess::MUST_WRITE, Inst, ZeroOffset, 1, true, Subscripts, Sizes); + BaseAddresses.insert(Inst); Functions.push_back(std::make_pair(ScalarAccess, Inst)); } } @@ -419,6 +550,9 @@ buildCondition(BB, R.getEntry()); } + // After all basic blocks are traversed we can build global accesses. + buildGlobalAccesses(); + buildLoopBounds(*TScop); return TScop; Index: test/ScopDetect/mod_ref_read_pointer.ll =================================================================== --- /dev/null +++ test/ScopDetect/mod_ref_read_pointer.ll @@ -0,0 +1,41 @@ +; RUN: opt %loadPolly -basicaa -polly-detect -analyze < %s | FileCheck %s +; +; CHECK: Valid Region for Scop: for.cond => for.end +; +; #pragma readonly +; int func(int *A); +; +; void jd(int *A) { +; for (int i = 0; i < 1024; i++) +; A[i + 2] = func(A); +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +declare i32 @func(i32* %A) #1 + +define void @jd(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %call = call i32 @func(i32* %A) + %tmp = add nsw i64 %indvars.iv, 2 + %arrayidx = getelementptr inbounds i32* %A, i64 %tmp + store i32 %call, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +attributes #1 = { nounwind readonly } Index: test/ScopInfo/mod_ref_access_pointee_arguments.ll =================================================================== --- /dev/null +++ test/ScopInfo/mod_ref_access_pointee_arguments.ll @@ -0,0 +1,55 @@ +; RUN: opt %loadPolly -basicaa -polly-scops -analyze < %s | FileCheck %s +; +; Verify that we model the read and may-write access of the prefetch intrinsic +; correctly, thus that A is accessed by it but B is not. +; +; CHECK: Stmt_for_body +; CHECK: Domain := +; CHECK: { Stmt_for_body[i0] : i0 >= 0 and i0 <= 1023 }; +; CHECK: Scattering := +; CHECK: { Stmt_for_body[i0] -> scattering[0, i0, 0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[o0] }; +; CHECK: MayWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[o0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_B[i0] }; +; CHECK: MustWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[i0] }; +; +; void jd(int *restirct A, int *restrict B) { +; for (int i = 0; i < 1024; i++) { +; @llvm.prefetch(A); +; A[i] = B[i]; +; } +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @jd(i32* noalias %A, i32* noalias %B) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32* %A, i64 %indvars.iv + %arrayidx2 = getelementptr inbounds i32* %B, i64 %indvars.iv + %bc = bitcast i32* %arrayidx to i8* + call void @llvm.prefetch(i8* %bc, i32 1, i32 1, i32 1) + %tmp = load i32* %arrayidx2 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +declare void @llvm.prefetch(i8*, i32, i32, i32) Index: test/ScopInfo/mod_ref_read_pointee_arguments.ll =================================================================== --- /dev/null +++ test/ScopInfo/mod_ref_read_pointee_arguments.ll @@ -0,0 +1,62 @@ +; RUN: opt %loadPolly -basicaa -polly-scops -analyze < %s | FileCheck %s +; +; Verify that we model the read access of the gcread intrinsic +; correctly, thus that A is read by it but B is not. +; +; CHECK: Stmt_for_body +; CHECK: Domain := +; CHECK: { Stmt_for_body[i0] : i0 >= 0 and i0 <= 1023 }; +; CHECK: Scattering := +; CHECK: { Stmt_for_body[i0] -> scattering[0, i0, 0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[o0] }; +; CHECK: MustWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_dummyloc[0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_B[i0] }; +; CHECK: MustWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[i0] }; +; +; void jd(int *restirct A, int *restrict B) { +; char **dummyloc; +; for (int i = 0; i < 1024; i++) { +; char *dummy = @llvm.gcread(A, nullptr); +; *dummyloc = dummy; +; A[i] = B[i]; +; } +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @jd(i32* noalias %A, i32* noalias %B) gc "dummy" { +entry: + %dummyloc = alloca i8* + br label %entry.split + +entry.split: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry.split + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry.split ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32* %A, i64 %indvars.iv + %arrayidx2 = getelementptr inbounds i32* %B, i64 %indvars.iv + %bc = bitcast i32* %arrayidx to i8* + %dummy = call i8* @llvm.gcread(i8* %bc, i8** null) + store i8* %dummy, i8** %dummyloc, align 4 + %tmp = load i32* %arrayidx2 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +declare i8* @llvm.gcread(i8*, i8**) Index: test/ScopInfo/mod_ref_read_pointer.ll =================================================================== --- /dev/null +++ test/ScopInfo/mod_ref_read_pointer.ll @@ -0,0 +1,52 @@ +; RUN: opt %loadPolly -basicaa -polly-scops -analyze < %s | FileCheck %s +; +; Check that we assume the call to func has a read on the whole A array. +; TODO: Use range analysis metadata! +; +; CHECK: Stmt_for_body +; CHECK: Domain := +; CHECK: { Stmt_for_body[i0] : i0 >= 0 and i0 <= 1023 }; +; CHECK: Scattering := +; CHECK: { Stmt_for_body[i0] -> scattering[0, i0, 0] }; +; CHECK: MustWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[2 + i0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[o0] }; +; +; #pragma readonly +; int func(int *A); +; +; void jd(int *A) { +; for (int i = 0; i < 1024; i++) +; A[i + 2] = func(A); +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @jd(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %call = call i32 @func(i32* %A) #2 + %tmp = add nsw i64 %indvars.iv, 2 + %arrayidx = getelementptr inbounds i32* %A, i64 %tmp + store i32 %call, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +declare i32 @func(i32*) #1 + +attributes #1 = { nounwind readonly } Index: test/ScopInfo/mod_ref_read_pointers.ll =================================================================== --- /dev/null +++ test/ScopInfo/mod_ref_read_pointers.ll @@ -0,0 +1,59 @@ +; RUN: opt %loadPolly -basicaa -polly-scops -analyze < %s | FileCheck %s +; +; Check that the call to func will "read" not only the A array but also the +; B array. The reason is the readonly annotation of func. +; +; CHECK: Stmt_for_body +; CHECK: Domain := +; CHECK: { Stmt_for_body[i0] : i0 >= 0 and i0 <= 1023 }; +; CHECK: Scattering := +; CHECK: { Stmt_for_body[i0] -> scattering[0, i0, 0] }; +; CHECK: ReadAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_B[i0] }; +; CHECK: MustWriteAccess := [Reduction Type: NONE] +; CHECK: { Stmt_for_body[i0] -> MemRef_A[2 + i0] }; +; CHECK-DAG: ReadAccess := [Reduction Type: NONE] +; CHECK-DAG: { Stmt_for_body[i0] -> MemRef_B[o0] }; +; CHECK-DAG: ReadAccess := [Reduction Type: NONE] +; CHECK-DAG: { Stmt_for_body[i0] -> MemRef_A[o0] }; +; +; #pragma readonly +; int func(int *A); +; +; void jd(int *restrict A, int *restrict B) { +; for (int i = 0; i < 1024; i++) +; A[i + 2] = func(A) + B[i]; +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @jd(i32* noalias %A, i32* noalias %B) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %call = call i32 @func(i32* %A) #2 + %arrayidx = getelementptr inbounds i32* %B, i64 %indvars.iv + %tmp = load i32* %arrayidx, align 4 + %add = add nsw i32 %call, %tmp + %tmp2 = add nsw i64 %indvars.iv, 2 + %arrayidx3 = getelementptr inbounds i32* %A, i64 %tmp2 + store i32 %add, i32* %arrayidx3, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +declare i32 @func(i32*) #1 + +attributes #1 = { nounwind readonly }