#include "polly/ManualOptimizer.h"
#include "polly/DependenceInfo.h"
#include "polly/Options.h"
#include "polly/ScheduleTreeTransform.h"
#include "polly/Support/ScopHelper.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Transforms/Utils/LoopUtils.h"
#include <optional>
#include "polly/Support/PollyDebug.h"
#define DEBUG_TYPE "polly-opt-manual"
using namespace polly;
using namespace llvm;
namespace {
static cl::opt<bool> IgnoreDepcheck(
"polly-pragma-ignore-depcheck",
cl::desc("Skip the dependency check for pragma-based transformations"),
cl::cat(PollyCategory));
static TransformationMode hasUnrollTransformation(MDNode *LoopID) {
if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.disable"))
return TM_SuppressedByUser;
std::optional<int> Count =
getOptionalIntLoopAttribute(LoopID, "llvm.loop.unroll.count");
if (Count)
return *Count == 1 ? TM_SuppressedByUser : TM_ForcedByUser;
if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.enable"))
return TM_ForcedByUser;
if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.full"))
return TM_ForcedByUser;
if (hasDisableAllTransformsHint(LoopID))
return TM_Disable;
return TM_Unspecified;
}
static DebugLoc findFirstDebugLoc(MDNode *MD) {
if (MD) {
for (const MDOperand &X : drop_begin(MD->operands(), 1)) {
Metadata *A = X.get();
if (!isa<DILocation>(A))
continue;
return cast<DILocation>(A);
}
}
return {};
}
static DebugLoc findTransformationDebugLoc(MDNode *LoopMD, StringRef Name) {
MDNode *MD = findOptionMDForLoopID(LoopMD, Name);
if (DebugLoc K = findFirstDebugLoc(MD))
return K;
return findFirstDebugLoc(LoopMD);
}
static isl::schedule applyLoopUnroll(MDNode *LoopMD,
isl::schedule_node BandToUnroll) {
TransformationMode UnrollMode = ::hasUnrollTransformation(LoopMD);
if (UnrollMode & TM_Disable)
return {};
assert(!BandToUnroll.is_null());
int64_t Factor =
getOptionalIntLoopAttribute(LoopMD, "llvm.loop.unroll.count").value_or(0);
bool Full = getBooleanLoopAttribute(LoopMD, "llvm.loop.unroll.full");
assert((!Full || !(Factor > 0)) &&
"Cannot unroll fully and partially at the same time");
if (Full)
return applyFullUnroll(BandToUnroll);
if (Factor > 0)
return applyPartialUnroll(BandToUnroll, Factor);
return {};
}
static isl::schedule applyLoopFission(MDNode *LoopMD,
isl::schedule_node BandToFission) {
return applyMaxFission(BandToFission);
}
static auto getLoopMDProps(MDNode *LoopMD) {
return map_range(
make_filter_range(
drop_begin(LoopMD->operands(), 1),
[](const MDOperand &MDOp) { return isa<MDNode>(MDOp.get()); }),
[](const MDOperand &MDOp) { return cast<MDNode>(MDOp.get()); });
}
class SearchTransformVisitor final
: public RecursiveScheduleTreeVisitor<SearchTransformVisitor> {
private:
using BaseTy = RecursiveScheduleTreeVisitor<SearchTransformVisitor>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
polly::Scop *S;
const Dependences *D;
OptimizationRemarkEmitter *ORE;
isl::schedule Result;
isl::schedule
checkDependencyViolation(llvm::MDNode *LoopMD, llvm::Value *CodeRegion,
const isl::schedule_node &OrigBand,
StringRef DebugLocAttr, StringRef TransPrefix,
StringRef RemarkName, StringRef TransformationName) {
if (D->isValidSchedule(*S, Result))
return Result;
LLVMContext &Ctx = LoopMD->getContext();
POLLY_DEBUG(dbgs() << "Dependency violation detected\n");
DebugLoc TransformLoc = findTransformationDebugLoc(LoopMD, DebugLocAttr);
if (IgnoreDepcheck) {
POLLY_DEBUG(dbgs() << "Still accepting transformation due to "
"-polly-pragma-ignore-depcheck\n");
if (ORE) {
ORE->emit(
OptimizationRemark(DEBUG_TYPE, RemarkName, TransformLoc, CodeRegion)
<< (Twine("Could not verify dependencies for ") +
TransformationName +
"; still applying because of -polly-pragma-ignore-depcheck")
.str());
}
return Result;
}
POLLY_DEBUG(dbgs() << "Rolling back transformation\n");
if (ORE) {
ORE->emit(DiagnosticInfoOptimizationFailure(DEBUG_TYPE, RemarkName,
TransformLoc, CodeRegion)
<< (Twine("not applying ") + TransformationName +
": cannot ensure semantic equivalence due to possible "
"dependency violations")
.str());
}
MDNode *NewLoopMD =
makePostTransformationMetadata(Ctx, LoopMD, {TransPrefix}, {});
BandAttr *Attr = getBandAttr(OrigBand);
Attr->Metadata = NewLoopMD;
return OrigBand.get_schedule();
}
public:
SearchTransformVisitor(polly::Scop *S, const Dependences *D,
OptimizationRemarkEmitter *ORE)
: S(S), D(D), ORE(ORE) {}
static isl::schedule applyOneTransformation(polly::Scop *S,
const Dependences *D,
OptimizationRemarkEmitter *ORE,
const isl::schedule &Sched) {
SearchTransformVisitor Transformer(S, D, ORE);
Transformer.visit(Sched);
return Transformer.Result;
}
void visitBand(isl::schedule_node_band Band) {
getBase().visitBand(Band);
if (!Result.is_null())
return;
if (isl_schedule_node_band_n_member(Band.get()) != 1)
return;
BandAttr *Attr = getBandAttr(Band);
if (!Attr) {
return;
}
Loop *Loop = Attr->OriginalLoop;
Value *CodeRegion = nullptr;
if (Loop)
CodeRegion = Loop->getHeader();
MDNode *LoopMD = Attr->Metadata;
if (!LoopMD)
return;
for (MDNode *MD : getLoopMDProps(LoopMD)) {
auto *NameMD = dyn_cast<MDString>(MD->getOperand(0).get());
if (!NameMD)
continue;
StringRef AttrName = NameMD->getString();
if (AttrName == "llvm.loop.unroll.enable" ||
AttrName == "llvm.loop.unroll.count" ||
AttrName == "llvm.loop.unroll.full") {
Result = applyLoopUnroll(LoopMD, Band);
if (!Result.is_null())
return;
} else if (AttrName == "llvm.loop.distribute.enable") {
Result = applyLoopFission(LoopMD, Band);
if (!Result.is_null())
Result = checkDependencyViolation(
LoopMD, CodeRegion, Band, "llvm.loop.distribute.loc",
"llvm.loop.distribute.", "FailedRequestedFission",
"loop fission/distribution");
if (!Result.is_null())
return;
}
}
}
void visitNode(isl::schedule_node Other) {
if (!Result.is_null())
return;
getBase().visitNode(Other);
}
};
}
isl::schedule
polly::applyManualTransformations(Scop *S, isl::schedule Sched,
const Dependences &D,
OptimizationRemarkEmitter *ORE) {
while (true) {
isl::schedule Result =
SearchTransformVisitor::applyOneTransformation(S, &D, ORE, Sched);
if (Result.is_null()) {
break;
}
Sched = Result;
}
return Sched;
}