[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-05 Thread Andy Kaylor via cfe-commits

https://github.com/andykaylor closed 
https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-03 Thread via cfe-commits

https://github.com/Andres-Salamanca updated 
https://github.com/llvm/llvm-project/pull/169967

>From 13eb8a04845074675980d5e7923b81c331e29c7b Mon Sep 17 00:00:00 2001
From: Andres Salamanca 
Date: Fri, 28 Nov 2025 19:27:31 -0500
Subject: [PATCH 1/4] [CIR] Upstream support for cir.indirectBr

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 61 +
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 19 +++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  | 53 
 clang/lib/CIR/CodeGen/CIRGenFunction.h| 12 +++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp| 38 
 clang/lib/CIR/CodeGen/CIRGenModule.h  | 18 +++-
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp  | 21 -
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp   | 59 +
 .../Dialect/Transforms/CIRCanonicalize.cpp|  2 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  6 ++
 clang/test/CIR/CodeGen/label-values.c | 86 ---
 clang/test/CIR/IR/indirect-br.cir | 46 ++
 12 files changed, 399 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/CIR/IR/indirect-br.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ae199f35cb10e..71c35d24cf744 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1462,6 +1462,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods
+  , SameVariadicOperandSize, Terminator, Pure]> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid for instance, when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments
+);
+
+  let successors = (successor VariadicSuccessor:$successors);
+  let assemblyFormat = [{
+$addr ( `poison` $poison^ )? `:` type($addr) `,`
+custom(ref(type($addr)),
+  $successors,
+  $succOperands,
+  type($succOperands))
+attr-dict
+  }];
+}
+
 
//===--===//
 // Common loop op definitions
 
