Skip to content

Commit

Permalink
[CodeGen] Support partial write accesses.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Meinersbur committed May 21, 2017
1 parent 3318970 commit 706f79a
Show file tree
Hide file tree
Showing 21 changed files with 834 additions and 82 deletions.
28 changes: 28 additions & 0 deletions polly/include/polly/CodeGen/BlockGenerators.h
Expand Up @@ -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"
Expand Down Expand Up @@ -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<void()> &GenThenFunc);

/// Generate the scalar stores for the given statement.
///
/// After the statement @p Stmt was copied all inner-SCoP scalar dependences
Expand Down
4 changes: 4 additions & 0 deletions polly/include/polly/ScopInfo.h
Expand Up @@ -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; }

Expand Down
29 changes: 20 additions & 9 deletions polly/lib/Analysis/ScopInfo.cpp
Expand Up @@ -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) &&
Expand Down Expand Up @@ -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 {
Expand Down
194 changes: 138 additions & 56 deletions polly/lib/CodeGen/BlockGenerators.cpp
Expand Up @@ -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));

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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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<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);
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<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);

});
}
}

Expand Down Expand Up @@ -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);
});
}
}

Expand Down

0 comments on commit 706f79a

Please sign in to comment.