[CodeGen] Support partial write accesses.

Allow the BlockGenerator to generate memory writes that are not defined
over the complete statement domain, but only over a subset of it. It
generates a condition that evaluates to 1 if executing the subdomain,
and only then execute the access.

Only write accesses are supported. Read accesses would require a PHINode
which has a value if the access is not executed.

Partial write makes DeLICM able to apply mappings that are not defined
over the entire domain (for instance, a branch that leaves a loop with
a PHINode in its header; a MemoryKind::PHI write when leaving is never
read by its PHI read).

Differential Revision: https://reviews.llvm.org/D33255

llvm-svn: 303517
This commit is contained in:
Michael Kruse 2017-05-21 22:46:57 +00:00
parent 3318970cd3
commit 706f79ab14
21 changed files with 830 additions and 78 deletions

View File

@ -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 @@ protected:
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<void()> &GenThenFunc);
/// Generate the scalar stores for the given statement.
///
/// After the statement @p Stmt was copied all inner-SCoP scalar dependences

View File

@ -1043,6 +1043,10 @@ public:
/// 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; }

View File

@ -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 {

View File

@ -318,16 +318,22 @@ Value *BlockGenerator::generateArrayLoad(ScopStmt &Stmt, LoadInst *Load,
void BlockGenerator::generateArrayStore(ScopStmt &Stmt, StoreInst *Store,
ValueMapT &BBMap, LoopToScevMapT &LTS,
isl_id_to_ast_expr *NewAccesses) {
Value *NewPointer =
generateLocationAccessed(Stmt, Store, BBMap, LTS, NewAccesses);
Value *ValueOperand = getNewValue(Stmt, Store->getValueOperand(), BBMap, LTS,
getLoopForStmt(Stmt));
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());
if (PollyDebugPrinting)
RuntimeDebugBuilder::createCPUPrinter(Builder, "Store to ", NewPointer,
": ", ValueOperand, "\n");
generateConditionalExecution(Stmt, AccDom, Subject, [&, this]() {
Value *NewPointer =
generateLocationAccessed(Stmt, Store, BBMap, LTS, NewAccesses);
Value *ValueOperand = getNewValue(Stmt, Store->getValueOperand(), BBMap,
LTS, getLoopForStmt(Stmt));
Builder.CreateAlignedStore(ValueOperand, NewPointer, Store->getAlignment());
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<void()> &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<BranchInst>(HeadBlock->getTerminator());
BasicBlock *ThenBlock = Branch->getSuccessor(0);
BasicBlock *TailBlock = Branch->getSuccessor(1);
// Assign descriptive names.
if (auto *CondInst = dyn_cast<Instruction>(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 &LTS, 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
isl::set AccDom = give(isl_map_domain(MA->getAccessRelation()));
const char *Subject = isl_id_get_name(give(MA->getId()).keep());
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<BasicBlock *, Value *> 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);
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<BasicBlock *, Value *> 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<Instruction>(Val) ||
DT.dominates(cast<Instruction>(Val)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
assert((!isa<Instruction>(Address) ||
DT.dominates(cast<Instruction>(Address)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
Builder.CreateStore(Val, Address);
Val = getNewValue(Stmt, Val, BBMap, LTS, L);
assert((!isa<Instruction>(Val) ||
DT.dominates(cast<Instruction>(Val)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
assert((!isa<Instruction>(Address) ||
DT.dominates(cast<Instruction>(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<Instruction>(NewVal) ||
DT.dominates(cast<Instruction>(NewVal)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
assert((!isa<Instruction>(Address) ||
DT.dominates(cast<Instruction>(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<Instruction>(NewVal) ||
DT.dominates(cast<Instruction>(NewVal)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
assert((!isa<Instruction>(Address) ||
DT.dominates(cast<Instruction>(Address)->getParent(),
Builder.GetInsertBlock())) &&
"Domination violation");
Builder.CreateStore(NewVal, Address);
});
}
}

View File

@ -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<ScopStmt *>(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);
}

View File

@ -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);

View File

@ -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:

View File

@ -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] }"
}
]
}

View File

@ -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] }"
}
]
}

View File

@ -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

View File

@ -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] }"
}
]
}

View File

@ -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] }"
}
]
}

View File

@ -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

View File

@ -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] }"
}
]
}

View File

@ -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] }"
}
]
}

View File

@ -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:

View File

@ -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] }"
}
]
}

View File

@ -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] }"
}
]
}

View File

@ -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:

View File

@ -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] }"
}
]
}

View File

@ -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] }"
}
]
}