[clang] [CIR] Upstream support for cir.indirectbr (PR #169967)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
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)
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)
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()),
