MLIR 24.0.0git
Async.cpp
Go to the documentation of this file.
1//===- Async.cpp - MLIR Async Operations ----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
12
13using namespace mlir;
14using namespace mlir::async;
15
16#include "mlir/Dialect/Async/IR/AsyncOpsDialect.cpp.inc"
17
18void AsyncDialect::initialize() {
19 addOperations<
20#define GET_OP_LIST
21#include "mlir/Dialect/Async/IR/AsyncOps.cpp.inc"
22 >();
23 addTypes<
24#define GET_TYPEDEF_LIST
25#include "mlir/Dialect/Async/IR/AsyncOpsTypes.cpp.inc"
26 >();
27}
28
29//===----------------------------------------------------------------------===//
30/// ExecuteOp
31//===----------------------------------------------------------------------===//
32
33constexpr char kOperandSegmentSizesAttr[] = "operandSegmentSizes";
34
35OperandRange ExecuteOp::getEntrySuccessorOperands(RegionSuccessor successor) {
36 assert(successor.getSuccessor() == &getBodyRegion() &&
37 "invalid region index");
38 return getBodyOperands();
39}
40
41bool ExecuteOp::areTypesCompatible(Type lhs, Type rhs) {
42 const auto getValueOrTokenType = [](Type type) {
43 if (auto value = llvm::dyn_cast<ValueType>(type))
44 return value.getValueType();
45 return type;
46 };
47 return getValueOrTokenType(lhs) == getValueOrTokenType(rhs);
48}
49
50void ExecuteOp::getSuccessorRegions(RegionBranchPoint point,
52 // The `body` region branch back to the parent operation.
53 if (!point.isParent() &&
54 point.getTerminatorPredecessorOrNull()->getParentRegion() ==
55 &getBodyRegion()) {
56 regions.push_back(RegionSuccessor(getOperation()));
57 return;
58 }
59
60 // Otherwise the successor is the body region.
61 regions.push_back(RegionSuccessor(&getBodyRegion()));
62}
63
64ValueRange ExecuteOp::getSuccessorInputs(RegionSuccessor successor) {
65 return successor.isOperation() ? ValueRange(getBodyResults())
66 : ValueRange(getBodyRegion().getArguments());
67}
68
69void ExecuteOp::build(OpBuilder &builder, OperationState &result,
70 TypeRange resultTypes, ValueRange dependencies,
71 ValueRange operands, BodyBuilderFn bodyBuilder) {
72 OpBuilder::InsertionGuard guard(builder);
73 result.addOperands(dependencies);
74 result.addOperands(operands);
75
76 // Add derived `operandSegmentSizes` attribute based on parsed operands.
77 int32_t numDependencies = dependencies.size();
78 int32_t numOperands = operands.size();
79 auto operandSegmentSizes =
80 builder.getDenseI32ArrayAttr({numDependencies, numOperands});
81 result.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes);
82
83 // First result is always a token, and then `resultTypes` wrapped into
84 // `async.value`.
85 result.addTypes({async::TokenType::get(result.getContext())});
86 for (Type type : resultTypes)
87 result.addTypes(ValueType::get(type));
88
89 // Add a body region with block arguments as unwrapped async value operands.
90 Region *bodyRegion = result.addRegion();
91 Block *bodyBlock = builder.createBlock(bodyRegion);
92 for (Value operand : operands) {
93 auto valueType = llvm::dyn_cast<ValueType>(operand.getType());
94 bodyBlock->addArgument(valueType ? valueType.getValueType()
95 : operand.getType(),
96 operand.getLoc());
97 }
98
99 // Create the default terminator if the builder is not provided and if the
100 // expected result is empty. Otherwise, leave this to the caller
101 // because we don't know which values to return from the execute op.
102 if (resultTypes.empty() && !bodyBuilder) {
103 async::YieldOp::create(builder, result.location, ValueRange());
104 } else if (bodyBuilder) {
105 bodyBuilder(builder, result.location, bodyBlock->getArguments());
106 }
107}
108
109void ExecuteOp::print(OpAsmPrinter &p) {
110 // [%tokens,...]
111 if (!getDependencies().empty())
112 p << " [" << getDependencies() << "]";
113
114 // (%value as %unwrapped: !async.value<!arg.type>, ...)
115 if (!getBodyOperands().empty()) {
116 p << " (";
117 Block *entry = getBodyRegion().empty() ? nullptr : &getBodyRegion().front();
118 llvm::interleaveComma(
119 getBodyOperands(), p, [&, n = 0](Value operand) mutable {
120 Value argument = entry ? entry->getArgument(n++) : Value();
121 p << operand << " as " << argument << ": " << operand.getType();
122 });
123 p << ")";
124 }
125
126 // -> (!async.value<!return.type>, ...)
127 p.printOptionalArrowTypeList(llvm::drop_begin(getResultTypes()));
129 (*this)->getDiscardableAttrDictionary().getValue(),
130 {kOperandSegmentSizesAttr});
131 p << ' ';
132 p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false);
133}
134
135ParseResult ExecuteOp::parse(OpAsmParser &parser, OperationState &result) {
136 MLIRContext *ctx = result.getContext();
137
138 // Sizes of parsed variadic operands, will be updated below after parsing.
139 int32_t numDependencies = 0;
140
141 auto tokenTy = async::TokenType::get(ctx);
142
143 // Parse dependency tokens.
144 if (succeeded(parser.parseOptionalLSquare())) {
146 if (parser.parseOperandList(tokenArgs) ||
147 parser.resolveOperands(tokenArgs, tokenTy, result.operands) ||
148 parser.parseRSquare())
149 return failure();
150
151 numDependencies = tokenArgs.size();
152 }
153
154 // Parse async value operands (%value as %unwrapped : !async.value<!type>).
157 SmallVector<Type, 4> valueTypes;
158
159 // Parse a single instance of `%value as %unwrapped : !async.value<!type>`.
160 auto parseAsyncValueArg = [&]() -> ParseResult {
161 if (parser.parseOperand(valueArgs.emplace_back()) ||
162 parser.parseKeyword("as") ||
163 parser.parseArgument(unwrappedArgs.emplace_back()) ||
164 parser.parseColonType(valueTypes.emplace_back()))
165 return failure();
166
167 auto valueTy = llvm::dyn_cast<ValueType>(valueTypes.back());
168 unwrappedArgs.back().type = valueTy ? valueTy.getValueType() : Type();
169 return success();
170 };
171
172 auto argsLoc = parser.getCurrentLocation();
174 parseAsyncValueArg) ||
175 parser.resolveOperands(valueArgs, valueTypes, argsLoc, result.operands))
176 return failure();
177
178 int32_t numOperands = valueArgs.size();
179
180 // Add derived `operandSegmentSizes` attribute based on parsed operands.
181 auto operandSegmentSizes =
182 parser.getBuilder().getDenseI32ArrayAttr({numDependencies, numOperands});
183 result.addAttribute(kOperandSegmentSizesAttr, operandSegmentSizes);
184
185 // Parse the types of results returned from the async execute op.
186 SmallVector<Type, 4> resultTypes;
187 NamedAttrList attrs;
188 if (parser.parseOptionalArrowTypeList(resultTypes) ||
189 // Async execute first result is always a completion token.
190 parser.addTypeToList(tokenTy, result.types) ||
191 parser.addTypesToList(resultTypes, result.types) ||
192 // Parse operation attributes.
194 return failure();
195
196 result.addAttributes(attrs);
197
198 // Parse asynchronous region.
199 Region *body = result.addRegion();
200 return parser.parseRegion(*body, /*arguments=*/unwrappedArgs);
201}
202
203LogicalResult ExecuteOp::verifyRegions() {
204 // Unwrap async.execute value operands types.
205 auto unwrappedTypes = llvm::map_range(getBodyOperands(), [](Value operand) {
206 return llvm::cast<ValueType>(operand.getType()).getValueType();
207 });
208
209 // Verify that unwrapped argument types matches the body region arguments.
210 if (getBodyRegion().getArgumentTypes() != unwrappedTypes)
211 return emitOpError("async body region argument types do not match the "
212 "execute operation arguments types");
213
214 return success();
215}
216
217//===----------------------------------------------------------------------===//
218/// CreateGroupOp
219//===----------------------------------------------------------------------===//
220
221LogicalResult CreateGroupOp::canonicalize(CreateGroupOp op,
222 PatternRewriter &rewriter) {
223 // Find all `await_all` users of the group.
224 llvm::SmallVector<AwaitAllOp> awaitAllUsers;
225
226 auto isAwaitAll = [&](Operation *op) -> bool {
227 if (AwaitAllOp awaitAll = dyn_cast<AwaitAllOp>(op)) {
228 awaitAllUsers.push_back(awaitAll);
229 return true;
230 }
231 return false;
232 };
233
234 // Check if all users of the group are `await_all` operations.
235 if (!llvm::all_of(op->getUsers(), isAwaitAll))
236 return failure();
237
238 // If group is only awaited without adding anything to it, we can safely erase
239 // the create operation and all users.
240 for (AwaitAllOp awaitAll : awaitAllUsers)
241 rewriter.eraseOp(awaitAll);
242 rewriter.eraseOp(op);
243
244 return success();
245}
246
247//===----------------------------------------------------------------------===//
248/// AwaitOp
249//===----------------------------------------------------------------------===//
250
251void AwaitOp::build(OpBuilder &builder, OperationState &result, Value operand,
253 result.addOperands({operand});
254 result.attributes.append(attrs.begin(), attrs.end());
255
256 // Add unwrapped async.value type to the returned values types.
257 if (auto valueType = llvm::dyn_cast<ValueType>(operand.getType()))
258 result.addTypes(valueType.getValueType());
259}
260
261static ParseResult parseAwaitResultType(OpAsmParser &parser, Type &operandType,
262 Type &resultType) {
263 if (parser.parseType(operandType))
264 return failure();
265
266 // Add unwrapped async.value type to the returned values types.
267 if (auto valueType = llvm::dyn_cast<ValueType>(operandType))
268 resultType = valueType.getValueType();
269
270 return success();
271}
272
274 Type operandType, Type resultType) {
275 p << operandType;
276}
277
278LogicalResult AwaitOp::verify() {
279 Type argType = getOperand().getType();
280
281 // Awaiting on a token does not have any results.
282 if (llvm::isa<async::TokenType>(argType) && !getResultTypes().empty())
283 return emitOpError("awaiting on a token must have empty result");
284
285 // Awaiting on a value unwraps the async value type.
286 if (auto value = llvm::dyn_cast<ValueType>(argType)) {
287 if (*getResultType() != value.getValueType())
288 return emitOpError() << "result type " << *getResultType()
289 << " does not match async value type "
290 << value.getValueType();
291 }
292
293 return success();
294}
295
296//===----------------------------------------------------------------------===//
297// FuncOp
298//===----------------------------------------------------------------------===//
299
300void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
301 FunctionType type, ArrayRef<NamedAttribute> attrs,
302 ArrayRef<DictionaryAttr> argAttrs) {
303 state.getOrAddProperties<Properties>().sym_name = builder.getStringAttr(name);
304 state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
305
306 state.attributes.append(attrs.begin(), attrs.end());
307 state.addRegion();
308
309 if (argAttrs.empty())
310 return;
311 assert(type.getNumInputs() == argAttrs.size());
313 builder, state, argAttrs, /*resultAttrs=*/{},
314 getArgAttrsAttrName(state.name), getResAttrsAttrName(state.name));
315}
316
317ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
318 auto buildFuncType =
319 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
321 std::string &) { return builder.getFunctionType(argTypes, results); };
322
324 parser, result, /*allowVariadic=*/false,
325 getFunctionTypeAttrName(result.name), buildFuncType,
326 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
327}
328
329void FuncOp::print(OpAsmPrinter &p) {
331 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
332 getArgAttrsAttrName(), getResAttrsAttrName());
333}
334
335/// Check that the result type of async.func is not void and must be
336/// some async token or async values.
337LogicalResult FuncOp::verify() {
338 auto resultTypes = getResultTypes();
339 if (resultTypes.empty())
340 return emitOpError()
341 << "result is expected to be at least of size 1, but got "
342 << resultTypes.size();
343
344 for (unsigned i = 0, e = resultTypes.size(); i != e; ++i) {
345 auto type = resultTypes[i];
346 if (!llvm::isa<async::TokenType>(type) && !llvm::isa<ValueType>(type))
347 return emitOpError() << "result type must be async value type or async "
348 "token type, but got "
349 << type;
350 // We only allow AsyncToken appear as the first return value
351 if (llvm::isa<async::TokenType>(type) && i != 0) {
352 return emitOpError()
353 << " results' (optional) async token type is expected "
354 "to appear as the 1st return value, but got "
355 << i + 1;
356 }
357 }
358
359 return success();
360}
361
362//===----------------------------------------------------------------------===//
363/// CallOp
364//===----------------------------------------------------------------------===//
365
366LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
367 // Check that the callee attribute was specified.
368 auto fnAttr = getCalleeAttr();
369 if (!fnAttr)
370 return emitOpError("requires a 'callee' symbol reference attribute");
371 FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
372 if (!fn)
373 return emitOpError() << "'" << fnAttr.getValue()
374 << "' does not reference a valid async function";
375
376 // Verify that the operand and result types match the callee.
378}
379
380FunctionType CallOp::getCalleeType() {
381 return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
382}
383
384//===----------------------------------------------------------------------===//
385/// ReturnOp
386//===----------------------------------------------------------------------===//
387
388LogicalResult ReturnOp::verify() {
389 auto funcOp = (*this)->getParentOfType<FuncOp>();
390 ArrayRef<Type> resultTypes = funcOp.isStateful()
391 ? funcOp.getResultTypes().drop_front()
392 : funcOp.getResultTypes();
393 // Get the underlying value types from async types returned from the
394 // parent `async.func` operation.
395 auto types = llvm::map_range(resultTypes, [](const Type &result) {
396 return llvm::cast<ValueType>(result).getValueType();
397 });
398
399 if (getOperandTypes() != types)
400 return emitOpError("operand types do not match the types returned from "
401 "the parent FuncOp");
402
403 return success();
404}
405
406//===----------------------------------------------------------------------===//
407// TableGen'd op method definitions
408//===----------------------------------------------------------------------===//
409
410#define GET_OP_CLASSES
411#include "mlir/Dialect/Async/IR/AsyncOps.cpp.inc"
412
413//===----------------------------------------------------------------------===//
414// TableGen'd type method definitions
415//===----------------------------------------------------------------------===//
416
417#define GET_TYPEDEF_CLASSES
418#include "mlir/Dialect/Async/IR/AsyncOpsTypes.cpp.inc"
419
420void ValueType::print(AsmPrinter &printer) const {
421 printer << "<";
422 printer.printType(getValueType());
423 printer << '>';
424}
425
426Type ValueType::parse(mlir::AsmParser &parser) {
427 Type ty;
428 if (parser.parseLess() || parser.parseType(ty) || parser.parseGreater()) {
429 parser.emitError(parser.getNameLoc(), "failed to parse async value type");
430 return Type();
431 }
432 return ValueType::get(ty);
433}
return success()
static ParseResult parseAwaitResultType(OpAsmParser &parser, Type &operandType, Type &resultType)
Definition Async.cpp:261
constexpr char kOperandSegmentSizesAttr[]
ExecuteOp.
Definition Async.cpp:33
static void printAwaitResultType(OpAsmPrinter &p, Operation *op, Type operandType, Type resultType)
Definition Async.cpp:273
b getContext())
static Type getValueType(Attribute attr)
Definition SPIRVOps.cpp:845
This base class exposes generic asm parser hooks, usable across the various derived parsers.
@ OptionalParen
Parens supporting zero or more operands, or nothing.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseRSquare()=0
Parse a ] token.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
ParseResult addTypesToList(ArrayRef< Type > types, SmallVectorImpl< Type > &result)
Add the specified types to the end of the specified type list and return success.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
This base class exposes generic asm printer hooks, usable across the various derived printers.
virtual void printType(Type type)
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
Block represents an ordered list of Operations.
Definition Block.h:34
bool empty()
Definition Block.h:173
BlockArgument getArgument(unsigned i)
Definition Block.h:154
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
BlockArgListType getArguments()
Definition Block.h:112
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:84
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:71
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgument(Argument &result, bool allowType=false, bool allowAttrs=false)=0
Parse a single argument with the following syntax:
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
RegionBranchTerminatorOpInterface getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:392
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
A named class for passing around the variadic flag.
LogicalResult verifyCallOpInterface(CallOpInterface call, TypeRange argumentTypes, TypeRange resultTypes)
Verify that the forwarded operands and results of call are in a 1:1 relationship with the given argum...
void addArgAndResultAttrs(Builder &builder, OperationState &result, ArrayRef< DictionaryAttr > argAttrs, ArrayRef< DictionaryAttr > resultAttrs, StringAttr argAttrsName, StringAttr resAttrsName)
Adds argument and result attributes, provided as argAttrs and resultAttrs arguments,...
void printFunctionOp(OpAsmPrinter &p, FunctionOpInterface op, bool isVariadic, StringRef typeAttrName, StringAttr argAttrsName, StringAttr resAttrsName)
Printer implementation for function-like operations.
ParseResult parseFunctionOp(OpAsmParser &parser, OperationState &result, bool allowVariadic, StringAttr typeAttrName, FuncTypeBuilder funcTypeBuilder, StringAttr argAttrsName, StringAttr resAttrsName)
Parser implementation for function-like operations.
Include the generated interface declarations.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
T & getOrAddProperties()
Get (or create) the properties of the provided type to be set on the operation on creation.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
Region * addRegion()
Create a region that should be attached to the operation.