Skip to content

Commit 611ca17

Browse files
[CIR] Add BlockAddressOp (#1875)
This PR adds support for the new `BlockAddressOp`, used for GCC labels as values. Support for indirect `goto` and `ConstantLValueEmitter::VisitAddrLabelExpr` will be added in a future PR.
1 parent ecb3678 commit 611ca17

File tree

10 files changed

+292
-18
lines changed

10 files changed

+292
-18
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6118,6 +6118,39 @@ def CIR_LinkerOptionsOp : CIR_Op<"linker_options", [
61186118
}];
61196119
}
61206120

6121+
//===----------------------------------------------------------------------===//
6122+
// BlockAddressOp
6123+
//===----------------------------------------------------------------------===//
6124+
6125+
def CIR_BlockAddressOp : CIR_Op<"blockaddress", [Pure]> {
6126+
let summary = "Get the address of a cir.label within a function";
6127+
let description = [{
6128+
The `cir.blockaddress` operation takes a function name and a label and
6129+
produces a pointer value that represents the address of that cir.label within
6130+
the specified function.
6131+
6132+
This operation models GCC's "labels as values" extension (`&&label`), which
6133+
allows taking the address of a local label and using it as a computed
6134+
jump target (e.g., with `goto *addr;`).
6135+
6136+
Example:
6137+
```mlir
6138+
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
6139+
%addr = cir.blockaddress("foo", "label") -> !cir.ptr<!cir.void>
6140+
cir.store align(8) %addr, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
6141+
cir.br ^bb1
6142+
^bb1:
6143+
cir.label "label"
6144+
```
6145+
}];
6146+
6147+
let arguments = (ins FlatSymbolRefAttr:$func, StrAttr:$label);
6148+
let results = (outs CIR_VoidPtrType:$addr);
6149+
let assemblyFormat = [{
6150+
`(` $func `,` $label `)` `->` qualified(type($addr)) attr-dict
6151+
}];
6152+
}
6153+
61216154
//===----------------------------------------------------------------------===//
61226155
// Standard library function calls
61236156
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,18 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
226226
}
227227

228228
mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E);
229-
mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *E) {
230-
llvm_unreachable("NYI");
229+
230+
mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
231+
auto func = cast<cir::FuncOp>(CGF.CurFn);
232+
llvm::StringRef symName = func.getSymName();
233+
mlir::FlatSymbolRefAttr funName =
234+
mlir::FlatSymbolRefAttr::get(&CGF.getMLIRContext(), symName);
235+
mlir::StringAttr labelName =
236+
mlir::StringAttr::get(&CGF.getMLIRContext(), e->getLabel()->getName());
237+
return cir::BlockAddressOp::create(Builder, CGF.getLoc(e->getSourceRange()),
238+
CGF.convertType(e->getType()), funName,
239+
labelName);
240+
;
231241
}
232242
mlir::Value VisitSizeOfPackExpr(SizeOfPackExpr *E) {
233243
llvm_unreachable("NYI");

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *S,
180180
case Stmt::CXXForRangeStmtClass:
181181
return emitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs);
182182

183-
case Stmt::IndirectGotoStmtClass:
184183
case Stmt::ReturnStmtClass:
185184
// When implemented, GCCAsmStmtClass should fall-through to MSAsmStmtClass.
186185
case Stmt::GCCAsmStmtClass:
@@ -196,6 +195,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *S,
196195
case Stmt::OMPBarrierDirectiveClass:
197196
return emitOMPBarrierDirective(cast<OMPBarrierDirective>(*S));
198197
// Unsupported AST nodes:
198+
case Stmt::IndirectGotoStmtClass:
199199
case Stmt::CapturedStmtClass:
200200
case Stmt::ObjCAtTryStmtClass:
201201
case Stmt::ObjCAtThrowStmtClass:

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

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
#include "clang/CIR/Dialect/IR/CIRTypes.h"
1818
#include "clang/CIR/Interfaces/CIRLoopOpInterface.h"
1919
#include "clang/CIR/MissingFeatures.h"
20+
#include "llvm/ADT/SetOperations.h"
21+
#include "llvm/ADT/SmallSet.h"
2022
#include "llvm/ADT/StringExtras.h"
2123
#include "llvm/ADT/TypeSwitch.h"
2224
#include "llvm/Support/ErrorHandling.h"
2325
#include "llvm/Support/LogicalResult.h"
2426
#include <numeric>
2527
#include <optional>
26-
#include <set>
2728

2829
#include "mlir/Dialect/Func/IR/FuncOps.h"
2930
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
@@ -2928,23 +2929,46 @@ LogicalResult cir::FuncOp::verify() {
29282929
<< "' must have empty body";
29292930
}
29302931

2931-
std::set<llvm::StringRef> labels;
2932-
std::set<llvm::StringRef> gotos;
2933-
2932+
llvm::SmallSet<llvm::StringRef, 16> labels;
2933+
llvm::SmallSet<llvm::StringRef, 16> gotos;
2934+
llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
2935+
bool invalidBlockAddress = false;
29342936
getOperation()->walk([&](mlir::Operation *op) {
29352937
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
2936-
labels.emplace(lab.getLabel());
2938+
labels.insert(lab.getLabel());
29372939
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
2938-
gotos.emplace(goTo.getLabel());
2940+
gotos.insert(goTo.getLabel());
2941+
} else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
2942+
if (blkAdd.getFunc() != getSymName()) {
2943+
// Stop the walk early, no need to continue
2944+
invalidBlockAddress = true;
2945+
return mlir::WalkResult::interrupt();
2946+
}
2947+
blockAddresses.insert(blkAdd.getLabel());
29392948
}
2949+
return mlir::WalkResult::advance();
29402950
});
29412951

