MLIR-AIE 1.4.5.dev
AIESCFToControlFlow.cpp
Go to the documentation of this file.
1//===- AIESCFToControlFlow.cpp ----------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7//
8// Lower scf to cf everywhere in the module EXCEPT inside aie.runtime_sequence
9// bodies.
10//
11// A runtime sequence is NoTerminator and is translated to a flat NPU
12// instruction stream with no concept of branches. Its loops are lowered by the
13// dedicated runtime-sequence path (unrolling for compile-time-constant trip
14// counts; the dynamic EmitC path otherwise), not by generic scf->cf. Applying
15// convert-scf-to-cf to a runtime sequence would (1) break the NoTerminator
16// block invariant -- the ops after a lowered loop land in a terminator-less
17// block -- and (2) emit cf.br / cf.cond_br that the flat NPU emitter silently
18// drops, a miscompile. So we mark scf inside a runtime sequence as legal
19// (leave it alone) and convert everything else.
20//
21//===----------------------------------------------------------------------===//
22
26
27#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
28#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
29#include "mlir/Dialect/SCF/IR/SCF.h"
30#include "mlir/Pass/Pass.h"
31#include "mlir/Transforms/DialectConversion.h"
32
33namespace xilinx::AIEX {
34#define GEN_PASS_DEF_AIESCFTOCONTROLFLOW
35#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
36} // namespace xilinx::AIEX
37
38using namespace mlir;
39using namespace xilinx;
40using namespace xilinx::AIEX;
41
42namespace {
43
44struct AIESCFToControlFlowPass
45 : xilinx::AIEX::impl::AIESCFToControlFlowBase<AIESCFToControlFlowPass> {
46
47 void runOnOperation() override {
48 ModuleOp module = getOperation();
49
50 ConversionTarget target(getContext());
51 // Everything is legal by default; only scf outside a runtime sequence is
52 // illegal. (Matching upstream convert-scf-to-cf, which marks every other
53 // op legal so the structural block-splitting can relocate them freely.)
54 target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
55 target.addLegalDialect<cf::ControlFlowDialect>();
56 // scf is illegal (must be converted) UNLESS it lives inside a runtime
57 // sequence, whose control flow has its own lowering path.
58 target.addDynamicallyLegalDialect<scf::SCFDialect>([](Operation *op) {
59 return op->getParentOfType<AIE::RuntimeSequenceOp>() != nullptr;
60 });
61
62 RewritePatternSet patterns(&getContext());
63 populateSCFToControlFlowConversionPatterns(patterns);
64
65 if (failed(applyPartialConversion(module, target, std::move(patterns))))
66 signalPassFailure();
67 }
68};
69
70} // namespace
71
72std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
74 return std::make_unique<AIESCFToControlFlowPass>();
75}
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIESCFToControlFlowPass()