Skip to content

Commit 05bd742

Browse files
[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.
1 parent 72059be commit 05bd742

File tree

11 files changed

+312
-5
lines changed

11 files changed

+312
-5
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,4 +1026,29 @@ def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> {
10261026
let storageType = [{ CatchUnwind }];
10271027
}
10281028

1029+
//===----------------------------------------------------------------------===//
1030+
// CIR_BlockAddrInfoAttr
1031+
//===----------------------------------------------------------------------===//
1032+
1033+
def CIR_BlockAddrInfoAttr : CIR_Attr<"BlockAddrInfo", "block_addr_info"> {
1034+
let summary = "Block Addres attribute";
1035+
let description = [{
1036+
This attribute is used to represent the address of a basic block
1037+
within a function. It combines the symbol reference to a function
1038+
with the name of a label inside that function.
1039+
}];
1040+
let parameters = (ins "mlir::FlatSymbolRefAttr":$func,
1041+
"mlir::StringAttr":$label);
1042+
1043+
let assemblyFormat = "`<` $func `,` $label `>`";
1044+
let builders = [
1045+
AttrBuilder<(ins "llvm::StringRef":$func_name,
1046+
"llvm::StringRef":$label_name
1047+
), [{
1048+
return $_get($_ctxt, mlir::FlatSymbolRefAttr::get($_ctxt, func_name),
1049+
mlir::StringAttr::get($_ctxt, label_name));
1050+
}]>
1051+
];
1052+
}
1053+
10291054
#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,4 +4897,38 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
48974897
}];
48984898
}
48994899

4900+
//===----------------------------------------------------------------------===//
4901+
// BlockAddressOp
4902+
//===----------------------------------------------------------------------===//
4903+
4904+
def CIR_BlockAddressOp : CIR_Op<"block_address", [Pure]> {
4905+
let summary = "Get the address of a cir.label within a function";
4906+
let description = [{
4907+
The `cir.blockaddress` operation takes a function name and a label and
4908+
produces a pointer value that represents the address of that cir.label
4909+
within the specified function.
4910+
4911+
This operation models GCC's "labels as values" extension (`&&label`), which
4912+
allows taking the address of a local label and using it as a computed
4913+
jump target (e.g., with `goto *addr;`).
4914+
4915+
Example:
4916+
```mlir
4917+
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
4918+
{alignment = 8 : i64}
4919+
%addr = cir.block_address <@c, "label1"> : !cir.ptr<!cir.void>
4920+
cir.store align(8) %addr, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
4921+
cir.br ^bb1
4922+
^bb1:
4923+
cir.label "label"
4924+
```
4925+
}];
4926+
4927+
let arguments = (ins CIR_BlockAddrInfoAttr:$block_addr_info);
4928+
let results = (outs CIR_VoidPtrType:$addr);
4929+
let assemblyFormat = [{
4930+
$block_addr_info `:` qualified(type($addr)) attr-dict
4931+
}];
4932+
}
4933+
49004934
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
168168
return emitLoadOfLValue(e);
169169
}
170170

171+
mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
172+
auto func = cast<cir::FuncOp>(cgf.curFn);
173+
auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
174+
&cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
175+
return cir::BlockAddressOp::create(builder, cgf.getLoc(e->getSourceRange()),
176+
cgf.convertType(e->getType()),
177+
blockInfoAttr);
178+
}
179+
171180
mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
172181
mlir::Type type = cgf.convertType(e->getType());
173182
return cir::ConstantOp::create(builder, cgf.getLoc(e->getExprLoc()),

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,22 +1912,45 @@ mlir::LogicalResult cir::FuncOp::verify() {
19121912

19131913
llvm::SmallSet<llvm::StringRef, 16> labels;
19141914
llvm::SmallSet<llvm::StringRef, 16> gotos;
1915-
1915+
llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
1916+
bool invalidBlockAddress = false;
19161917
getOperation()->walk([&](mlir::Operation *op) {
19171918
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
19181919
labels.insert(lab.getLabel());
19191920
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
19201921
gotos.insert(goTo.getLabel());
1922+
} else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
1923+
if (blkAdd.getBlockAddrInfoAttr().getFunc().getAttr() != getSymName()) {
1924+
// Stop the walk early, no need to continue
1925+
invalidBlockAddress = true;
1926+
return mlir::WalkResult::interrupt();
1927+
}
1928+
blockAddresses.insert(blkAdd.getBlockAddrInfoAttr().getLabel());
19211929
}
1930+
return mlir::WalkResult::advance();
19221931
});
19231932

