[llvm-branch-commits] [clang] [CIR][NFC] Generate getSuccessorRegions for fixed-region ops (PR #220512)

Henrich Lauko via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Sep 2 01:21:37 PDT 2026


https://github.com/xlauko created https://github.com/llvm/llvm-project/pull/220512

Six of the ten CIR ops implementing `RegionBranchOpInterface` had the same
`getSuccessorRegions` shape: a fixed list of regions can be entered from the
parent, and every region exit goes back to the parent operation. That makes the
whole method derivable from the region accessor names, so
`CIR_RegionBranchOpBase` grows an `entryRegions` parameter and generates it.

`ScopeOp`, `CaseOp`, `SwitchOp` and `CoroBodyOp` name one entry region;
`CleanupScopeOp` and `TernaryOp` name two.

Four ops keep their hand-written definitions, because their entry edges are not
a fixed list:

- `IfOp` falls back to the parent when the else region is empty
- `GlobalOp` skips its optional ctor and dtor regions, and reports no
  successors when both are absent
- `TryOp` iterates variadic handler regions
- `AwaitOp` enters `ready` only, which then branches to `resume` or `suspend`

To check that this is NFC: `CIROps.h.inc` is byte-identical before and after,
and `CIROps.cpp.inc` gains exactly the six definitions deleted from
`CIRDialect.cpp`, with the same bodies and the same region order. `CIRUnitTests`
passes 78/78 and `check-clang-cir` still fails only the three call-conv and
ABI-lowering tests that already fail on `main`.


>From 3accf6677f66bb508d0ae8a1f61d6e09bbd1bddb Mon Sep 17 00:00:00 2001
From: Henrich Lauko <hlauko at nvidia.com>
Date: Wed, 2 Sep 2026 08:21:08 +0000
Subject: [PATCH] [CIR][NFC] Generate getSuccessorRegions for fixed-region ops

Six of the ten CIR ops implementing RegionBranchOpInterface reported a fixed
list of entry regions and the parent operation on every region exit, which
makes the whole method derivable from the region accessor names. Extend
CIR_RegionBranchOpBase with an entryRegions parameter that generates it and
delete the six hand-written definitions.

IfOp, GlobalOp, TryOp and AwaitOp keep theirs, since their entry edges are not
a fixed list: IfOp falls back to the parent when the else region is empty,
GlobalOp skips its optional ctor and dtor regions, TryOp iterates variadic
handler regions, and AwaitOp routes ready to resume and suspend.

The generated CIROps.h.inc is unchanged and CIROps.cpp.inc gains exactly the
six definitions removed from CIRDialect.cpp.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td | 47 ++++++++++----
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp      | 67 --------------------
 2 files changed, 33 insertions(+), 81 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 4754e033eabb1..2fd259d55bead 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -122,12 +122,18 @@ class CIR_Op<string mnemonic, list<Trait> traits = []> :
 }
 
 // Base class for structured control flow ops whose regions take no inputs and
-// yield the parent's results on the way out. Users still have to define
-// `getSuccessorRegions` themselves.
+// yield the parent's results on the way out.
+//
+// `entryRegions` names the region accessors that may be entered from the parent
+// operation; when it is non-empty `getSuccessorRegions` is generated too. Leave
+// it empty for ops that define `getSuccessorRegions` themselves. Its lines are
+// appended as raw text and have to keep the indentation of the code block above,
+// since TableGen dedents a definition by its smallest indentation.
 //
 // Sets `extraClassDefinition`, so an op that needs its own definitions has to
 // re-paste this one; TableGen cannot append to an inherited field.
-class CIR_RegionBranchOpBase<string mnemonic, list<Trait> traits = []>
+class CIR_RegionBranchOpBase<string mnemonic, list<string> entryRegions = [],
+                             list<Trait> traits = []>
     : CIR_Op<mnemonic, !listconcat([
         DeclareOpInterfaceMethods<RegionBranchOpInterface,
                                   ["getSuccessorInputs"]>], traits)> {
@@ -139,7 +145,18 @@ class CIR_RegionBranchOpBase<string mnemonic, list<Trait> traits = []>
                  ? ValueRange(getOperation()->getResults())
                  : ValueRange();
     }
