blob: d540f211e959971a2d340738a62c66280a4fc1e4 [file]
//===- LoopAnnotationImporter.cpp - Loop annotation import ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LoopAnnotationImporter.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/Constants.h"
using namespace mlir;
using namespace mlir::LLVM;
using namespace mlir::LLVM::detail;
namespace {
/// Helper class that keeps the state of one metadata to attribute conversion.
struct LoopMetadataConversion {
/// Converts a loop ID, owning the recursion state for its follow-ups.
static LoopAnnotationAttr convert(const llvm::MDNode *node, Location loc,
LoopAnnotationImporter &importer);
private:
LoopMetadataConversion(
const llvm::MDNode *node, Location loc,
LoopAnnotationImporter &loopAnnotationImporter,
llvm::SmallPtrSetImpl<const llvm::MDNode *> &activeNodes)
: node(node), loc(loc), loopAnnotationImporter(loopAnnotationImporter),
ctx(loc->getContext()), activeNodes(activeNodes) {}
/// Converts a follow-up using the current recursion state.
LoopAnnotationAttr convertFollowup(const llvm::MDNode *followup);
/// Converts the properties after the validated loop ID or follow-up header.
LoopAnnotationAttr convertProperties();
/// Initializes the shared state for the conversion member functions.
LogicalResult initConversionState();
/// Helper function to get and erase a property.
const llvm::MDNode *lookupAndEraseProperty(StringRef name);
/// Helper functions to lookup and convert MDNodes into a specifc attribute
/// kind. These functions return null-attributes if there is no node with the
/// specified name, or failure, if the node is ill-formatted.
FailureOr<BoolAttr> lookupUnitNode(StringRef name);
FailureOr<BoolAttr> lookupBoolNode(StringRef name, bool negated = false);
FailureOr<BoolAttr> lookupIntNodeAsBoolAttr(StringRef name);
FailureOr<IntegerAttr> lookupIntNode(StringRef name);
FailureOr<SmallVector<llvm::MDNode *>> lookupMDNodes(StringRef name);
FailureOr<LoopAnnotationAttr> lookupFollowupNode(StringRef name);
FailureOr<BoolAttr> lookupBooleanUnitNode(StringRef enableName,
StringRef disableName,
bool negated = false);
/// Conversion functions for sub-attributes.
FailureOr<LoopVectorizeAttr> convertVectorizeAttr();
FailureOr<LoopInterleaveAttr> convertInterleaveAttr();
FailureOr<LoopUnrollAttr> convertUnrollAttr();
FailureOr<LoopUnrollAndJamAttr> convertUnrollAndJamAttr();
FailureOr<LoopLICMAttr> convertLICMAttr();
FailureOr<LoopDistributeAttr> convertDistributeAttr();
FailureOr<LoopPipelineAttr> convertPipelineAttr();
FailureOr<LoopPeeledAttr> convertPeeledAttr();
FailureOr<LoopUnswitchAttr> convertUnswitchAttr();
FailureOr<SmallVector<AccessGroupAttr>> convertParallelAccesses();
FusedLoc convertStartLoc();
FailureOr<FusedLoc> convertEndLoc();
llvm::SmallVector<llvm::DILocation *, 2> locations;
llvm::StringMap<const llvm::MDNode *> propertyMap;
const llvm::MDNode *node;
Location loc;
LoopAnnotationImporter &loopAnnotationImporter;
MLIRContext *ctx;
llvm::SmallPtrSetImpl<const llvm::MDNode *> &activeNodes;
};
} // namespace
LogicalResult LoopMetadataConversion::initConversionState() {
for (const llvm::MDOperand &operand : llvm::drop_begin(node->operands())) {
if (auto *diLoc = dyn_cast<llvm::DILocation>(operand)) {
locations.push_back(diLoc);
continue;
}
auto *property = dyn_cast<llvm::MDNode>(operand);
if (!property)
return emitWarning(loc) << "expected all loop properties to be either "
"debug locations or metadata nodes";
if (property->getNumOperands() == 0)
return emitWarning(loc) << "cannot import empty loop property";
auto *nameNode = dyn_cast<llvm::MDString>(property->getOperand(0));
if (!nameNode)
return emitWarning(loc) << "cannot import loop property without a name";
StringRef name = nameNode->getString();
bool succ = propertyMap.try_emplace(name, property).second;
if (!succ)
return emitWarning(loc)
<< "cannot import loop properties with duplicated names " << name;
}
return success();
}
const llvm::MDNode *
LoopMetadataConversion::lookupAndEraseProperty(StringRef name) {
auto it = propertyMap.find(name);
if (it == propertyMap.end())
return nullptr;
const llvm::MDNode *property = it->getValue();
propertyMap.erase(it);
return property;
}
FailureOr<BoolAttr> LoopMetadataConversion::lookupUnitNode(StringRef name) {
const llvm::MDNode *property = lookupAndEraseProperty(name);
if (!property)
return BoolAttr(nullptr);
if (property->getNumOperands() != 1)
return emitWarning(loc)
<< "expected metadata node " << name << " to hold no value";
return BoolAttr::get(ctx, true);
}
FailureOr<BoolAttr> LoopMetadataConversion::lookupBooleanUnitNode(
StringRef enableName, StringRef disableName, bool negated) {
auto enable = lookupUnitNode(enableName);
auto disable = lookupUnitNode(disableName);
if (failed(enable) || failed(disable))
return failure();
if (*enable && *disable)
return emitWarning(loc)
<< "expected metadata nodes " << enableName << " and " << disableName
<< " to be mutually exclusive.";
if (*enable)
return BoolAttr::get(ctx, !negated);
if (*disable)
return BoolAttr::get(ctx, negated);
return BoolAttr(nullptr);
}
FailureOr<BoolAttr> LoopMetadataConversion::lookupBoolNode(StringRef name,
bool negated) {
const llvm::MDNode *property = lookupAndEraseProperty(name);
if (!property)
return BoolAttr(nullptr);
auto emitNodeWarning = [&]() {
return emitWarning(loc)
<< "expected metadata node " << name << " to hold a boolean value";
};
if (property->getNumOperands() != 2)
return emitNodeWarning();
llvm::ConstantInt *val =
llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
if (!val || val->getBitWidth() != 1)
return emitNodeWarning();
return BoolAttr::get(ctx, val->getValue().getLimitedValue(1) ^ negated);
}
FailureOr<BoolAttr>
LoopMetadataConversion::lookupIntNodeAsBoolAttr(StringRef name) {
const llvm::MDNode *property = lookupAndEraseProperty(name);
if (!property)
return BoolAttr(nullptr);
auto emitNodeWarning = [&]() {
return emitWarning(loc)
<< "expected metadata node " << name << " to hold an integer value";
};
if (property->getNumOperands() != 2)
return emitNodeWarning();
llvm::ConstantInt *val =
llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
if (!val || val->getBitWidth() != 32)
return emitNodeWarning();
return BoolAttr::get(ctx, val->getValue().getLimitedValue(1));
}
FailureOr<IntegerAttr> LoopMetadataConversion::lookupIntNode(StringRef name) {
const llvm::MDNode *property = lookupAndEraseProperty(name);
if (!property)
return IntegerAttr(nullptr);
auto emitNodeWarning = [&]() {
return emitWarning(loc)
<< "expected metadata node " << name << " to hold an i32 value";
};
if (property->getNumOperands() != 2)
return emitNodeWarning();
llvm::ConstantInt *val =
llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
if (!val || val->getBitWidth() != 32)
return emitNodeWarning();
return IntegerAttr::get(IntegerType::get(ctx, 32),
val->getValue().getLimitedValue());
}
FailureOr<SmallVector<llvm::MDNode *>>
LoopMetadataConversion::lookupMDNodes(StringRef name) {
const llvm::MDNode *property = lookupAndEraseProperty(name);
SmallVector<llvm::MDNode *> res;
if (!property)
return res;
auto emitNodeWarning = [&]() {
return emitWarning(loc) << "expected metadata node " << name
<< " to hold one or multiple MDNodes";
};
if (property->getNumOperands() < 2)
return emitNodeWarning();
for (unsigned i = 1, e = property->getNumOperands(); i < e; ++i) {
auto *node = dyn_cast<llvm::MDNode>(property->getOperand(i));
if (!node)
return emitNodeWarning();
res.push_back(node);
}
return res;
}
FailureOr<LoopAnnotationAttr>
LoopMetadataConversion::lookupFollowupNode(StringRef name) {
const llvm::MDNode *followup = lookupAndEraseProperty(name);
if (!followup)
return LoopAnnotationAttr(nullptr);
LoopAnnotationAttr attr = convertFollowup(followup);
if (!attr)
return failure();
return attr;
}
static bool isEmptyOrNull(const Attribute attr) { return !attr; }
template <typename T>
static bool isEmptyOrNull(const SmallVectorImpl<T> &vec) {
return vec.empty();
}
/// Helper function that only creates and attribute of type T if all argument
/// conversion were successfull and at least one of them holds a non-null value.
template <typename T, typename... P>
static T createIfNonNull(MLIRContext *ctx, const P &...args) {
bool anyFailed = (failed(args) || ...);
if (anyFailed)
return {};
bool allEmpty = (isEmptyOrNull(*args) && ...);
if (allEmpty)
return {};
return T::get(ctx, *args...);
}
FailureOr<LoopVectorizeAttr> LoopMetadataConversion::convertVectorizeAttr() {
FailureOr<BoolAttr> enable = lookupBooleanUnitNode(
"llvm.loop.vectorize.enable", "llvm.loop.vectorize.disable",
/*negated=*/true);
FailureOr<BoolAttr> predicateEnable =
lookupBooleanUnitNode("llvm.loop.vectorize.predicate.enable",
"llvm.loop.vectorize.predicate.disable");
FailureOr<BoolAttr> scalableEnable =
lookupBooleanUnitNode("llvm.loop.vectorize.scalable.enable",
"llvm.loop.vectorize.scalable.disable");
FailureOr<IntegerAttr> width = lookupIntNode("llvm.loop.vectorize.width");
FailureOr<LoopAnnotationAttr> followupVec =
lookupFollowupNode("llvm.loop.vectorize.followup_vectorized");
FailureOr<LoopAnnotationAttr> followupEpi =
lookupFollowupNode("llvm.loop.vectorize.followup_epilogue");
FailureOr<LoopAnnotationAttr> followupAll =
lookupFollowupNode("llvm.loop.vectorize.followup_all");
return createIfNonNull<LoopVectorizeAttr>(ctx, enable, predicateEnable,
scalableEnable, width, followupVec,
followupEpi, followupAll);
}
FailureOr<LoopInterleaveAttr> LoopMetadataConversion::convertInterleaveAttr() {
FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.interleave.count");
return createIfNonNull<LoopInterleaveAttr>(ctx, count);
}
FailureOr<LoopUnrollAttr> LoopMetadataConversion::convertUnrollAttr() {
FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
"llvm.loop.unroll.enable", "llvm.loop.unroll.disable", /*negated=*/true);
FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.unroll.count");
FailureOr<BoolAttr> runtimeDisable =
lookupUnitNode("llvm.loop.unroll.runtime.disable");
FailureOr<BoolAttr> full = lookupUnitNode("llvm.loop.unroll.full");
FailureOr<LoopAnnotationAttr> followupUnrolled =
lookupFollowupNode("llvm.loop.unroll.followup_unrolled");
FailureOr<LoopAnnotationAttr> followupRemainder =
lookupFollowupNode("llvm.loop.unroll.followup_remainder");
FailureOr<LoopAnnotationAttr> followupAll =
lookupFollowupNode("llvm.loop.unroll.followup_all");
return createIfNonNull<LoopUnrollAttr>(ctx, disable, count, runtimeDisable,
full, followupUnrolled,
followupRemainder, followupAll);
}
FailureOr<LoopUnrollAndJamAttr>
LoopMetadataConversion::convertUnrollAndJamAttr() {
FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
"llvm.loop.unroll_and_jam.enable", "llvm.loop.unroll_and_jam.disable",
/*negated=*/true);
FailureOr<IntegerAttr> count =
lookupIntNode("llvm.loop.unroll_and_jam.count");
FailureOr<LoopAnnotationAttr> followupOuter =
lookupFollowupNode("llvm.loop.unroll_and_jam.followup_outer");
FailureOr<LoopAnnotationAttr> followupInner =
lookupFollowupNode("llvm.loop.unroll_and_jam.followup_inner");
FailureOr<LoopAnnotationAttr> followupRemainderOuter =
lookupFollowupNode("llvm.loop.unroll_and_jam.followup_remainder_outer");
FailureOr<LoopAnnotationAttr> followupRemainderInner =
lookupFollowupNode("llvm.loop.unroll_and_jam.followup_remainder_inner");
FailureOr<LoopAnnotationAttr> followupAll =
lookupFollowupNode("llvm.loop.unroll_and_jam.followup_all");
return createIfNonNull<LoopUnrollAndJamAttr>(
ctx, disable, count, followupOuter, followupInner, followupRemainderOuter,
followupRemainderInner, followupAll);
}
FailureOr<LoopLICMAttr> LoopMetadataConversion::convertLICMAttr() {
FailureOr<BoolAttr> disable = lookupUnitNode("llvm.licm.disable");
FailureOr<BoolAttr> versioningDisable =
lookupUnitNode("llvm.loop.licm_versioning.disable");
return createIfNonNull<LoopLICMAttr>(ctx, disable, versioningDisable);
}
FailureOr<LoopDistributeAttr> LoopMetadataConversion::convertDistributeAttr() {
FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
"llvm.loop.distribute.enable", "llvm.loop.distribute.disable",
/*negated=*/true);
FailureOr<LoopAnnotationAttr> followupCoincident =
lookupFollowupNode("llvm.loop.distribute.followup_coincident");
FailureOr<LoopAnnotationAttr> followupSequential =
lookupFollowupNode("llvm.loop.distribute.followup_sequential");
FailureOr<LoopAnnotationAttr> followupFallback =
lookupFollowupNode("llvm.loop.distribute.followup_fallback");
FailureOr<LoopAnnotationAttr> followupAll =
lookupFollowupNode("llvm.loop.distribute.followup_all");
return createIfNonNull<LoopDistributeAttr>(ctx, disable, followupCoincident,
followupSequential,
followupFallback, followupAll);
}
FailureOr<LoopPipelineAttr> LoopMetadataConversion::convertPipelineAttr() {
FailureOr<BoolAttr> disable = lookupBoolNode("llvm.loop.pipeline.disable");
FailureOr<IntegerAttr> initiationinterval =
lookupIntNode("llvm.loop.pipeline.initiationinterval");
return createIfNonNull<LoopPipelineAttr>(ctx, disable, initiationinterval);
}
FailureOr<LoopPeeledAttr> LoopMetadataConversion::convertPeeledAttr() {
FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.peeled.count");
return createIfNonNull<LoopPeeledAttr>(ctx, count);
}
FailureOr<LoopUnswitchAttr> LoopMetadataConversion::convertUnswitchAttr() {
FailureOr<BoolAttr> partialDisable =
lookupUnitNode("llvm.loop.unswitch.partial.disable");
return createIfNonNull<LoopUnswitchAttr>(ctx, partialDisable);
}
FailureOr<SmallVector<AccessGroupAttr>>
LoopMetadataConversion::convertParallelAccesses() {
FailureOr<SmallVector<llvm::MDNode *>> nodes =
lookupMDNodes("llvm.loop.parallel_accesses");
if (failed(nodes))
return failure();
SmallVector<AccessGroupAttr> refs;
for (llvm::MDNode *node : *nodes) {
FailureOr<SmallVector<AccessGroupAttr>> accessGroups =
loopAnnotationImporter.lookupAccessGroupAttrs(node);
if (failed(accessGroups)) {
emitWarning(loc) << "could not lookup access group";
continue;
}
llvm::append_range(refs, *accessGroups);
}
return refs;
}
FusedLoc LoopMetadataConversion::convertStartLoc() {
if (locations.empty())
return {};
return dyn_cast<FusedLoc>(
loopAnnotationImporter.moduleImport.translateLoc(locations[0]));
}
FailureOr<FusedLoc> LoopMetadataConversion::convertEndLoc() {
if (locations.size() < 2)
return FusedLoc();
if (locations.size() > 2)
return emitError(loc)
<< "expected loop metadata to have at most two DILocations";
return dyn_cast<FusedLoc>(
loopAnnotationImporter.moduleImport.translateLoc(locations[1]));
}
LoopAnnotationAttr
LoopMetadataConversion::convert(const llvm::MDNode *node, Location loc,
LoopAnnotationImporter &importer) {
if (node->getNumOperands() == 0 ||
dyn_cast<llvm::MDNode>(node->getOperand(0)) != node) {
emitWarning(loc) << "invalid loop node";
return {};
}
llvm::SmallPtrSet<const llvm::MDNode *, 4> activeNodes;
return LoopMetadataConversion(node, loc, importer, activeNodes)
.convertProperties();
}
LoopAnnotationAttr
LoopMetadataConversion::convertFollowup(const llvm::MDNode *followup) {
if (followup->getNumOperands() == 0 ||
!isa<llvm::MDString>(followup->getOperand(0))) {
emitWarning(loc) << "invalid loop followup node";
return {};
}
// An explicit empty follow-up is different from an absent follow-up.
if (followup->getNumOperands() == 1)
return LoopAnnotationAttr::get(ctx, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {});
return LoopMetadataConversion(followup, loc, loopAnnotationImporter,
activeNodes)
.convertProperties();
}
LoopAnnotationAttr LoopMetadataConversion::convertProperties() {
if (!activeNodes.insert(node).second) {
emitWarning(loc) << "cannot import cyclic loop annotation";
return {};
}
llvm::scope_exit guard([&] { activeNodes.erase(node); });
if (failed(initConversionState()))
return {};
FailureOr<BoolAttr> disableNonForced =
lookupUnitNode("llvm.loop.disable_nonforced");
FailureOr<LoopVectorizeAttr> vecAttr = convertVectorizeAttr();
FailureOr<LoopInterleaveAttr> interleaveAttr = convertInterleaveAttr();
FailureOr<LoopUnrollAttr> unrollAttr = convertUnrollAttr();
FailureOr<LoopUnrollAndJamAttr> unrollAndJamAttr = convertUnrollAndJamAttr();
FailureOr<LoopLICMAttr> licmAttr = convertLICMAttr();
FailureOr<LoopDistributeAttr> distributeAttr = convertDistributeAttr();
FailureOr<LoopPipelineAttr> pipelineAttr = convertPipelineAttr();
FailureOr<LoopPeeledAttr> peeledAttr = convertPeeledAttr();
FailureOr<LoopUnswitchAttr> unswitchAttr = convertUnswitchAttr();
FailureOr<BoolAttr> mustProgress = lookupUnitNode("llvm.loop.mustprogress");
FailureOr<BoolAttr> isVectorized =
lookupIntNodeAsBoolAttr("llvm.loop.isvectorized");
FailureOr<SmallVector<AccessGroupAttr>> parallelAccesses =
convertParallelAccesses();
// Drop the metadata if there are parts that cannot be imported.
if (!propertyMap.empty()) {
for (auto name : propertyMap.keys())
emitWarning(loc) << "unknown loop annotation " << name;
return {};
}
FailureOr<FusedLoc> startLoc = convertStartLoc();
FailureOr<FusedLoc> endLoc = convertEndLoc();
return createIfNonNull<LoopAnnotationAttr>(
ctx, disableNonForced, vecAttr, interleaveAttr, unrollAttr,
unrollAndJamAttr, licmAttr, distributeAttr, pipelineAttr, peeledAttr,
unswitchAttr, mustProgress, isVectorized, startLoc, endLoc,
parallelAccesses);
}
LoopAnnotationAttr
LoopAnnotationImporter::translateLoopAnnotation(const llvm::MDNode *node,
Location loc) {
if (!node)
return {};
// Note: This check is necessary to distinguish between failed translations
// and not yet attempted translations.
auto it = loopMetadataMapping.find(node);
if (it != loopMetadataMapping.end())
return it->getSecond();
LoopAnnotationAttr attr = LoopMetadataConversion::convert(node, loc, *this);
mapLoopMetadata(node, attr);
return attr;
}
LogicalResult
LoopAnnotationImporter::translateAccessGroup(const llvm::MDNode *node,
Location loc) {
SmallVector<const llvm::MDNode *> accessGroups;
if (!node->getNumOperands())
accessGroups.push_back(node);
for (const llvm::MDOperand &operand : node->operands()) {
auto *childNode = dyn_cast<llvm::MDNode>(operand);
if (!childNode)
return failure();
accessGroups.push_back(cast<llvm::MDNode>(operand.get()));
}
// Convert all entries of the access group list to access group operations.
for (const llvm::MDNode *accessGroup : accessGroups) {
if (accessGroupMapping.count(accessGroup))
continue;
// Verify the access group node is distinct and empty.
if (accessGroup->getNumOperands() != 0 || !accessGroup->isDistinct())
return emitWarning(loc)
<< "expected an access group node to be empty and distinct";
// Add a mapping from the access group node to the newly created attribute.
accessGroupMapping[accessGroup] = builder.getAttr<AccessGroupAttr>();
}
return success();
}
FailureOr<SmallVector<AccessGroupAttr>>
LoopAnnotationImporter::lookupAccessGroupAttrs(const llvm::MDNode *node) const {
// An access group node is either a single access group or an access group
// list.
SmallVector<AccessGroupAttr> accessGroups;
if (!node->getNumOperands())
accessGroups.push_back(accessGroupMapping.lookup(node));
for (const llvm::MDOperand &operand : node->operands()) {
auto *node = cast<llvm::MDNode>(operand.get());
accessGroups.push_back(accessGroupMapping.lookup(node));
}
// Exit if one of the access group node lookups failed.
if (llvm::is_contained(accessGroups, nullptr))
return failure();
return accessGroups;
}