Skip to content

Commit f370cf6

Browse files
[CIR] Upstream the initial BlockAddressOp implementation
1 parent 66da12a commit f370cf6

File tree

11 files changed

+311
-5
lines changed

11 files changed

+311
-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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4800,4 +4800,37 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
48004800
}];
48014801
}
48024802

4803+
//===----------------------------------------------------------------------===//
4804+
// BlockAddressOp
4805+
//===----------------------------------------------------------------------===//
4806+
4807+
def CIR_BlockAddressOp : CIR_Op<"blockaddress", [Pure]> {
4808+
let summary = "Get the address of a cir.label within a function";
4809+
let description = [{
4810+
The `cir.blockaddress` operation takes a function name and a label and
4811+
produces a pointer value that represents the address of that cir.label within
4812+
the specified function.
4813+
4814+
This operation models GCC's "labels as values" extension (`&&label`), which
4815+
allows taking the address of a local label and using it as a computed
4816+
jump target (e.g., with `goto *addr;`).
4817+
4818+
Example:
4819+
```mlir
4820+
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
4821+
%addr = cir.blockaddress("foo", "label") -> !cir.ptr<!cir.void>
4822+
cir.store align(8) %addr, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
4823+
cir.br ^bb1
4824+
^bb1:
4825+
cir.label "label"
4826+
```
4827+
}];
4828+
4829+
let arguments = (ins CIR_BlockAddrInfoAttr:$blockAddrInfo);
4830+
let results = (outs CIR_VoidPtrType:$addr);
4831+
let assemblyFormat = [{
4832+
$blockAddrInfo `->` qualified(type($addr)) attr-dict
4833+
}];
4834+
}
4835+
48034836
#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
@@ -166,6 +166,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
166166
return emitLoadOfLValue(e);
167167
}
168168

169+
mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
170+
auto func = cast<cir::FuncOp>(cgf.curFn);
171+
auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
172+
&cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
173+
return cir::BlockAddressOp::create(builder, cgf.getLoc(e->getSourceRange()),
174+
cgf.convertType(e->getType()),
175+
blockInfoAttr);
176+
}
177+
169178
mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
170179
mlir::Type type = cgf.convertType(e->getType());
171180
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
@@ -1902,22 +1902,45 @@ mlir::LogicalResult cir::FuncOp::verify() {
19021902

19031903
llvm::SmallSet<llvm::StringRef, 16> labels;
19041904
llvm::SmallSet<llvm::StringRef, 16> gotos;
1905-
1905+
llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
1906+
bool invalidBlockAddress = false;
19061907
getOperation()->walk([&](mlir::Operation *op) {
19071908
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
19081909
labels.insert(lab.getLabel());
19091910
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
19101911
gotos.insert(goTo.getLabel());
1912+
} else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
1913+
if (blkAdd.getBlockAddrInfoAttr().getFunc().getAttr() != getSymName()) {
1914+
// Stop the walk early, no need to continue
1915+
invalidBlockAddress = true;
1916+
return mlir::WalkResult::interrupt();
1917+
}
1918+
blockAddresses.insert(blkAdd.getBlockAddrInfoAttr().getLabel());
19111919
}
1920+
return mlir::WalkResult::advance();
19121921
});
19131922

1923+
if (invalidBlockAddress)
1924+
return emitOpError() << "blockaddress references a different function";
1925+
1926+
llvm::SmallSet<llvm::StringRef, 16> mismatched;
19141927
if (!labels.empty() || !gotos.empty()) {
1915-
llvm::SmallSet<llvm::StringRef, 16> mismatched =
1916-
llvm::set_difference(gotos, labels);
1928+
mismatched = llvm::set_difference(gotos, labels);
19171929

19181930
if (!mismatched.empty())
19191931
return emitOpError() << "goto/label mismatch";
19201932
}
1933+
1934+
mismatched.clear();
1935+
1936+
if (!labels.empty() || !blockAddresses.empty()) {
1937+
mismatched = llvm::set_difference(blockAddresses, labels);
1938+
1939+
if (!mismatched.empty())
1940+
return emitOpError()
1941+
<< "expects an existing label target in the referenced function";
1942+
}
1943+
19211944
return success();
19221945
}
19231946

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
@@ -3778,6 +3778,12 @@ mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
37783778
return mlir::success();
37793779
}
37803780

3781+
mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
3782+
cir::BlockAddressOp op, OpAdaptor adaptor,
3783+
mlir::ConversionPatternRewriter &rewriter) const {
3784+
return mlir::failure();
3785+
}
3786+
37813787
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
37823788
return std::make_unique<ConvertCIRToLLVMPass>();
37833789
}
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 = &&A;
6+
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.blockaddress <@A, "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 "A"
16+
// CIR: cir.return
17+
18+
void B(void) {
19+
B:
20+
void *ptr = &&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 "B"
28+
// CIR: [[BLOCK:%.*]] = cir.blockaddress <@B, "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) ? &&A : &&B;
34+
A:
35+
return;
36+
B:
37+
return;
38+
}
39+
40+
// CIR: cir.func dso_local @C
41+
// CIR: [[BLOCK1:%.*]] = cir.blockaddress <@C, "A"> -> !cir.ptr<!void>
42+
// CIR: [[BLOCK2:%.*]] = cir.blockaddress <@C, "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 "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 "B"
53+
// CIR: cir.br ^bb2
54+
55+
void D(void) {
56+
void *ptr = &&A;
57+
void *ptr2 = &&A;
58+
A:
59+
void *ptr3 = &&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.blockaddress <@D, "A"> -> !cir.ptr<!void>
68+
// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
69+
// CIR: %[[BLK2:.*]] = cir.blockaddress <@D, "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 "A"
74+
// CIR: %[[BLK3:.*]] = cir.blockaddress <@D, "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 | cir-opt | FileCheck %s
2+
3+
!void = !cir.void
4+
5+
module {
6+
cir.func @block_address(){
7+
%0 = cir.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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 -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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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.blockaddress <@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)