2942-
std::vector<llvm::StringRef> mismatched;
2943-
std::set_difference(gotos.begin(), gotos.end(), labels.begin(), labels.end(),
2944-
std::back_inserter(mismatched));
2952+
if (invalidBlockAddress)
2953+
return emitOpError() << "blockaddress references a different function";
2954+
2955+
llvm::SmallSet<llvm::StringRef, 16> mismatched;
2956+
if (!labels.empty() || !gotos.empty()) {
2957+
mismatched = llvm::set_difference(gotos, labels);
2958+
2959+
if (!mismatched.empty())
2960+
return emitOpError() << "goto/label mismatch";
2961+
}
29452962

2946-
if (!mismatched.empty())
2947-
return emitOpError() << "goto/label mismatch";
2963+
mismatched.clear();
2964+
2965+
if (!labels.empty() || !blockAddresses.empty()) {
2966+
mismatched = llvm::set_difference(blockAddresses, labels);
2967+
2968+
if (!mismatched.empty())
2969+
return emitOpError()
2970+
<< "expects an existing label target in the referenced function";
2971+
}
29482972

29492973
return success();
29502974
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "clang/CIR/Dialect/IR/CIRDialect.h"
44
#include "clang/CIR/Dialect/Passes.h"
55

6+
#include "llvm/ADT/SmallSet.h"
67
#include "llvm/ADT/SmallVector.h"
78
#include "llvm/ADT/StringMap.h"
89
#include "llvm/ADT/StringRef.h"
@@ -24,17 +25,29 @@ static void process(cir::FuncOp func) {
2425
mlir::OpBuilder rewriter(func.getContext());
2526
llvm::StringMap<Block *> labels;
2627
llvm::SmallVector<cir::GotoOp, 4> gotos;
28+
llvm::SmallSet<StringRef, 4> blockAddrLabel;
2729

2830
func.getBody().walk([&](mlir::Operation *op) {
2931
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
30-
// Will construct a string copy inplace. Safely erase the label
3132
labels.try_emplace(lab.getLabel(), lab->getBlock());
32-
lab.erase();
3333
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
3434
gotos.push_back(goTo);
35+
} else if (auto blockAddr = dyn_cast<cir::BlockAddressOp>(op)) {
36+
blockAddrLabel.insert(blockAddr.getLabel());
3537
}
3638
});
3739

40+
for (auto &lab : labels) {
41+
StringRef labelName = lab.getKey();
42+
Block *block = lab.getValue();
43+
if (!blockAddrLabel.contains(labelName)) {
44+
// erase the LabelOp inside the block if safe
45+
if (auto lab = dyn_cast<cir::LabelOp>(&block->front())) {
46+
lab.erase();
47+
}
48+
}
49+
}
50+
3851
for (auto goTo : gotos) {
3952
mlir::OpBuilder::InsertionGuard guard(rewriter);
4053
rewriter.setInsertionPoint(goTo);
@@ -54,4 +67,4 @@ void GotoSolverPass::runOnOperation() {
5467

5568
std::unique_ptr<Pass> mlir::createGotoSolverPass() {
5669
return std::make_unique<GotoSolverPass>();
57-
}
70+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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: cir.label "B"
25+
// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
26+
// CIR: [[BLOCK:%.*]] = cir.blockaddress(@B, "B") -> !cir.ptr<!void>
27+
// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
28+
// CIR: cir.return
29+
30+
void C(int x) {
31+
void *ptr = (x == 0) ? &&A : &&B;
32+
A:
33+
return;
34+
B:
35+
return;
36+
}
37+
38+
// CIR: cir.func dso_local @C
39+
// CIR: [[BLOCK1:%.*]] = cir.blockaddress(@C, "A") -> !cir.ptr<!void>
40+
// CIR: [[BLOCK2:%.*]] = cir.blockaddress(@C, "B") -> !cir.ptr<!void>
41+
// CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr<!void>, !cir.ptr<!void>) -> !cir.ptr<!void>
42+
// CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
43+
// CIR: cir.br ^bb2
44+
// CIR: ^bb1: // 2 preds: ^bb2, ^bb3
45+
// CIR: cir.return
46+
// CIR: ^bb2: // pred: ^bb0
47+
// CIR: cir.label "A"
48+
// CIR: cir.br ^bb1
49+
// CIR: ^bb3: // no predecessors
50+
// CIR: cir.label "B"
51+
// CIR: cir.br ^bb1
52+
53+
void D(void) {
54+
void *ptr = &&A;
55+
void *ptr2 = &&A;
56+
A:
57+
void *ptr3 = &&A;
58+
return;
59+
}
60+
61+
// CIR: cir.func dso_local @D
62+
// CIR: %[[PTR:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
63+
// CIR: %[[PTR2:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr2", init]
64+
// CIR: %[[PTR3:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr3", init]
65+
// CIR: %[[BLK1:.*]] = cir.blockaddress(@D, "A") -> !cir.ptr<!void>
66+
// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
67+
// CIR: %[[BLK2:.*]] = cir.blockaddress(@D, "A") -> !cir.ptr<!void>
68+
// CIR: cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
69+
// CIR: cir.br ^bb1
70+
// CIR: ^bb1: // pred: ^bb0
71+
// CIR: cir.label "A"
72+
// CIR: %[[BLK3:.*]] = cir.blockaddress(@D, "A") -> !cir.ptr<!void>
73+
// CIR: cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
74+
// 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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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>>
63+

0 commit comments

Comments
 (0)