//===--===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 3e9d3db768bea..cfed8a5694d34 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -174,11 +174,22 @@ class ScalarExprEmitter : public 
StmtVisitor {
 
   mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
 auto func = cast(cgf.curFn);
-auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
+cir::BlockAddrInfoAttr blockInfoAttr = cir::BlockAddrInfoAttr::get(
 &cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
-return cir::BlockAddressOp::create(builder, 
cgf.getLoc(e->getSourceRange()),
-   cgf.convertType(e->getType()),
-   blockInfoAttr);
+cir::BlockAddressOp blockAddressOp = cir::BlockAddressOp::create(
+builder, cgf.getLoc(e->getSourceRange()), 
cgf.convertType(e->getType()),
+blockInfoAttr);
+cir::LabelOp resolvedLabel = cgf.cgm.lookupBlockAddressInfo(blockInfoAttr);
+if (!resolvedLabel) {
+  cgf.cgm.mapUnresolvedBlockAddress(blockAddressOp);
+  // Still add the op to maintain insertion order it will be resolved in
+  // resolveBlockAddresses
+  cgf.cgm.mapResolvedBlockAddress(blockAddressOp, nullptr);
+} else {
+  

[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -1433,6 +1433,68 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods,
+  SameVariadicOperandSize, Terminator, Pure]
+> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid, for instance when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments
+);
+
+  let successors = (successor VariadicSuccessor:$successors);
+  let assemblyFormat = [{
+$addr ( `poison` $poison^ )? `:` type($addr) `,`

Andres-Salamanca wrote:

Thanks this looks better. I had missed that.

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -69,8 +129,77 @@ void D(void) {
 // CIR:%[[BLK2:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr
 // CIR:cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr, 
!cir.ptr>
 // CIR:cir.br ^bb1
-// CIR:  ^bb1:  // pred: ^bb0
+// CIR:  ^bb1([[PHI:%*.]]: !cir.ptr {{.*}}):  // pred: ^bb0
+// CIR:cir.indirectbr [[PHI]] : , [
+// CIR-DAG:^bb2,
+// CIR-DAG:^bb2,
+// CIR-DAG:^bb2
+// CIR:]
+// CIR:  ^bb2:  // 3 preds: ^bb1, ^bb1, ^bb1
 // CIR:cir.label "LABEL_A"
 // CIR:%[[BLK3:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr
 // CIR:cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr, 
!cir.ptr>
 // CIR:cir.return
+
+// OGCG: define dso_local void @D
+// OGCG:   %[[PTR:.*]] = alloca ptr, align 8
+// OGCG:   %[[PTR2:.*]] = alloca ptr, align 8
+// OGCG:   %[[PTR3:.*]] = alloca ptr, align 8
+// OGCG:   store ptr blockaddress(@D, %LABEL_A), ptr %[[PTR]], align 8
+// OGCG:   store ptr blockaddress(@D, %LABEL_A), ptr %[[PTR2]], align 8
+// OGCG:   %[[BLOCKADD:.*]] = load ptr, ptr %[[PTR2]], align 8
+// OGCG:   br label %indirectgoto
+// OGCG: LABEL_A:; preds = 
%indirectgoto, %indirectgoto, %indirectgoto
+// OGCG:   store ptr blockaddress(@D, %LABEL_A), ptr %[[PTR3]], align 8
+// OGCG:   ret void
+// OGCG: indirectgoto: ; preds = %entry
+// OGCG:   %indirect.goto.dest = phi ptr [ %[[BLOCKADD]], %entry ]
+// OGCG:   indirectbr ptr %indirect.goto.dest, [label %LABEL_A, label 
%LABEL_A, label %LABEL_A]
+
+// This test checks that CIR preserves insertion order of blockaddresses
+// for indirectbr, even if some were resolved immediately and others later.
+void E(void) {
+  void *ptr = &&LABEL_D;
+  void *ptr2 = &&LABEL_C;
+LABEL_A:
+LABEL_B:
+  void *ptr3 = &&LABEL_B;
+  void *ptr4 = &&LABEL_A;
+LABEL_C:
+LABEL_D:
+  return;
+}
+
+//CIR:  cir.func dso_local @E()

Andres-Salamanca wrote:

Done

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -1433,6 +1433,68 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods,
+  SameVariadicOperandSize, Terminator, Pure]
+> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid, for instance when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]

Andres-Salamanca wrote:

Done

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -1433,6 +1433,68 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods,
+  SameVariadicOperandSize, Terminator, Pure]
+> {

Andres-Salamanca wrote:

Done. yes, this looks better.

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -1433,6 +1433,68 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods,
+  SameVariadicOperandSize, Terminator, Pure]
+> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid, for instance when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments

Andres-Salamanca wrote:

Done

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits

https://github.com/Andres-Salamanca updated 
https://github.com/llvm/llvm-project/pull/169967

>From cd4fde36ffbe2b6d78687e45e1313ba03ffefd3a Mon Sep 17 00:00:00 2001
From: Andres Salamanca 
Date: Fri, 28 Nov 2025 19:27:31 -0500
Subject: [PATCH 1/4] [CIR] Upstream support for cir.indirectBr

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 61 +
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 19 +++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  | 53 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h| 12 +++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp| 38 
 clang/lib/CIR/CodeGen/CIRGenModule.h  | 18 +++-
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp  | 21 -
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp   | 59 +
 .../Dialect/Transforms/CIRCanonicalize.cpp|  2 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  6 ++
 clang/test/CIR/CodeGen/label-values.c | 87 ---
 clang/test/CIR/IR/indirect-br.cir | 46 ++
 12 files changed, 400 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/CIR/IR/indirect-br.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a19c4f951fff9..76d44c29b7da8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1433,6 +1433,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods
+  , SameVariadicOperandSize, Terminator, Pure]> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid for instance, when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments
+);
+
+  let successors = (successor VariadicSuccessor:$successors);
+  let assemblyFormat = [{
+$addr ( `poison` $poison^ )? `:` type($addr) `,`
+custom(ref(type($addr)),
+  $successors,
+  $succOperands,
+  type($succOperands))
+attr-dict
+  }];
+}
+
 
//===--===//
 // Common loop op definitions
 
