2020-09-28 22:47:00 -07:00
|
|
|
//===- Async.cpp - MLIR Async Operations ----------------------------------===//
|
|
|
|
//
|
|
|
|
// 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/Dialect/Async/IR/Async.h"
|
|
|
|
|
|
|
|
#include "mlir/IR/DialectImplementation.h"
|
|
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
|
|
|
2020-10-08 13:28:09 -07:00
|
|
|
using namespace mlir;
|
|
|
|
using namespace mlir::async;
|
2020-09-28 22:47:00 -07:00
|
|
|
|
2021-06-28 22:54:11 +00:00
|
|
|
#include "mlir/Dialect/Async/IR/AsyncOpsDialect.cpp.inc"
|
|
|
|
|
2021-07-28 15:25:00 -07:00
|
|
|
constexpr StringRef AsyncDialect::kAllowedToBlockAttrName;
|
|
|
|
|
2020-09-28 22:47:00 -07:00
|
|
|
void AsyncDialect::initialize() {
|
|
|
|
addOperations<
|
|
|
|
#define GET_OP_LIST
|
|
|
|
#include "mlir/Dialect/Async/IR/AsyncOps.cpp.inc"
|
|
|
|
>();
|
2021-01-25 14:14:12 -08:00
|
|
|
addTypes<
|
|
|
|
#define GET_TYPEDEF_LIST
|
|
|
|
#include "mlir/Dialect/Async/IR/AsyncOpsTypes.cpp.inc"
|
|
|
|
>();
|
2020-09-29 13:55:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// YieldOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-02 10:24:48 -08:00
|
|
|
LogicalResult YieldOp::verify() {
|
2020-09-29 13:55:33 -07:00
|
|
|
// Get the underlying value types from async values returned from the
|
|
|
|
// parent `async.execute` operation.
|
2022-02-02 10:24:48 -08:00
|
|
|
auto executeOp = (*this)->getParentOfType<ExecuteOp>();
|
2020-10-08 13:28:09 -07:00
|
|
|
auto types = llvm::map_range(executeOp.results(), [](const OpResult &result) {
|
2020-09-29 13:55:33 -07:00
|
|
|
return result.getType().cast<ValueType>().getValueType();
|
|
|
|
});
|
|
|
|
|
2022-02-02 10:24:48 -08:00
|
|
|
if (getOperandTypes() != types)
|
|
|
|
return emitOpError("operand types do not match the types returned from "
|
|
|
|
"the parent ExecuteOp");
|
2020-09-29 13:55:33 -07:00
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2021-08-19 16:28:16 +03:00
|
|
|
MutableOperandRange
|
|
|
|
YieldOp::getMutableSuccessorOperands(Optional<unsigned> index) {
|
|
|
|
return operandsMutable();
|
|
|
|
}
|
|
|
|
|
2020-09-29 13:55:33 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// ExecuteOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-10-08 13:28:09 -07:00
|
|
|
constexpr char kOperandSegmentSizesAttr[] = "operand_segment_sizes";
|
|
|
|
|
2021-08-19 16:28:16 +03:00
|
|
|
OperandRange ExecuteOp::getSuccessorEntryOperands(unsigned index) {
|
|
|
|
assert(index == 0 && "invalid region index");
|
|
|
|
return operands();
|
|
|
|
}
|
|
|
|
|
[mlir] Region/BranchOpInterface: Allow implicit type conversions along control-flow edges
RegionBranchOpInterface and BranchOpInterface are allowed to make implicit type conversions along control-flow edges. In effect, this adds an interface method, `areTypesCompatible`, to both interfaces, which should return whether the types of corresponding successor operands and block arguments are compatible. Users of the interfaces, here on forth, must be aware that types may mismatch, although current users (in MLIR core), are not affected by this change. By default, type equality is used.
`async.execute` already has unequal types along control-flow edges (`!async.value<f32>` vs. `f32`), but it opted out of calling `RegionBranchOpInterface::verifyTypes` in its verifier. That method has now been removed and `RegionBranchOpInterface` will verify types along control edges by default in its verifier.
Reviewed By: rriddle
Differential Revision: https://reviews.llvm.org/D120790
2022-03-04 20:23:24 +00:00
|
|
|
bool ExecuteOp::areTypesCompatible(Type lhs, Type rhs) {
|
|
|
|
const auto getValueOrTokenType = [](Type type) {
|
|
|
|
if (auto value = type.dyn_cast<ValueType>())
|
|
|
|
return value.getValueType();
|
|
|
|
return type;
|
|
|
|
};
|
|
|
|
return getValueOrTokenType(lhs) == getValueOrTokenType(rhs);
|
|
|
|
}
|
|
|
|
|
2020-11-11 01:38:51 -08:00
|
|
|
void ExecuteOp::getSuccessorRegions(Optional<unsigned> index,
|
2021-08-19 16:28:16 +03:00
|
|
|
ArrayRef<Attribute>,
|
2020-11-11 01:38:51 -08:00
|
|
|
SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
|
|
// The `body` region branch back to the parent operation.
|
|
|
|
if (index.hasValue()) {
|
2021-08-19 16:28:16 +03:00
|
|
|
assert(*index == 0 && "invalid region index");
|
|
|
|
regions.push_back(RegionSuccessor(results()));
|
2020-11-11 01:38:51 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise the successor is the body region.
|
2021-08-19 16:28:16 +03:00
|
|
|
regions.push_back(RegionSuccessor(&body(), body().getArguments()));
|
2020-11-11 01:38:51 -08:00
|
|
|
}
|
|
|
|
|
2020-11-13 03:01:52 -08:00
|
|
|
void ExecuteOp::build(OpBuilder &builder, OperationState &result,
|
|
|
|
TypeRange resultTypes, ValueRange dependencies,
|
|
|
|
ValueRange operands, BodyBuilderFn bodyBuilder) {
|
|
|
|
|
|
|
|
result.addOperands(dependencies);
|
|
|
|
result.addOperands(operands);
|
|
|
|
|
|
|
|
// Add derived `operand_segment_sizes` attribute based on parsed operands.
|
|
|
|
int32_t numDependencies = dependencies.size();
|
|
|
|
int32_t numOperands = operands.size();
|
|
|
|
auto operandSegmentSizes = DenseIntElementsAttr::get(
|
2020-12-17 12:24:45 -08:00
|
|
|
VectorType::get({2}, builder.getIntegerType(32)),
|
2020-11-13 03:01:52 -08:00
|
|
|
{numDependencies, numOperands});
|
|
|
|
result.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes);
|
|
|
|
|
|
|
|
// First result is always a token, and then `resultTypes` wrapped into
|
|
|
|
// `async.value`.
|
|
|
|
result.addTypes({TokenType::get(result.getContext())});
|
|
|
|
for (Type type : resultTypes)
|
|
|
|
result.addTypes(ValueType::get(type));
|
|
|
|
|
|
|
|
// Add a body region with block arguments as unwrapped async value operands.
|
|
|
|
Region *bodyRegion = result.addRegion();
|
|
|
|
bodyRegion->push_back(new Block);
|
|
|
|
Block &bodyBlock = bodyRegion->front();
|
|
|
|
for (Value operand : operands) {
|
|
|
|
auto valueType = operand.getType().dyn_cast<ValueType>();
|
|
|
|
bodyBlock.addArgument(valueType ? valueType.getValueType()
|
2022-01-18 18:28:51 -08:00
|
|
|
: operand.getType(),
|
|
|
|
operand.getLoc());
|
2020-11-13 03:01:52 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the default terminator if the builder is not provided and if the
|
|
|
|
// expected result is empty. Otherwise, leave this to the caller
|
|
|
|
// because we don't know which values to return from the execute op.
|
|
|
|
if (resultTypes.empty() && !bodyBuilder) {
|
|
|
|
OpBuilder::InsertionGuard guard(builder);
|
|
|
|
builder.setInsertionPointToStart(&bodyBlock);
|
|
|
|
builder.create<async::YieldOp>(result.location, ValueRange());
|
|
|
|
} else if (bodyBuilder) {
|
|
|
|
OpBuilder::InsertionGuard guard(builder);
|
|
|
|
builder.setInsertionPointToStart(&bodyBlock);
|
|
|
|
bodyBuilder(builder, result.location, bodyBlock.getArguments());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 17:54:04 -08:00
|
|
|
void ExecuteOp::print(OpAsmPrinter &p) {
|
2020-10-08 13:28:09 -07:00
|
|
|
// [%tokens,...]
|
2022-02-07 17:54:04 -08:00
|
|
|
if (!dependencies().empty())
|
|
|
|
p << " [" << dependencies() << "]";
|
2020-10-08 13:28:09 -07:00
|
|
|
|
|
|
|
// (%value as %unwrapped: !async.value<!arg.type>, ...)
|
2022-02-07 17:54:04 -08:00
|
|
|
if (!operands().empty()) {
|
2020-10-08 13:28:09 -07:00
|
|
|
p << " (";
|
2022-02-07 17:54:04 -08:00
|
|
|
Block *entry = body().empty() ? nullptr : &body().front();
|
|
|
|
llvm::interleaveComma(operands(), p, [&, n = 0](Value operand) mutable {
|
2021-06-04 13:52:34 +02:00
|
|
|
Value argument = entry ? entry->getArgument(n++) : Value();
|
|
|
|
p << operand << " as " << argument << ": " << operand.getType();
|
2020-10-08 13:28:09 -07:00
|
|
|
});
|
|
|
|
p << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
// -> (!async.value<!return.type>, ...)
|
2022-02-07 17:54:04 -08:00
|
|
|
p.printOptionalArrowTypeList(llvm::drop_begin(getResultTypes()));
|
|
|
|
p.printOptionalAttrDictWithKeyword((*this)->getAttrs(),
|
2021-02-25 19:35:53 +01:00
|
|
|
{kOperandSegmentSizesAttr});
|
2022-01-18 07:47:25 +00:00
|
|
|
p << ' ';
|
2022-02-07 17:54:04 -08:00
|
|
|
p.printRegion(body(), /*printEntryBlockArgs=*/false);
|
2020-09-29 13:55:33 -07:00
|
|
|
}
|
|
|
|
|
2022-02-07 17:54:04 -08:00
|
|
|
ParseResult ExecuteOp::parse(OpAsmParser &parser, OperationState &result) {
|
2020-09-29 13:55:33 -07:00
|
|
|
MLIRContext *ctx = result.getContext();
|
|
|
|
|
2020-10-08 13:28:09 -07:00
|
|
|
// Sizes of parsed variadic operands, will be updated below after parsing.
|
|
|
|
int32_t numDependencies = 0;
|
|
|
|
|
|
|
|
auto tokenTy = TokenType::get(ctx);
|
|
|
|
|
|
|
|
// Parse dependency tokens.
|
|
|
|
if (succeeded(parser.parseOptionalLSquare())) {
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> tokenArgs;
|
|
|
|
if (parser.parseOperandList(tokenArgs) ||
|
|
|
|
parser.resolveOperands(tokenArgs, tokenTy, result.operands) ||
|
|
|
|
parser.parseRSquare())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
numDependencies = tokenArgs.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse async value operands (%value as %unwrapped : !async.value<!type>).
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> valueArgs;
|
|
|
|
SmallVector<OpAsmParser::OperandType, 4> unwrappedArgs;
|
|
|
|
SmallVector<Type, 4> valueTypes;
|
|
|
|
SmallVector<Type, 4> unwrappedTypes;
|
|
|
|
|
2021-09-20 18:27:40 -07:00
|
|
|
// Parse a single instance of `%value as %unwrapped : !async.value<!type>`.
|
|
|
|
auto parseAsyncValueArg = [&]() -> ParseResult {
|
|
|
|
if (parser.parseOperand(valueArgs.emplace_back()) ||
|
|
|
|
parser.parseKeyword("as") ||
|
|
|
|
parser.parseOperand(unwrappedArgs.emplace_back()) ||
|
|
|
|
parser.parseColonType(valueTypes.emplace_back()))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
auto valueTy = valueTypes.back().dyn_cast<ValueType>();
|
|
|
|
unwrappedTypes.emplace_back(valueTy ? valueTy.getValueType() : Type());
|
|
|
|
|
|
|
|
return success();
|
|
|
|
};
|
|
|
|
|
|
|
|
auto argsLoc = parser.getCurrentLocation();
|
|
|
|
if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::OptionalParen,
|
|
|
|
parseAsyncValueArg) ||
|
|
|
|
parser.resolveOperands(valueArgs, valueTypes, argsLoc, result.operands))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
int32_t numOperands = valueArgs.size();
|
2020-10-08 13:28:09 -07:00
|
|
|
|
|
|
|
// Add derived `operand_segment_sizes` attribute based on parsed operands.
|
|
|
|
auto operandSegmentSizes = DenseIntElementsAttr::get(
|
|
|
|
VectorType::get({2}, parser.getBuilder().getI32Type()),
|
|
|
|
{numDependencies, numOperands});
|
|
|
|
result.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes);
|
|
|
|
|
|
|
|
// Parse the types of results returned from the async execute op.
|
|
|
|
SmallVector<Type, 4> resultTypes;
|
|
|
|
if (parser.parseOptionalArrowTypeList(resultTypes))
|
2020-09-29 13:55:33 -07:00
|
|
|
return failure();
|
|
|
|
|
2020-10-08 13:28:09 -07:00
|
|
|
// Async execute first result is always a completion token.
|
|
|
|
parser.addTypeToList(tokenTy, result.types);
|
|
|
|
parser.addTypesToList(resultTypes, result.types);
|
|
|
|
|
2020-09-29 13:55:33 -07:00
|
|
|
// Parse operation attributes.
|
|
|
|
NamedAttrList attrs;
|
2020-10-08 13:28:09 -07:00
|
|
|
if (parser.parseOptionalAttrDictWithKeyword(attrs))
|
2020-09-29 13:55:33 -07:00
|
|
|
return failure();
|
|
|
|
result.addAttributes(attrs);
|
|
|
|
|
2020-10-08 13:28:09 -07:00
|
|
|
// Parse asynchronous region.
|
|
|
|
Region *body = result.addRegion();
|
|
|
|
if (parser.parseRegion(*body, /*arguments=*/{unwrappedArgs},
|
|
|
|
/*argTypes=*/{unwrappedTypes},
|
2022-01-19 23:44:43 +00:00
|
|
|
/*argLocations=*/{},
|
2020-10-08 13:28:09 -07:00
|
|
|
/*enableNameShadowing=*/false))
|
2020-09-29 13:55:33 -07:00
|
|
|
return failure();
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-02 10:24:48 -08:00
|
|
|
LogicalResult ExecuteOp::verify() {
|
2020-10-08 13:28:09 -07:00
|
|
|
// Unwrap async.execute value operands types.
|
2022-02-02 10:24:48 -08:00
|
|
|
auto unwrappedTypes = llvm::map_range(operands(), [](Value operand) {
|
2020-10-08 13:28:09 -07:00
|
|
|
return operand.getType().cast<ValueType>().getValueType();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Verify that unwrapped argument types matches the body region arguments.
|
2022-02-02 10:24:48 -08:00
|
|
|
if (body().getArgumentTypes() != unwrappedTypes)
|
|
|
|
return emitOpError("async body region argument types do not match the "
|
|
|
|
"execute operation arguments types");
|
2020-10-08 13:28:09 -07:00
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
2020-09-29 13:55:33 -07:00
|
|
|
|
2021-06-27 17:44:31 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// CreateGroupOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult CreateGroupOp::canonicalize(CreateGroupOp op,
|
|
|
|
PatternRewriter &rewriter) {
|
|
|
|
// Find all `await_all` users of the group.
|
|
|
|
llvm::SmallVector<AwaitAllOp> awaitAllUsers;
|
|
|
|
|
|
|
|
auto isAwaitAll = [&](Operation *op) -> bool {
|
|
|
|
if (AwaitAllOp awaitAll = dyn_cast<AwaitAllOp>(op)) {
|
|
|
|
awaitAllUsers.push_back(awaitAll);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check if all users of the group are `await_all` operations.
|
|
|
|
if (!llvm::all_of(op->getUsers(), isAwaitAll))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// If group is only awaited without adding anything to it, we can safely erase
|
|
|
|
// the create operation and all users.
|
|
|
|
for (AwaitAllOp awaitAll : awaitAllUsers)
|
|
|
|
rewriter.eraseOp(awaitAll);
|
|
|
|
rewriter.eraseOp(op);
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-10-12 14:38:42 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// AwaitOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void AwaitOp::build(OpBuilder &builder, OperationState &result, Value operand,
|
|
|
|
ArrayRef<NamedAttribute> attrs) {
|
|
|
|
result.addOperands({operand});
|
|
|
|
result.attributes.append(attrs.begin(), attrs.end());
|
|
|
|
|
|
|
|
// Add unwrapped async.value type to the returned values types.
|
|
|
|
if (auto valueType = operand.getType().dyn_cast<ValueType>())
|
|
|
|
result.addTypes(valueType.getValueType());
|
|
|
|
}
|
|
|
|
|
|
|
|
static ParseResult parseAwaitResultType(OpAsmParser &parser, Type &operandType,
|
|
|
|
Type &resultType) {
|
|
|
|
if (parser.parseType(operandType))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Add unwrapped async.value type to the returned values types.
|
|
|
|
if (auto valueType = operandType.dyn_cast<ValueType>())
|
|
|
|
resultType = valueType.getValueType();
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printAwaitResultType(OpAsmPrinter &p, Operation *op,
|
|
|
|
Type operandType, Type resultType) {
|
2020-10-12 14:38:42 -07:00
|
|
|
p << operandType;
|
|
|
|
}
|
|
|
|
|
2022-02-02 10:24:48 -08:00
|
|
|
LogicalResult AwaitOp::verify() {
|
|
|
|
Type argType = operand().getType();
|
2020-10-12 14:38:42 -07:00
|
|
|
|
|
|
|
// Awaiting on a token does not have any results.
|
2022-02-02 10:24:48 -08:00
|
|
|
if (argType.isa<TokenType>() && !getResultTypes().empty())
|
|
|
|
return emitOpError("awaiting on a token must have empty result");
|
2020-10-12 14:38:42 -07:00
|
|
|
|
|
|
|
// Awaiting on a value unwraps the async value type.
|
|
|
|
if (auto value = argType.dyn_cast<ValueType>()) {
|
2022-02-02 10:24:48 -08:00
|
|
|
if (*getResultType() != value.getValueType())
|
|
|
|
return emitOpError() << "result type " << *getResultType()
|
|
|
|
<< " does not match async value type "
|
|
|
|
<< value.getValueType();
|
2020-10-12 14:38:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2021-01-25 14:14:12 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TableGen'd op method definitions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-09-28 22:47:00 -07:00
|
|
|
#define GET_OP_CLASSES
|
|
|
|
#include "mlir/Dialect/Async/IR/AsyncOps.cpp.inc"
|
2021-01-25 14:14:12 -08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TableGen'd type method definitions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#define GET_TYPEDEF_CLASSES
|
|
|
|
#include "mlir/Dialect/Async/IR/AsyncOpsTypes.cpp.inc"
|
|
|
|
|
2021-11-11 06:12:06 +00:00
|
|
|
void ValueType::print(AsmPrinter &printer) const {
|
2021-01-25 14:14:12 -08:00
|
|
|
printer << "<";
|
|
|
|
printer.printType(getValueType());
|
|
|
|
printer << '>';
|
|
|
|
}
|
|
|
|
|
2021-11-11 06:12:06 +00:00
|
|
|
Type ValueType::parse(mlir::AsmParser &parser) {
|
2021-01-25 14:14:12 -08:00
|
|
|
Type ty;
|
|
|
|
if (parser.parseLess() || parser.parseType(ty) || parser.parseGreater()) {
|
|
|
|
parser.emitError(parser.getNameLoc(), "failed to parse async value type");
|
|
|
|
return Type();
|
|
|
|
}
|
|
|
|
return ValueType::get(ty);
|
|
|
|
}
|