forked from OSchip/llvm-project
[mlir][ASM] Refactor how attribute/type aliases are specified.
Previously they were separated into "instance" and "kind" aliases, and also required that the dialect know ahead of time all of the instances that would have a corresponding alias. This approach was very clunky and not ergonomic to interact with. The new approach is to provide the dialect with an instance of an attribute/type to provide an alias for, fully replacing the original split approach. Differential Revision: https://reviews.llvm.org/D89354
This commit is contained in:
parent
1455259546
commit
a463ea50a4
|
@ -801,21 +801,17 @@ class OpAsmDialectInterface
|
|||
public:
|
||||
OpAsmDialectInterface(Dialect *dialect) : Base(dialect) {}
|
||||
|
||||
/// Hooks for getting identifier aliases for symbols. The identifier is used
|
||||
/// in place of the symbol when printing textual IR.
|
||||
///
|
||||
/// Hook for defining Attribute kind aliases. This will generate an alias for
|
||||
/// all attributes of the given kind in the form : <alias>[0-9]+. These
|
||||
/// aliases must not contain `.`.
|
||||
virtual void getAttributeKindAliases(
|
||||
SmallVectorImpl<std::pair<TypeID, StringRef>> &aliases) const {}
|
||||
/// Hook for defining Attribute aliases. These aliases must not contain `.` or
|
||||
/// end with a numeric digit([0-9]+).
|
||||
virtual void getAttributeAliases(
|
||||
SmallVectorImpl<std::pair<Attribute, StringRef>> &aliases) const {}
|
||||
/// Hook for defining Type aliases.
|
||||
virtual void
|
||||
getTypeAliases(SmallVectorImpl<std::pair<Type, StringRef>> &aliases) const {}
|
||||
/// Hooks for getting an alias identifier alias for a given symbol, that is
|
||||
/// not necessarily a part of this dialect. The identifier is used in place of
|
||||
/// the symbol when printing textual IR. These aliases must not contain `.` or
|
||||
/// end with a numeric digit([0-9]+). Returns success if an alias was
|
||||
/// provided, failure otherwise.
|
||||
virtual LogicalResult getAlias(Attribute attr, raw_ostream &os) const {
|
||||
return failure();
|
||||
}
|
||||
virtual LogicalResult getAlias(Type type, raw_ostream &os) const {
|
||||
return failure();
|
||||
}
|
||||
|
||||
/// Get a special name to use when printing the given operation. See
|
||||
/// OpAsmInterface.td#getAsmResultNames for usage details and documentation.
|
||||
|
|
|
@ -233,252 +233,287 @@ public:
|
|||
initialize(Operation *op,
|
||||
DialectInterfaceCollection<OpAsmDialectInterface> &interfaces);
|
||||
|
||||
/// Return a name used for an attribute alias, or empty if there is no alias.
|
||||
Twine getAttributeAlias(Attribute attr) const;
|
||||
/// Get an alias for the given attribute if it has one and print it in `os`.
|
||||
/// Returns success if an alias was printed, failure otherwise.
|
||||
LogicalResult getAlias(Attribute attr, raw_ostream &os) const;
|
||||
|
||||
/// Print all of the referenced attribute aliases.
|
||||
void printAttributeAliases(raw_ostream &os, NewLineCounter &newLine) const;
|
||||
|
||||
/// Return a string to use as an alias for the given type, or empty if there
|
||||
/// is no alias recorded.
|
||||
StringRef getTypeAlias(Type ty) const;
|
||||
/// Get an alias for the given type if it has one and print it in `os`.
|
||||
/// Returns success if an alias was printed, failure otherwise.
|
||||
LogicalResult getAlias(Type ty, raw_ostream &os) const;
|
||||
|
||||
/// Print all of the referenced type aliases.
|
||||
void printTypeAliases(raw_ostream &os, NewLineCounter &newLine) const;
|
||||
|
||||
private:
|
||||
/// A special index constant used for non-kind attribute aliases.
|
||||
enum { NonAttrKindAlias = -1 };
|
||||
/// This class represents a utility that initializes the set of attribute and
|
||||
/// type aliases, without the need to store the extra information within the
|
||||
/// main AliasState class or pass it around via function arguments.
|
||||
class AliasInitializer {
|
||||
public:
|
||||
AliasInitializer(
|
||||
DialectInterfaceCollection<OpAsmDialectInterface> &interfaces,
|
||||
llvm::BumpPtrAllocator &aliasAllocator)
|
||||
: interfaces(interfaces), aliasAllocator(aliasAllocator),
|
||||
aliasOS(aliasBuffer) {}
|
||||
|
||||
/// Record a reference to the given attribute.
|
||||
void recordAttributeReference(Attribute attr);
|
||||
void
|
||||
initialize(Operation *op,
|
||||
llvm::MapVector<Attribute, std::pair<StringRef, Optional<int>>>
|
||||
&attrToAlias,
|
||||
llvm::MapVector<Type, std::pair<StringRef, Optional<int>>>
|
||||
&typeToAlias);
|
||||
|
||||
/// Record a reference to the given type.
|
||||
void recordTypeReference(Type ty);
|
||||
private:
|
||||
void visit(Attribute attr);
|
||||
void visit(Type type);
|
||||
|
||||
// Visit functions.
|
||||
void visitOperation(Operation *op);
|
||||
void visitType(Type type);
|
||||
void visitAttribute(Attribute attr);
|
||||
/// Try to generate an alias for the provided symbol. If an alias is
|
||||
/// generated, the provided alias mapping and reverse mapping are updated.
|
||||
template <typename T>
|
||||
void
|
||||
generateAlias(T symbol,
|
||||
llvm::MapVector<StringRef, std::vector<T>> &aliasToSymbol);
|
||||
|
||||
/// Set of attributes known to be used within the module.
|
||||
llvm::SetVector<Attribute> usedAttributes;
|
||||
/// The set of asm interfaces within the context.
|
||||
DialectInterfaceCollection<OpAsmDialectInterface> &interfaces;
|
||||
|
||||
/// Mapping between an alias and the set of symbols mapped to it.
|
||||
llvm::MapVector<StringRef, std::vector<Attribute>> aliasToAttr;
|
||||
llvm::MapVector<StringRef, std::vector<Type>> aliasToType;
|
||||
|
||||
/// An allocator used for alias names.
|
||||
llvm::BumpPtrAllocator &aliasAllocator;
|
||||
|
||||
/// The set of visited attributes.
|
||||
DenseSet<Attribute> visitedAttributes;
|
||||
|
||||
/// The set of visited types.
|
||||
DenseSet<Type> visitedTypes;
|
||||
|
||||
/// Storage and stream used when generating an alias.
|
||||
SmallString<32> aliasBuffer;
|
||||
llvm::raw_svector_ostream aliasOS;
|
||||
};
|
||||
|
||||
/// Mapping between attribute and a pair comprised of a base alias name and a
|
||||
/// count suffix. If the suffix is set to -1, it is not displayed.
|
||||
llvm::MapVector<Attribute, std::pair<StringRef, int>> attrToAlias;
|
||||
/// count suffix. If the suffix is set to None, it is not displayed.
|
||||
llvm::MapVector<Attribute, std::pair<StringRef, Optional<int>>> attrToAlias;
|
||||
llvm::MapVector<Type, std::pair<StringRef, Optional<int>>> typeToAlias;
|
||||
|
||||
/// Mapping between attribute kind and a pair comprised of a base alias name
|
||||
/// and a unique list of attributes belonging to this kind sorted by location
|
||||
/// seen in the module.
|
||||
llvm::MapVector<TypeID, std::pair<StringRef, std::vector<Attribute>>>
|
||||
attrKindToAlias;
|
||||
|
||||
/// Set of types known to be used within the module.
|
||||
llvm::SetVector<Type> usedTypes;
|
||||
|
||||
/// A mapping between a type and a given alias.
|
||||
DenseMap<Type, StringRef> typeToAlias;
|
||||
/// An allocator used for alias names.
|
||||
llvm::BumpPtrAllocator aliasAllocator;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
// Utility to generate a function to register a symbol alias.
|
||||
static bool canRegisterAlias(StringRef name, llvm::StringSet<> &usedAliases) {
|
||||
assert(!name.empty() && "expected alias name to be non-empty");
|
||||
// TODO: Assert that the provided alias name can be lexed as
|
||||
// an identifier.
|
||||
/// Sanitize the given name such that it can be used as a valid identifier. If
|
||||
/// the string needs to be modified in any way, the provided buffer is used to
|
||||
/// store the new copy,
|
||||
static StringRef sanitizeIdentifier(StringRef name, SmallString<16> &buffer,
|
||||
StringRef allowedPunctChars = "$._-",
|
||||
bool allowTrailingDigit = true) {
|
||||
assert(!name.empty() && "Shouldn't have an empty name here");
|
||||
|
||||
// Check that the alias doesn't contain a '.' character and the name is not
|
||||
// already in use.
|
||||
return !name.contains('.') && usedAliases.insert(name).second;
|
||||
auto copyNameToBuffer = [&] {
|
||||
for (char ch : name) {
|
||||
if (llvm::isAlnum(ch) || allowedPunctChars.contains(ch))
|
||||
buffer.push_back(ch);
|
||||
else if (ch == ' ')
|
||||
buffer.push_back('_');
|
||||
else
|
||||
buffer.append(llvm::utohexstr((unsigned char)ch));
|
||||
}
|
||||
};
|
||||
|
||||
// Check to see if this name is valid. If it starts with a digit, then it
|
||||
// could conflict with the autogenerated numeric ID's, so add an underscore
|
||||
// prefix to avoid problems.
|
||||
if (isdigit(name[0])) {
|
||||
buffer.push_back('_');
|
||||
copyNameToBuffer();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// If the name ends with a trailing digit, add a '_' to avoid potential
|
||||
// conflicts with autogenerated ID's.
|
||||
if (!allowTrailingDigit && isdigit(name.back())) {
|
||||
copyNameToBuffer();
|
||||
buffer.push_back('_');
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Check to see that the name consists of only valid identifier characters.
|
||||
for (char ch : name) {
|
||||
if (!llvm::isAlnum(ch) && !allowedPunctChars.contains(ch)) {
|
||||
copyNameToBuffer();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no invalid characters, return the original name.
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Given a collection of aliases and symbols, initialize a mapping from a
|
||||
/// symbol to a given alias.
|
||||
template <typename T>
|
||||
static void initializeAliases(
|
||||
llvm::MapVector<StringRef, std::vector<T>> &aliasToSymbol,
|
||||
llvm::MapVector<T, std::pair<StringRef, Optional<int>>> &symbolToAlias) {
|
||||
std::vector<std::pair<StringRef, std::vector<T>>> aliases =
|
||||
aliasToSymbol.takeVector();
|
||||
llvm::array_pod_sort(aliases.begin(), aliases.end(),
|
||||
[](const auto *lhs, const auto *rhs) {
|
||||
return lhs->first.compare(rhs->first);
|
||||
});
|
||||
|
||||
for (auto &it : aliases) {
|
||||
// If there is only one instance for this alias, use the name directly.
|
||||
if (it.second.size() == 1) {
|
||||
symbolToAlias.insert({it.second.front(), {it.first, llvm::None}});
|
||||
continue;
|
||||
}
|
||||
// Otherwise, add the index to the name.
|
||||
for (auto &symbolIt : llvm::enumerate(it.second))
|
||||
symbolToAlias.insert({symbolIt.value(), {it.first, symbolIt.index()}});
|
||||
}
|
||||
}
|
||||
|
||||
void AliasState::AliasInitializer::initialize(
|
||||
Operation *op,
|
||||
llvm::MapVector<Attribute, std::pair<StringRef, Optional<int>>>
|
||||
&attrToAlias,
|
||||
llvm::MapVector<Type, std::pair<StringRef, Optional<int>>> &typeToAlias) {
|
||||
op->walk([&](Operation *op) {
|
||||
// Visit all the types used in the operation.
|
||||
for (auto type : op->getOperandTypes())
|
||||
visit(type);
|
||||
for (auto type : op->getResultTypes())
|
||||
visit(type);
|
||||
for (auto ®ion : op->getRegions())
|
||||
for (auto &block : region)
|
||||
for (auto arg : block.getArguments())
|
||||
visit(arg.getType());
|
||||
|
||||
// Visit each of the attributes.
|
||||
for (auto elt : op->getAttrs())
|
||||
visit(elt.second);
|
||||
});
|
||||
|
||||
// Initialize the aliases sorted by name.
|
||||
initializeAliases(aliasToAttr, attrToAlias);
|
||||
initializeAliases(aliasToType, typeToAlias);
|
||||
}
|
||||
|
||||
void AliasState::AliasInitializer::visit(Attribute attr) {
|
||||
if (!visitedAttributes.insert(attr).second)
|
||||
return;
|
||||
|
||||
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
|
||||
for (Attribute element : arrayAttr.getValue())
|
||||
visit(element);
|
||||
} else if (auto typeAttr = attr.dyn_cast<TypeAttr>()) {
|
||||
visit(typeAttr.getValue());
|
||||
}
|
||||
|
||||
// Try to generate an alias for this attribute.
|
||||
generateAlias(attr, aliasToAttr);
|
||||
}
|
||||
|
||||
void AliasState::AliasInitializer::visit(Type type) {
|
||||
if (!visitedTypes.insert(type).second)
|
||||
return;
|
||||
|
||||
// Visit several subtypes that contain types or atttributes.
|
||||
if (auto funcType = type.dyn_cast<FunctionType>()) {
|
||||
// Visit input and result types for functions.
|
||||
for (auto input : funcType.getInputs())
|
||||
visit(input);
|
||||
for (auto result : funcType.getResults())
|
||||
visit(result);
|
||||
} else if (auto shapedType = type.dyn_cast<ShapedType>()) {
|
||||
visit(shapedType.getElementType());
|
||||
|
||||
// Visit affine maps in memref type.
|
||||
if (auto memref = type.dyn_cast<MemRefType>())
|
||||
for (auto map : memref.getAffineMaps())
|
||||
visit(AffineMapAttr::get(map));
|
||||
}
|
||||
|
||||
// Try to generate an alias for this type.
|
||||
generateAlias(type, aliasToType);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AliasState::AliasInitializer::generateAlias(
|
||||
T symbol, llvm::MapVector<StringRef, std::vector<T>> &aliasToSymbol) {
|
||||
SmallString<16> tempBuffer;
|
||||
for (const auto &interface : interfaces) {
|
||||
interface.getAlias(symbol, aliasOS);
|
||||
StringRef name = aliasOS.str();
|
||||
if (name.empty())
|
||||
continue;
|
||||
name = sanitizeIdentifier(name, tempBuffer, /*allowedPunctChars=*/"$_-",
|
||||
/*allowTrailingDigit=*/false);
|
||||
name = name.copy(aliasAllocator);
|
||||
|
||||
aliasToSymbol[name].push_back(symbol);
|
||||
aliasBuffer.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AliasState::initialize(
|
||||
Operation *op,
|
||||
DialectInterfaceCollection<OpAsmDialectInterface> &interfaces) {
|
||||
// Track the identifiers in use for each symbol so that the same identifier
|
||||
// isn't used twice.
|
||||
llvm::StringSet<> usedAliases;
|
||||
|
||||
// Collect the set of aliases from each dialect.
|
||||
SmallVector<std::pair<TypeID, StringRef>, 8> attributeKindAliases;
|
||||
SmallVector<std::pair<Attribute, StringRef>, 8> attributeAliases;
|
||||
SmallVector<std::pair<Type, StringRef>, 16> typeAliases;
|
||||
|
||||
// AffineMap/Integer set have specific kind aliases.
|
||||
attributeKindAliases.emplace_back(AffineMapAttr::getTypeID(), "map");
|
||||
attributeKindAliases.emplace_back(IntegerSetAttr::getTypeID(), "set");
|
||||
|
||||
for (auto &interface : interfaces) {
|
||||
interface.getAttributeKindAliases(attributeKindAliases);
|
||||
interface.getAttributeAliases(attributeAliases);
|
||||
interface.getTypeAliases(typeAliases);
|
||||
}
|
||||
|
||||
// Setup the attribute kind aliases.
|
||||
StringRef alias;
|
||||
TypeID attrKind;
|
||||
for (auto &attrAliasPair : attributeKindAliases) {
|
||||
std::tie(attrKind, alias) = attrAliasPair;
|
||||
assert(!alias.empty() && "expected non-empty alias string");
|
||||
if (!usedAliases.count(alias) && !alias.contains('.'))
|
||||
attrKindToAlias.insert({attrKind, {alias, {}}});
|
||||
}
|
||||
|
||||
// Clear the set of used identifiers so that the attribute kind aliases are
|
||||
// just a prefix and not the full alias, i.e. there may be some overlap.
|
||||
usedAliases.clear();
|
||||
|
||||
// Register the attribute aliases.
|
||||
// Create a regex for the attribute kind alias names, these have a prefix with
|
||||
// a counter appended to the end. We prevent normal aliases from having these
|
||||
// names to avoid collisions.
|
||||
llvm::Regex reservedAttrNames("[0-9]+$");
|
||||
|
||||
// Attribute value aliases.
|
||||
Attribute attr;
|
||||
for (auto &attrAliasPair : attributeAliases) {
|
||||
std::tie(attr, alias) = attrAliasPair;
|
||||
if (!reservedAttrNames.match(alias) && canRegisterAlias(alias, usedAliases))
|
||||
attrToAlias.insert({attr, {alias, NonAttrKindAlias}});
|
||||
}
|
||||
|
||||
// Clear the set of used identifiers as types can have the same identifiers as
|
||||
// affine structures.
|
||||
usedAliases.clear();
|
||||
|
||||
// Type aliases.
|
||||
for (auto &typeAliasPair : typeAliases)
|
||||
if (canRegisterAlias(typeAliasPair.second, usedAliases))
|
||||
typeToAlias.insert(typeAliasPair);
|
||||
|
||||
// Traverse the given IR to generate the set of used attributes/types.
|
||||
op->walk([&](Operation *op) { visitOperation(op); });
|
||||
AliasInitializer initializer(interfaces, aliasAllocator);
|
||||
initializer.initialize(op, attrToAlias, typeToAlias);
|
||||
}
|
||||
|
||||
/// Return a name used for an attribute alias, or empty if there is no alias.
|
||||
Twine AliasState::getAttributeAlias(Attribute attr) const {
|
||||
auto alias = attrToAlias.find(attr);
|
||||
if (alias == attrToAlias.end())
|
||||
return Twine();
|
||||
|
||||
// Return the alias for this attribute, along with the index if this was
|
||||
// generated by a kind alias.
|
||||
int kindIndex = alias->second.second;
|
||||
return alias->second.first +
|
||||
(kindIndex == NonAttrKindAlias ? Twine() : Twine(kindIndex));
|
||||
static void printAlias(raw_ostream &os,
|
||||
const std::pair<StringRef, Optional<int>> &alias,
|
||||
char prefix) {
|
||||
os << prefix << alias.first;
|
||||
if (alias.second)
|
||||
os << *alias.second;
|
||||
}
|
||||
|
||||
LogicalResult AliasState::getAlias(Attribute attr, raw_ostream &os) const {
|
||||
auto it = attrToAlias.find(attr);
|
||||
if (it == attrToAlias.end())
|
||||
return failure();
|
||||
|
||||
printAlias(os, it->second, '#');
|
||||
return success();
|
||||
}
|
||||
|
||||
/// Print all of the referenced attribute aliases.
|
||||
void AliasState::printAttributeAliases(raw_ostream &os,
|
||||
NewLineCounter &newLine) const {
|
||||
auto printAlias = [&](StringRef alias, Attribute attr, int index) {
|
||||
os << '#' << alias;
|
||||
if (index != NonAttrKindAlias)
|
||||
os << index;
|
||||
os << " = " << attr << newLine;
|
||||
};
|
||||
|
||||
// Print all of the attribute kind aliases.
|
||||
for (auto &kindAlias : attrKindToAlias) {
|
||||
auto &aliasAttrsPair = kindAlias.second;
|
||||
for (unsigned i = 0, e = aliasAttrsPair.second.size(); i != e; ++i)
|
||||
printAlias(aliasAttrsPair.first, aliasAttrsPair.second[i], i);
|
||||
os << newLine;
|
||||
}
|
||||
|
||||
// In a second pass print all of the remaining attribute aliases that aren't
|
||||
// kind aliases.
|
||||
for (Attribute attr : usedAttributes) {
|
||||
auto alias = attrToAlias.find(attr);
|
||||
if (alias != attrToAlias.end() && alias->second.second == NonAttrKindAlias)
|
||||
printAlias(alias->second.first, attr, alias->second.second);
|
||||
for (const auto &it : attrToAlias) {
|
||||
printAlias(os, it.second, '#');
|
||||
os << " = " << it.first << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a string to use as an alias for the given type, or empty if there
|
||||
/// is no alias recorded.
|
||||
StringRef AliasState::getTypeAlias(Type ty) const {
|
||||
return typeToAlias.lookup(ty);
|
||||
LogicalResult AliasState::getAlias(Type ty, raw_ostream &os) const {
|
||||
auto it = typeToAlias.find(ty);
|
||||
if (it == typeToAlias.end())
|
||||
return failure();
|
||||
|
||||
printAlias(os, it->second, '!');
|
||||
return success();
|
||||
}
|
||||
|
||||
/// Print all of the referenced type aliases.
|
||||
void AliasState::printTypeAliases(raw_ostream &os,
|
||||
NewLineCounter &newLine) const {
|
||||
for (Type type : usedTypes) {
|
||||
auto alias = typeToAlias.find(type);
|
||||
if (alias != typeToAlias.end())
|
||||
os << '!' << alias->second << " = type " << type << newLine;
|
||||
for (const auto &it : typeToAlias) {
|
||||
printAlias(os, it.second, '!');
|
||||
os << " = " << it.first << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
/// Record a reference to the given attribute.
|
||||
void AliasState::recordAttributeReference(Attribute attr) {
|
||||
// Don't recheck attributes that have already been seen or those that
|
||||
// already have an alias.
|
||||
if (!usedAttributes.insert(attr) || attrToAlias.count(attr))
|
||||
return;
|
||||
|
||||
// If this attribute kind has an alias, then record one for this attribute.
|
||||
auto alias = attrKindToAlias.find(attr.getTypeID());
|
||||
if (alias == attrKindToAlias.end())
|
||||
return;
|
||||
std::pair<StringRef, int> attrAlias(alias->second.first,
|
||||
alias->second.second.size());
|
||||
attrToAlias.insert({attr, attrAlias});
|
||||
alias->second.second.push_back(attr);
|
||||
}
|
||||
|
||||
/// Record a reference to the given type.
|
||||
void AliasState::recordTypeReference(Type ty) { usedTypes.insert(ty); }
|
||||
|
||||
// TODO: Support visiting other types/operations when implemented.
|
||||
void AliasState::visitType(Type type) {
|
||||
recordTypeReference(type);
|
||||
|
||||
if (auto funcType = type.dyn_cast<FunctionType>()) {
|
||||
// Visit input and result types for functions.
|
||||
for (auto input : funcType.getInputs())
|
||||
visitType(input);
|
||||
for (auto result : funcType.getResults())
|
||||
visitType(result);
|
||||
} else if (auto shapedType = type.dyn_cast<ShapedType>()) {
|
||||
visitType(shapedType.getElementType());
|
||||
|
||||
// Visit affine maps in memref type.
|
||||
if (auto memref = type.dyn_cast<MemRefType>())
|
||||
for (auto map : memref.getAffineMaps())
|
||||
recordAttributeReference(AffineMapAttr::get(map));
|
||||
}
|
||||
}
|
||||
|
||||
void AliasState::visitAttribute(Attribute attr) {
|
||||
recordAttributeReference(attr);
|
||||
|
||||
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
|
||||
for (auto elt : arrayAttr.getValue())
|
||||
visitAttribute(elt);
|
||||
} else if (auto typeAttr = attr.dyn_cast<TypeAttr>()) {
|
||||
visitType(typeAttr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
void AliasState::visitOperation(Operation *op) {
|
||||
// Visit all the types used in the operation.
|
||||
for (auto type : op->getOperandTypes())
|
||||
visitType(type);
|
||||
for (auto type : op->getResultTypes())
|
||||
visitType(type);
|
||||
for (auto ®ion : op->getRegions())
|
||||
for (auto &block : region)
|
||||
for (auto arg : block.getArguments())
|
||||
visitType(arg.getType());
|
||||
|
||||
// Visit each of the attributes.
|
||||
for (auto elt : op->getAttrs())
|
||||
visitAttribute(elt.second);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SSANameState
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -798,42 +833,9 @@ void SSANameState::setValueName(Value value, StringRef name) {
|
|||
valueNames[value] = uniqueValueName(name);
|
||||
}
|
||||
|
||||
/// Returns true if 'c' is an allowable punctuation character: [$._-]
|
||||
/// Returns false otherwise.
|
||||
static bool isPunct(char c) {
|
||||
return c == '$' || c == '.' || c == '_' || c == '-';
|
||||
}
|
||||
|
||||
StringRef SSANameState::uniqueValueName(StringRef name) {
|
||||
assert(!name.empty() && "Shouldn't have an empty name here");
|
||||
|
||||
// Check to see if this name is valid. If it starts with a digit, then it
|
||||
// could conflict with the autogenerated numeric ID's (we unique them in a
|
||||
// different map), so add an underscore prefix to avoid problems.
|
||||
if (isdigit(name[0])) {
|
||||
SmallString<16> tmpName("_");
|
||||
tmpName += name;
|
||||
return uniqueValueName(tmpName);
|
||||
}
|
||||
|
||||
// Check to see if the name consists of all-valid identifiers. If not, we
|
||||
// need to escape them.
|
||||
for (char ch : name) {
|
||||
if (isalpha(ch) || isPunct(ch) || isdigit(ch))
|
||||
continue;
|
||||
|
||||
SmallString<16> tmpName;
|
||||
for (char ch : name) {
|
||||
if (isalpha(ch) || isPunct(ch) || isdigit(ch))
|
||||
tmpName += ch;
|
||||
else if (ch == ' ')
|
||||
tmpName += '_';
|
||||
else {
|
||||
tmpName += llvm::utohexstr((unsigned char)ch);
|
||||
}
|
||||
}
|
||||
return uniqueValueName(tmpName);
|
||||
}
|
||||
SmallString<16> tmpBuffer;
|
||||
name = sanitizeIdentifier(name, tmpBuffer);
|
||||
|
||||
// Check to see if this name is already unique.
|
||||
if (!usedNames.count(name)) {
|
||||
|
@ -845,12 +847,12 @@ StringRef SSANameState::uniqueValueName(StringRef name) {
|
|||
SmallString<64> probeName(name);
|
||||
probeName.push_back('_');
|
||||
while (true) {
|
||||
probeName.resize(name.size() + 1);
|
||||
probeName += llvm::utostr(nextConflictID++);
|
||||
if (!usedNames.count(probeName)) {
|
||||
name = StringRef(probeName).copy(usedNameAllocator);
|
||||
break;
|
||||
}
|
||||
probeName.resize(name.size() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1287,14 +1289,9 @@ void ModulePrinter::printAttribute(Attribute attr,
|
|||
return;
|
||||
}
|
||||
|
||||
// Check for an alias for this attribute.
|
||||
if (state) {
|
||||
Twine alias = state->getAliasState().getAttributeAlias(attr);
|
||||
if (!alias.isTriviallyEmpty()) {
|
||||
os << '#' << alias;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Try to print an alias for this attribute.
|
||||
if (state && succeeded(state->getAliasState().getAlias(attr, os)))
|
||||
return;
|
||||
|
||||
auto attrType = attr.getType();
|
||||
if (auto opaqueAttr = attr.dyn_cast<OpaqueAttr>()) {
|
||||
|
@ -1581,14 +1578,9 @@ void ModulePrinter::printType(Type type) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check for an alias for this type.
|
||||
if (state) {
|
||||
StringRef alias = state->getAliasState().getTypeAlias(type);
|
||||
if (!alias.empty()) {
|
||||
os << '!' << alias;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Try to print an alias for this type.
|
||||
if (state && succeeded(state->getAliasState().getAlias(type, os)))
|
||||
return;
|
||||
|
||||
TypeSwitch<Type>(type)
|
||||
.Case<OpaqueType>([&](OpaqueType opaqueTy) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mlir/IR/IntegerSet.h"
|
||||
#include "mlir/IR/Location.h"
|
||||
#include "mlir/IR/Module.h"
|
||||
#include "mlir/IR/OpImplementation.h"
|
||||
#include "mlir/IR/Types.h"
|
||||
#include "mlir/Support/ThreadLocalCache.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
@ -86,6 +87,22 @@ void mlir::registerMLIRContextCLOptions() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct BuiltinOpAsmDialectInterface : public OpAsmDialectInterface {
|
||||
using OpAsmDialectInterface::OpAsmDialectInterface;
|
||||
|
||||
LogicalResult getAlias(Attribute attr, raw_ostream &os) const override {
|
||||
if (attr.isa<AffineMapAttr>()) {
|
||||
os << "map";
|
||||
return success();
|
||||
}
|
||||
if (attr.isa<IntegerSetAttr>()) {
|
||||
os << "set";
|
||||
return success();
|
||||
}
|
||||
return failure();
|
||||
}
|
||||
};
|
||||
|
||||
/// A builtin dialect to define types/etc that are necessary for the validity of
|
||||
/// the IR.
|
||||
struct BuiltinDialect : public Dialect {
|
||||
|
@ -102,6 +119,7 @@ struct BuiltinDialect : public Dialect {
|
|||
UnitAttr>();
|
||||
addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, OpaqueLoc,
|
||||
UnknownLoc>();
|
||||
addInterfaces<BuiltinOpAsmDialectInterface>();
|
||||
|
||||
// TODO: These operations should be moved to a different dialect when they
|
||||
// have been fully decoupled from the core.
|
||||
|
|
|
@ -107,7 +107,7 @@ func @invariant_code_inside_affine_if() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: %1 = affine.apply #map{{[0-9]+}}(%arg0)
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %1) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %1) {
|
||||
// CHECK-NEXT: %2 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %2, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
|
@ -252,7 +252,7 @@ func @invariant_affine_if() {
|
|||
// CHECK: %0 = alloc() : memref<10xf32>
|
||||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
|
@ -280,7 +280,7 @@ func @invariant_affine_if2() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.for %arg1 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg1] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
|
@ -311,10 +311,10 @@ func @invariant_affine_nested_if() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.for %arg1 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg1] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
|
@ -347,10 +347,10 @@ func @invariant_affine_nested_if_else() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.for %arg1 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: } else {
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg1] : memref<10xf32>
|
||||
|
@ -386,10 +386,10 @@ func @invariant_affine_nested_if_else2() {
|
|||
// CHECK-NEXT: %1 = alloc() : memref<10xf32>
|
||||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %2 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: %3 = affine.load %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.store %2, %1[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: } else {
|
||||
// CHECK-NEXT: %4 = affine.load %0[%arg0] : memref<10xf32>
|
||||
|
@ -420,10 +420,10 @@ func @invariant_affine_nested_if2() {
|
|||
// CHECK: %0 = alloc() : memref<10xf32>
|
||||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: %2 = affine.load %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %3 = affine.load %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
|
@ -453,7 +453,7 @@ func @invariant_affine_for_inside_affine_if() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.for %arg1 = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %arg0) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %arg0) {
|
||||
// CHECK-NEXT: %1 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %1, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: affine.for %arg2 = 0 to 10 {
|
||||
|
|
|
@ -423,7 +423,7 @@ func @fold_empty_loop() {
|
|||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: [[$SET:#set[0-9]+]] = affine_set<(d0, d1)[s0] : (d0 >= 0, -d0 + 1022 >= 0, d1 >= 0, -d1 + s0 - 2 >= 0)>
|
||||
// CHECK-DAG: [[$SET:#set[0-9]*]] = affine_set<(d0, d1)[s0] : (d0 >= 0, -d0 + 1022 >= 0, d1 >= 0, -d1 + s0 - 2 >= 0)>
|
||||
|
||||
// CHECK-LABEL: func @canonicalize_affine_if
|
||||
// CHECK-SAME: [[M:%.*]]: index,
|
||||
|
|
|
@ -208,7 +208,7 @@ func @yield_loop(%buffer: memref<1024xf32>) -> f32 {
|
|||
}
|
||||
// CHECK: %[[const_0:.*]] = constant 0.000000e+00 : f32
|
||||
// CHECK-NEXT: %[[output:.*]] = affine.for %{{.*}} = 0 to 10 step 2 iter_args(%{{.*}} = %[[const_0]]) -> (f32) {
|
||||
// CHECK: affine.if #set0(%{{.*}}) -> f32 {
|
||||
// CHECK: affine.if #set(%{{.*}}) -> f32 {
|
||||
// CHECK: affine.yield %{{.*}} : f32
|
||||
// CHECK-NEXT: } else {
|
||||
// CHECK-NEXT: affine.yield %{{.*}} : f32
|
||||
|
|
|
@ -23,7 +23,7 @@ func @for(%outer: index, %A: memref<?xf32>, %B: memref<?xf32>,
|
|||
// CHECK: [[CST_0:%.*]] = constant 0 : index
|
||||
// CHECK: [[CST_1:%.*]] = constant 1 : index
|
||||
// CHECK: [[DIM_0:%.*]] = dim [[ARG1]], [[CST_0]] : memref<?xf32>
|
||||
// CHECK: [[MIN:%.*]] = affine.min #map0(){{\[}}[[DIM_0]], [[ARG0]]]
|
||||
// CHECK: [[MIN:%.*]] = affine.min #map(){{\[}}[[DIM_0]], [[ARG0]]]
|
||||
// CHECK: [[CST_1024:%.*]] = constant 1024 : index
|
||||
// CHECK: [[PRED:%.*]] = cmpi "eq", [[MIN]], [[CST_1024]] : index
|
||||
// CHECK: scf.if [[PRED]] {
|
||||
|
|
|
@ -13,7 +13,7 @@ func @parallel_loop(%arg0 : index, %arg1 : index, %arg2 : index,
|
|||
return
|
||||
}
|
||||
|
||||
// CHECK: #map0 = affine_map<(d0, d1, d2) -> (d0, d1 - d2)>
|
||||
// CHECK: #map = affine_map<(d0, d1, d2) -> (d0, d1 - d2)>
|
||||
// CHECK-LABEL: func @parallel_loop(
|
||||
// CHECK-SAME: [[ARG1:%.*]]: index, [[ARG2:%.*]]: index, [[ARG3:%.*]]: index, [[ARG4:%.*]]: index, [[ARG5:%.*]]: index, [[ARG6:%.*]]: index, [[ARG7:%.*]]: memref<?x?xf32>, [[ARG8:%.*]]: memref<?x?xf32>, [[ARG9:%.*]]: memref<?x?xf32>, [[ARG10:%.*]]: memref<?x?xf32>) {
|
||||
// CHECK: [[C0:%.*]] = constant 0 : index
|
||||
|
@ -22,8 +22,8 @@ func @parallel_loop(%arg0 : index, %arg1 : index, %arg2 : index,
|
|||
// CHECK: [[V1:%.*]] = muli [[ARG5]], [[C1]] : index
|
||||
// CHECK: [[V2:%.*]] = muli [[ARG6]], [[C4]] : index
|
||||
// CHECK: scf.parallel ([[V3:%.*]], [[V4:%.*]]) = ([[ARG1]], [[ARG2]]) to ([[ARG3]], [[ARG4]]) step ([[V1]], [[V2]]) {
|
||||
// CHECK: [[V5:%.*]] = affine.min #map0([[V1]], [[ARG3]], [[V3]])
|
||||
// CHECK: [[V6:%.*]] = affine.min #map0([[V2]], [[ARG4]], [[V4]])
|
||||
// CHECK: [[V5:%.*]] = affine.min #map([[V1]], [[ARG3]], [[V3]])
|
||||
// CHECK: [[V6:%.*]] = affine.min #map([[V2]], [[ARG4]], [[V4]])
|
||||
// CHECK: scf.parallel ([[V7:%.*]], [[V8:%.*]]) = ([[C0]], [[C0]]) to ([[V5]], [[V6]]) step ([[ARG5]], [[ARG6]]) {
|
||||
// CHECK: [[V9:%.*]] = addi [[V7]], [[V3]] : index
|
||||
// CHECK: [[V10:%.*]] = addi [[V8]], [[V4]] : index
|
||||
|
@ -91,7 +91,7 @@ func @tile_nested_innermost() {
|
|||
// CHECK: [[V3:%.*]] = muli [[C1]], [[C1_1]] : index
|
||||
// CHECK: [[V4:%.*]] = muli [[C1]], [[C4]] : index
|
||||
// CHECK: scf.parallel ([[V5:%.*]], [[V6:%.*]]) = ([[C0]], [[C0]]) to ([[C2]], [[C2]]) step ([[V3]], [[V4]]) {
|
||||
// CHECK: [[V7:%.*]] = affine.min #map0([[V4]], [[C2]], [[V6]])
|
||||
// CHECK: [[V7:%.*]] = affine.min #map([[V4]], [[C2]], [[V6]])
|
||||
// CHECK: scf.parallel ([[V8:%.*]], [[V9:%.*]]) = ([[C0_1]], [[C0_1]]) to ([[V3]], [[V7]]) step ([[C1]], [[C1]]) {
|
||||
// CHECK: = addi [[V8]], [[V5]] : index
|
||||
// CHECK: = addi [[V9]], [[V6]] : index
|
||||
|
@ -104,7 +104,7 @@ func @tile_nested_innermost() {
|
|||
// CHECK: [[V10:%.*]] = muli [[C1]], [[C1_2]] : index
|
||||
// CHECK: [[V11:%.*]] = muli [[C1]], [[C4_1]] : index
|
||||
// CHECK: scf.parallel ([[V12:%.*]], [[V13:%.*]]) = ([[C0]], [[C0]]) to ([[C2]], [[C2]]) step ([[V10]], [[V11]]) {
|
||||
// CHECK: [[V14:%.*]] = affine.min #map0([[V11]], [[C2]], [[V13]])
|
||||
// CHECK: [[V14:%.*]] = affine.min #map([[V11]], [[C2]], [[V13]])
|
||||
// CHECK: scf.parallel ([[V15:%.*]], [[V16:%.*]]) = ([[C0_2]], [[C0_2]]) to ([[V10]], [[V14]]) step ([[C1]], [[C1]]) {
|
||||
// CHECK: = addi [[V15]], [[V12]] : index
|
||||
// CHECK: = addi [[V16]], [[V13]] : index
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: mlir-opt %s | FileCheck %s
|
||||
|
||||
// CHECK: #map0 = affine_map<(d0, d1)[s0] -> (d0 + s0, d1)>
|
||||
// CHECK: #map = affine_map<(d0, d1)[s0] -> (d0 + s0, d1)>
|
||||
|
||||
// CHECK-LABEL: func @alloc() {
|
||||
func @alloc() {
|
||||
|
@ -17,11 +17,11 @@ func @alloc() {
|
|||
%1 = alloc(%c0, %c1) : memref<?x?xf32, affine_map<(d0, d1) -> (d0, d1)>, 1>
|
||||
|
||||
// Test alloc with no dynamic dimensions and one symbol.
|
||||
// CHECK: %2 = alloc()[%c0] : memref<2x4xf32, #map0, 1>
|
||||
// CHECK: %2 = alloc()[%c0] : memref<2x4xf32, #map, 1>
|
||||
%2 = alloc()[%c0] : memref<2x4xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
|
||||
|
||||
// Test alloc with dynamic dimensions and one symbol.
|
||||
// CHECK: %3 = alloc(%c1)[%c0] : memref<2x?xf32, #map0, 1>
|
||||
// CHECK: %3 = alloc(%c1)[%c0] : memref<2x?xf32, #map, 1>
|
||||
%3 = alloc(%c1)[%c0] : memref<2x?xf32, affine_map<(d0, d1)[s0] -> (d0 + s0, d1)>, 1>
|
||||
|
||||
// Alloc with no mappings.
|
||||
|
@ -48,11 +48,11 @@ func @alloca() {
|
|||
%1 = alloca(%c0, %c1) : memref<?x?xf32, affine_map<(d0, d1) -> (d0, d1)>, 1>
|
||||
|
||||
// Test alloca with no dynamic dimensions and one symbol.
|
||||
// CHECK: %2 = alloca()[%c0] : memref<2x4xf32, #map0, 1>
|
||||
// CHECK: %2 = alloca()[%c0] : memref<2x4xf32, #map, 1>
|
||||
%2 = alloca()[%c0] : memref<2x4xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
|
||||
|
||||
// Test alloca with dynamic dimensions and one symbol.
|
||||
// CHECK: %3 = alloca(%c1)[%c0] : memref<2x?xf32, #map0, 1>
|
||||
// CHECK: %3 = alloca(%c1)[%c0] : memref<2x?xf32, #map, 1>
|
||||
%3 = alloca(%c1)[%c0] : memref<2x?xf32, affine_map<(d0, d1)[s0] -> (d0 + s0, d1)>, 1>
|
||||
|
||||
// Alloca with no mappings, but with alignment.
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// RUN: mlir-opt %s | FileCheck %s
|
||||
|
||||
|
||||
// CHECK-DAG: #test2Ealias = "alias_test:dot_in_name"
|
||||
"test.op"() {alias_test = "alias_test:dot_in_name"} : () -> ()
|
||||
|
||||
// CHECK-DAG: #test_alias0_ = "alias_test:trailing_digit"
|
||||
"test.op"() {alias_test = "alias_test:trailing_digit"} : () -> ()
|
||||
|
||||
// CHECK-DAG: #_0_test_alias = "alias_test:prefixed_digit"
|
||||
"test.op"() {alias_test = "alias_test:prefixed_digit"} : () -> ()
|
||||
|
||||
// CHECK-DAG: #test_alias_conflict0_0 = "alias_test:sanitize_conflict_a"
|
||||
// CHECK-DAG: #test_alias_conflict0_1 = "alias_test:sanitize_conflict_b"
|
||||
"test.op"() {alias_test = ["alias_test:sanitize_conflict_a", "alias_test:sanitize_conflict_b"]} : () -> ()
|
|
@ -462,7 +462,7 @@ func @should_not_fuse_if_inst_in_loop_nest() {
|
|||
// CHECK-NEXT: affine.store %{{.*}}, %{{.*}}[%{{.*}}] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
// CHECK: affine.for %{{.*}} = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%{{.*}}) {
|
||||
// CHECK-NEXT: affine.if #set(%{{.*}}) {
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: affine.load %{{.*}}[%{{.*}}] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
|
|
|
@ -88,7 +88,7 @@ func @invariant_code_inside_affine_if() {
|
|||
// CHECK-NEXT: %cst = constant 8.000000e+00 : f32
|
||||
// CHECK-NEXT: affine.for %arg0 = 0 to 10 {
|
||||
// CHECK-NEXT: %1 = affine.apply #map0(%arg0)
|
||||
// CHECK-NEXT: affine.if #set0(%arg0, %1) {
|
||||
// CHECK-NEXT: affine.if #set(%arg0, %1) {
|
||||
// CHECK-NEXT: %2 = addf %cst, %cst : f32
|
||||
// CHECK-NEXT: affine.store %2, %0[%arg0] : memref<10xf32>
|
||||
// CHECK-NEXT: }
|
||||
|
@ -115,7 +115,7 @@ func @invariant_affine_if() {
|
|||
// CHECK-NEXT: affine.for %[[ARG:.*]] = 0 to 10 {
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: affine.for %[[ARG:.*]] = 0 to 10 {
|
||||
// CHECK-NEXT: affine.if #set0(%[[ARG]], %[[ARG]]) {
|
||||
// CHECK-NEXT: affine.if #set(%[[ARG]], %[[ARG]]) {
|
||||
// CHECK-NEXT: addf %[[CST]], %[[CST]] : f32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
|
|
@ -35,6 +35,30 @@ namespace {
|
|||
struct TestOpAsmInterface : public OpAsmDialectInterface {
|
||||
using OpAsmDialectInterface::OpAsmDialectInterface;
|
||||
|
||||
LogicalResult getAlias(Attribute attr, raw_ostream &os) const final {
|
||||
StringAttr strAttr = attr.dyn_cast<StringAttr>();
|
||||
if (!strAttr)
|
||||
return failure();
|
||||
|
||||
// Check the contents of the string attribute to see what the test alias
|
||||
// should be named.
|
||||
Optional<StringRef> aliasName =
|
||||
StringSwitch<Optional<StringRef>>(strAttr.getValue())
|
||||
.Case("alias_test:dot_in_name", StringRef("test.alias"))
|
||||
.Case("alias_test:trailing_digit", StringRef("test_alias0"))
|
||||
.Case("alias_test:prefixed_digit", StringRef("0_test_alias"))
|
||||
.Case("alias_test:sanitize_conflict_a",
|
||||
StringRef("test_alias_conflict0"))
|
||||
.Case("alias_test:sanitize_conflict_b",
|
||||
StringRef("test_alias_conflict0_"))
|
||||
.Default(llvm::None);
|
||||
if (!aliasName)
|
||||
return failure();
|
||||
|
||||
os << *aliasName;
|
||||
return success();
|
||||
}
|
||||
|
||||
void getAsmResultNames(Operation *op,
|
||||
OpAsmSetValueNameFn setNameFn) const final {
|
||||
if (auto asmOp = dyn_cast<AsmDialectInterfaceOp>(op))
|
||||
|
|
Loading…
Reference in New Issue