//===--===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 7c94743d5ffc6..d5bcafc9c81f4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -174,11 +174,22 @@ class ScalarExprEmitter : public 
StmtVisitor {
 
   mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
 auto func = cast(cgf.curFn);
-auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
+cir::BlockAddrInfoAttr blockInfoAttr = cir::BlockAddrInfoAttr::get(
 &cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
-return cir::BlockAddressOp::create(builder, 
cgf.getLoc(e->getSourceRange()),
-   cgf.convertType(e->getType()),
-   blockInfoAttr);
+cir::BlockAddressOp blockAddressOp = cir::BlockAddressOp::create(
+builder, cgf.getLoc(e->getSourceRange()), 
cgf.convertType(e->getType()),
+blockInfoAttr);
+cir::LabelOp resolvedLabel = cgf.cgm.lookupBlockAddressInfo(blockInfoAttr);
+if (!resolvedLabel) {
+  cgf.cgm.mapUnresolvedBlockAddress(blockAddressOp);
+  // Still add the op to maintain insertion order it will be resolved in
+  // resolveBlockAddresses
+  cgf.cgm.mapResolvedBlockAddress(blockAddressOp, nullptr);
+} else {
+  

[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread via cfe-commits


@@ -1433,6 +1433,68 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods,
+  SameVariadicOperandSize, Terminator, Pure]
+> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid, for instance when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,

Andres-Salamanca wrote:

Done

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)

2025-12-02 Thread Bruno Cardoso Lopes via cfe-commits

https://github.com/bcardosolopes approved this pull request.

LGTM

https://github.com/llvm/llvm-project/pull/169967
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR] Upstream support for cir.indirectBr (PR #169967)

2025-11-28 Thread via cfe-commits

https://github.com/Andres-Salamanca created 
https://github.com/llvm/llvm-project/pull/169967

This PR upstreams support for the `cir.indirectBr` operation, which is used to 
implement GCC’s labels-as-values `indirect goto`.
To ensure correct lowering, we introduce precise bookkeeping to associate each 
`block_address` operation with its corresponding `label` op. This is required 
because a `block_address` may be emitted before the `label` it refers to. In 
such cases, the reference is deferred and later resolved by 
`resolveBlockAddresses`, which guarantees that all `indirectBr` successors are 
emitted in the correct and fully resolved order.

>From cd4fde36ffbe2b6d78687e45e1313ba03ffefd3a Mon Sep 17 00:00:00 2001
From: Andres Salamanca 
Date: Fri, 28 Nov 2025 19:27:31 -0500
Subject: [PATCH] [CIR] Upstream support for cir.indirectBr

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 61 +
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 19 +++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  | 53 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h| 12 +++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp| 38 
 clang/lib/CIR/CodeGen/CIRGenModule.h  | 18 +++-
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp  | 21 -
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp   | 59 +
 .../Dialect/Transforms/CIRCanonicalize.cpp|  2 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  6 ++
 clang/test/CIR/CodeGen/label-values.c | 87 ---
 clang/test/CIR/IR/indirect-br.cir | 46 ++
 12 files changed, 400 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/CIR/IR/indirect-br.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a19c4f951fff9..76d44c29b7da8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1433,6 +1433,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods
+  , SameVariadicOperandSize, Terminator, Pure]> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid for instance, when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments
+);
+
+  let successors = (successor VariadicSuccessor:$successors);
+  let assemblyFormat = [{
+$addr ( `poison` $poison^ )? `:` type($addr) `,`
+custom(ref(type($addr)),
+  $successors,
+  $succOperands,
+  type($succOperands))
+attr-dict
+  }];
+}
+
 
//===--===//
 // Common loop op definitions
 
