-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Upstream the initial BlockAddressOp implementation #168151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-clangir Author: None (Andres-Salamanca) ChangesThis PR adds initial support for codegen of Full diff: https://github.com/llvm/llvm-project/pull/168151.diff 11 Files Affected:
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 16258513239d9..d7270bc81b68a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4800,4 +4800,37 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
}];
}
+//===----------------------------------------------------------------------===//
+// BlockAddressOp
+//===----------------------------------------------------------------------===//
+
+def CIR_BlockAddressOp : CIR_Op<"blockaddress", [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.blockaddress("foo", "label") -> !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:$blockAddrInfo);
+ let results = (outs CIR_VoidPtrType:$addr);
+ let assemblyFormat = [{
+ $blockAddrInfo `->` 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 4461875fcf678..e3e8f2827d841 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -166,6 +166,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 7ba03ce40140c..95dfcc7ce1c13 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1902,22 +1902,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 different function";
+
+ llvm::SmallSet<llvm::StringRef, 16> mismatched;
if (!labels.empty() || !gotos.empty()) {
- llvm::SmallSet<llvm::StringRef, 16> mismatched =
- llvm::set_difference(gotos, labels);
+ mismatched = llvm::set_difference(gotos, labels);
if (!mismatched.empty())
return emitOpError() << "goto/label mismatch";
}
+
+ mismatched.clear();
+
+ if (!labels.empty() || !blockAddresses.empty()) {
+ mismatched = llvm::set_difference(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 b4afed7019417..13835949789ce 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3778,6 +3778,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..20042ce1e6e8b
--- /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 = &&A;
+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.blockaddress <@A, "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 "A"
+// CIR: cir.return
+
+void B(void) {
+B:
+ void *ptr = &&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 "B"
+// CIR: [[BLOCK:%.*]] = cir.blockaddress <@B, "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) ? &&A : &&B;
+A:
+ return;
+B:
+ return;
+}
+
+// CIR: cir.func dso_local @C
+// CIR: [[BLOCK1:%.*]] = cir.blockaddress <@C, "A"> -> !cir.ptr<!void>
+// CIR: [[BLOCK2:%.*]] = cir.blockaddress <@C, "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 "A"
+// CIR: cir.br ^bb2
+// CIR: ^bb2: // 2 preds: ^bb1, ^bb3
+// CIR: cir.return
+// CIR: ^bb3: // no predecessors
+// CIR: cir.label "B"
+// CIR: cir.br ^bb2
+
+void D(void) {
+ void *ptr = &&A;
+ void *ptr2 = &&A;
+A:
+ void *ptr3 = &&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.blockaddress <@D, "A"> -> !cir.ptr<!void>
+// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: %[[BLK2:.*]] = cir.blockaddress <@D, "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 "A"
+// CIR: %[[BLK3:.*]] = cir.blockaddress <@D, "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..ae9662461d08f
--- /dev/null
+++ b/clang/test/CIR/IR/block-adress.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s | cir-opt | FileCheck %s
+
+!void = !cir.void
+
+module {
+ cir.func @block_address(){
+ %0 = cir.blockaddress <@block_address, "label"> -> !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "label"
+ cir.return
+ }
+// CHECK: cir.func @block_address
+// CHECK: %0 = cir.blockaddress <@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.blockaddress <@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.blockaddress <@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..c3545406bfcff
--- /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@+1 {{expects an existing label target in the referenced function}}
+cir.func @bad_block_address() -> () {
+ %0 = cir.blockaddress <@bad_block_address, "label"> -> !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "wrong_label"
+ cir.return
+}
+
+// expected-error@+1 {{blockaddress references a different function}}
+cir.func @bad_block_func() -> () {
+ %0 = cir.blockaddress <@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..13ec4b8d79006
--- /dev/null
+++ b/clang/test/CIR/Transforms/goto_solver.cir
@@ -0,0 +1,62 @@
+// RUN: cir-opt %s -cir-goto-solver -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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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(
|
|
@llvm/pr-subscribers-clang Author: None (Andres-Salamanca) ChangesThis PR adds initial support for codegen of Full diff: https://github.com/llvm/llvm-project/pull/168151.diff 11 Files Affected:
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 16258513239d9..d7270bc81b68a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4800,4 +4800,37 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
}];
}
+//===----------------------------------------------------------------------===//
+// BlockAddressOp
+//===----------------------------------------------------------------------===//
+
+def CIR_BlockAddressOp : CIR_Op<"blockaddress", [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.blockaddress("foo", "label") -> !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:$blockAddrInfo);
+ let results = (outs CIR_VoidPtrType:$addr);
+ let assemblyFormat = [{
+ $blockAddrInfo `->` 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 4461875fcf678..e3e8f2827d841 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -166,6 +166,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 7ba03ce40140c..95dfcc7ce1c13 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1902,22 +1902,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 different function";
+
+ llvm::SmallSet<llvm::StringRef, 16> mismatched;
if (!labels.empty() || !gotos.empty()) {
- llvm::SmallSet<llvm::StringRef, 16> mismatched =
- llvm::set_difference(gotos, labels);
+ mismatched = llvm::set_difference(gotos, labels);
if (!mismatched.empty())
return emitOpError() << "goto/label mismatch";
}
+
+ mismatched.clear();
+
+ if (!labels.empty() || !blockAddresses.empty()) {
+ mismatched = llvm::set_difference(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 b4afed7019417..13835949789ce 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3778,6 +3778,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..20042ce1e6e8b
--- /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 = &&A;
+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.blockaddress <@A, "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 "A"
+// CIR: cir.return
+
+void B(void) {
+B:
+ void *ptr = &&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 "B"
+// CIR: [[BLOCK:%.*]] = cir.blockaddress <@B, "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) ? &&A : &&B;
+A:
+ return;
+B:
+ return;
+}
+
+// CIR: cir.func dso_local @C
+// CIR: [[BLOCK1:%.*]] = cir.blockaddress <@C, "A"> -> !cir.ptr<!void>
+// CIR: [[BLOCK2:%.*]] = cir.blockaddress <@C, "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 "A"
+// CIR: cir.br ^bb2
+// CIR: ^bb2: // 2 preds: ^bb1, ^bb3
+// CIR: cir.return
+// CIR: ^bb3: // no predecessors
+// CIR: cir.label "B"
+// CIR: cir.br ^bb2
+
+void D(void) {
+ void *ptr = &&A;
+ void *ptr2 = &&A;
+A:
+ void *ptr3 = &&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.blockaddress <@D, "A"> -> !cir.ptr<!void>
+// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR: %[[BLK2:.*]] = cir.blockaddress <@D, "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 "A"
+// CIR: %[[BLK3:.*]] = cir.blockaddress <@D, "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..ae9662461d08f
--- /dev/null
+++ b/clang/test/CIR/IR/block-adress.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s | cir-opt | FileCheck %s
+
+!void = !cir.void
+
+module {
+ cir.func @block_address(){
+ %0 = cir.blockaddress <@block_address, "label"> -> !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "label"
+ cir.return
+ }
+// CHECK: cir.func @block_address
+// CHECK: %0 = cir.blockaddress <@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.blockaddress <@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.blockaddress <@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..c3545406bfcff
--- /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@+1 {{expects an existing label target in the referenced function}}
+cir.func @bad_block_address() -> () {
+ %0 = cir.blockaddress <@bad_block_address, "label"> -> !cir.ptr<!void>
+ cir.br ^bb1
+ ^bb1:
+ cir.label "wrong_label"
+ cir.return
+}
+
+// expected-error@+1 {{blockaddress references a different function}}
+cir.func @bad_block_func() -> () {
+ %0 = cir.blockaddress <@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..13ec4b8d79006
--- /dev/null
+++ b/clang/test/CIR/Transforms/goto_solver.cir
@@ -0,0 +1,62 @@
+// RUN: cir-opt %s -cir-goto-solver -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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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(
|
andykaylor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good. Just a couple of nits.
xlauko
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % last nit
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/110/builds/6359 Here is the relevant piece of the build log for the reference |
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 withindirectBrOp, which will be implemented in a future PR. Lowering will be added in a later PR.