diff --git a/mlir/include/mlir/IR/Function.h b/mlir/include/mlir/IR/Function.h index c580f695366c..e0fb45b75190 100644 --- a/mlir/include/mlir/IR/Function.h +++ b/mlir/include/mlir/IR/Function.h @@ -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 { + 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("type").getValue().cast(); + return getAttrOfType(getTypeAttrName()) + .getValue() + .cast(); } /// 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 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 getArgAttrs(unsigned index) { - auto argDict = getArgAttrDict(index); - return argDict ? argDict.getValue() : llvm::None; - } - - /// Return all argument attributes of this function. - void getAllArgAttrs(SmallVectorImpl &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 - AttrClass getArgAttrOfType(unsigned index, Identifier name) { - return getArgAttr(index, name).dyn_cast_or_null(); - } - template - AttrClass getArgAttrOfType(unsigned index, StringRef name) { - return getArgAttr(index, name).dyn_cast_or_null(); - } - - /// Set the attributes held by the argument at 'index'. - void setArgAttrs(unsigned index, ArrayRef attributes); - void setArgAttrs(unsigned index, NamedAttributeList attributes); - void setAllArgAttrs(ArrayRef 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 &out); + // This trait needs access to `getNumFuncArguments` and `verifyType` hooks + // defined below. + friend class OpTrait::FunctionLike; - /// 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(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()) + return emitOpError("requires '" + getTypeAttrName() + + "' attribute of function type"); + return success(); } }; } // end namespace mlir diff --git a/mlir/include/mlir/IR/FunctionSupport.h b/mlir/include/mlir/IR/FunctionSupport.h new file mode 100644 index 000000000000..953fc6cac6a9 --- /dev/null +++ b/mlir/include/mlir/IR/FunctionSupport.h @@ -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 +class FunctionLike : public OpTrait::TraitBase { +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( + 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( + getTypeAttrName()); + } + + bool isTypeAttrValid() { + auto typeAttr = getTypeAttr(); + if (!typeAttr) + return false; + return typeAttr.getValue() != Type{}; + } + + //===--------------------------------------------------------------------===// + // Argument Handling + //===--------------------------------------------------------------------===// + + unsigned getNumArguments() { + return static_cast(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 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 getArgAttrs(unsigned index) { + auto argDict = getArgAttrDict(index); + return argDict ? argDict.getValue() : llvm::None; + } + + /// Return all argument attributes of this function. + void getAllArgAttrs(SmallVectorImpl &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 + AttrClass getArgAttrOfType(unsigned index, Identifier name) { + return getArgAttr(index, name).template dyn_cast_or_null(); + } + template + AttrClass getArgAttrOfType(unsigned index, StringRef name) { + return getArgAttr(index, name).template dyn_cast_or_null(); + } + + /// Set the attributes held by the argument at 'index'. + void setArgAttrs(unsigned index, ArrayRef attributes); + void setArgAttrs(unsigned index, NamedAttributeList attributes); + void setAllArgAttrs(ArrayRef 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 &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( + 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 +LogicalResult FunctionLike::verifyTrait(Operation *op) { + MLIRContext *ctx = op->getContext(); + auto funcOp = cast(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 +void FunctionLike::setArgAttrs( + unsigned index, ArrayRef attributes) { + assert(index < getNumArguments() && "invalid argument number"); + SmallString<8> nameOut; + getArgAttrName(index, nameOut); + Operation *op = this->getOperation(); + + if (attributes.empty()) + return (void)static_cast(this)->removeAttr(nameOut); + op->setAttr(nameOut, DictionaryAttr::get(attributes, op->getContext())); +} + +template +void FunctionLike::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(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 +void FunctionLike::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 +NamedAttributeList::RemoveResult +FunctionLike::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 +StringRef +FunctionLike::getArgAttrName(unsigned arg, + SmallVectorImpl &out) { + out.clear(); + return ("arg" + Twine(arg)).toStringRef(out); +} + +} // end namespace OpTrait + +} // end namespace mlir + +#endif // MLIR_IR_FUNCTIONSUPPORT_H diff --git a/mlir/include/mlir/LLVMIR/LLVMOps.td b/mlir/include/mlir/LLVMIR/LLVMOps.td index 054a7abb5bce..9bb3b6a05816 100644 --- a/mlir/include/mlir/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/LLVMIR/LLVMOps.td @@ -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 attrs, " + "ArrayRef argAttrs = {}"> + ]; + + let extraClassDeclaration = [{ + LLVMType getType() { + return getAttrOfType(getTypeAttrName()) + .getValue().cast(); + } + 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); }]; diff --git a/mlir/lib/IR/Function.cpp b/mlir/lib/IR/Function.cpp index bb291a1f1d1e..3d2cc90322d9 100644 --- a/mlir/lib/IR/Function.cpp +++ b/mlir/lib/IR/Function.cpp @@ -57,7 +57,7 @@ void FuncOp::build(Builder *builder, OperationState *result, StringRef name, FunctionType type, ArrayRef 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 ignoredAttrs = {SymbolTable::getSymbolAttrName(), - "type"}; + getTypeAttrName()}; // Ignore any argument attributes. std::vector> 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(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 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 &out) { - out.clear(); - return ("arg" + Twine(arg)).toStringRef(out); -} diff --git a/mlir/lib/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/LLVMIR/IR/LLVMDialect.cpp index c72128185245..da46e8d5f0c8 100644 --- a/mlir/lib/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/LLVMIR/IR/LLVMDialect.cpp @@ -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 attrs, + ArrayRef 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(); + 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(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(); + 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. //===----------------------------------------------------------------------===// diff --git a/mlir/test/LLVMIR/func.mlir b/mlir/test/LLVMIR/func.mlir new file mode 100644 index 000000000000..f056d375f20e --- /dev/null +++ b/mlir/test/LLVMIR/func.mlir @@ -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)">} : () -> () +}