1933+
if (invalidBlockAddress)
1934+
return emitOpError() << "blockaddress references a different function";
1935+
1936+
llvm::SmallSet<llvm::StringRef, 16> mismatched;
19241937
if (!labels.empty() || !gotos.empty()) {
1925-
llvm::SmallSet<llvm::StringRef, 16> mismatched =
1926-
llvm::set_difference(gotos, labels);
1938+
mismatched = llvm::set_difference(gotos, labels);
19271939

19281940
if (!mismatched.empty())
19291941
return emitOpError() << "goto/label mismatch";
19301942
}
1943+
1944+
mismatched.clear();
1945+
1946+
if (!labels.empty() || !blockAddresses.empty()) {
1947+
mismatched = llvm::set_difference(blockAddresses, labels);
1948+
1949+
if (!mismatched.empty())
1950+
return emitOpError()
1951+
<< "expects an existing label target in the referenced function";
1952+
}
1953+
19311954
return success();
19321955
}
19331956

clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "PassDetail.h"
99
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1010
#include "clang/CIR/Dialect/Passes.h"
11+
#include "llvm/ADT/SmallSet.h"
1112
#include "llvm/Support/TimeProfiler.h"
1213
#include <memory>
1314

@@ -30,17 +31,29 @@ static void process(cir::FuncOp func) {
3031
mlir::OpBuilder rewriter(func.getContext());
3132
llvm::StringMap<Block *> labels;
3233
llvm::SmallVector<cir::GotoOp, 4> gotos;
34+
llvm::SmallSet<StringRef, 4> blockAddrLabel;
3335

3436
func.getBody().walk([&](mlir::Operation *op) {
3537
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
36-
// Will construct a string copy inplace. Safely erase the label
3738
labels.try_emplace(lab.getLabel(), lab->getBlock());
38-
lab.erase();
3939
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
4040
gotos.push_back(goTo);
41+
} else if (auto blockAddr = dyn_cast<cir::BlockAddressOp>(op)) {
42+
blockAddrLabel.insert(blockAddr.getBlockAddrInfo().getLabel());
4143
}
4244
});
4345

46+
for (auto &lab : labels) {
47+
StringRef labelName = lab.getKey();
48+
Block *block = lab.getValue();
49+
if (!blockAddrLabel.contains(labelName)) {
50+
// erase the LabelOp inside the block if safe
51+
if (auto lab = dyn_cast<cir::LabelOp>(&block->front())) {
52+
lab.erase();
53+
}
54+
}
55+
}
56+
4457
for (auto goTo : gotos) {
4558
mlir::OpBuilder::InsertionGuard guard(rewriter);
4659
rewriter.setInsertionPoint(goTo);

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3837,6 +3837,12 @@ mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
38373837
return mlir::success();
38383838
}
38393839

3840+
mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
3841+
cir::BlockAddressOp op, OpAdaptor adaptor,
3842+
mlir::ConversionPatternRewriter &rewriter) const {
3843+
return mlir::failure();
3844+
}
3845+
38403846
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
38413847
return std::make_unique<ConvertCIRToLLVMPass>();
38423848
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
4+
void A(void) {
5+
void *ptr = &&LABEL_A;
6+
LABEL_A:
7+
return;
8+
}
9+
// CIR: cir.func dso_local @A
10+
// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
11+
// CIR: [[BLOCK:%.*]] = cir.block_address <@A, "LABEL_A"> : !cir.ptr<!void>
12+
// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
13+
// CIR: cir.br ^bb1
14+
// CIR: ^bb1: // pred: ^bb0
15+
// CIR: cir.label "LABEL_A"
16+
// CIR: cir.return
17+
18+
void B(void) {
19+
LABEL_B:
20+
void *ptr = &&LABEL_B;
21+
}
22+
23+
// CIR: cir.func dso_local @B()
24+
// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
25+
// CIR: cir.br ^bb1
26+
// CIR: ^bb1:
27+
// CIR: cir.label "LABEL_B"
28+
// CIR: [[BLOCK:%.*]] = cir.block_address <@B, "LABEL_B"> : !cir.ptr<!void>
29+
// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
30+
// CIR: cir.return
31+
32+
void C(int x) {
33+
void *ptr = (x == 0) ? &&LABEL_A : &&LABEL_B;
34+
LABEL_A:
35+
return;
36+
LABEL_B:
37+
return;
38+
}
39+
40+
// CIR: cir.func dso_local @C
41+
// CIR: [[BLOCK1:%.*]] = cir.block_address <@C, "LABEL_A"> : !cir.ptr<!void>
42+
// CIR: [[BLOCK2:%.*]] = cir.block_address <@C, "LABEL_B"> : !cir.ptr<!void>
43+
// CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr<!void>, !cir.ptr<!void>) -> !cir.ptr<!void>
44+
// CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
45+
// CIR: cir.br ^bb1
46+
// CIR: ^bb1: // pred: ^bb0
47+
// CIR: cir.label "LABEL_A"
48+
// CIR: cir.br ^bb2
49+
// CIR: ^bb2: // 2 preds: ^bb1, ^bb3
50+
// CIR: cir.return
51+
// CIR: ^bb3: // no predecessors
52+
// CIR: cir.label "LABEL_B"
53+
// CIR: cir.br ^bb2
54+
55+
void D(void) {
56+
void *ptr = &&LABEL_A;
57+
void *ptr2 = &&LABEL_A;
58+
LABEL_A:
59+
void *ptr3 = &&LABEL_A;
60+
return;
61+
}
62+
63+
// CIR: cir.func dso_local @D
64+
// CIR: %[[PTR:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
65+
// CIR: %[[PTR2:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr2", init]
66+
// CIR: %[[PTR3:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr3", init]
67+
// CIR: %[[BLK1:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
68+
// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
69+
// CIR: %[[BLK2:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
70+
// CIR: cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
71+
// CIR: cir.br ^bb1
72+
// CIR: ^bb1: // pred: ^bb0
73+
// CIR: cir.label "LABEL_A"
74+
// CIR: %[[BLK3:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
75+
// CIR: cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
76+
// CIR: cir.return

