diff --git a/polly/include/polly/CodeGen/BlockGenerators.h b/polly/include/polly/CodeGen/BlockGenerators.h index b2ab376033ef4..c55b2e6901ad0 100644 --- a/polly/include/polly/CodeGen/BlockGenerators.h +++ b/polly/include/polly/CodeGen/BlockGenerators.h @@ -17,6 +17,7 @@ #define POLLY_BLOCK_GENERATORS_H #include "polly/CodeGen/IRBuilder.h" +#include "polly/Support/GICHelper.h" #include "polly/Support/ScopHelper.h" #include "llvm/ADT/MapVector.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" @@ -328,6 +329,33 @@ class BlockGenerator { ValueMapT &BBMap, __isl_keep isl_id_to_ast_expr *NewAccesses); + /// Generate instructions that compute whether one instance of @p Set is + /// executed. + /// + /// @param Stmt The statement we generate code for. + /// @param Subdomain A set in the space of @p Stmt's domain. Elements not in + /// @p Stmt's domain are ignored. + /// + /// @return An expression of type i1, generated into the current builder + /// position, that evaluates to 1 if the executed instance is part of + /// @p Set. + Value *buildContainsCondition(ScopStmt &Stmt, const isl::set &Subdomain); + + /// Generate code that executes in a subset of @p Stmt's domain. + /// + /// @param Stmt The statement we generate code for. + /// @param Subdomain The condition for some code to be executed. + /// @param Subject A name for the code that is executed + /// conditionally. Used to name new basic blocks and + /// instructions. + /// @param GenThenFunc Callback which generates the code to be executed + /// when the current executed instance is in @p Set. The + /// IRBuilder's position is moved to within the block that + /// executes conditionally for this callback. + void generateConditionalExecution(ScopStmt &Stmt, const isl::set &Subdomain, + StringRef Subject, + const std::function &GenThenFunc); + /// Generate the scalar stores for the given statement. /// /// After the statement @p Stmt was copied all inner-SCoP scalar dependences diff --git a/polly/include/polly/ScopInfo.h b/polly/include/polly/ScopInfo.h index b64e5230763d4..c6b47c134ae5a 100644 --- a/polly/include/polly/ScopInfo.h +++ b/polly/include/polly/ScopInfo.h @@ -1043,6 +1043,10 @@ class MemoryAccess { /// Set the updated access relation read from JSCOP file. void setNewAccessRelation(__isl_take isl_map *NewAccessRelation); + /// Return whether the MemoryyAccess is a partial access. That is, the access + /// is not executed in some instances of the parent statement's domain. + bool isLatestPartialAccess() const; + /// Mark this a reduction like access void markAsReductionLike(ReductionType RT) { RedType = RT; } diff --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp index b01d7748dc04a..8937c98d8ef16 100644 --- a/polly/lib/Analysis/ScopInfo.cpp +++ b/polly/lib/Analysis/ScopInfo.cpp @@ -1204,15 +1204,19 @@ void MemoryAccess::setNewAccessRelation(__isl_take isl_map *NewAccess) { isl_space_free(NewDomainSpace); isl_space_free(OriginalDomainSpace); - // Check whether there is an access for every statement instance. - auto *StmtDomain = getStatement()->getDomain(); - StmtDomain = isl_set_intersect_params( - StmtDomain, getStatement()->getParent()->getContext()); - auto *NewDomain = isl_map_domain(isl_map_copy(NewAccess)); - assert(isl_set_is_subset(StmtDomain, NewDomain) && - "Partial accesses not supported"); - isl_set_free(NewDomain); - isl_set_free(StmtDomain); + // Reads must be executed unconditionally. Writes might be executed in a + // subdomain only. + if (isRead()) { + // Check whether there is an access for every statement instance. + auto *StmtDomain = getStatement()->getDomain(); + StmtDomain = isl_set_intersect_params( + StmtDomain, getStatement()->getParent()->getContext()); + auto *NewDomain = isl_map_domain(isl_map_copy(NewAccess)); + assert(isl_set_is_subset(StmtDomain, NewDomain) && + "Partial READ accesses not supported"); + isl_set_free(NewDomain); + isl_set_free(StmtDomain); + } auto *NewAccessSpace = isl_space_range(NewSpace); assert(isl_space_has_tuple_id(NewAccessSpace, isl_dim_set) && @@ -1243,6 +1247,13 @@ void MemoryAccess::setNewAccessRelation(__isl_take isl_map *NewAccess) { NewAccessRelation = NewAccess; } +bool MemoryAccess::isLatestPartialAccess() const { + isl::set StmtDom = give(getStatement()->getDomain()); + isl::set AccDom = give(isl_map_domain(getLatestAccessRelation())); + + return isl_set_is_subset(StmtDom.keep(), AccDom.keep()) == isl_bool_false; +} + //===----------------------------------------------------------------------===// __isl_give isl_map *ScopStmt::getSchedule() const { diff --git a/polly/lib/CodeGen/BlockGenerators.cpp b/polly/lib/CodeGen/BlockGenerators.cpp index 58291af91ef07..7f1571ff46394 100644 --- a/polly/lib/CodeGen/BlockGenerators.cpp +++ b/polly/lib/CodeGen/BlockGenerators.cpp @@ -318,16 +318,22 @@ Value *BlockGenerator::generateArrayLoad(ScopStmt &Stmt, LoadInst *Load, void BlockGenerator::generateArrayStore(ScopStmt &Stmt, StoreInst *Store, ValueMapT &BBMap, LoopToScevMapT <S, isl_id_to_ast_expr *NewAccesses) { - Value *NewPointer = - generateLocationAccessed(Stmt, Store, BBMap, LTS, NewAccesses); - Value *ValueOperand = getNewValue(Stmt, Store->getValueOperand(), BBMap, LTS, - getLoopForStmt(Stmt)); - - if (PollyDebugPrinting) - RuntimeDebugBuilder::createCPUPrinter(Builder, "Store to ", NewPointer, - ": ", ValueOperand, "\n"); - - Builder.CreateAlignedStore(ValueOperand, NewPointer, Store->getAlignment()); + MemoryAccess &MA = Stmt.getArrayAccessFor(Store); + isl::set AccDom = give(isl_map_domain(MA.getAccessRelation())); + const char *Subject = isl_id_get_name(give(MA.getId()).keep()); + + generateConditionalExecution(Stmt, AccDom, Subject, [&, this]() { + Value *NewPointer = + generateLocationAccessed(Stmt, Store, BBMap, LTS, NewAccesses); + Value *ValueOperand = getNewValue(Stmt, Store->getValueOperand(), BBMap, + LTS, getLoopForStmt(Stmt)); + + if (PollyDebugPrinting) + RuntimeDebugBuilder::createCPUPrinter(Builder, "Store to ", NewPointer, + ": ", ValueOperand, "\n"); + + Builder.CreateAlignedStore(ValueOperand, NewPointer, Store->getAlignment()); + }); } bool BlockGenerator::canSyntheziseInStmt(ScopStmt &Stmt, Instruction *Inst) { @@ -553,6 +559,79 @@ void BlockGenerator::generateScalarLoads( } } +Value *BlockGenerator::buildContainsCondition(ScopStmt &Stmt, + const isl::set &Subdomain) { + isl::ast_build AstBuild = give(isl_ast_build_copy(Stmt.getAstBuild())); + isl::set Domain = give(Stmt.getDomain()); + isl::union_set UDomain = give(isl_union_set_from_set(Domain.copy())); + + isl::union_map USchedule = give(isl_ast_build_get_schedule(AstBuild.keep())); + USchedule = + give(isl_union_map_intersect_domain(USchedule.take(), UDomain.copy())); + assert(isl_union_map_is_empty(USchedule.keep()) == isl_bool_false); + isl::map Schedule = give(isl_map_from_union_map(USchedule.copy())); + + isl::set ScheduledDomain = give(isl_map_range(Schedule.copy())); + isl::set ScheduledSet = + give(isl_set_apply(Subdomain.copy(), Schedule.copy())); + + isl::ast_build RestrictedBuild = + give(isl_ast_build_restrict(AstBuild.copy(), ScheduledDomain.copy())); + + isl::ast_expr IsInSet = give( + isl_ast_build_expr_from_set(RestrictedBuild.keep(), ScheduledSet.copy())); + Value *IsInSetExpr = ExprBuilder->create(IsInSet.copy()); + IsInSetExpr = Builder.CreateICmpNE( + IsInSetExpr, ConstantInt::get(IsInSetExpr->getType(), 0)); + + return IsInSetExpr; +} + +void BlockGenerator::generateConditionalExecution( + ScopStmt &Stmt, const isl::set &Subdomain, StringRef Subject, + const std::function &GenThenFunc) { + isl::set StmtDom = give(Stmt.getDomain()); + + // Don't call GenThenFunc if it is never executed. An ast index expression + // might not be defined in this case. + bool IsEmpty = isl_set_is_empty(Subdomain.keep()) == isl_bool_true; + if (IsEmpty) + return; + + // If the condition is a tautology, don't generate a condition around the + // code. + bool IsPartial = + isl_set_is_subset(StmtDom.keep(), Subdomain.keep()) == isl_bool_false; + if (!IsPartial) { + GenThenFunc(); + return; + } + + // Generate the condition. + Value *Cond = buildContainsCondition(Stmt, Subdomain); + BasicBlock *HeadBlock = Builder.GetInsertBlock(); + StringRef BlockName = HeadBlock->getName(); + + // Generate the conditional block. + SplitBlockAndInsertIfThen(Cond, &*Builder.GetInsertPoint(), false, nullptr, + &DT, &LI); + BranchInst *Branch = cast(HeadBlock->getTerminator()); + BasicBlock *ThenBlock = Branch->getSuccessor(0); + BasicBlock *TailBlock = Branch->getSuccessor(1); + + // Assign descriptive names. + if (auto *CondInst = dyn_cast(Cond)) + CondInst->setName("polly." + Subject + ".cond"); + ThenBlock->setName(BlockName + "." + Subject + ".partial"); + TailBlock->setName(BlockName + ".cont"); + + // Put the client code into the conditional block and continue in the merge + // block afterwards. + Builder.SetInsertPoint(ThenBlock, ThenBlock->getFirstInsertionPt()); + GenThenFunc(); + Builder.SetInsertPoint(TailBlock, TailBlock->getFirstInsertionPt()); +} + void BlockGenerator::generateScalarStores( ScopStmt &Stmt, LoopToScevMapT <S, ValueMapT &BBMap, __isl_keep isl_id_to_ast_expr *NewAccesses) { @@ -566,40 +645,38 @@ void BlockGenerator::generateScalarStores( if (MA->isOriginalArrayKind() || MA->isRead()) continue; -#ifndef NDEBUG - auto *StmtDom = Stmt.getDomain(); - auto *AccDom = isl_map_domain(MA->getAccessRelation()); - assert(isl_set_is_subset(StmtDom, AccDom) && - "Scalar must be stored in all statement instances"); - isl_set_free(StmtDom); - isl_set_free(AccDom); -#endif - - Value *Val = MA->getAccessValue(); - if (MA->isAnyPHIKind()) { - assert(MA->getIncoming().size() >= 1 && - "Block statements have exactly one exiting block, or multiple but " - "with same incoming block and value"); - assert(std::all_of(MA->getIncoming().begin(), MA->getIncoming().end(), - [&](std::pair p) -> bool { - return p.first == Stmt.getBasicBlock(); - }) && - "Incoming block must be statement's block"); - Val = MA->getIncoming()[0].second; - } - auto Address = - getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS, BBMap, NewAccesses); - - Val = getNewValue(Stmt, Val, BBMap, LTS, L); - assert((!isa(Val) || - DT.dominates(cast(Val)->getParent(), - Builder.GetInsertBlock())) && - "Domination violation"); - assert((!isa(Address) || - DT.dominates(cast(Address)->getParent(), - Builder.GetInsertBlock())) && - "Domination violation"); - Builder.CreateStore(Val, Address); + isl::set AccDom = give(isl_map_domain(MA->getAccessRelation())); + const char *Subject = isl_id_get_name(give(MA->getId()).keep()); + + generateConditionalExecution(Stmt, AccDom, Subject, [&, this, MA]() { + Value *Val = MA->getAccessValue(); + if (MA->isAnyPHIKind()) { + assert( + MA->getIncoming().size() >= 1 && + "Block statements have exactly one exiting block, or multiple but " + "with same incoming block and value"); + assert(std::all_of(MA->getIncoming().begin(), MA->getIncoming().end(), + [&](std::pair p) -> bool { + return p.first == Stmt.getBasicBlock(); + }) && + "Incoming block must be statement's block"); + Val = MA->getIncoming()[0].second; + } + auto Address = getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS, BBMap, + NewAccesses); + + Val = getNewValue(Stmt, Val, BBMap, LTS, L); + assert((!isa(Val) || + DT.dominates(cast(Val)->getParent(), + Builder.GetInsertBlock())) && + "Domination violation"); + assert((!isa(Address) || + DT.dominates(cast(Address)->getParent(), + Builder.GetInsertBlock())) && + "Domination violation"); + Builder.CreateStore(Val, Address); + + }); } } @@ -1470,18 +1547,23 @@ void RegionGenerator::generateScalarStores( if (MA->isOriginalArrayKind() || MA->isRead()) continue; - Value *NewVal = getExitScalar(MA, LTS, BBMap); - Value *Address = - getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS, BBMap, NewAccesses); - assert((!isa(NewVal) || - DT.dominates(cast(NewVal)->getParent(), - Builder.GetInsertBlock())) && - "Domination violation"); - assert((!isa(Address) || - DT.dominates(cast(Address)->getParent(), - Builder.GetInsertBlock())) && - "Domination violation"); - Builder.CreateStore(NewVal, Address); + isl::set AccDom = give(isl_map_domain(MA->getAccessRelation())); + const char *Subject = isl_id_get_name(give(MA->getId()).keep()); + generateConditionalExecution(Stmt, AccDom, Subject, [&, this, MA]() { + + Value *NewVal = getExitScalar(MA, LTS, BBMap); + Value *Address = getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS, BBMap, + NewAccesses); + assert((!isa(NewVal) || + DT.dominates(cast(NewVal)->getParent(), + Builder.GetInsertBlock())) && + "Domination violation"); + assert((!isa(Address) || + DT.dominates(cast(Address)->getParent(), + Builder.GetInsertBlock())) && + "Domination violation"); + Builder.CreateStore(NewVal, Address); + }); } } diff --git a/polly/lib/CodeGen/IslNodeBuilder.cpp b/polly/lib/CodeGen/IslNodeBuilder.cpp index 2929109016f8e..b59ee60c660d7 100644 --- a/polly/lib/CodeGen/IslNodeBuilder.cpp +++ b/polly/lib/CodeGen/IslNodeBuilder.cpp @@ -667,13 +667,40 @@ void IslNodeBuilder::createForParallel(__isl_take isl_ast_node *For) { isl_id_free(IteratorID); } +/// Return whether any of @p Node's statements contain partial accesses. +/// +/// Partial accesses are not supported by Polly's vector code generator. +static bool hasPartialAccesses(__isl_take isl_ast_node *Node) { + return isl_ast_node_foreach_descendant_top_down( + Node, + [](isl_ast_node *Node, void *User) -> isl_bool { + if (isl_ast_node_get_type(Node) != isl_ast_node_user) + return isl_bool_true; + + isl::ast_expr Expr = give(isl_ast_node_user_get_expr(Node)); + isl::ast_expr StmtExpr = + give(isl_ast_expr_get_op_arg(Expr.keep(), 0)); + isl::id Id = give(isl_ast_expr_get_id(StmtExpr.keep())); + + ScopStmt *Stmt = + static_cast(isl_id_get_user(Id.keep())); + isl::set StmtDom = give(Stmt->getDomain()); + for (auto *MA : *Stmt) { + if (MA->isLatestPartialAccess()) + return isl_bool_error; + } + return isl_bool_true; + }, + nullptr) == isl_stat_error; +} + void IslNodeBuilder::createFor(__isl_take isl_ast_node *For) { bool Vector = PollyVectorizerChoice == VECTORIZER_POLLY; if (Vector && IslAstInfo::isInnermostParallel(For) && !IslAstInfo::isReductionParallel(For)) { int VectorWidth = getNumberOfIterations(For); - if (1 < VectorWidth && VectorWidth <= 16) { + if (1 < VectorWidth && VectorWidth <= 16 && !hasPartialAccesses(For)) { createForVector(For, VectorWidth); return; } @@ -765,24 +792,34 @@ IslNodeBuilder::createNewAccesses(ScopStmt *Stmt, auto Schedule = isl_ast_build_get_schedule(Build); #ifndef NDEBUG - auto Dom = Stmt->getDomain(); - auto SchedDom = isl_set_from_union_set( - isl_union_map_domain(isl_union_map_copy(Schedule))); - auto AccDom = isl_map_domain(MA->getAccessRelation()); - Dom = isl_set_intersect_params(Dom, Stmt->getParent()->getContext()); - SchedDom = - isl_set_intersect_params(SchedDom, Stmt->getParent()->getContext()); - assert(isl_set_is_subset(SchedDom, AccDom) && - "Access relation not defined on full schedule domain"); - assert(isl_set_is_subset(Dom, AccDom) && - "Access relation not defined on full domain"); - isl_set_free(AccDom); - isl_set_free(SchedDom); - isl_set_free(Dom); + if (MA->isRead()) { + auto Dom = Stmt->getDomain(); + auto SchedDom = isl_set_from_union_set( + isl_union_map_domain(isl_union_map_copy(Schedule))); + auto AccDom = isl_map_domain(MA->getAccessRelation()); + Dom = isl_set_intersect_params(Dom, Stmt->getParent()->getContext()); + SchedDom = + isl_set_intersect_params(SchedDom, Stmt->getParent()->getContext()); + assert(isl_set_is_subset(SchedDom, AccDom) && + "Access relation not defined on full schedule domain"); + assert(isl_set_is_subset(Dom, AccDom) && + "Access relation not defined on full domain"); + isl_set_free(AccDom); + isl_set_free(SchedDom); + isl_set_free(Dom); + } #endif auto PWAccRel = MA->applyScheduleToAccessRelation(Schedule); + // isl cannot generate an index expression for access-nothing accesses. + isl::set AccDomain = + give(isl_pw_multi_aff_domain(isl_pw_multi_aff_copy(PWAccRel))); + if (isl_set_is_empty(AccDomain.keep()) == isl_bool_true) { + isl_pw_multi_aff_free(PWAccRel); + continue; + } + auto AccessExpr = isl_ast_build_access_from_pw_multi_aff(Build, PWAccRel); NewAccesses = isl_id_to_ast_expr_set(NewAccesses, MA->getId(), AccessExpr); } diff --git a/polly/lib/Exchange/JSONExporter.cpp b/polly/lib/Exchange/JSONExporter.cpp index 21722d08e6b7f..0e8ff564eaa49 100644 --- a/polly/lib/Exchange/JSONExporter.cpp +++ b/polly/lib/Exchange/JSONExporter.cpp @@ -446,8 +446,9 @@ bool JSONImporter::importAccesses(Scop &S, Json::Value &JScop, CurrentAccessDomain = isl_set_intersect_params(CurrentAccessDomain, S.getContext()); - if (isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) == - isl_bool_false) { + if (MA->isRead() && + isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) == + isl_bool_false) { errs() << "Mapping not defined for all iteration domain elements\n"; isl_set_free(CurrentAccessDomain); isl_set_free(NewAccessDomain); diff --git a/polly/test/Isl/CodeGen/partial_write_array.ll b/polly/test/Isl/CodeGen/partial_write_array.ll new file mode 100644 index 0000000000000..ad20e977876ff --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_array.ll @@ -0,0 +1,44 @@ +; RUN: opt %loadPolly -polly-import-jscop -polly-import-jscop-dir=%S -polly-import-jscop-postfix=transformed -polly-codegen -S < %s | FileCheck %s +; +; Partial write of an array access. +; +; for (int j = 0; j < n; j += 1) +; A[0] = 42.0 +; + +define void @partial_write_array(i32 %n, double* noalias nonnull %A) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %body, label %exit + + body: + store double 42.0, double* %A + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK: polly.stmt.body: +; CHECK-NEXT: %1 = icmp sge i64 %polly.indvar, 5 +; CHECK-NEXT: %polly.Stmt_body_Write0.cond = icmp ne i1 %1, false +; CHECK-NEXT: br i1 %polly.Stmt_body_Write0.cond, label %polly.stmt.body.Stmt_body_Write0.partial, label %polly.stmt.body.cont + +; CHECK: polly.stmt.body.Stmt_body_Write0.partial: +; CHECK-NEXT: %polly.access.A = getelementptr double, double* %A, i64 0 +; CHECK-NEXT: store double 4.200000e+01, double* %polly.access.A, !alias.scope !0, !noalias !2 +; CHECK-NEXT: br label %polly.stmt.body.cont + +; CHECK: polly.stmt.body.cont: diff --git a/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop b/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop new file mode 100644 index 0000000000000..a0472bc116b69 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop @@ -0,0 +1,24 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[i0] -> MemRef_A[0] }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop.transformed b/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop.transformed new file mode 100644 index 0000000000000..59a76cba461e2 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_array___%for---%return.jscop.transformed @@ -0,0 +1,24 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[j] -> MemRef_A[0] : j >= 5 }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_emptyset.ll b/polly/test/Isl/CodeGen/partial_write_emptyset.ll new file mode 100644 index 0000000000000..2052ac47fb0f5 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_emptyset.ll @@ -0,0 +1,37 @@ +; RUN: opt %loadPolly -polly-import-jscop -polly-import-jscop-dir=%S -polly-import-jscop-postfix=transformed -polly-codegen -S < %s | FileCheck %s +; +; Partial write, where "partial" is the empty set. +; The store is never executed in this case and we do generate it in the +; first place. +; +; for (int j = 0; j < n; j += 1) +; A[0] = 42.0 +; + +define void @partial_write_emptyset(i32 %n, double* noalias nonnull %A) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %body, label %exit + + body: + store double 42.0, double* %A + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK-LABEL: polly.stmt.body: +; CHECK-NOT: store diff --git a/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop b/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop new file mode 100644 index 0000000000000..a0472bc116b69 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop @@ -0,0 +1,24 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[i0] -> MemRef_A[0] }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop.transformed b/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop.transformed new file mode 100644 index 0000000000000..8f7f2a00cf1f7 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_emptyset___%for---%return.jscop.transformed @@ -0,0 +1,24 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[j] -> MemRef_A[0] : 1 = 0 }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar.ll b/polly/test/Isl/CodeGen/partial_write_mapped_scalar.ll new file mode 100644 index 0000000000000..048058b067a01 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar.ll @@ -0,0 +1,57 @@ +; RUN: opt %loadPolly -polly-import-jscop -polly-import-jscop-dir=%S -polly-import-jscop-postfix=transformed -polly-codegen -S < %s | FileCheck %s +; +; Partial write of a (mapped) scalar. +; +; for (int j = 0; j < n; j += 1) { +;body: +; val = 21.0 + 21.0; +; if (j >= 5) +;user: +; A[0] = val; +; } + +define void @partial_write_mapped_scalar(i32 %n, double* noalias nonnull %A) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %body, label %exit + + body: + %val = fadd double 21.0, 21.0 + %if.cond = icmp sgt i32 %j, 5 + br i1 %if.cond, label %user, label %inc + + user: + store double %val, double* %A + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK: polly.stmt.body: +; CHECK-NEXT: %p_val = fadd double 2.100000e+01, 2.100000e+01 +; CHECK-NEXT: %1 = trunc i64 %polly.indvar to i32 +; CHECK-NEXT: %p_if.cond = icmp sgt i32 %1, 5 +; CHECK-NEXT: %2 = icmp sge i64 %polly.indvar, 5 +; CHECK-NEXT: %polly.Stmt_body_Write0.cond = icmp ne i1 %2, false +; CHECK-NEXT: br i1 %polly.Stmt_body_Write0.cond, label %polly.stmt.body.Stmt_body_Write0.partial, label %polly.stmt.body.cont + +; CHECK: polly.stmt.body.Stmt_body_Write0.partial: +; CHECK-NEXT: %polly.access.A = getelementptr double, double* %A, i64 1 +; CHECK-NEXT: store double %p_val, double* %polly.access.A +; CHECK-NEXT: br label %polly.stmt.body.cont + +; CHECK: polly.stmt.body.cont: +; CHECK-NEXT: br label %polly.cond diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop b/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop new file mode 100644 index 0000000000000..87b05b3f27a1d --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[i0] -> MemRef_val[] }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_A[0] }" + }, + { + "kind" : "read", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_val[] }" + } + ], + "domain" : "[n] -> { Stmt_user[i0] : 6 <= i0 < n }", + "name" : "Stmt_user", + "schedule" : "[n] -> { Stmt_user[i0] -> [i0, 1] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop.transformed b/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop.transformed new file mode 100644 index 0000000000000..6e446d76ddc27 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar___%for---%return.jscop.transformed @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body[j] -> MemRef_A[1] : j >= 5 }" + } + ], + "domain" : "[n] -> { Stmt_body[i0] : 0 <= i0 < n }", + "name" : "Stmt_body", + "schedule" : "[n] -> { Stmt_body[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_A[0] }" + }, + { + "kind" : "read", + "relation" : "[n] -> { Stmt_user[j] -> MemRef_A[1] }" + } + ], + "domain" : "[n] -> { Stmt_user[i0] : 6 <= i0 < n }", + "name" : "Stmt_user", + "schedule" : "[n] -> { Stmt_user[i0] -> [i0, 1] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion.ll b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion.ll new file mode 100644 index 0000000000000..fac8728bace2d --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion.ll @@ -0,0 +1,64 @@ +; RUN: opt %loadPolly -polly-import-jscop -polly-import-jscop-dir=%S -polly-import-jscop-postfix=transformed -polly-codegen -S < %s | FileCheck %s +; +; Partial write of a (mapped) scalar in a non-affine subregion. +; +; for (int j = 0; j < n; j += 1) { +;subregion: +; val = 21.0 + 21.0; +; if (undef > undef) +;subregion_true: ; +; +;subregion_exit: +; if (j >= 5) +;user: +; A[0] = val; +; } + +define void @partial_write_mapped_scalar_subregion(i32 %n, double* noalias nonnull %A) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %subregion, label %exit + + subregion: + %val = fadd double 21.0, 21.0 + %nonaffine.cond = fcmp ogt double undef, undef + br i1 %nonaffine.cond, label %subregion_true, label %subregion_exit + + subregion_true: + br label %subregion_exit + + subregion_exit: + %if.cond = icmp sgt i32 %j, 5 + br i1 %if.cond, label %user, label %inc + + user: + store double %val, double* %A + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK-LABEL: polly.stmt.subregion_exit.exit: +; CHECK-NEXT: %1 = icmp sge i64 %polly.indvar, 5 +; CHECK-NEXT: %polly.Stmt_subregion__TO__subregion_exit_Write0.cond = icmp ne i1 %1, false +; CHECK-NEXT: br i1 %polly.Stmt_subregion__TO__subregion_exit_Write0.cond, label %polly.stmt.subregion_exit.exit.Stmt_subregion__TO__subregion_exit_Write0.partial, label %polly.stmt.subregion_exit.exit.cont + +; CHECK-LABEL: polly.stmt.subregion_exit.exit.Stmt_subregion__TO__subregion_exit_Write0.partial: +; CHECK-NEXT: %polly.access.A = getelementptr double, double* %A, i64 1 +; CHECK-NEXT: store double %p_val, double* %polly.access.A +; CHECK-NEXT: br label %polly.stmt.subregion_exit.exit.cont + +; CHECK-LABEL: polly.stmt.subregion_exit.exit.cont: diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop new file mode 100644 index 0000000000000..9c0b684097d08 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body__TO__subregion_exit[i0] -> MemRef_val[] }" + } + ], + "domain" : "[n] -> { Stmt_body__TO__subregion_exit[i0] : 0 <= i0 < n }", + "name" : "Stmt_body__TO__subregion_exit", + "schedule" : "[n] -> { Stmt_body__TO__subregion_exit[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_A[0] }" + }, + { + "kind" : "read", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_val[] }" + } + ], + "domain" : "[n] -> { Stmt_user[i0] : 6 <= i0 < n }", + "name" : "Stmt_user", + "schedule" : "[n] -> { Stmt_user[i0] -> [i0, 1] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop.transformed b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop.transformed new file mode 100644 index 0000000000000..e76755d42fdf2 --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_scalar_subregion___%for---%return.jscop.transformed @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "[n] -> { : -2147483648 <= n <= 2147483647 }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_body__TO__subregion_exit[j] -> MemRef_A[1] : j >= 5 }" + } + ], + "domain" : "[n] -> { Stmt_body__TO__subregion_exit[i0] : 0 <= i0 < n }", + "name" : "Stmt_body__TO__subregion_exit", + "schedule" : "[n] -> { Stmt_body__TO__subregion_exit[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_A[0] }" + }, + { + "kind" : "read", + "relation" : "[n] -> { Stmt_user[i0] -> MemRef_A[1] }" + } + ], + "domain" : "[n] -> { Stmt_user[i0] : 6 <= i0 < n }", + "name" : "Stmt_user", + "schedule" : "[n] -> { Stmt_user[i0] -> [i0, 1] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_vector.ll b/polly/test/Isl/CodeGen/partial_write_mapped_vector.ll new file mode 100644 index 0000000000000..f5fd57abe7a9f --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_vector.ll @@ -0,0 +1,57 @@ +; RUN: opt %loadPolly -basicaa -polly-import-jscop -polly-import-jscop-dir=%S -polly-import-jscop-postfix=transformed -polly-vectorizer=polly -polly-opt-isl -polly-ast -polly-codegen -S < %s | FileCheck %s +; +; Polly's vectorizer does not support partial accesses. +; +; for (int j = 0; j < 4; j += 1) { +;body: +; val = 21.0 + 21.0; +; if (j > 1) +;user: +; A[0] = val; +; } + +define void @partial_write_mapped_vector(double* noalias nonnull %A) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, 4 + br i1 %j.cmp, label %body, label %exit + + body: + %val = fadd double 21.0, 21.0 + %if.cond = icmp sgt i32 %j, 1 + br i1 %if.cond, label %user, label %inc + + user: + %elt= getelementptr inbounds double, double* %A, i32 %j + store double %val, double* %elt + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK-LABEL: polly.stmt.body: +; CHECK-NEXT: %p_val = fadd double 2.100000e+01, 2.100000e+01 +; CHECK-NEXT: %0 = trunc i64 %polly.indvar to i32 +; CHECK-NEXT: %p_if.cond = icmp sgt i32 %0, 1 +; CHECK-NEXT: %1 = icmp sge i64 %polly.indvar, 2 +; CHECK-NEXT: %polly.Stmt_body_Write0.cond = icmp ne i1 %1, false +; CHECK-NEXT: br i1 %polly.Stmt_body_Write0.cond, label %polly.stmt.body.Stmt_body_Write0.partial, label %polly.stmt.body.cont + +; CHECK-LABEL: polly.stmt.body.Stmt_body_Write0.partial: +; CHECK-NEXT: %polly.access.A = getelementptr double, double* %A, i64 1 +; CHECK-NEXT: store double %p_val, double* %polly.access.A +; CHECK-NEXT: br label %polly.stmt.body.cont + +; CHECK-LABEL: polly.stmt.body.cont: diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop b/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop new file mode 100644 index 0000000000000..2aadaf12d59ad --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "{ : }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "{ Stmt_body[i0] -> MemRef_val[] }" + } + ], + "domain" : "{ Stmt_body[i0] : 0 <= i0 <= 3 }", + "name" : "Stmt_body", + "schedule" : "{ Stmt_body[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "{ Stmt_user[i0] -> MemRef_A[i0] }" + }, + { + "kind" : "read", + "relation" : "{ Stmt_user[i0] -> MemRef_val[] }" + } + ], + "domain" : "{ Stmt_user[i0] : 2 <= i0 <= 3 }", + "name" : "Stmt_user", + "schedule" : "{ Stmt_user[i0] -> [i0, 1] }" + } + ] +} diff --git a/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop.transformed b/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop.transformed new file mode 100644 index 0000000000000..39f97f0c292ad --- /dev/null +++ b/polly/test/Isl/CodeGen/partial_write_mapped_vector___%for---%return.jscop.transformed @@ -0,0 +1,39 @@ +{ + "arrays" : [ + { + "name" : "MemRef_A", + "sizes" : [ "*" ], + "type" : "double" + } + ], + "context" : "{ : }", + "name" : "%for---%return", + "statements" : [ + { + "accesses" : [ + { + "kind" : "write", + "relation" : "{ Stmt_body[j] -> MemRef_A[1] : j > 1 }" + } + ], + "domain" : "{ Stmt_body[i0] : 0 <= i0 <= 3 }", + "name" : "Stmt_body", + "schedule" : "{ Stmt_body[i0] -> [i0, 0] }" + }, + { + "accesses" : [ + { + "kind" : "write", + "relation" : "{ Stmt_user[i0] -> MemRef_A[i0] }" + }, + { + "kind" : "read", + "relation" : "{ Stmt_user[j] -> MemRef_A[1] }" + } + ], + "domain" : "{ Stmt_user[i0] : 2 <= i0 <= 3 }", + "name" : "Stmt_user", + "schedule" : "{ Stmt_user[i0] -> [i0, 1] }" + } + ] +}