Skip to content

Commit 9613a85

Browse files
committed
[mlir:PDL] Rework errors for pdl.operations with non-inferrable results
We currently emit an error during verification if a pdl.operation with non-inferrable results is used within a rewrite. This allows for catching some errors during compile time, but is slightly broken. For one, the verification at the PDL level assumes that all dialects have been loaded, which is true at run time, but may not be true when the PDL is generated (such as via PDLL). This commit fixes this by not emitting the error if the operation isn't registered, i.e. it uses the `mightHave` variant of trait/interface methods. Secondly, we currently don't verify when a pdl.operation has no explicit results, but the operation being created is known to expect at least one. This commit adds a heuristic error to detect these cases when possible and fail. We can't always capture when the user made an error, but we can capture the most common case where the user expected an operation to infer its result types (when it actually isn't possible). Differential Revision: https://reviews.llvm.org/D124583
1 parent d4381b3 commit 9613a85

File tree

4 files changed

+74
-7
lines changed

4 files changed

+74
-7
lines changed

mlir/include/mlir/Dialect/PDL/IR/PDLOps.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ def PDL_OperationOp : PDL_Op<"operation", [AttrSizedOperandSegments]> {
369369
/// Returns true if the operation type referenced supports result type
370370
/// inference.
371371
bool hasTypeInference();
372+
373+
/// Returns true if the operation type referenced might support result type
374+
/// inference, i.e. it supports type reference or is currently not
375+
/// registered in the context. Returns false if the root operation name
376+
/// has not been set.
377+
bool mightHaveTypeInference();
372378
}];
373379
let hasVerifier = 1;
374380
}

mlir/include/mlir/IR/OperationSupport.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ class OperationName {
167167
return impl->interfaceMap.contains(interfaceID);
168168
}
169169

170+
/// Returns true if the operation *might* have the provided interface. This
171+
/// means that either the operation is unregistered, or it was registered with
172+
/// the provide interface.
173+
template <typename T>
174+
bool mightHaveInterface() const {
175+
return mightHaveInterface(TypeID::get<T>());
176+
}
177+
bool mightHaveInterface(TypeID interfaceID) const {
178+
return !isRegistered() || hasInterface(interfaceID);
179+
}
180+
170181
/// Return the dialect this operation is registered to if the dialect is
171182
/// loaded in the context, or nullptr if the dialect isn't loaded.
172183
Dialect *getDialect() const {

mlir/lib/Dialect/PDL/IR/PDL.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,36 @@ static LogicalResult verifyResultTypesAreInferrable(OperationOp op,
198198
if (llvm::any_of(op.op().getUses(), canInferTypeFromUse))
199199
return success();
200200

201+
// Handle the case where the operation has no explicit result types.
202+
if (resultTypes.empty()) {
203+
// If we don't know the concrete operation, don't attempt any verification.
204+
// We can't make assumptions if we don't know the concrete operation.
205+
Optional<StringRef> rawOpName = op.name();
206+
if (!rawOpName)
207+
return success();
208+
Optional<RegisteredOperationName> opName =
209+
RegisteredOperationName::lookup(*rawOpName, op.getContext());
210+
if (!opName)
211+
return success();
212+
213+
// If no explicit result types were provided, check to see if the operation
214+
// expected at least one result. This doesn't cover all cases, but this
215+
// should cover many cases in which the user intended to infer the results
216+
// of an operation, but it isn't actually possible.
217+
bool expectedAtLeastOneResult =
218+
!opName->hasTrait<OpTrait::ZeroResult>() &&
219+
!opName->hasTrait<OpTrait::VariadicResults>();
220+
if (expectedAtLeastOneResult) {
221+
return op
222+
.emitOpError("must have inferable or constrained result types when "
223+
"nested within `pdl.rewrite`")
224+
.attachNote()
225+
.append("operation is created in a non-inferrable context, but '",
226+
*opName, "' does not implement InferTypeOpInterface");
227+
}
228+
return success();
229+
}
230+
201231
// Otherwise, make sure each of the types can be inferred.
202232
for (const auto &it : llvm::enumerate(resultTypes)) {
203233
Operation *resultTypeOp = it.value().getDefiningOp();
@@ -248,7 +278,7 @@ LogicalResult OperationOp::verify() {
248278

249279
// If the operation is within a rewrite body and doesn't have type inference,
250280
// ensure that the result types can be resolved.
251-
if (isWithinRewrite && !hasTypeInference()) {
281+
if (isWithinRewrite && !mightHaveTypeInference()) {
252282
if (failed(verifyResultTypesAreInferrable(*this, types())))
253283
return failure();
254284
}
@@ -257,12 +287,18 @@ LogicalResult OperationOp::verify() {
257287
}
258288

259289
bool OperationOp::hasTypeInference() {
260-
Optional<StringRef> opName = name();
261-
if (!opName)
262-
return false;
290+
if (Optional<StringRef> rawOpName = name()) {
291+
OperationName opName(*rawOpName, getContext());
292+
return opName.hasInterface<InferTypeOpInterface>();
293+
}
294+
return false;
295+
}
263296

264-
if (auto rInfo = RegisteredOperationName::lookup(*opName, getContext()))
265-
return rInfo->hasInterface<InferTypeOpInterface>();
297+
bool OperationOp::mightHaveTypeInference() {
298+
if (Optional<StringRef> rawOpName = name()) {
299+
OperationName opName(*rawOpName, getContext());
300+
return opName.mightHaveInterface<InferTypeOpInterface>();
301+
}
266302
return false;
267303
}
268304

mlir/test/Dialect/PDL/invalid.mlir

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,21 @@ pdl.pattern : benefit(1) {
136136

137137
// expected-error@below {{op must have inferable or constrained result types when nested within `pdl.rewrite`}}
138138
// expected-note@below {{result type #0 was not constrained}}
139-
%newOp = operation "foo.op" -> (%type : !pdl.type)
139+
%newOp = operation "builtin.unrealized_conversion_cast" -> (%type : !pdl.type)
140+
}
141+
}
142+
143+
// -----
144+
145+
// Unused operation only necessary to ensure the func dialect is loaded.
146+
func.func private @unusedOpToLoadFuncDialect()
147+
148+
pdl.pattern : benefit(1) {
149+
%op = operation "foo.op"
150+
rewrite %op {
151+
// expected-error@below {{op must have inferable or constrained result types when nested within `pdl.rewrite`}}
152+
// expected-note@below {{operation is created in a non-inferrable context, but 'func.constant' does not implement InferTypeOpInterface}}
153+
%newOp = operation "func.constant"
140154
}
141155
}
142156

0 commit comments

Comments
 (0)