ScopInfo: Split out invariant load hoisting into multiple functions [NFC]
This reduces indentation and makes the code more readable. llvm-svn: 255468
This commit is contained in:
parent
a535dff471
commit
29f38ab732
|
@ -1302,6 +1302,31 @@ private:
|
|||
/// invariant location.
|
||||
void buildInvariantEquivalenceClasses();
|
||||
|
||||
/// @brief Check if a memory access can be hoisted.
|
||||
///
|
||||
/// @param Access The access to verify.
|
||||
/// @param Writes The set of all memory writes in the scop.
|
||||
///
|
||||
/// @return Return true if a memory access can be hoisted.
|
||||
bool isHoistableAccess(MemoryAccess *Access,
|
||||
__isl_keep isl_union_map *Writes);
|
||||
|
||||
/// @brief Verify that all required invariant loads have been hoisted.
|
||||
///
|
||||
/// Invariant load hoisting is not guaranteed to hoist all loads that were
|
||||
/// assumed to be scop invariant during scop detection. This function checks
|
||||
/// for cases where the hoisting failed, but where it would have been
|
||||
/// necessary for our scop modeling to be correct. In case of insufficent
|
||||
/// hoisting the scop is marked as invalid.
|
||||
///
|
||||
/// In the example below Bound[1] is required to be invariant:
|
||||
///
|
||||
/// for (int i = 1; i < Bound[0]; i++)
|
||||
/// for (int j = 1; j < Bound[1]; j++)
|
||||
/// ...
|
||||
///
|
||||
void verifyInvariantLoads();
|
||||
|
||||
/// @brief Hoist invariant memory loads and check for required ones.
|
||||
///
|
||||
/// We first identify "common" invariant loads, thus loads that are invariant
|
||||
|
|
|
@ -2851,24 +2851,20 @@ void Scop::addInvariantLoads(ScopStmt &Stmt, MemoryAccessList &InvMAs) {
|
|||
isl_set_free(DomainCtx);
|
||||
}
|
||||
|
||||
void Scop::hoistInvariantLoads() {
|
||||
isl_union_map *Writes = getWrites();
|
||||
for (ScopStmt &Stmt : *this) {
|
||||
|
||||
bool Scop::isHoistableAccess(MemoryAccess *Access,
|
||||
__isl_keep isl_union_map *Writes) {
|
||||
// TODO: Loads that are not loop carried, hence are in a statement with
|
||||
// zero iterators, are by construction invariant, though we
|
||||
// currently "hoist" them anyway. This is necessary because we allow
|
||||
// them to be treated as parameters (e.g., in conditions) and our code
|
||||
// generation would otherwise use the old value.
|
||||
|
||||
BasicBlock *BB = Stmt.isBlockStmt() ? Stmt.getBasicBlock()
|
||||
: Stmt.getRegion()->getEntry();
|
||||
isl_set *Domain = Stmt.getDomain();
|
||||
MemoryAccessList InvMAs;
|
||||
auto &Stmt = *Access->getStatement();
|
||||
BasicBlock *BB =
|
||||
Stmt.isBlockStmt() ? Stmt.getBasicBlock() : Stmt.getRegion()->getEntry();
|
||||
|
||||
for (MemoryAccess *MA : Stmt) {
|
||||
if (MA->isScalarKind() || MA->isWrite() || !MA->isAffine())
|
||||
continue;
|
||||
if (Access->isScalarKind() || Access->isWrite() || !Access->isAffine())
|
||||
return false;
|
||||
|
||||
// Skip accesses that have an invariant base pointer which is defined but
|
||||
// not loaded inside the SCoP. This can happened e.g., if a readnone call
|
||||
|
@ -2879,37 +2875,36 @@ void Scop::hoistInvariantLoads() {
|
|||
// that it is invariant, thus it will be hoisted too. However, if there is
|
||||
// no base pointer origin we check that the base pointer is defined
|
||||
// outside the region.
|
||||
const ScopArrayInfo *SAI = MA->getScopArrayInfo();
|
||||
const ScopArrayInfo *SAI = Access->getScopArrayInfo();
|
||||
while (auto *BasePtrOriginSAI = SAI->getBasePtrOriginSAI())
|
||||
SAI = BasePtrOriginSAI;
|
||||
|
||||
if (auto *BasePtrInst = dyn_cast<Instruction>(SAI->getBasePtr()))
|
||||
if (R.contains(BasePtrInst))
|
||||
continue;
|
||||
return false;
|
||||
|
||||
// Skip accesses in non-affine subregions as they might not be executed
|
||||
// under the same condition as the entry of the non-affine subregion.
|
||||
if (BB != MA->getAccessInstruction()->getParent())
|
||||
continue;
|
||||
if (BB != Access->getAccessInstruction()->getParent())
|
||||
return false;
|
||||
|
||||
isl_map *AccessRelation = MA->getAccessRelation();
|
||||
isl_map *AccessRelation = Access->getAccessRelation();
|
||||
|
||||
// Skip accesses that have an empty access relation. These can be caused
|
||||
// by multiple offsets with a type cast in-between that cause the overall
|
||||
// byte offset to be not divisible by the new types sizes.
|
||||
if (isl_map_is_empty(AccessRelation)) {
|
||||
isl_map_free(AccessRelation);
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isl_map_involves_dims(AccessRelation, isl_dim_in, 0,
|
||||
Stmt.getNumIterators())) {
|
||||
isl_map_free(AccessRelation);
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessRelation =
|
||||
isl_map_intersect_domain(AccessRelation, isl_set_copy(Domain));
|
||||
AccessRelation = isl_map_intersect_domain(AccessRelation, Stmt.getDomain());
|
||||
isl_set *AccessRange = isl_map_range(AccessRelation);
|
||||
|
||||
isl_union_map *Written = isl_union_map_intersect_range(
|
||||
|
@ -2918,40 +2913,47 @@ void Scop::hoistInvariantLoads() {
|
|||
isl_union_map_free(Written);
|
||||
|
||||
if (IsWritten)
|
||||
continue;
|
||||
return false;
|
||||
|
||||
InvMAs.push_front(MA);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scop::verifyInvariantLoads() {
|
||||
auto &RIL = *SD.getRequiredInvariantLoads(&getRegion());
|
||||
for (LoadInst *LI : RIL) {
|
||||
assert(LI && getRegion().contains(LI));
|
||||
ScopStmt *Stmt = getStmtForBasicBlock(LI->getParent());
|
||||
if (Stmt && Stmt->lookupAccessesFor(LI)) {
|
||||
invalidate(INVARIANTLOAD, LI->getDebugLoc());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scop::hoistInvariantLoads() {
|
||||
isl_union_map *Writes = getWrites();
|
||||
for (ScopStmt &Stmt : *this) {
|
||||
|
||||
MemoryAccessList InvariantAccesses;
|
||||
|
||||
for (MemoryAccess *Access : Stmt)
|
||||
if (isHoistableAccess(Access, Writes))
|
||||
InvariantAccesses.push_front(Access);
|
||||
|
||||
// We inserted invariant accesses always in the front but need them to be
|
||||
// sorted in a "natural order". The statements are already sorted in reverse
|
||||
// post order and that suffices for the accesses too. The reason we require
|
||||
// an order in the first place is the dependences between invariant loads
|
||||
// that can be caused by indirect loads.
|
||||
InvMAs.reverse();
|
||||
InvariantAccesses.reverse();
|
||||
|
||||
// Transfer the memory access from the statement to the SCoP.
|
||||
Stmt.removeMemoryAccesses(InvMAs);
|
||||
addInvariantLoads(Stmt, InvMAs);
|
||||
|
||||
isl_set_free(Domain);
|
||||
Stmt.removeMemoryAccesses(InvariantAccesses);
|
||||
addInvariantLoads(Stmt, InvariantAccesses);
|
||||
}
|
||||
isl_union_map_free(Writes);
|
||||
|
||||
auto &ScopRIL = *SD.getRequiredInvariantLoads(&getRegion());
|
||||
// Check required invariant loads that were tagged during SCoP detection.
|
||||
for (LoadInst *LI : ScopRIL) {
|
||||
assert(LI && getRegion().contains(LI));
|
||||
ScopStmt *Stmt = getStmtForBasicBlock(LI->getParent());
|
||||
if (Stmt && Stmt->lookupAccessesFor(LI) != nullptr) {
|
||||
DEBUG(dbgs() << "\n\nWARNING: Load (" << *LI
|
||||
<< ") is required to be invariant but was not marked as "
|
||||
"such. SCoP for "
|
||||
<< getRegion() << " will be dropped\n\n");
|
||||
invalidate(INVARIANTLOAD, LI->getDebugLoc());
|
||||
return;
|
||||
}
|
||||
}
|
||||
verifyInvariantLoads();
|
||||
}
|
||||
|
||||
const ScopArrayInfo *
|
||||
|
|
Loading…
Reference in New Issue