//===--===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 7c94743d5ffc6..d5bcafc9c81f4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -174,11 +174,22 @@ class ScalarExprEmitter : public 
StmtVisitor {
 
   mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
 auto func = cast(cgf.curFn);
-auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
+cir::BlockAddrInfoAttr blockInfoAttr = cir::BlockAddrInfoAttr::get(
 &cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
-return cir::BlockAddressOp::create(builder, 
cgf.getLoc(e->getSourceRange()),
-   cgf.convertType(e->getType()),
-   blockInfoAttr);
+cir::BlockAddressOp 

[clang] [CIR] Upstream support for cir.indirectBr (PR #169967)

2025-11-28 Thread via cfe-commits

llvmbot wrote:



@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: None (Andres-Salamanca)


Changes

This PR upstreams support for the `cir.indirectBr` operation, which is used to 
implement GCC’s labels-as-values `indirect goto`.
To ensure correct lowering, we introduce precise bookkeeping to associate each 
`block_address` operation with its corresponding `label` op. This is required 
because a `block_address` may be emitted before the `label` it refers to. In 
such cases, the reference is deferred and later resolved by 
`resolveBlockAddresses`, which guarantees that all `indirectBr` successors are 
emitted in the correct and fully resolved order.

---

Patch is 25.35 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/169967.diff


12 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+61) 
- (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+15-4) 
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+53) 
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+12) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+38) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+17-1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+19-2) 
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+59) 
- (modified) clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp (+1-1) 
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+6) 
- (modified) clang/test/CIR/CodeGen/label-values.c (+73-14) 
- (added) clang/test/CIR/IR/indirect-br.cir (+46) 


``diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a19c4f951fff9..76d44c29b7da8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1433,6 +1433,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
   }];
 }
 
+//===--===//
+// IndirectBrOp
+//===--===//
+
+def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
+  DeclareOpInterfaceMethods
+  , SameVariadicOperandSize, Terminator, Pure]> {
+  let summary = "Indirect branch";
+  let description = [{
+The `cir.indirectbr` operation represents an indirect branch to one of
+several possible successor blocks. The target block is computed from
+the value of the given address operand.
+
+This operation is typically generated when handling constructs like
+the GCC extension `&&label` combined with an indirect `goto *ptr;`.
+
+The `poison` attribute is used to mark an `indirectbr` that was created
+but is known to be invalid for instance, when a label address was
+taken but no indirect branch was ever emitted.
+
+Example:
+
+```mlir
+  %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init]
+  %1 = cir.block_address <@A, "A"> : !cir.ptr
+  cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr>
+  %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr
+  cir.br ^bb1(%2 : !cir.ptr)
+^bb1(%3: !cir.ptr):
+  cir.indirectbr %3 : , [
+  ^bb2
+  ]
+```
+or with a poison:
+
+```mlir
+  cir.indirectbr %0 poison : , [
+  ^bb3,
+  ^bb2
+  ]
+```
+  }];
+
+  let arguments = (ins
+CIR_VoidPtrType:$addr,
+UnitAttr:$poison,
+VariadicOfVariadic:$succOperands,
+DenseI32ArrayAttr:$indbr_operand_segments
+);
+
+  let successors = (successor VariadicSuccessor:$successors);
+  let assemblyFormat = [{
+$addr ( `poison` $poison^ )? `:` type($addr) `,`
+custom(ref(type($addr)),
+  $successors,
+  $succOperands,
+  type($succOperands))
+attr-dict
+  }];
+}
+
 
//===--===//
 // Common loop op definitions
 
//===--===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 7c94743d5ffc6..d5bcafc9c81f4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -174,11 +174,22 @@ class ScalarExprEmitter : public 
StmtVisitor {
 
   mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
 auto func = cast(cgf.curFn);
-auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
+cir::BlockAddrInfoAttr blockInfoAttr = cir::BlockAddrInfoAttr::get(
 &cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
-return cir::BlockAddressOp::create(builder, 
cgf.getLoc(e->getSourceRange()),
-   cgf.convertType(e->getType()),
-   blockInfoAttr);
+cir::BlockAddressOp blockAddressOp = cir::BlockAddressOp::create(
+builder, cgf.getLoc(e->getSourceRange()),