clang/test/CIR/IR/block-adress.cir

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
2+
3+
!void = !cir.void
4+
5+
module {
6+
cir.func @block_address(){
7+
%0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
8+
cir.br ^bb1
9+
^bb1:
10+
cir.label "label"
11+
cir.return
12+
}
13+
// CHECK: cir.func @block_address
14+
// CHECK: %0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
15+
// CHECK: cir.br ^bb1
16+
// CHECK: ^bb1:
17+
// CHECK: cir.label "label"
18+
// CHECK: cir.return
19+
20+
cir.func @block_address_inside_scope() -> () {
21+
cir.scope{
22+
%0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
23+
}
24+
cir.br ^bb1
25+
^bb1:
26+
cir.label "label"
27+
cir.return
28+
}
29+
// CHECK: cir.func @block_address_inside_scope
30+
// CHECK: cir.scope
31+
// CHECK: %0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
32+
// CHECK: cir.label "label"
33+
// CHECK: cir.return
34+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: cir-opt %s -verify-diagnostics -split-input-file
2+
3+
!void = !cir.void
4+
5+
// expected-error@+1 {{expects an existing label target in the referenced function}}
6+
cir.func @bad_block_address() -> () {
7+
%0 = cir.block_address <@bad_block_address, "label"> : !cir.ptr<!void>
8+
cir.br ^bb1
9+
^bb1:
10+
cir.label "wrong_label"
11+
cir.return
12+
}
13+
14+
// expected-error@+1 {{blockaddress references a different function}}
15+
cir.func @bad_block_func() -> () {
16+
%0 = cir.block_address <@mismatch_func, "label"> : !cir.ptr<!void>
17+
cir.br ^bb1
18+
^bb1:
19+
cir.label "label"
20+
cir.return
21+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: cir-opt %s -cir-goto-solver --verify-roundtrip -o - | FileCheck %s
2+
3+
!void = !cir.void
4+
5+
cir.func @a(){
6+
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
7+
%1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
8+
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
9+
cir.br ^bb1
10+
^bb1:
11+
cir.label "label1"
12+
cir.br ^bb2
13+
^bb2:
14+
// This label is not referenced by any blockaddressOp, so it should be removed
15+
cir.label "label2"
16+
cir.return
17+
}
18+
19+
// CHECK: cir.func @a()
20+
// CHECK: %1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
21+
// CHECK: ^bb1:
22+
// CHECK: cir.label "label1"
23+
// CHECK: cir.br ^bb2
24+
// CHECK: ^bb2:
25+
// CHECK-NOT: cir.label "label2"
26+
27+
cir.func @b(){
28+
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
29+
%1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
30+
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
31+
cir.goto "label2"
32+
^bb1:
33+
cir.label "label1"
34+
cir.br ^bb2
35+
^bb2:
36+
// This label is not referenced by any blockaddressOp, so it should be removed
37+
cir.label "label2"
38+
cir.return
39+
}
40+
41+
// CHECK: cir.func @b() {
42+
// CHECK: %1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
43+
// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
44+
// CHECK: cir.br ^bb2
45+
// CHECK: ^bb1:
46+
// CHECK: cir.label "label1"
47+
// CHECK: cir.br ^bb2
48+
// CHECK: ^bb2:
49+
// CHECK-NOT: cir.label "label2"
50+
51+
cir.func @c() {
52+
cir.label "label1"
53+
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
54+
%1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
55+
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
56+
cir.return
57+
}
58+
59+
// CHECK: cir.func @c
60+
// CHECK: cir.label "label1"
61+
// CHECK: %1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
62+
// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>

0 commit comments

Comments
 (0)