| //===- CallInterfaces.cpp - ControlFlow Interfaces ------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Interfaces/CallInterfaces.h" | |
| #include "mlir/IR/Builders.h" | |
| using namespace mlir; | |
| //===----------------------------------------------------------------------===// | |
| // Argument and result attributes utilities | |
| //===----------------------------------------------------------------------===// | |
| static ParseResult | |
| parseTypeAndAttrList(OpAsmParser &parser, SmallVectorImpl<Type> &types, | |
| SmallVectorImpl<DictionaryAttr> &attrs) { | |
| // Parse individual function results. | |
| return parser.parseCommaSeparatedList([&]() -> ParseResult { | |
| types.emplace_back(); | |
| attrs.emplace_back(); | |
| NamedAttrList attrList; | |
| if (parser.parseType(types.back()) || | |
| parser.parseOptionalAttrDict(attrList)) | |
| return failure(); | |
| attrs.back() = attrList.getDictionary(parser.getContext()); | |
| return success(); | |
| }); | |
| } | |
| ParseResult call_interface_impl::parseFunctionResultList( | |
| OpAsmParser &parser, SmallVectorImpl<Type> &resultTypes, | |
| SmallVectorImpl<DictionaryAttr> &resultAttrs) { | |
| if (failed(parser.parseOptionalLParen())) { | |
| // We already know that there is no `(`, so parse a type. | |
| // Because there is no `(`, it cannot be a function type. | |
| Type ty; | |
| if (parser.parseType(ty)) | |
| return failure(); | |
| resultTypes.push_back(ty); | |
| resultAttrs.emplace_back(); | |
| return success(); | |
| } | |
| // Special case for an empty set of parens. | |
| if (succeeded(parser.parseOptionalRParen())) | |
| return success(); | |
| if (parseTypeAndAttrList(parser, resultTypes, resultAttrs)) | |
| return failure(); | |
| return parser.parseRParen(); | |
| } | |
| ParseResult call_interface_impl::parseFunctionSignature( | |
| OpAsmParser &parser, SmallVectorImpl<Type> &argTypes, | |
| SmallVectorImpl<DictionaryAttr> &argAttrs, | |
| SmallVectorImpl<Type> &resultTypes, | |
| SmallVectorImpl<DictionaryAttr> &resultAttrs, bool mustParseEmptyResult) { | |
| // Parse arguments. | |
| if (parser.parseLParen()) | |
| return failure(); | |
| if (failed(parser.parseOptionalRParen())) { | |
| if (parseTypeAndAttrList(parser, argTypes, argAttrs)) | |
| return failure(); | |
| if (parser.parseRParen()) | |
| return failure(); | |
| } | |
| // Parse results. | |
| if (succeeded(parser.parseOptionalArrow())) | |
| return call_interface_impl::parseFunctionResultList(parser, resultTypes, | |
| resultAttrs); | |
| if (mustParseEmptyResult) | |
| return failure(); | |
| return success(); | |
| } | |
| /// Print a function result list. The provided `attrs` must either be null, or | |
| /// contain a set of DictionaryAttrs of the same arity as `types`. | |
| static void printFunctionResultList(OpAsmPrinter &p, TypeRange types, | |
| ArrayAttr attrs) { | |
| assert(!types.empty() && "Should not be called for empty result list."); | |
| assert((!attrs || attrs.size() == types.size()) && | |
| "Invalid number of attributes."); | |
| auto &os = p.getStream(); | |
| bool needsParens = types.size() > 1 || llvm::isa<FunctionType>(types[0]) || | |
| (attrs && !llvm::cast<DictionaryAttr>(attrs[0]).empty()); | |
| if (needsParens) | |
| os << '('; | |
| llvm::interleaveComma(llvm::seq<size_t>(0, types.size()), os, [&](size_t i) { | |
| p.printType(types[i]); | |
| if (attrs) | |
| p.printOptionalAttrDict(llvm::cast<DictionaryAttr>(attrs[i]).getValue()); | |
| }); | |
| if (needsParens) | |
| os << ')'; | |
| } | |
| void call_interface_impl::printFunctionSignature( | |
| OpAsmPrinter &p, TypeRange argTypes, ArrayAttr argAttrs, bool isVariadic, | |
| TypeRange resultTypes, ArrayAttr resultAttrs, Region *body, | |
| bool printEmptyResult) { | |
| bool isExternal = !body || body->empty(); | |
| if (!isExternal && !isVariadic && !argAttrs && !resultAttrs && | |
| printEmptyResult) { | |
| p.printFunctionalType(argTypes, resultTypes); | |
| return; | |
| } | |
| p << '('; | |
| for (unsigned i = 0, e = argTypes.size(); i < e; ++i) { | |
| if (i > 0) | |
| p << ", "; | |
| if (!isExternal) { | |
| ArrayRef<NamedAttribute> attrs; | |
| if (argAttrs) | |
| attrs = llvm::cast<DictionaryAttr>(argAttrs[i]).getValue(); | |
| p.printRegionArgument(body->getArgument(i), attrs); | |
| } else { | |
| p.printType(argTypes[i]); | |
| if (argAttrs) | |
| p.printOptionalAttrDict( | |
| llvm::cast<DictionaryAttr>(argAttrs[i]).getValue()); | |
| } | |
| } | |
| if (isVariadic) { | |
| if (!argTypes.empty()) | |
| p << ", "; | |
| p << "..."; | |
| } | |
| p << ')'; | |
| if (!resultTypes.empty()) { | |
| p << " -> "; | |
| printFunctionResultList(p, resultTypes, resultAttrs); | |
| } else if (printEmptyResult) { | |
| p << " -> ()"; | |
| } | |
| } | |
| void call_interface_impl::addArgAndResultAttrs( | |
| Builder &builder, OperationState &result, ArrayRef<DictionaryAttr> argAttrs, | |
| ArrayRef<DictionaryAttr> resultAttrs, StringAttr argAttrsName, | |
| StringAttr resAttrsName) { | |
| auto nonEmptyAttrsFn = [](DictionaryAttr attrs) { | |
| return attrs && !attrs.empty(); | |
| }; | |
| // Convert the specified array of dictionary attrs (which may have null | |
| // entries) to an ArrayAttr of dictionaries. | |
| auto getArrayAttr = [&](ArrayRef<DictionaryAttr> dictAttrs) { | |
| SmallVector<Attribute> attrs; | |
| for (auto &dict : dictAttrs) | |
| attrs.push_back(dict ? dict : builder.getDictionaryAttr({})); | |
| return builder.getArrayAttr(attrs); | |
| }; | |
| // Add the attributes to the operation arguments. | |
| if (llvm::any_of(argAttrs, nonEmptyAttrsFn)) | |
| result.addAttribute(argAttrsName, getArrayAttr(argAttrs)); | |
| // Add the attributes to the operation results. | |
| if (llvm::any_of(resultAttrs, nonEmptyAttrsFn)) | |
| result.addAttribute(resAttrsName, getArrayAttr(resultAttrs)); | |
| } | |
| void call_interface_impl::addArgAndResultAttrs( | |
| Builder &builder, OperationState &result, | |
| ArrayRef<OpAsmParser::Argument> args, ArrayRef<DictionaryAttr> resultAttrs, | |
| StringAttr argAttrsName, StringAttr resAttrsName) { | |
| SmallVector<DictionaryAttr> argAttrs; | |
| for (const auto &arg : args) | |
| argAttrs.push_back(arg.attrs); | |
| addArgAndResultAttrs(builder, result, argAttrs, resultAttrs, argAttrsName, | |
| resAttrsName); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CallOpInterface | |
| //===----------------------------------------------------------------------===// | |
| Operation * | |
| call_interface_impl::resolveCallable(CallOpInterface call, | |
| SymbolTableCollection *symbolTable) { | |
| CallInterfaceCallable callable = call.getCallableForCallee(); | |
| // Operations may not have a callee at all, e.g., when the callee is stored in | |
| // an optional attribute that is missing in invalid IR. | |
| if (!callable) | |
| return nullptr; | |
| if (auto symbolVal = dyn_cast<Value>(callable)) | |
| return symbolVal.getDefiningOp(); | |
| // If the callable isn't a value, lookup the symbol reference. | |
| auto symbolRef = cast<SymbolRefAttr>(callable); | |
| if (symbolTable) | |
| return symbolTable->lookupNearestSymbolFrom(call.getOperation(), symbolRef); | |
| return SymbolTable::lookupNearestSymbolFrom(call.getOperation(), symbolRef); | |
| } | |
| LogicalResult call_interface_impl::verifyCallOpInterface( | |
| CallOpInterface call, TypeRange argumentTypes, TypeRange resultTypes) { | |
| Operation *op = call.getOperation(); | |
| // The forwarded operands are in a 1:1 relationship with the arguments of the | |
| // callee. | |
| OperandRange argOperands = call.getArgOperands(); | |
| if (argOperands.size() != argumentTypes.size()) | |
| return op->emitOpError("incorrect number of operands for callee: expected ") | |
| << argumentTypes.size() << ", but got " << argOperands.size(); | |
| for (unsigned i = 0, e = argOperands.size(); i != e; ++i) { | |
| Type operandType = argOperands[i].getType(); | |
| if (operandType != argumentTypes[i]) | |
| return op->emitOpError("operand type mismatch: expected operand type ") | |
| << argumentTypes[i] << ", but provided " << operandType | |
| << " for operand number " << i; | |
| } | |
| // The forwarded results are in a 1:1 relationship with the results of the | |
| // callee. | |
| ResultRange forwardedResults = call.getForwardedResults(); | |
| if (forwardedResults.size() != resultTypes.size()) | |
| return op->emitOpError("incorrect number of results for callee: expected ") | |
| << resultTypes.size() << ", but got " << forwardedResults.size(); | |
| for (unsigned i = 0, e = forwardedResults.size(); i != e; ++i) { | |
| if (forwardedResults[i].getType() == resultTypes[i]) | |
| continue; | |
| InFlightDiagnostic diag = op->emitOpError("result type mismatch at index ") | |
| << i; | |
| diag.attachNote() << " op result types: " << forwardedResults.getTypes(); | |
| diag.attachNote() << "callee result types: " << resultTypes; | |
| return diag; | |
| } | |
| return success(); | |
| } | |
| LogicalResult | |
| call_interface_impl::verifyCallOpInterface(CallOpInterface call, | |
| CallableOpInterface callable) { | |
| return verifyCallOpInterface(call, callable.getArgumentTypes(), | |
| callable.getResultTypes()); | |
| } | |
| /// Verify `call` against `resolved`, the operation its callee resolved to. | |
| /// Nothing is verified if the callee did not resolve to a callable operation. | |
| static LogicalResult verifyResolvedCallee(CallOpInterface call, | |
| Operation *resolved) { | |
| auto callable = dyn_cast_if_present<CallableOpInterface>(resolved); | |
| if (!callable) | |
| return success(); | |
| return call_interface_impl::verifyCallOpInterface(call, callable); | |
| } | |
| LogicalResult | |
| call_interface_impl::verifyCallOpInterface(CallOpInterface call, | |
| SymbolTableCollection &symbolTable) { | |
| return verifyResolvedCallee(call, call.resolveCallableInTable(&symbolTable)); | |
| } | |
| LogicalResult call_interface_impl::verifyCallOpInterface(CallOpInterface call) { | |
| return verifyResolvedCallee(call, call.resolveCallable()); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CallInterfaces | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Interfaces/CallInterfaces.cpp.inc" |