forked from OSchip/llvm-project
Introduce LLVMFuncOp
Originally, MLIR only supported functions of the built-in FunctionType. On the conversion path to LLVM IR, we were creating MLIR functions that contained LLVM dialect operations and used LLVM IR types for everything expect top-level functions (e.g., a second-order function would have a FunctionType that consume or produces a wrapped LLVM function pointer type). With MLIR functions becoming operations, it is now possible to introduce non-built-in function operations. This will let us use conversion patterns for function conversion, simplify the MLIR-to-LLVM translation by removing the knowledge of the MLIR built-in function types, and provide stronger correctness verifications (e.g. LLVM functions only accept LLVM types). Furthermore, we can currently construct a situation where the same function is used with two different types: () -> () when its specified and called directly, and !llvm<"void ()"> when it's passed somewhere on called indirectly. Having a special function-op that is always of !llvm<"void ()"> type makes the function model and the llvm dialect type system more consistent. Introduce LLVMFuncOp to represent a function in the LLVM dialect. Unlike standard FuncOp, this function has an LLVMType wrapping an LLVM IR function type. Generalize the common behavior of function-defining operations (functions live in a symbol table of a module, contain a single region, are iterable as a list of blocks, and support argument attributes). This only defines the operation. Custom syntax, conversion and translation rules will be added in follow-ups. The operation name mentions LLVM explicitly to avoid confusion with standard FuncOp, especially in multiple files that use both `mlir` and `mlir::LLVM` namespaces. PiperOrigin-RevId: 259550940
This commit is contained in:
parent
42a767b23d
commit
8543f8aaba
|
@ -23,8 +23,8 @@
|
|||
#define MLIR_IR_FUNCTION_H
|
||||
|
||||
#include "mlir/IR/Block.h"
|
||||
#include "mlir/IR/FunctionSupport.h"
|
||||
#include "mlir/IR/OpDefinition.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
||||
namespace mlir {
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -37,7 +37,7 @@ namespace mlir {
|
|||
/// Function arguments or attributes that establish a symbolic connection(e.g.
|
||||
/// symbols referenced by name via a string attribute).
|
||||
class FuncOp : public Op<FuncOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
|
||||
OpTrait::IsIsolatedFromAbove> {
|
||||
OpTrait::IsIsolatedFromAbove, OpTrait::FunctionLike> {
|
||||
public:
|
||||
using Op::Op;
|
||||
using Op::print;
|
||||
|
@ -63,15 +63,11 @@ public:
|
|||
void print(OpAsmPrinter *p);
|
||||
LogicalResult verify();
|
||||
|
||||
/// Returns the name of this function.
|
||||
StringRef getName();
|
||||
|
||||
/// Set the name of this function.
|
||||
void setName(StringRef name);
|
||||
|
||||
/// Returns the type of this function.
|
||||
FunctionType getType() {
|
||||
return getAttrOfType<TypeAttr>("type").getValue().cast<FunctionType>();
|
||||
return getAttrOfType<TypeAttr>(getTypeAttrName())
|
||||
.getValue()
|
||||
.cast<FunctionType>();
|
||||
}
|
||||
|
||||
/// Change the type of this function in place. This is an extremely dangerous
|
||||
|
@ -82,12 +78,9 @@ public:
|
|||
/// parameters we drop the extra attributes, if there are more parameters
|
||||
/// they won't have any attributes.
|
||||
void setType(FunctionType newType) {
|
||||
setAttr("type", TypeAttr::get(newType));
|
||||
setAttr(getTypeAttrName(), TypeAttr::get(newType));
|
||||
}
|
||||
|
||||
/// Returns true if this function is external, i.e. it has no body.
|
||||
bool isExternal() { return empty(); }
|
||||
|
||||
/// Create a deep copy of this function and all of its blocks, remapping
|
||||
/// any operands that use values outside of the function using the map that is
|
||||
/// provided (leaving them alone if no entry is present). If the mapper
|
||||
|
@ -106,130 +99,27 @@ public:
|
|||
// Body Handling
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
Region &getBody() { return getOperation()->getRegion(0); }
|
||||
|
||||
/// Delete all blocks from this function.
|
||||
void eraseBody();
|
||||
|
||||
/// This is the list of blocks in the function.
|
||||
using RegionType = Region::RegionType;
|
||||
RegionType &getBlocks() { return getBody().getBlocks(); }
|
||||
|
||||
// Iteration over the block in the function.
|
||||
using iterator = RegionType::iterator;
|
||||
using reverse_iterator = RegionType::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.
|
||||
void addEntryBlock();
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Argument Handling
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Returns number of arguments.
|
||||
unsigned getNumArguments() { return getType().getInputs().size(); }
|
||||
|
||||
/// Gets argument.
|
||||
BlockArgument *getArgument(unsigned idx) {
|
||||
return getBlocks().front().getArgument(idx);
|
||||
}
|
||||
|
||||
// Supports non-const operand iteration.
|
||||
using args_iterator = Block::args_iterator;
|
||||
args_iterator args_begin() { return front().args_begin(); }
|
||||
args_iterator args_end() { return front().args_end(); }
|
||||
llvm::iterator_range<args_iterator> getArguments() {
|
||||
return {args_begin(), args_end()};
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Argument Attributes
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// FuncOp allows 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) {
|
||||
auto argDict = getArgAttrDict(index);
|
||||
return argDict ? argDict.getValue() : llvm::None;
|
||||
}
|
||||
|
||||
/// Return all argument attributes of this function.
|
||||
void getAllArgAttrs(SmallVectorImpl<NamedAttributeList> &result) {
|
||||
for (unsigned i = 0, e = getNumArguments(); i != e; ++i)
|
||||
result.emplace_back(getArgAttrDict(i));
|
||||
}
|
||||
|
||||
/// Return the specified attribute, if present, for the argument at 'index',
|
||||
/// null otherwise.
|
||||
Attribute getArgAttr(unsigned index, Identifier 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, Identifier name) {
|
||||
return getArgAttr(index, name).dyn_cast_or_null<AttrClass>();
|
||||
}
|
||||
template <typename AttrClass>
|
||||
AttrClass getArgAttrOfType(unsigned index, StringRef name) {
|
||||
return getArgAttr(index, name).dyn_cast_or_null<AttrClass>();
|
||||
}
|
||||
|
||||
/// Set the attributes held by the argument at 'index'.
|
||||
void setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes);
|
||||
void setArgAttrs(unsigned index, NamedAttributeList attributes);
|
||||
void setAllArgAttrs(ArrayRef<NamedAttributeList> attributes) {
|
||||
assert(attributes.size() == getNumArguments());
|
||||
for (unsigned i = 0, e = attributes.size(); i != e; ++i)
|
||||
setArgAttrs(i, attributes[i]);
|
||||
}
|
||||
|
||||
/// 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, Identifier name, Attribute value);
|
||||
void setArgAttr(unsigned index, StringRef name, Attribute value) {
|
||||
setArgAttr(index, Identifier::get(name, getContext()), value);
|
||||
}
|
||||
|
||||
/// Remove the attribute 'name' from the argument at 'index'.
|
||||
NamedAttributeList::RemoveResult removeArgAttr(unsigned index,
|
||||
Identifier name);
|
||||
|
||||
private:
|
||||
/// Returns the attribute entry name for the set of argument attributes at
|
||||
/// index 'arg'.
|
||||
static StringRef getArgAttrName(unsigned arg, SmallVectorImpl<char> &out);
|
||||
// This trait needs access to `getNumFuncArguments` and `verifyType` hooks
|
||||
// defined below.
|
||||
friend class OpTrait::FunctionLike<FuncOp>;
|
||||
|
||||
/// 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");
|
||||
SmallString<8> nameOut;
|
||||
return getAttrOfType<DictionaryAttr>(getArgAttrName(index, nameOut));
|
||||
/// Returns the number of arguments. This is a hook for OpTrait::FunctionLike.
|
||||
unsigned getNumFuncArguments() { return getType().getInputs().size(); }
|
||||
|
||||
/// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
|
||||
/// attribute is present and checks if it holds a function type. Ensures
|
||||
/// getType and getNumFuncArguments can be called safely.
|
||||
LogicalResult verifyType() {
|
||||
auto type = getTypeAttr().getValue();
|
||||
if (!type.isa<FunctionType>())
|
||||
return emitOpError("requires '" + getTypeAttrName() +
|
||||
"' attribute of function type");
|
||||
return success();
|
||||
}
|
||||
};
|
||||
} // end namespace mlir
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
//===- FunctionSupport.h - Utility types for function-like ops --*- C++ -*-===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// 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/OpDefinition.h"
|
||||
#include "mlir/IR/SymbolTable.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
||||
namespace mlir {
|
||||
namespace OpTrait {
|
||||
|
||||
/// This trait provides APIs for Ops that behave like functions. In particular:
|
||||
/// - Ops can be used with SymbolTable in the parent Op and have names;
|
||||
/// - Ops have a single region with multiple blocks that corresponds to the body
|
||||
/// of the function;
|
||||
/// - the absence of a region corresonds to an external function;
|
||||
/// - 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 does *NOT* provide type support for the functions, meaning that
|
||||
/// concrete Ops must handle the type of the declared or defined function.
|
||||
/// `getTypeAttrName()` is a convenience function that returns the name of the
|
||||
/// attribute that can be used to store the function type, but the trait makes
|
||||
/// no assumption based on it.
|
||||
///
|
||||
/// - 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).
|
||||
/// - 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` from the verifier.
|
||||
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);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Name Handling.
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Returns the name of this function.
|
||||
StringRef getName() {
|
||||
return this->getOperation()
|
||||
->template getAttrOfType<StringAttr>(
|
||||
mlir::SymbolTable::getSymbolAttrName())
|
||||
.getValue();
|
||||
}
|
||||
|
||||
/// Set the name of this function.
|
||||
void setName(StringRef name) {
|
||||
this->getOperation()->setAttr(
|
||||
mlir::SymbolTable::getSymbolAttrName(),
|
||||
StringAttr::get(name, this->getOperation()->getContext()));
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Body Handling
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Returns true if this function is external, i.e. it has no body.
|
||||
bool isExternal() { return empty(); }
|
||||
|
||||
Region &getBody() { return this->getOperation()->getRegion(0); }
|
||||
|
||||
/// Delete all blocks from this function.
|
||||
void eraseBody() {
|
||||
getBody().dropAllReferences();
|
||||
getBody().getBlocks().clear();
|
||||
}
|
||||
|
||||
/// This is the list of blocks in the function.
|
||||
using RegionType = Region::RegionType;
|
||||
RegionType &getBlocks() { return getBody().getBlocks(); }
|
||||
|
||||
// Iteration over the block in the function.
|
||||
using iterator = RegionType::iterator;
|
||||
using reverse_iterator = RegionType::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(); }
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Type Attribute Handling
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Return the name of the attribute used for function types.
|
||||
static StringRef getTypeAttrName() { return "type"; }
|
||||
|
||||
TypeAttr getTypeAttr() {
|
||||
return this->getOperation()->template getAttrOfType<TypeAttr>(
|
||||
getTypeAttrName());
|
||||
}
|
||||
|
||||
bool isTypeAttrValid() {
|
||||
auto typeAttr = getTypeAttr();
|
||||
if (!typeAttr)
|
||||
return false;
|
||||
return typeAttr.getValue() != Type{};
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Argument Handling
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
unsigned getNumArguments() {
|
||||
return static_cast<ConcreteType *>(this)->getNumFuncArguments();
|
||||
}
|
||||
|
||||
/// Gets argument.
|
||||
BlockArgument *getArgument(unsigned idx) {
|
||||
return getBlocks().front().getArgument(idx);
|
||||
}
|
||||
|
||||
// Supports non-const operand iteration.
|
||||
using args_iterator = Block::args_iterator;
|
||||
args_iterator args_begin() { return front().args_begin(); }
|
||||
args_iterator args_end() { return front().args_end(); }
|
||||
llvm::iterator_range<args_iterator> getArguments() {
|
||||
return {args_begin(), args_end()};
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// 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) {
|
||||
auto argDict = getArgAttrDict(index);
|
||||
return argDict ? argDict.getValue() : llvm::None;
|
||||
}
|
||||
|
||||
/// Return all argument attributes of this function.
|
||||
void getAllArgAttrs(SmallVectorImpl<NamedAttributeList> &result) {
|
||||
for (unsigned i = 0, e = getNumArguments(); i != e; ++i)
|
||||
result.emplace_back(getArgAttrDict(i));
|
||||
}
|
||||
|
||||
/// Return the specified attribute, if present, for the argument at 'index',
|
||||
/// null otherwise.
|
||||
Attribute getArgAttr(unsigned index, Identifier 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, Identifier 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);
|
||||
void setArgAttrs(unsigned index, NamedAttributeList attributes);
|
||||
void setAllArgAttrs(ArrayRef<NamedAttributeList> attributes) {
|
||||
assert(attributes.size() == getNumArguments());
|
||||
for (unsigned i = 0, e = attributes.size(); i != e; ++i)
|
||||
setArgAttrs(i, attributes[i]);
|
||||
}
|
||||
|
||||
/// 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, Identifier name, Attribute value);
|
||||
void setArgAttr(unsigned index, StringRef name, Attribute value) {
|
||||
setArgAttr(index, Identifier::get(name, this->getOperation()->getContext()),
|
||||
value);
|
||||
}
|
||||
|
||||
/// Remove the attribute 'name' from the argument at 'index'.
|
||||
NamedAttributeList::RemoveResult removeArgAttr(unsigned index,
|
||||
Identifier name);
|
||||
|
||||
protected:
|
||||
/// Returns the attribute entry name for the set of argument attributes at
|
||||
/// index 'arg'.
|
||||
static StringRef getArgAttrName(unsigned arg, SmallVectorImpl<char> &out);
|
||||
|
||||
/// 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");
|
||||
SmallString<8> nameOut;
|
||||
return this->getOperation()->template getAttrOfType<DictionaryAttr>(
|
||||
getArgAttrName(index, nameOut));
|
||||
}
|
||||
|
||||
/// Hook for concrete classes to verify that the type attribute respects
|
||||
/// op-specific invariants. Default implementation always succeeds.
|
||||
LogicalResult verifyType() { return success(); }
|
||||
};
|
||||
|
||||
template <typename ConcreteType>
|
||||
LogicalResult FunctionLike<ConcreteType>::verifyTrait(Operation *op) {
|
||||
MLIRContext *ctx = op->getContext();
|
||||
auto funcOp = cast<ConcreteType>(op);
|
||||
|
||||
if (!funcOp.isTypeAttrValid())
|
||||
return funcOp.emitOpError("requires a type attribute '")
|
||||
<< getTypeAttrName() << '\'';
|
||||
|
||||
if (failed(funcOp.verifyType()))
|
||||
return failure();
|
||||
|
||||
for (unsigned i = 0, e = funcOp.getNumArguments(); i != e; ++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 : funcOp.getArgAttrs(i)) {
|
||||
if (!attr.first.strref().contains('.'))
|
||||
return funcOp.emitOpError("arguments may only have dialect attributes");
|
||||
auto dialectNamePair = attr.first.strref().split('.');
|
||||
if (auto *dialect = ctx->getRegisteredDialect(dialectNamePair.first)) {
|
||||
if (failed(dialect->verifyRegionArgAttribute(op, /*regionIndex=*/0,
|
||||
/*argIndex=*/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");
|
||||
|
||||
// Check that if the entry block exists, it has the same number of arguments
|
||||
// as the function-like operation.
|
||||
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();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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");
|
||||
SmallString<8> nameOut;
|
||||
getArgAttrName(index, nameOut);
|
||||
Operation *op = this->getOperation();
|
||||
|
||||
if (attributes.empty())
|
||||
return (void)static_cast<ConcreteType *>(this)->removeAttr(nameOut);
|
||||
op->setAttr(nameOut, DictionaryAttr::get(attributes, op->getContext()));
|
||||
}
|
||||
|
||||
template <typename ConcreteType>
|
||||
void FunctionLike<ConcreteType>::setArgAttrs(unsigned index,
|
||||
NamedAttributeList attributes) {
|
||||
assert(index < getNumArguments() && "invalid argument number");
|
||||
SmallString<8> nameOut;
|
||||
if (auto newAttr = attributes.getDictionary())
|
||||
return this->getOperation()->setAttr(getArgAttrName(index, nameOut),
|
||||
newAttr);
|
||||
static_cast<ConcreteType *>(this)->removeAttr(getArgAttrName(index, nameOut));
|
||||
}
|
||||
|
||||
/// 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, Identifier name,
|
||||
Attribute value) {
|
||||
auto curAttr = getArgAttrDict(index);
|
||||
NamedAttributeList attrList(curAttr);
|
||||
attrList.set(name, value);
|
||||
|
||||
// If the attribute changed, then set the new arg attribute list.
|
||||
if (curAttr != attrList.getDictionary())
|
||||
setArgAttrs(index, attrList);
|
||||
}
|
||||
|
||||
/// Remove the attribute 'name' from the argument at 'index'.
|
||||
template <typename ConcreteType>
|
||||
NamedAttributeList::RemoveResult
|
||||
FunctionLike<ConcreteType>::removeArgAttr(unsigned index, Identifier name) {
|
||||
// Build an attribute list and remove the attribute at 'name'.
|
||||
NamedAttributeList attrList(getArgAttrDict(index));
|
||||
auto result = attrList.remove(name);
|
||||
|
||||
// If the attribute was removed, then update the argument dictionary.
|
||||
if (result == NamedAttributeList::RemoveResult::Removed)
|
||||
setArgAttrs(index, attrList);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the attribute entry name for the set of argument attributes at index
|
||||
/// 'arg'.
|
||||
template <typename ConcreteType>
|
||||
StringRef
|
||||
FunctionLike<ConcreteType>::getArgAttrName(unsigned arg,
|
||||
SmallVectorImpl<char> &out) {
|
||||
out.clear();
|
||||
return ("arg" + Twine(arg)).toStringRef(out);
|
||||
}
|
||||
|
||||
} // end namespace OpTrait
|
||||
|
||||
} // end namespace mlir
|
||||
|
||||
#endif // MLIR_IR_FUNCTIONSUPPORT_H
|
|
@ -302,6 +302,42 @@ def LLVM_ReturnOp : LLVM_TerminatorOp<"return", []> {
|
|||
|
||||
// Pseudo-operations (do not appear in LLVM IR but necessary for the dialect to
|
||||
// work correctly).
|
||||
def LLVM_LLVMFuncOp : LLVM_ZeroResultOp<"func",
|
||||
[NativeOpTrait<"IsIsolatedFromAbove">, NativeOpTrait<"FunctionLike">]> {
|
||||
let summary = "LLVM dialect function, has wrapped LLVM IR function type";
|
||||
|
||||
let regions = (region AnyRegion:$body);
|
||||
|
||||
let skipDefaultBuilders = 1;
|
||||
|
||||
let builders = [
|
||||
OpBuilder<"Builder *builder, OperationState *result, StringRef name, "
|
||||
"LLVMType type, ArrayRef<NamedAttribute> attrs, "
|
||||
"ArrayRef<NamedAttributeList> argAttrs = {}">
|
||||
];
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
LLVMType getType() {
|
||||
return getAttrOfType<TypeAttr>(getTypeAttrName())
|
||||
.getValue().cast<LLVMType>();
|
||||
}
|
||||
bool isVarArg() {
|
||||
return getType().getUnderlyingType()->isFunctionVarArg();
|
||||
}
|
||||
|
||||
// Hook for OpTrait::FunctionLike, returns the number of function arguments.
|
||||
// Depends on the type attribute being correct as checked by verifyType.
|
||||
unsigned getNumFuncArguments();
|
||||
|
||||
// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
|
||||
// attribute is present. This can check for preconditions of the
|
||||
// getNumArguments hook not failing.
|
||||
LogicalResult verifyType();
|
||||
}];
|
||||
|
||||
let verifier = [{ return ::verify(*this); }];
|
||||
}
|
||||
|
||||
def LLVM_UndefOp : LLVM_OneResultOp<"undef", [NoSideEffect]>,
|
||||
LLVM_Builder<"$res = llvm::UndefValue::get($_resultType);"> {
|
||||
let parser = [{ return parseUndefOp(parser, result); }];
|
||||
|
|
|
@ -57,7 +57,7 @@ void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
|
|||
FunctionType type, ArrayRef<NamedAttribute> attrs) {
|
||||
result->addAttribute(SymbolTable::getSymbolAttrName(),
|
||||
builder->getStringAttr(name));
|
||||
result->addAttribute("type", builder->getTypeAttr(type));
|
||||
result->addAttribute(getTypeAttrName(), builder->getTypeAttr(type));
|
||||
result->attributes.append(attrs.begin(), attrs.end());
|
||||
result->addRegion();
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ ParseResult FuncOp::parse(OpAsmParser *parser, OperationState *result) {
|
|||
// Parse the function signature.
|
||||
if (parseFunctionSignature(parser, type, entryArgs, argAttrs))
|
||||
return failure();
|
||||
result->addAttribute("type", builder.getTypeAttr(type));
|
||||
result->addAttribute(getTypeAttrName(), builder.getTypeAttr(type));
|
||||
|
||||
// If function attributes are present, parse them.
|
||||
if (succeeded(parser->parseOptionalKeyword("attributes")))
|
||||
|
@ -219,7 +219,7 @@ void FuncOp::print(OpAsmPrinter *p) {
|
|||
|
||||
// Print out function attributes, if present.
|
||||
SmallVector<StringRef, 2> ignoredAttrs = {SymbolTable::getSymbolAttrName(),
|
||||
"type"};
|
||||
getTypeAttrName()};
|
||||
|
||||
// Ignore any argument attributes.
|
||||
std::vector<SmallString<8>> argAttrStorage;
|
||||
|
@ -242,34 +242,15 @@ void FuncOp::print(OpAsmPrinter *p) {
|
|||
}
|
||||
|
||||
LogicalResult FuncOp::verify() {
|
||||
auto fnInputTypes = getType().getInputs();
|
||||
auto *ctx = getContext();
|
||||
|
||||
/// Verify that all of the argument attributes are dialect attributes.
|
||||
for (unsigned i = 0, e = fnInputTypes.size(); i != e; ++i) {
|
||||
for (auto attr : getArgAttrs(i)) {
|
||||
if (!attr.first.strref().contains('.'))
|
||||
return emitOpError("arguments may only have dialect attributes");
|
||||
auto dialectNamePair = attr.first.strref().split('.');
|
||||
if (auto *dialect = ctx->getRegisteredDialect(dialectNamePair.first)) {
|
||||
if (failed(dialect->verifyRegionArgAttribute(*this, /*regionIndex=*/0,
|
||||
/*argIndex=*/i, attr)))
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this function is external there is nothing to do.
|
||||
if (isExternal())
|
||||
return success();
|
||||
|
||||
// Verify that the argument list of the function and the arg list of the entry
|
||||
// block line up.
|
||||
// block line up. The trait already verified that the number of arguments is
|
||||
// the same between the signature and the block.
|
||||
auto fnInputTypes = getType().getInputs();
|
||||
Block &entryBlock = front();
|
||||
if (fnInputTypes.size() != entryBlock.getNumArguments())
|
||||
return emitOpError("entry block must have ")
|
||||
<< fnInputTypes.size() << " arguments to match function signature";
|
||||
|
||||
for (unsigned i = 0, e = entryBlock.getNumArguments(); i != e; ++i)
|
||||
if (fnInputTypes[i] != entryBlock.getArgument(i)->getType())
|
||||
return emitOpError("type of entry block argument #")
|
||||
|
@ -280,17 +261,6 @@ LogicalResult FuncOp::verify() {
|
|||
return success();
|
||||
}
|
||||
|
||||
/// Returns the name of this function.
|
||||
StringRef FuncOp::getName() {
|
||||
return getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();
|
||||
}
|
||||
|
||||
/// Set the name of this function.
|
||||
void FuncOp::setName(StringRef name) {
|
||||
return setAttr(SymbolTable::getSymbolAttrName(),
|
||||
StringAttr::get(name, getContext()));
|
||||
}
|
||||
|
||||
/// Add an entry block to an empty function, and set up the block arguments
|
||||
/// to match the signature of the function.
|
||||
void FuncOp::addEntryBlock() {
|
||||
|
@ -316,14 +286,6 @@ void FuncOp::cloneInto(FuncOp dest, BlockAndValueMapping &mapper) {
|
|||
getBody().cloneInto(&dest.getBody(), mapper);
|
||||
}
|
||||
|
||||
/// Delete all blocks from this function.
|
||||
void FuncOp::eraseBody() {
|
||||
// First, drop all references in the blocks because they may point to values
|
||||
// defined in the dominating blocks.
|
||||
getBody().dropAllReferences();
|
||||
getBody().getBlocks().clear();
|
||||
}
|
||||
|
||||
/// Create a deep copy of this function and all of its blocks, remapping
|
||||
/// any operands that use values outside of the function using the map that is
|
||||
/// provided (leaving them alone if no entry is present). Replaces references
|
||||
|
@ -362,58 +324,3 @@ FuncOp FuncOp::clone() {
|
|||
BlockAndValueMapping mapper;
|
||||
return clone(mapper);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Function Argument Attribute.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Set the attributes held by the argument at 'index'.
|
||||
void FuncOp::setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes) {
|
||||
assert(index < getNumArguments() && "invalid argument number");
|
||||
SmallString<8> nameOut;
|
||||
getArgAttrName(index, nameOut);
|
||||
|
||||
if (attributes.empty())
|
||||
return (void)removeAttr(nameOut);
|
||||
setAttr(nameOut, DictionaryAttr::get(attributes, getContext()));
|
||||
}
|
||||
|
||||
void FuncOp::setArgAttrs(unsigned index, NamedAttributeList attributes) {
|
||||
assert(index < getNumArguments() && "invalid argument number");
|
||||
SmallString<8> nameOut;
|
||||
if (auto newAttr = attributes.getDictionary())
|
||||
return setAttr(getArgAttrName(index, nameOut), newAttr);
|
||||
removeAttr(getArgAttrName(index, nameOut));
|
||||
}
|
||||
|
||||
/// 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 FuncOp::setArgAttr(unsigned index, Identifier name, Attribute value) {
|
||||
auto curAttr = getArgAttrDict(index);
|
||||
NamedAttributeList attrList(curAttr);
|
||||
attrList.set(name, value);
|
||||
|
||||
// If the attribute changed, then set the new arg attribute list.
|
||||
if (curAttr != attrList.getDictionary())
|
||||
setArgAttrs(index, attrList);
|
||||
}
|
||||
|
||||
/// Remove the attribute 'name' from the argument at 'index'.
|
||||
NamedAttributeList::RemoveResult FuncOp::removeArgAttr(unsigned index,
|
||||
Identifier name) {
|
||||
// Build an attribute list and remove the attribute at 'name'.
|
||||
NamedAttributeList attrList(getArgAttrDict(index));
|
||||
auto result = attrList.remove(name);
|
||||
|
||||
// If the attribute was removed, then update the argument dictionary.
|
||||
if (result == NamedAttributeList::RemoveResult::Removed)
|
||||
setArgAttrs(index, attrList);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the attribute entry name for the set of argument attributes at index
|
||||
/// 'arg'.
|
||||
StringRef FuncOp::getArgAttrName(unsigned arg, SmallVectorImpl<char> &out) {
|
||||
out.clear();
|
||||
return ("arg" + Twine(arg)).toStringRef(out);
|
||||
}
|
||||
|
|
|
@ -702,6 +702,69 @@ static ParseResult parseConstantOp(OpAsmParser *parser,
|
|||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Builder and verifier for LLVM::LLVMFuncOp.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void LLVMFuncOp::build(Builder *builder, OperationState *result, StringRef name,
|
||||
LLVMType type, ArrayRef<NamedAttribute> attrs,
|
||||
ArrayRef<NamedAttributeList> argAttrs) {
|
||||
result->addRegion();
|
||||
result->addAttribute(SymbolTable::getSymbolAttrName(),
|
||||
builder->getStringAttr(name));
|
||||
result->addAttribute("type", builder->getTypeAttr(type));
|
||||
result->attributes.append(attrs.begin(), attrs.end());
|
||||
if (argAttrs.empty())
|
||||
return;
|
||||
|
||||
unsigned numInputs = type.getUnderlyingType()->getFunctionNumParams();
|
||||
assert(numInputs == argAttrs.size() &&
|
||||
"expected as many argument attribute lists as arguments");
|
||||
SmallString<8> argAttrName;
|
||||
for (unsigned i = 0; i < numInputs; ++i)
|
||||
if (auto argDict = argAttrs[i].getDictionary())
|
||||
result->addAttribute(getArgAttrName(i, argAttrName), argDict);
|
||||
}
|
||||
|
||||
// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
|
||||
// attribute is present. This can check for preconditions of the
|
||||
// getNumArguments hook not failing.
|
||||
LogicalResult LLVMFuncOp::verifyType() {
|
||||
auto llvmType = getTypeAttr().getValue().dyn_cast_or_null<LLVMType>();
|
||||
if (!llvmType || !llvmType.getUnderlyingType()->isFunctionTy())
|
||||
return emitOpError("requires '" + getTypeAttrName() +
|
||||
"' attribute of wrapped LLVM function type");
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
// Hook for OpTrait::FunctionLike, returns the number of function arguments.
|
||||
// Depends on the type attribute being correct as checked by verifyType
|
||||
unsigned LLVMFuncOp::getNumFuncArguments() {
|
||||
return getType().getUnderlyingType()->getFunctionNumParams();
|
||||
}
|
||||
|
||||
static LogicalResult verify(LLVMFuncOp op) {
|
||||
if (op.isExternal())
|
||||
return success();
|
||||
|
||||
auto *funcType = cast<llvm::FunctionType>(op.getType().getUnderlyingType());
|
||||
unsigned numArguments = funcType->getNumParams();
|
||||
Block &entryBlock = op.front();
|
||||
for (unsigned i = 0; i < numArguments; ++i) {
|
||||
Type argType = entryBlock.getArgument(i)->getType();
|
||||
auto argLLVMType = argType.dyn_cast<LLVMType>();
|
||||
if (!argLLVMType)
|
||||
return op.emitOpError("entry block argument #")
|
||||
<< i << " is not of LLVM type";
|
||||
if (funcType->getParamType(i) != argLLVMType.getUnderlyingType())
|
||||
return op.emitOpError("the type of entry block argument #")
|
||||
<< i << " does not match the function signature";
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LLVMDialect initialization, type parsing, and registration.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
|
||||
|
||||
module {
|
||||
// CHECK: "llvm.func"
|
||||
// CHECK: sym_name = "foo"
|
||||
// CHECK-SAME: type = !llvm<"void ()">
|
||||
// CHECK-SAME: () -> ()
|
||||
"llvm.func"() ({
|
||||
}) {sym_name = "foo", type = !llvm<"void ()">} : () -> ()
|
||||
|
||||
// CHECK: "llvm.func"
|
||||
// CHECK: sym_name = "bar"
|
||||
// CHECK-SAME: type = !llvm<"i64 (i64, i64)">
|
||||
// CHECK-SAME: () -> ()
|
||||
"llvm.func"() ({
|
||||
}) {sym_name = "bar", type = !llvm<"i64 (i64, i64)">} : () -> ()
|
||||
|
||||
// CHECK: "llvm.func"
|
||||
"llvm.func"() ({
|
||||
// CHECK: ^bb0
|
||||
^bb0(%arg0: !llvm.i64):
|
||||
// CHECK: llvm.return
|
||||
llvm.return %arg0 : !llvm.i64
|
||||
|
||||
// CHECK: sym_name = "baz"
|
||||
// CHECK-SAME: type = !llvm<"i64 (i64)">
|
||||
// CHECK-SAME: () -> ()
|
||||
}) {sym_name = "baz", type = !llvm<"i64 (i64)">} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{expects one region}}
|
||||
"llvm.func"() {sym_name = "no_region", type = !llvm<"void ()">} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{requires a type attribute 'type'}}
|
||||
"llvm.func"() ({}) {sym_name = "missing_type"} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{requires 'type' attribute of wrapped LLVM function type}}
|
||||
"llvm.func"() ({}) {sym_name = "non_llvm_type", type = i64} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{requires 'type' attribute of wrapped LLVM function type}}
|
||||
"llvm.func"() ({}) {sym_name = "non_function_type", type = !llvm<"i64">} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{entry block must have 0 arguments}}
|
||||
"llvm.func"() ({
|
||||
^bb0(%arg0: !llvm.i64):
|
||||
llvm.return
|
||||
}) {sym_name = "wrong_arg_number", type = !llvm<"void ()">} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{entry block argument #0 is not of LLVM type}}
|
||||
"llvm.func"() ({
|
||||
^bb0(%arg0: i64):
|
||||
llvm.return
|
||||
}) {sym_name = "wrong_arg_number", type = !llvm<"void (i64)">} : () -> ()
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
module {
|
||||
// expected-error@+1 {{entry block argument #0 does not match the function signature}}
|
||||
"llvm.func"() ({
|
||||
^bb0(%arg0: !llvm.i32):
|
||||
llvm.return
|
||||
}) {sym_name = "wrong_arg_number", type = !llvm<"void (i64)">} : () -> ()
|
||||
}
|
Loading…
Reference in New Issue