blob: e4660f9ee758e8c81d06ca67c405df91d6c5f9fd [file] [edit]
//===- 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"