-  }];
+  }] # !if(!empty(entryRegions), "", [{
+    void $cppClass::getSuccessorRegions(
+        mlir::RegionBranchPoint point,
+        llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
+      // Every region branches back to the parent operation on exit.
+      if (!point.isParent()) {
+        regions.emplace_back(getOperation());
+        return;
+      }
+  }] # "\n" # !interleave(!foreach(regionName, entryRegions,
+        "      regions.emplace_back(&" # regionName # "());"), "\n")
+     # "\n    }\n");
 }
 
 //===----------------------------------------------------------------------===//
@@ -1004,7 +1021,7 @@ def CIR_ReturnOp : CIR_Op<"return", [
 // IfOp
 //===----------------------------------------------------------------------===//
 
-def CIR_IfOp : CIR_RegionBranchOpBase<"if", [
+def CIR_IfOp : CIR_RegionBranchOpBase<"if", [], [
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
 ]> {
   let summary = "the if-then-else operation";
@@ -1295,7 +1312,7 @@ def CIR_ResumeFlatOp : CIR_Op<"resume.flat", [
 // ScopeOp
 //===----------------------------------------------------------------------===//
 
-def CIR_ScopeOp : CIR_RegionBranchOpBase<"scope", [
+def CIR_ScopeOp : CIR_RegionBranchOpBase<"scope", ["getScopeRegion"], [
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
   RecursiveMemoryEffects
 ]> {
@@ -1396,7 +1413,8 @@ def CIR_CleanupKindAttr : CIR_EnumAttr<CIR_CleanupKind, "cleanup_kind"> {
   }];
 }
 
-def CIR_CleanupScopeOp : CIR_RegionBranchOpBase<"cleanup.scope", [
+def CIR_CleanupScopeOp : CIR_RegionBranchOpBase<"cleanup.scope",
+    ["getBodyRegion", "getCleanupRegion"], [
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
   RecursiveMemoryEffects
 ]> {
@@ -1537,7 +1555,7 @@ def CIR_CaseOpKind : CIR_I32EnumAttr<"CaseOpKind", "case kind", [
   I32EnumAttrCase<"Range", 3, "range">
 ]>;
 
-def CIR_CaseOp : CIR_RegionBranchOpBase<"case", [
+def CIR_CaseOp : CIR_RegionBranchOpBase<"case", ["getCaseRegion"], [
   RecursivelySpeculatable, AutomaticAllocationScope
 ]> {
   let summary = "Case operation";
@@ -1573,7 +1591,7 @@ def CIR_CaseOp : CIR_RegionBranchOpBase<"case", [
   let hasLLVMLowering = false;
 }
 
-def CIR_SwitchOp : CIR_RegionBranchOpBase<"switch", [
+def CIR_SwitchOp : CIR_RegionBranchOpBase<"switch", ["getBody"], [
   SameVariadicOperandSize,
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
   RecursiveMemoryEffects
@@ -3240,7 +3258,8 @@ def CIR_SelectOp : CIR_Op<"select", [
 // TernaryOp
 //===----------------------------------------------------------------------===//
 
-def CIR_TernaryOp : CIR_RegionBranchOpBase<"ternary", [
+def CIR_TernaryOp : CIR_RegionBranchOpBase<"ternary",
+    ["getTrueRegion", "getFalseRegion"], [
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
 ]> {
   let summary = "The `cond ? a : b` C/C++ ternary operation";
@@ -3349,7 +3368,7 @@ def CIR_TLSModelAttr: CIR_EnumAttr<CIR_TLSModel, "tls_model"> {
   }];
 }
 
-def CIR_GlobalOp : CIR_RegionBranchOpBase<"global", [
+def CIR_GlobalOp : CIR_RegionBranchOpBase<"global", [], [
   SymbolName, SymbolVisibility,
   DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
   NoRegionArguments
@@ -4738,7 +4757,7 @@ def CIR_AwaitKind : CIR_I32EnumAttr<"AwaitKind", "await kind", [
   I32EnumAttrCase<"Final", 3, "final">
 ]>;
 
-def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [
+def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [], [
   RecursivelySpeculatable, NoRegionArguments
 ]> {
   let summary = "Wraps C++ co_await implicit logic";
@@ -4823,7 +4842,7 @@ def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [
 //===----------------------------------------------------------------------===//
 // CoroBody
 //===----------------------------------------------------------------------===//
-def CIR_CoroBodyOp : CIR_RegionBranchOpBase<"coro.body", [
+def CIR_CoroBodyOp : CIR_RegionBranchOpBase<"coro.body", ["getBody"], [
   RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
   RecursiveMemoryEffects
 ]> {
@@ -8141,7 +8160,7 @@ def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> {
 // TryOp
 //===----------------------------------------------------------------------===//
 
-def CIR_TryOp : CIR_RegionBranchOpBase<"try", [
+def CIR_TryOp : CIR_RegionBranchOpBase<"try", [], [
   RecursivelySpeculatable, AutomaticAllocationScope
 ]> {
   let summary = "C++ try block";
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 22162b14bb2f5..2a9bcdcecc810 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1602,18 +1602,6 @@ void cir::IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
 /// during the flow of control. `operands` is a set of optional attributes
 /// that correspond to a constant value for each operand, or null if that
 /// operand is not a constant.
-void cir::ScopeOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
-  // The only region always branch back to the parent operation.
-  if (!point.isParent()) {
-    regions.emplace_back(getOperation());
-    return;
-  }
-
-  // If the condition isn't constant, both regions may be executed.
-  regions.push_back(RegionSuccessor(&getScopeRegion()));
-}
-
 void cir::ScopeOp::build(
     OpBuilder &builder, OperationState &result,
     function_ref<void(OpBuilder &, Type &, Location)> scopeBuilder) {
@@ -1680,18 +1668,6 @@ LogicalResult cir::ScopeOp::fold(FoldAdaptor /*adaptor*/,
 // CleanupScopeOp
 //===----------------------------------------------------------------------===//
 
-void cir::CleanupScopeOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
-  if (!point.isParent()) {
-    regions.emplace_back(getOperation());
-    return;
-  }
-
-  // Execution always proceeds from the body region to the cleanup region.
-  regions.push_back(RegionSuccessor(&getBodyRegion()));
-  regions.push_back(RegionSuccessor(&getCleanupRegion()));
-}
-
 LogicalResult cir::CleanupScopeOp::canonicalize(CleanupScopeOp op,
                                                 PatternRewriter &rewriter) {
   auto isRegionTrivial = [](Region &region) {
@@ -1874,15 +1850,6 @@ Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
 // CaseOp
 //===----------------------------------------------------------------------===//
 
-void cir::CaseOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
-  if (!point.isParent()) {
-    regions.emplace_back(getOperation());
-    return;
-  }
-  regions.push_back(RegionSuccessor(&getCaseRegion()));
-}
-
 void cir::CaseOp::build(OpBuilder &builder, OperationState &result,
                         ArrayAttr value, CaseOpKind kind,
                         OpBuilder::InsertPoint &insertPoint) {
@@ -1900,16 +1867,6 @@ void cir::CaseOp::build(OpBuilder &builder, OperationState &result,
 // SwitchOp
 //===----------------------------------------------------------------------===//
 
-void cir::SwitchOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &region) {
-  if (!point.isParent()) {
-    region.emplace_back(getOperation());
-    return;
-  }
-
-  region.push_back(RegionSuccessor(&getBody()));
-}
-
 void cir::SwitchOp::build(OpBuilder &builder, OperationState &result,
                           Value cond, BuilderOpStateCallbackRef switchBuilder) {
   assert(switchBuilder && "the builder callback for regions must be present");
@@ -2938,20 +2895,6 @@ LogicalResult cir::SubOp::verify() {
 /// during the flow of control. `operands` is a set of optional attributes that
 /// correspond to a constant value for each operand, or null if that operand is
 /// not a constant.
-void cir::TernaryOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
-  // The `true` and the `false` region branch back to the parent operation.
-  if (!point.isParent()) {
-    regions.emplace_back(getOperation());
-    return;
-  }
-
-  // When branching from the parent operation, both the true and false
-  // regions are considered possible successors
-  regions.push_back(RegionSuccessor(&getTrueRegion()));
-  regions.push_back(RegionSuccessor(&getFalseRegion()));
-}
-
 void cir::TernaryOp::build(
     OpBuilder &builder, OperationState &result, Value cond,
     function_ref<void(OpBuilder &, Location)> trueBuilder,
@@ -3265,16 +3208,6 @@ LogicalResult cir::AwaitOp::verify() {
 // CoroBody
 //===----------------------------------------------------------------------===//
 
-void cir::CoroBodyOp::getSuccessorRegions(
-    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
-  if (!point.isParent()) {
-    regions.emplace_back(getOperation());
-    return;
-  }
-
-  regions.push_back(RegionSuccessor(&getBody()));
-}
-
 LogicalResult cir::CoroBodyOp::verify() {
   if (!getOperation()->getParentOfType<FuncOp>().getCoroutine())
     return emitOpError("enclosing function must be a coroutine");



More information about the llvm-branch-commits mailing list