blob: cc086abd1b86d39fe888a9d1e8a3ecccbac8cb18 [file] [log] [blame]
//===- FunctionSupport.h - Utility types for function-like ops --*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines support types for Operations that represent function-like
// constructs to use.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_FUNCTIONSUPPORT_H
#define MLIR_IR_FUNCTIONSUPPORT_H
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/OpDefinition.h"
#include "llvm/ADT/SmallString.h"
namespace mlir {
namespace function_like_impl {
/// Return the name of the attribute used for function types.
inline StringRef getTypeAttrName() { return "type"; }
/// Return the name of the attribute used for function argument attributes.
inline StringRef getArgDictAttrName() { return "arg_attrs"; }
/// Return the name of the attribute used for function argument attributes.
inline StringRef getResultDictAttrName() { return "res_attrs"; }
/// Returns the dictionary attribute corresponding to the argument at 'index'.
/// If there are no argument attributes at 'index', a null attribute is
/// returned.
DictionaryAttr getArgAttrDict(Operation *op, unsigned index);
/// Returns the dictionary attribute corresponding to the result at 'index'.
/// If there are no result attributes at 'index', a null attribute is
/// returned.
DictionaryAttr getResultAttrDict(Operation *op, unsigned index);
namespace detail {
/// Update the given index into an argument or result attribute dictionary.
void setArgResAttrDict(Operation *op, StringRef attrName,
unsigned numTotalIndices, unsigned index,
DictionaryAttr attrs);
} // namespace detail
/// Set all of the argument or result attribute dictionaries for a function. The
/// size of `attrs` is expected to match the number of arguments/results of the
/// given `op`.
void setAllArgAttrDicts(Operation *op, ArrayRef<DictionaryAttr> attrs);
void setAllArgAttrDicts(Operation *op, ArrayRef<Attribute> attrs);
void setAllResultAttrDicts(Operation *op, ArrayRef<DictionaryAttr> attrs);
void setAllResultAttrDicts(Operation *op, ArrayRef<Attribute> attrs);
/// Return all of the attributes for the argument at 'index'.
inline ArrayRef<NamedAttribute> getArgAttrs(Operation *op, unsigned index) {
auto argDict = getArgAttrDict(op, index);
return argDict ? argDict.getValue() : llvm::None;
}
/// Return all of the attributes for the result at 'index'.
inline ArrayRef<NamedAttribute> getResultAttrs(Operation *op, unsigned index) {
auto resultDict = getResultAttrDict(op, index);
return resultDict ? resultDict.getValue() : llvm::None;
}
/// Insert the specified arguments and update the function type attribute.
void insertFunctionArguments(Operation *op, ArrayRef<unsigned> argIndices,
TypeRange argTypes,
ArrayRef<DictionaryAttr> argAttrs,
ArrayRef<Optional<Location>> argLocs,
unsigned originalNumArgs, Type newType);
/// Insert the specified results and update the function type attribute.
void insertFunctionResults(Operation *op, ArrayRef<unsigned> resultIndices,
TypeRange resultTypes,
ArrayRef<DictionaryAttr> resultAttrs,
unsigned originalNumResults, Type newType);
/// Erase the specified arguments and update the function type attribute.
void eraseFunctionArguments(Operation *op, ArrayRef<unsigned> argIndices,
unsigned originalNumArgs, Type newType);
/// Erase the specified results and update the function type attribute.
void eraseFunctionResults(Operation *op, ArrayRef<unsigned> resultIndices,
unsigned originalNumResults, Type newType);
/// Get and set a FunctionLike operation's type signature.
FunctionType getFunctionType(Operation *op);
void setFunctionType(Operation *op, FunctionType newType);
/// Get a FunctionLike operation's body.
Region &getFunctionBody(Operation *op);
} // namespace function_like_impl
namespace OpTrait {
/// This trait provides APIs for Ops that behave like functions. In particular:
/// - Ops must be symbols, i.e. also have the `Symbol` trait;
/// - Ops have a single region with multiple blocks that corresponds to the body
/// of the function;
/// - An op with a single empty region corresponds to an external function;
/// - leading arguments of the first block of the region are treated as function
/// arguments;
/// - they can have argument attributes that are stored in a dictionary
/// attribute on the Op itself.
///
/// This trait provides limited type support for the declared or defined
/// functions. The convenience function `getTypeAttrName()` returns the name of
/// an attribute that can be used to store the function type. In addition, this
/// trait provides `getType` and `setType` helpers to store a `FunctionType` in
/// the attribute named by `getTypeAttrName()`.
///
/// In general, this trait assumes concrete ops use `FunctionType` under the
/// hood. If this is not the case, in order to use the function type support,
/// concrete ops must define the following methods, using the same name, to hide
/// the ones defined for `FunctionType`: `addBodyBlock`, `getType`,
/// `getTypeWithoutArgsAndResults` and `setType`.
///
/// Besides the requirements above, concrete ops must interact with this trait
/// using the following functions:
/// - Concrete ops *must* define a member function `getNumFuncArguments()` that
/// returns the number of function arguments based exclusively on type (so
/// that it can be called on function declarations).
/// - Concrete ops *must* define a member function `getNumFuncResults()` that
/// returns the number of function results based exclusively on type (so that
/// it can be called on function declarations).
/// - To verify that the type respects op-specific invariants, concrete ops may
/// redefine the `verifyType()` hook that will be called after verifying the
/// presence of the `type` attribute and before any call to
/// `getNumFuncArguments`/`getNumFuncResults` from the verifier.
/// - To verify that the body respects op-specific invariants, concrete ops may
/// redefine the `verifyBody()` hook that will be called after verifying the
/// function type and the presence of the (potentially empty) body region.
template <typename ConcreteType>
class FunctionLike : public OpTrait::TraitBase<ConcreteType, FunctionLike> {
public:
/// Verify that all of the argument attributes are dialect attributes.
static LogicalResult verifyTrait(Operation *op);
//===--------------------------------------------------------------------===//
// Body Handling
//===--------------------------------------------------------------------===//
/// Returns true if this function is external, i.e. it has no body.
bool isExternal() { return empty(); }
Region &getBody() {
return function_like_impl::getFunctionBody(this->getOperation());
}
/// Delete all blocks from this function.
void eraseBody() {
getBody().dropAllReferences();
getBody().getBlocks().clear();
}
/// This is the list of blocks in the function.
using BlockListType = Region::BlockListType;
BlockListType &getBlocks() { return getBody().getBlocks(); }
// Iteration over the block in the function.
using iterator = BlockListType::iterator;
using reverse_iterator = BlockListType::reverse_iterator;
iterator begin() { return getBody().begin(); }
iterator end() { return getBody().end(); }
reverse_iterator rbegin() { return getBody().rbegin(); }
reverse_iterator rend() { return getBody().rend(); }
bool empty() { return getBody().empty(); }
void push_back(Block *block) { getBody().push_back(block); }
void push_front(Block *block) { getBody().push_front(block); }
Block &back() { return getBody().back(); }
Block &front() { return getBody().front(); }
/// Add an entry block to an empty function, and set up the block arguments
/// to match the signature of the function. The newly inserted entry block
/// is returned.
///
/// Note that the concrete class must define a method with the same name to
/// hide this one if the concrete class does not use FunctionType for the
/// function type under the hood.
Block *addEntryBlock();
/// Add a normal block to the end of the function's block list. The function
/// should at least already have an entry block.
Block *addBlock();
/// Hook for concrete ops to verify the contents of the body. Called as a
/// part of trait verification, after type verification and ensuring that a
/// region exists.
LogicalResult verifyBody();
//===--------------------------------------------------------------------===//
// Type Attribute Handling
//===--------------------------------------------------------------------===//
/// Return the name of the attribute used for function types.
static StringRef getTypeAttrName() {
return function_like_impl::getTypeAttrName();
}
TypeAttr getTypeAttr() {
return this->getOperation()->template getAttrOfType<TypeAttr>(
getTypeAttrName());
}
/// Return the type of this function.
///
/// Note that the concrete class must define a method with the same name to
/// hide this one if the concrete class does not use FunctionType for the
/// function type under the hood.
FunctionType getType() {
return function_like_impl::getFunctionType(this->getOperation());
}
/// Return the type of this function with the specified arguments and results
/// inserted. This is used to update the function's signature in the
/// `insertArguments` and `insertResults` methods. The arrays must be sorted
/// by increasing index.
///
/// Note that the concrete class must define a method with the same name to
/// hide this one if the concrete class does not use FunctionType for the
/// function type under the hood.
FunctionType getTypeWithArgsAndResults(ArrayRef<unsigned> argIndices,
TypeRange argTypes,
ArrayRef<unsigned> resultIndices,
TypeRange resultTypes) {
return getType().getWithArgsAndResults(argIndices, argTypes, resultIndices,
resultTypes);
}
/// Return the type of this function without the specified arguments and
/// results. This is used to update the function's signature in the
/// `eraseArguments` and `eraseResults` methods. The arrays of indices are
/// allowed to have duplicates and can be in any order.
///
/// Note that the concrete class must define a method with the same name to
/// hide this one if the concrete class does not use FunctionType for the
/// function type under the hood.
FunctionType getTypeWithoutArgsAndResults(ArrayRef<unsigned> argIndices,
ArrayRef<unsigned> resultIndices) {
return getType().getWithoutArgsAndResults(argIndices, resultIndices);
}
bool isTypeAttrValid() {
auto typeAttr = getTypeAttr();
if (!typeAttr)
return false;
return typeAttr.getValue() != Type{};
}
/// Change the type of this function in place. This is an extremely dangerous
/// operation and it is up to the caller to ensure that this is legal for this
/// function, and to restore invariants:
/// - the entry block args must be updated to match the function params.
/// - the argument/result attributes may need an update: if the new type
/// has less parameters we drop the extra attributes, if there are more
/// parameters they won't have any attributes.
///
/// Note that the concrete class must define a method with the same name to
/// hide this one if the concrete class does not use FunctionType for the
/// function type under the hood.
void setType(FunctionType newType);
//===--------------------------------------------------------------------===//
// Argument and Result Handling
//===--------------------------------------------------------------------===//
using BlockArgListType = Region::BlockArgListType;
unsigned getNumArguments() {
return static_cast<ConcreteType *>(this)->getNumFuncArguments();
}
unsigned getNumResults() {
return static_cast<ConcreteType *>(this)->getNumFuncResults();
}
/// Gets argument.
BlockArgument getArgument(unsigned idx) { return getBody().getArgument(idx); }
/// Support argument iteration.
using args_iterator = Region::args_iterator;
args_iterator args_begin() { return getBody().args_begin(); }
args_iterator args_end() { return getBody().args_end(); }
Block::BlockArgListType getArguments() { return getBody().getArguments(); }
ValueTypeRange<BlockArgListType> getArgumentTypes() {
return getBody().getArgumentTypes();
}
/// Insert a single argument of type `argType` with attributes `argAttrs` and
/// location `argLoc` at `argIndex`.
void insertArgument(unsigned argIndex, Type argType, DictionaryAttr argAttrs,
Optional<Location> argLoc = {}) {
insertArguments({argIndex}, {argType}, {argAttrs}, {argLoc});
}
/// Inserts arguments with the listed types, attributes, and locations at the
/// listed indices. `argIndices` must be sorted. Arguments are inserted in the
/// order they are listed, such that arguments with identical index will
/// appear in the same order that they were listed here.
void insertArguments(ArrayRef<unsigned> argIndices, TypeRange argTypes,
ArrayRef<DictionaryAttr> argAttrs,
ArrayRef<Optional<Location>> argLocs) {
unsigned originalNumArgs = getNumArguments();
Type newType = getTypeWithArgsAndResults(
argIndices, argTypes, /*resultIndices=*/{}, /*resultTypes=*/{});
function_like_impl::insertFunctionArguments(
this->getOperation(), argIndices, argTypes, argAttrs, argLocs,
originalNumArgs, newType);
}
/// Insert a single result of type `resultType` at `resultIndex`.
void insertResult(unsigned resultIndex, Type resultType,
DictionaryAttr resultAttrs) {
insertResults({resultIndex}, {resultType}, {resultAttrs});
}
/// Inserts results with the listed types at the listed indices.
/// `resultIndices` must be sorted. Results are inserted in the order they are
/// listed, such that results with identical index will appear in the same
/// order that they were listed here.
void insertResults(ArrayRef<unsigned> resultIndices, TypeRange resultTypes,
ArrayRef<DictionaryAttr> resultAttrs) {
unsigned originalNumResults = getNumResults();
Type newType = getTypeWithArgsAndResults(/*argIndices=*/{}, /*argTypes=*/{},
resultIndices, resultTypes);
function_like_impl::insertFunctionResults(
this->getOperation(), resultIndices, resultTypes, resultAttrs,
originalNumResults, newType);
}
/// Erase a single argument at `argIndex`.
void eraseArgument(unsigned argIndex) { eraseArguments({argIndex}); }
/// Erases the arguments listed in `argIndices`.
/// `argIndices` is allowed to have duplicates and can be in any order.
void eraseArguments(ArrayRef<unsigned> argIndices) {
unsigned originalNumArgs = getNumArguments();
Type newType = getTypeWithoutArgsAndResults(argIndices, {});
function_like_impl::eraseFunctionArguments(this->getOperation(), argIndices,
originalNumArgs, newType);
}
/// Erase a single result at `resultIndex`.
void eraseResult(unsigned resultIndex) { eraseResults({resultIndex}); }
/// Erases the results listed in `resultIndices`.
/// `resultIndices` is allowed to have duplicates and can be in any order.
void eraseResults(ArrayRef<unsigned> resultIndices) {
unsigned originalNumResults = getNumResults();
Type newType = getTypeWithoutArgsAndResults({}, resultIndices);
function_like_impl::eraseFunctionResults(
this->getOperation(), resultIndices, originalNumResults, newType);
}
//===--------------------------------------------------------------------===//
// Argument Attributes
//===--------------------------------------------------------------------===//
/// FunctionLike operations allow for attaching attributes to each of the
/// respective function arguments. These argument attributes are stored as
/// DictionaryAttrs in the main operation attribute dictionary. The name of
/// these entries is `arg` followed by the index of the argument. These
/// argument attribute dictionaries are optional, and will generally only
/// exist if they are non-empty.
/// Return all of the attributes for the argument at 'index'.
ArrayRef<NamedAttribute> getArgAttrs(unsigned index) {
return function_like_impl::getArgAttrs(this->getOperation(), index);
}
/// Return an ArrayAttr containing all argument attribute dictionaries of this
/// function, or nullptr if no arguments have attributes.
ArrayAttr getAllArgAttrs() {
return this->getOperation()->template getAttrOfType<ArrayAttr>(
function_like_impl::getArgDictAttrName());
}
/// Return all argument attributes of this function.
void getAllArgAttrs(SmallVectorImpl<DictionaryAttr> &result) {
if (ArrayAttr argAttrs = getAllArgAttrs()) {
auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>();
result.append(argAttrRange.begin(), argAttrRange.end());
} else {
result.append(getNumArguments(),
DictionaryAttr::get(this->getOperation()->getContext()));
}
}
/// Return the specified attribute, if present, for the argument at 'index',
/// null otherwise.
Attribute getArgAttr(unsigned index, StringAttr name) {
auto argDict = getArgAttrDict(index);
return argDict ? argDict.get(name) : nullptr;
}
Attribute getArgAttr(unsigned index, StringRef name) {
auto argDict = getArgAttrDict(index);
return argDict ? argDict.get(name) : nullptr;
}
template <typename AttrClass>
AttrClass getArgAttrOfType(unsigned index, StringAttr name) {
return getArgAttr(index, name).template dyn_cast_or_null<AttrClass>();
}
template <typename AttrClass>
AttrClass getArgAttrOfType(unsigned index, StringRef name) {
return getArgAttr(index, name).template dyn_cast_or_null<AttrClass>();
}
/// Set the attributes held by the argument at 'index'.
void setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes);
/// Set the attributes held by the argument at 'index'. `attributes` may be
/// null, in which case any existing argument attributes are removed.
void setArgAttrs(unsigned index, DictionaryAttr attributes);
void setAllArgAttrs(ArrayRef<DictionaryAttr> attributes) {
assert(attributes.size() == getNumArguments());
function_like_impl::setAllArgAttrDicts(this->getOperation(), attributes);
}
void setAllArgAttrs(ArrayRef<Attribute> attributes) {
assert(attributes.size() == getNumArguments());
function_like_impl::setAllArgAttrDicts(this->getOperation(), attributes);
}
void setAllArgAttrs(ArrayAttr attributes) {
assert(attributes.size() == getNumArguments());
this->getOperation()->setAttr(function_like_impl::getArgDictAttrName(),
attributes);
}
/// If the an attribute exists with the specified name, change it to the new
/// value. Otherwise, add a new attribute with the specified name/value.
void setArgAttr(unsigned index, StringAttr name, Attribute value);
void setArgAttr(unsigned index, StringRef name, Attribute value) {
setArgAttr(index, StringAttr::get(this->getOperation()->getContext(), name),
value);
}
/// Remove the attribute 'name' from the argument at 'index'. Return the
/// attribute that was erased, or nullptr if there was no attribute with such
/// name.
Attribute removeArgAttr(unsigned index, StringAttr name);
Attribute removeArgAttr(unsigned index, StringRef name) {
return removeArgAttr(
index, StringAttr::get(this->getOperation()->getContext(), name));
}
//===--------------------------------------------------------------------===//
// Result Attributes
//===--------------------------------------------------------------------===//
/// FunctionLike operations allow for attaching attributes to each of the
/// respective function results. These result attributes are stored as
/// DictionaryAttrs in the main operation attribute dictionary. The name of
/// these entries is `result` followed by the index of the result. These
/// result attribute dictionaries are optional, and will generally only
/// exist if they are non-empty.
/// Return all of the attributes for the result at 'index'.
ArrayRef<NamedAttribute> getResultAttrs(unsigned index) {
return function_like_impl::getResultAttrs(this->getOperation(), index);
}
/// Return an ArrayAttr containing all result attribute dictionaries of this
/// function, or nullptr if no result have attributes.
ArrayAttr getAllResultAttrs() {
return this->getOperation()->template getAttrOfType<ArrayAttr>(
function_like_impl::getResultDictAttrName());
}
/// Return all result attributes of this function.
void getAllResultAttrs(SmallVectorImpl<DictionaryAttr> &result) {
if (ArrayAttr argAttrs = getAllResultAttrs()) {
auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>();
result.append(argAttrRange.begin(), argAttrRange.end());
} else {
result.append(getNumResults(),
DictionaryAttr::get(this->getOperation()->getContext()));
}
}
/// Return the specified attribute, if present, for the result at 'index',
/// null otherwise.
Attribute getResultAttr(unsigned index, StringAttr name) {
auto argDict = getResultAttrDict(index);
return argDict ? argDict.get(name) : nullptr;
}
Attribute getResultAttr(unsigned index, StringRef name) {
auto argDict = getResultAttrDict(index);
return argDict ? argDict.get(name) : nullptr;
}
template <typename AttrClass>
AttrClass getResultAttrOfType(unsigned index, StringAttr name) {
return getResultAttr(index, name).template dyn_cast_or_null<AttrClass>();
}
template <typename AttrClass>
AttrClass getResultAttrOfType(unsigned index, StringRef name) {
return getResultAttr(index, name).template dyn_cast_or_null<AttrClass>();
}
/// Set the attributes held by the result at 'index'.
void setResultAttrs(unsigned index, ArrayRef<NamedAttribute> attributes);
/// Set the attributes held by the result at 'index'. `attributes` may be
/// null, in which case any existing argument attributes are removed.
void setResultAttrs(unsigned index, DictionaryAttr attributes);
void setAllResultAttrs(ArrayRef<DictionaryAttr> attributes) {
assert(attributes.size() == getNumResults());
function_like_impl::setAllResultAttrDicts(this->getOperation(), attributes);
}
void setAllResultAttrs(ArrayRef<Attribute> attributes) {
assert(attributes.size() == getNumResults());
function_like_impl::setAllResultAttrDicts(this->getOperation(), attributes);
}
void setAllResultAttrs(ArrayAttr attributes) {
assert(attributes.size() == getNumResults());
this->getOperation()->setAttr(function_like_impl::getResultDictAttrName(),
attributes);
}
/// If the an attribute exists with the specified name, change it to the new
/// value. Otherwise, add a new attribute with the specified name/value.
void setResultAttr(unsigned index, StringAttr name, Attribute value);
void setResultAttr(unsigned index, StringRef name, Attribute value) {
setResultAttr(index,
StringAttr::get(this->getOperation()->getContext(), name),
value);
}
/// Remove the attribute 'name' from the result at 'index'. Return the
/// attribute that was erased, or nullptr if there was no attribute with such
/// name.
Attribute removeResultAttr(unsigned index, StringAttr name);
protected:
/// Returns the dictionary attribute corresponding to the argument at 'index'.
/// If there are no argument attributes at 'index', a null attribute is
/// returned.
DictionaryAttr getArgAttrDict(unsigned index) {
assert(index < getNumArguments() && "invalid argument number");
return function_like_impl::getArgAttrDict(this->getOperation(), index);
}
/// Returns the dictionary attribute corresponding to the result at 'index'.
/// If there are no result attributes at 'index', a null attribute is
/// returned.
DictionaryAttr getResultAttrDict(unsigned index) {
assert(index < getNumResults() && "invalid result number");
return function_like_impl::getResultAttrDict(this->getOperation(), index);
}
/// Hook for concrete classes to verify that the type attribute respects
/// op-specific invariants. Default implementation always succeeds.
LogicalResult verifyType() { return success(); }
};
/// Default verifier checks that if the entry block exists, it has the same
/// number of arguments as the function-like operation.
template <typename ConcreteType>
LogicalResult FunctionLike<ConcreteType>::verifyBody() {
auto funcOp = cast<ConcreteType>(this->getOperation());
if (funcOp.isExternal())
return success();
unsigned numArguments = funcOp.getNumArguments();
if (funcOp.front().getNumArguments() != numArguments)
return funcOp.emitOpError("entry block must have ")
<< numArguments << " arguments to match function signature";
return success();
}
template <typename ConcreteType>
LogicalResult FunctionLike<ConcreteType>::verifyTrait(Operation *op) {
auto funcOp = cast<ConcreteType>(op);
if (!funcOp.isTypeAttrValid())
return funcOp.emitOpError("requires a type attribute '")
<< getTypeAttrName() << '\'';
if (failed(funcOp.verifyType()))
return failure();
if (ArrayAttr allArgAttrs = funcOp.getAllArgAttrs()) {
unsigned numArgs = funcOp.getNumArguments();
if (allArgAttrs.size() != numArgs) {
return funcOp.emitOpError()
<< "expects argument attribute array `"
<< function_like_impl::getArgDictAttrName()
<< "` to have the same number of elements as the number of "
"function arguments, got "
<< allArgAttrs.size() << ", but expected " << numArgs;
}
for (unsigned i = 0; i != numArgs; ++i) {
DictionaryAttr argAttrs =
allArgAttrs[i].dyn_cast_or_null<DictionaryAttr>();
if (!argAttrs) {
return funcOp.emitOpError() << "expects argument attribute dictionary "
"to be a DictionaryAttr, but got `"
<< allArgAttrs[i] << "`";
}
// Verify that all of the argument attributes are dialect attributes, i.e.
// that they contain a dialect prefix in their name. Call the dialect, if
// registered, to verify the attributes themselves.
for (auto attr : argAttrs) {
if (!attr.getName().strref().contains('.'))
return funcOp.emitOpError(
"arguments may only have dialect attributes");
if (Dialect *dialect = attr.getNameDialect()) {
if (failed(dialect->verifyRegionArgAttribute(op, /*regionIndex=*/0,
/*argIndex=*/i, attr)))
return failure();
}
}
}
}
if (ArrayAttr allResultAttrs = funcOp.getAllResultAttrs()) {
unsigned numResults = funcOp.getNumResults();
if (allResultAttrs.size() != numResults) {
return funcOp.emitOpError()
<< "expects result attribute array `"
<< function_like_impl::getResultDictAttrName()
<< "` to have the same number of elements as the number of "
"function results, got "
<< allResultAttrs.size() << ", but expected " << numResults;
}
for (unsigned i = 0; i != numResults; ++i) {
DictionaryAttr resultAttrs =
allResultAttrs[i].dyn_cast_or_null<DictionaryAttr>();
if (!resultAttrs) {
return funcOp.emitOpError() << "expects result attribute dictionary "
"to be a DictionaryAttr, but got `"
<< allResultAttrs[i] << "`";
}
// Verify that all of the result attributes are dialect attributes, i.e.
// that they contain a dialect prefix in their name. Call the dialect, if
// registered, to verify the attributes themselves.
for (auto attr : resultAttrs) {
if (!attr.getName().strref().contains('.'))
return funcOp.emitOpError("results may only have dialect attributes");
if (Dialect *dialect = attr.getNameDialect()) {
if (failed(dialect->verifyRegionResultAttribute(op, /*regionIndex=*/0,
/*resultIndex=*/i,
attr)))
return failure();
}
}
}
}
// Check that the op has exactly one region for the body.
if (op->getNumRegions() != 1)
return funcOp.emitOpError("expects one region");
return funcOp.verifyBody();
}
//===----------------------------------------------------------------------===//
// Function Body.
//===----------------------------------------------------------------------===//
template <typename ConcreteType>
Block *FunctionLike<ConcreteType>::addEntryBlock() {
assert(empty() && "function already has an entry block");
auto *entry = new Block();
push_back(entry);
entry->addArguments(getType().getInputs());
return entry;
}
template <typename ConcreteType>
Block *FunctionLike<ConcreteType>::addBlock() {
assert(!empty() && "function should at least have an entry block");
push_back(new Block());
return &back();
}
//===----------------------------------------------------------------------===//
// Function Type Attribute.
//===----------------------------------------------------------------------===//
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setType(FunctionType newType) {
function_like_impl::setFunctionType(this->getOperation(), newType);
}
//===----------------------------------------------------------------------===//
// Function Argument Attribute.
//===----------------------------------------------------------------------===//
/// Set the attributes held by the argument at 'index'.
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setArgAttrs(
unsigned index, ArrayRef<NamedAttribute> attributes) {
assert(index < getNumArguments() && "invalid argument number");
Operation *op = this->getOperation();
return function_like_impl::detail::setArgResAttrDict(
op, function_like_impl::getArgDictAttrName(), getNumArguments(), index,
DictionaryAttr::get(op->getContext(), attributes));
}
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setArgAttrs(unsigned index,
DictionaryAttr attributes) {
Operation *op = this->getOperation();
return function_like_impl::detail::setArgResAttrDict(
op, function_like_impl::getArgDictAttrName(), getNumArguments(), index,
attributes ? attributes : DictionaryAttr::get(op->getContext()));
}
/// If the an attribute exists with the specified name, change it to the new
/// value. Otherwise, add a new attribute with the specified name/value.
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setArgAttr(unsigned index, StringAttr name,
Attribute value) {
NamedAttrList attributes(getArgAttrDict(index));
Attribute oldValue = attributes.set(name, value);
// If the attribute changed, then set the new arg attribute list.
if (value != oldValue)
setArgAttrs(index, attributes.getDictionary(value.getContext()));
}
/// Remove the attribute 'name' from the argument at 'index'.
template <typename ConcreteType>
Attribute FunctionLike<ConcreteType>::removeArgAttr(unsigned index,
StringAttr name) {
// Build an attribute list and remove the attribute at 'name'.
NamedAttrList attributes(getArgAttrDict(index));
Attribute removedAttr = attributes.erase(name);
// If the attribute was removed, then update the argument dictionary.
if (removedAttr)
setArgAttrs(index, attributes.getDictionary(removedAttr.getContext()));
return removedAttr;
}
//===----------------------------------------------------------------------===//
// Function Result Attribute.
//===----------------------------------------------------------------------===//
/// Set the attributes held by the result at 'index'.
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setResultAttrs(
unsigned index, ArrayRef<NamedAttribute> attributes) {
assert(index < getNumResults() && "invalid result number");
Operation *op = this->getOperation();
return function_like_impl::detail::setArgResAttrDict(
op, function_like_impl::getResultDictAttrName(), getNumResults(), index,
DictionaryAttr::get(op->getContext(), attributes));
}
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setResultAttrs(unsigned index,
DictionaryAttr attributes) {
assert(index < getNumResults() && "invalid result number");
Operation *op = this->getOperation();
return function_like_impl::detail::setArgResAttrDict(
op, function_like_impl::getResultDictAttrName(), getNumResults(), index,
attributes ? attributes : DictionaryAttr::get(op->getContext()));
}
/// If the an attribute exists with the specified name, change it to the new
/// value. Otherwise, add a new attribute with the specified name/value.
template <typename ConcreteType>
void FunctionLike<ConcreteType>::setResultAttr(unsigned index, StringAttr name,
Attribute value) {
NamedAttrList attributes(getResultAttrDict(index));
Attribute oldAttr = attributes.set(name, value);
// If the attribute changed, then set the new arg attribute list.
if (oldAttr != value)
setResultAttrs(index, attributes.getDictionary(value.getContext()));
}
/// Remove the attribute 'name' from the result at 'index'.
template <typename ConcreteType>
Attribute FunctionLike<ConcreteType>::removeResultAttr(unsigned index,
StringAttr name) {
// Build an attribute list and remove the attribute at 'name'.
NamedAttrList attributes(getResultAttrDict(index));
Attribute removedAttr = attributes.erase(name);
// If the attribute was removed, then update the result dictionary.
if (removedAttr)
setResultAttrs(index, attributes.getDictionary(removedAttr.getContext()));
return removedAttr;
}
} // end namespace OpTrait
} // end namespace mlir
#endif // MLIR_IR_FUNCTIONSUPPORT_H