[clang] 05bd742 - [CIR] Upstream the initial BlockAddressOp implementation (#168151)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 17 10:01:08 PST 2025
Author: Andres-Salamanca
Date: 2025-11-17T13:01:04-05:00
New Revision: 05bd742ad790f207f5c94c4bf327d3e87b8819dc
URL: https://github.com/llvm/llvm-project/commit/05bd742ad790f207f5c94c4bf327d3e87b8819dc
DIFF: https://github.com/llvm/llvm-project/commit/05bd742ad790f207f5c94c4bf327d3e87b8819dc.diff
LOG: [CIR] Upstream the initial BlockAddressOp implementation (#168151)
This PR adds initial support for codegen of `blockAddressOp`. This is
emitted when using the GNU extension labels as values. The operation is
used together with `indirectBrOp`, which will be implemented in a future
PR. Lowering will be added in a later PR.
Added:
clang/test/CIR/CodeGen/label-values.c
clang/test/CIR/IR/block-adress.cir
clang/test/CIR/IR/invalid-block-address.cir
clang/test/CIR/Transforms/goto_solver.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/tools/cir-opt/cir-opt.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 1e0fb038b19d8..47ff9389e8028 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1026,4 +1026,29 @@ def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> {
let storageType = [{ CatchUnwind }];
}
+//===----------------------------------------------------------------------===//
+// CIR_BlockAddrInfoAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_BlockAddrInfoAttr : CIR_Attr<"BlockAddrInfo", "block_addr_info"> {
+ let summary = "Block Addres attribute";
+ let description = [{
+ This attribute is used to represent the address of a basic block
+ within a function. It combines the symbol reference to a function
+ with the name of a label inside that function.
+ }];
+ let parameters = (ins "mlir::FlatSymbolRefAttr":$func,
+ "mlir::StringAttr":$label);
+
+ let assemblyFormat = "`<` $func `,` $label `>`";
+ let builders = [
+ AttrBuilder<(ins "llvm::StringRef":$func_name,
+ "llvm::StringRef":$label_name
+ ), [{
+ return $_get($_ctxt, mlir::FlatSymbolRefAttr::get($_ctxt, func_name),
+ mlir::StringAttr::get($_ctxt, label_name));
+ }]>
+ ];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7b987ea49bf97..e612d6a0ba886 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4897,4 +4897,38 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
}];
}
+//===----------------------------------------------------------------------===//
+// BlockAddressOp
+//===----------------------------------------------------------------------===//
+
+def CIR_BlockAddressOp : CIR_Op<"block_address", [Pure]> {
+ let summary = "Get the address of a cir.label within a function";
+ let description = [{
+ The `cir.blockaddress` operation takes a function name and a label and
+ produces a pointer value that represents the address of that cir.label
+ within the specified function.
+
+ This operation models GCC's "labels as values" extension (`&&label`), which
+ allows taking the address of a local label and using it as a computed
+ jump target (e.g., with `goto *addr;`).
+
+ Example:
+ ```mlir
+ %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
+ {alignment = 8 : i64}
+ %addr = cir.block_address <@c, "label1"> : !cir.ptr<!cir.void>
+ cir.store align(8) %addr, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "label"
+ ```
+ }];
+
+ let arguments = (ins CIR_BlockAddrInfoAttr:$block_addr_info);
+ let results = (outs CIR_VoidPtrType:$addr);
+ let assemblyFormat = [{
+ $block_addr_info `:` qualified(type($addr)) attr-dict
+ }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 3b0977d213325..f777562ba6309 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -168,6 +168,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return emitLoadOfLValue(e);
}
+ mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
+ auto func = cast<cir::FuncOp>(cgf.curFn);
+ auto 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);
+ }
+
mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
mlir::Type type = cgf.convertType(e->getType());
return cir::ConstantOp::create(builder, cgf.getLoc(e->getExprLoc()),
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9ac5efe0e41c7..22aada882defc 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1912,22 +1912,45 @@ mlir::LogicalResult cir::FuncOp::verify() {
llvm::SmallSet<llvm::StringRef, 16> labels;
llvm::SmallSet<llvm::StringRef, 16> gotos;
-
+ llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
+ bool invalidBlockAddress = false;
getOperation()->walk([&](mlir::Operation *op) {
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
labels.insert(lab.getLabel());
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
gotos.insert(goTo.getLabel());
+ } else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
+ if (blkAdd.getBlockAddrInfoAttr().getFunc().getAttr() != getSymName()) {
+ // Stop the walk early, no need to continue
+ invalidBlockAddress = true;
+ return mlir::WalkResult::interrupt();
+ }
+ blockAddresses.insert(blkAdd.getBlockAddrInfoAttr().getLabel());
}
+ return mlir::WalkResult::advance();
});
+ if (invalidBlockAddress)
+ return emitOpError() << "blockaddress references a
diff erent function";
+
+ llvm::SmallSet<llvm::StringRef, 16> mismatched;
if (!labels.empty() || !gotos.empty()) {
- llvm::SmallSet<llvm::StringRef, 16> mismatched =
- llvm::set_
diff erence(gotos, labels);
+ mismatched = llvm::set_
diff erence(gotos, labels);
if (!mismatched.empty())
return emitOpError() << "goto/label mismatch";
}
+
+ mismatched.clear();
+
+ if (!labels.empty() || !blockAddresses.empty()) {
+ mismatched = llvm::set_
diff erence(blockAddresses, labels);
+
+ if (!mismatched.empty())
+ return emitOpError()
+ << "expects an existing label target in the referenced function";
+ }
+
return success();
}
diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
index 00972b6976295..d590ccce1f540 100644
--- a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
@@ -8,6 +8,7 @@
#include "PassDetail.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/TimeProfiler.h"
#include <memory>
@@ -30,17 +31,29 @@ static void process(cir::FuncOp func) {
mlir::OpBuilder rewriter(func.getContext());
llvm::StringMap<Block *> labels;
llvm::SmallVector<cir::GotoOp, 4> gotos;
+ llvm::SmallSet<StringRef, 4> blockAddrLabel;
func.getBody().walk([&](mlir::Operation *op) {
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
- // Will construct a string copy inplace. Safely erase the label
labels.try_emplace(lab.getLabel(), lab->getBlock());
- lab.erase();
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
gotos.push_back(goTo);
+ } else if (auto blockAddr = dyn_cast<cir::BlockAddressOp>(op)) {
+ blockAddrLabel.insert(blockAddr.getBlockAddrInfo().getLabel());
}
});
+ for (auto &lab : labels) {
+ StringRef labelName = lab.getKey();
+ Block *block = lab.getValue();
+ if (!blockAddrLabel.contains(labelName)) {
+ // erase the LabelOp inside the block if safe
+ if (auto lab = dyn_cast<cir::LabelOp>(&block->front())) {
+ lab.erase();
+ }
+ }
+ }
+
for (auto goTo : gotos) {
mlir::OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPoint(goTo);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 92434d730eb31..d43a462a25092 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3837,6 +3837,12 @@ mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
+ cir::BlockAddressOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ return mlir::failure();
+}
+
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
return std::make_unique<ConvertCIRToLLVMPass>();
}
diff --git a/clang/test/CIR/CodeGen/label-values.c b/clang/test/CIR/CodeGen/label-values.c
new file mode 100644
index 0000000000000..41178e3f62f20
--- /dev/null
+++ b/clang/test/CIR/CodeGen/label-values.c
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+
+void A(void) {
+ void *ptr = &&LABEL_A;
+LABEL_A:
+ return;
+}
+// CIR: cir.func dso_local @A
+// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
+// CIR: [[BLOCK:%.*]] = cir.block_address <@A, "LABEL_A"> : !cir.ptr<!void>
+// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: cir.br ^bb1
+// CIR: ^bb1: // pred: ^bb0
+// CIR: cir.label "LABEL_A"
+// CIR: cir.return
+
+void B(void) {
+LABEL_B:
+ void *ptr = &&LABEL_B;
+}
+
+// CIR: cir.func dso_local @B()
+// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
+// CIR: cir.br ^bb1
+// CIR: ^bb1:
+// CIR: cir.label "LABEL_B"
+// CIR: [[BLOCK:%.*]] = cir.block_address <@B, "LABEL_B"> : !cir.ptr<!void>
+// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: cir.return
+
+void C(int x) {
+ void *ptr = (x == 0) ? &&LABEL_A : &&LABEL_B;
+LABEL_A:
+ return;
+LABEL_B:
+ return;
+}
+
+// CIR: cir.func dso_local @C
+// CIR: [[BLOCK1:%.*]] = cir.block_address <@C, "LABEL_A"> : !cir.ptr<!void>
+// CIR: [[BLOCK2:%.*]] = cir.block_address <@C, "LABEL_B"> : !cir.ptr<!void>
+// CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr<!void>, !cir.ptr<!void>) -> !cir.ptr<!void>
+// CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: cir.br ^bb1
+// CIR: ^bb1: // pred: ^bb0
+// CIR: cir.label "LABEL_A"
+// CIR: cir.br ^bb2
+// CIR: ^bb2: // 2 preds: ^bb1, ^bb3
+// CIR: cir.return
+// CIR: ^bb3: // no predecessors
+// CIR: cir.label "LABEL_B"
+// CIR: cir.br ^bb2
+
+void D(void) {
+ void *ptr = &&LABEL_A;
+ void *ptr2 = &&LABEL_A;
+LABEL_A:
+ void *ptr3 = &&LABEL_A;
+ return;
+}
+
+// CIR: cir.func dso_local @D
+// CIR: %[[PTR:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
+// CIR: %[[PTR2:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr2", init]
+// CIR: %[[PTR3:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr3", init]
+// CIR: %[[BLK1:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
+// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: %[[BLK2:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
+// CIR: cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: cir.br ^bb1
+// CIR: ^bb1: // pred: ^bb0
+// CIR: cir.label "LABEL_A"
+// CIR: %[[BLK3:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
+// CIR: cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: cir.return
diff --git a/clang/test/CIR/IR/block-adress.cir b/clang/test/CIR/IR/block-adress.cir
new file mode 100644
index 0000000000000..9d6840819c2d4
--- /dev/null
+++ b/clang/test/CIR/IR/block-adress.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!void = !cir.void
+
+module {
+ cir.func @block_address(){
+ %0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "label"
+ cir.return
+ }
+// CHECK: cir.func @block_address
+// CHECK: %0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
+// CHECK: cir.br ^bb1
+// CHECK: ^bb1:
+// CHECK: cir.label "label"
+// CHECK: cir.return
+
+cir.func @block_address_inside_scope() -> () {
+ cir.scope{
+ %0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
+ }
+ cir.br ^bb1
+^bb1:
+ cir.label "label"
+ cir.return
+}
+// CHECK: cir.func @block_address_inside_scope
+// CHECK: cir.scope
+// CHECK: %0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
+// CHECK: cir.label "label"
+// CHECK: cir.return
+}
diff --git a/clang/test/CIR/IR/invalid-block-address.cir b/clang/test/CIR/IR/invalid-block-address.cir
new file mode 100644
index 0000000000000..4519485c28803
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-block-address.cir
@@ -0,0 +1,21 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!void = !cir.void
+
+// expected-error at +1 {{expects an existing label target in the referenced function}}
+cir.func @bad_block_address() -> () {
+ %0 = cir.block_address <@bad_block_address, "label"> : !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "wrong_label"
+ cir.return
+}
+
+// expected-error at +1 {{blockaddress references a
diff erent function}}
+cir.func @bad_block_func() -> () {
+ %0 = cir.block_address <@mismatch_func, "label"> : !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "label"
+ cir.return
+}
diff --git a/clang/test/CIR/Transforms/goto_solver.cir b/clang/test/CIR/Transforms/goto_solver.cir
new file mode 100644
index 0000000000000..6ae019b44a39e
--- /dev/null
+++ b/clang/test/CIR/Transforms/goto_solver.cir
@@ -0,0 +1,62 @@
+// RUN: cir-opt %s -cir-goto-solver --verify-roundtrip -o - | FileCheck %s
+
+!void = !cir.void
+
+cir.func @a(){
+ %0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
+ %1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
+ cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ cir.br ^bb1
+^bb1:
+ cir.label "label1"
+ cir.br ^bb2
+^bb2:
+ // This label is not referenced by any blockaddressOp, so it should be removed
+ cir.label "label2"
+ cir.return
+}
+
+// CHECK: cir.func @a()
+// CHECK: %1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
+// CHECK: ^bb1:
+// CHECK: cir.label "label1"
+// CHECK: cir.br ^bb2
+// CHECK: ^bb2:
+// CHECK-NOT: cir.label "label2"
+
+cir.func @b(){
+ %0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
+ %1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
+ cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ cir.goto "label2"
+^bb1:
+ cir.label "label1"
+ cir.br ^bb2
+^bb2:
+ // This label is not referenced by any blockaddressOp, so it should be removed
+ cir.label "label2"
+ cir.return
+}
+
+// CHECK: cir.func @b() {
+// CHECK: %1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
+// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CHECK: cir.br ^bb2
+// CHECK: ^bb1:
+// CHECK: cir.label "label1"
+// CHECK: cir.br ^bb2
+// CHECK: ^bb2:
+// CHECK-NOT: cir.label "label2"
+
+cir.func @c() {
+ cir.label "label1"
+ %0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
+ %1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
+ cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ cir.return
+}
+
+// CHECK: cir.func @c
+// CHECK: cir.label "label1"
+// CHECK: %1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
+// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index c4d29a2117c75..ee42015bb38e9 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -58,6 +58,10 @@ int main(int argc, char **argv) {
return mlir::createHoistAllocasPass();
});
+ ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
+ return mlir::createGotoSolverPass();
+ });
+
mlir::registerTransformsPasses();
return mlir::asMainReturnCode(MlirOptMain(
More information about the cfe-commits
mailing list