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:
Alex Zinenko 2019-07-23 09:26:15 -07:00 committed by A. Unique TensorFlower
parent 42a767b23d
commit 8543f8aaba
6 changed files with 568 additions and 230 deletions

View File

@ -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

View File

@ -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

View File

@ -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); }];

View File

@ -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);
}

View File

@ -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.
//===----------------------------------------------------------------------===//

View File

@ -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)">} : () -> ()
}