[MLIR] Fix crash in FrozenRewritePatternSet when PDL lowering is skipped by debug counter (#186159)

When using --mlir-debug-counter=pass-execution-skip=N, the MLIR debug
counter can skip the internal PDL-to-PDLInterp lowering pass that runs
inside FrozenRewritePatternSet's constructor. This caused an assertion
failure in PDLByteCode::Generator::generate() because the PDL module
wasn't properly converted to the interpreter dialect.

The fix adds a check after the PDL lowering pipeline runs to verify that
the expected matcher function symbol was produced. If the symbol is
absent (e.g., because the lowering was skipped by a debug counter),
bytecode generation is skipped entirely and PDL patterns are not
applied. This allows debug counter bisection to work without crashing.

Fixes #131441
Fixes #128342

Assisted-by: Claude Code
This commit is contained in:
Mehdi Amini 2026-03-18 16:01:49 +01:00 committed by GitHub
parent bf46a95f2c
commit 5e3202749a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16 additions and 0 deletions

View File

@ -18,6 +18,7 @@ using namespace mlir;
#if MLIR_ENABLE_PDL_IN_PATTERNMATCH
#include "mlir/Conversion/PDLToPDLInterp/PDLToPDLInterp.h"
#include "mlir/Dialect/PDL/IR/PDLOps.h"
#include "mlir/Dialect/PDLInterp/IR/PDLInterp.h"
static LogicalResult
convertPDLToPDLInterp(ModuleOp pdlModule,
@ -134,6 +135,15 @@ FrozenRewritePatternSet::FrozenRewritePatternSet(
llvm::report_fatal_error(
"failed to lower PDL pattern module to the PDL Interpreter");
// Verify that the PDL module was actually lowered to the interpreter
// dialect. If the lowering pass was skipped (e.g., by a debug counter
// via --mlir-debug-counter), the matcher function will not be present and
// we skip bytecode construction. PDL patterns will not be applied in this
// case.
if (!pdlModule.lookupSymbol(
pdl_interp::PDLInterpDialect::getMatcherFunctionName()))
return;
// Generate the pdl bytecode.
impl->pdlByteCode = std::make_unique<detail::PDLByteCode>(
pdlModule, pdlPatterns.takeConfigs(), configMap,

View File

@ -3,6 +3,7 @@
// RUN: mlir-opt %s -mlir-debug-counter=unique-tag-for-my-action-skip=-1 -mlir-print-debug-counter --pass-pipeline="builtin.module(func.func(canonicalize))" --mlir-disable-threading 2>&1 | FileCheck %s --check-prefix=CHECK-UNKNOWN-TAG
// RUN: mlir-opt %s -mlir-debug-counter=pass-execution-skip=1 -mlir-print-debug-counter --pass-pipeline="builtin.module(func.func(canonicalize))" --mlir-disable-threading 2>&1 | FileCheck %s --check-prefix=CHECK-PASS
// RUN: mlir-opt %s -mlir-debug-counter=pass-execution-skip=1 -test-dialect-conversion-pdll 2>&1 | FileCheck %s --check-prefix=CHECK-PDL-SKIP
func.func @foo() {
return
@ -13,3 +14,8 @@ func.func @foo() {
// CHECK-PASS: DebugCounter counters:
// CHECK-PASS: pass-execution : {1,1,-1}
// Regression test for https://github.com/llvm/llvm-project/issues/131441
// When --mlir-debug-counter skips the internal PDL lowering pass, the
// FrozenRewritePatternSet should handle it gracefully (no crash).
// CHECK-PDL-SKIP-LABEL: func @foo