Skip to content

Commit 4b112d2

Browse files
[CIR] Upstream LabelOp (#152802)
This PR introduces the `LabelOp`, which is required for implementing `GotoOp` lowering in the future. Lowering to LLVM IR is **not** included in this patch, since it depends on the upcoming `GotoSolver`. The `GotoSolver` traverses the function body, and if it finds a `LabelOp` without a matching `GotoOp`, it erases the label. This means our implementation differs from the classic codegen approach, where labels may be retained even if unused. Example: https://godbolt.org/z/37Mvr4MMr
1 parent 9a698a6 commit 4b112d2

File tree

8 files changed

+214
-2
lines changed

8 files changed

+214
-2
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,21 @@ def CIR_BrOp : CIR_Op<"br",[
10601060
}];
10611061
}
10621062

1063+
//===----------------------------------------------------------------------===//
1064+
// LabelOp
1065+
//===----------------------------------------------------------------------===//
1066+
1067+
// The LabelOp has AlwaysSpeculatable trait in order to not to be swept
1068+
// by canonicalizer
1069+
def CIR_LabelOp : CIR_Op<"label", [AlwaysSpeculatable]> {
1070+
let description = [{
1071+
An identifier which may be referred by cir.goto operation
1072+
}];
1073+
let arguments = (ins StrAttr:$label);
1074+
let assemblyFormat = [{ $label attr-dict }];
1075+
let hasVerifier = 1;
1076+
}
1077+
10631078
//===----------------------------------------------------------------------===//
10641079
// UnaryOp
10651080
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,9 @@ class CIRGenFunction : public CIRGenTypeCache {
11811181

11821182
mlir::Value emitOpOnBoolExpr(mlir::Location loc, const clang::Expr *cond);
11831183

1184+
mlir::LogicalResult emitLabel(const clang::LabelDecl &d);
1185+
mlir::LogicalResult emitLabelStmt(const clang::LabelStmt &s);
1186+
11841187
mlir::LogicalResult emitIfStmt(const clang::IfStmt &s);
11851188

11861189
/// Emit code to compute the specified expression,

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
256256
// NullStmt doesn't need any handling, but we need to say we handled it.
257257
case Stmt::NullStmtClass:
258258
break;
259+
260+
case Stmt::LabelStmtClass:
261+
return emitLabelStmt(cast<LabelStmt>(*s));
259262
case Stmt::CaseStmtClass:
260263
case Stmt::DefaultStmtClass:
261264
// If we reached here, we must not handling a switch case in the top level.
@@ -272,6 +275,17 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
272275
return mlir::success();
273276
}
274277

278+
mlir::LogicalResult CIRGenFunction::emitLabelStmt(const clang::LabelStmt &s) {
279+
280+
if (emitLabel(*s.getDecl()).failed())
281+
return mlir::failure();
282+
283+
if (getContext().getLangOpts().EHAsynch && s.isSideEntry())
284+
getCIRGenModule().errorNYI(s.getSourceRange(), "IsEHa: not implemented.");
285+
286+
return emitStmt(s.getSubStmt(), /*useCurrentScope*/ true);
287+
}
288+
275289
// Add a terminating yield on a body region if no other terminators are used.
276290
static void terminateBody(CIRGenBuilderTy &builder, mlir::Region &r,
277291
mlir::Location loc) {
@@ -429,6 +443,32 @@ CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {
429443
return mlir::success();
430444
}
431445

446+
mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) {
447+
// Create a new block to tag with a label and add a branch from
448+
// the current one to it. If the block is empty just call attach it
449+
// to this label.
450+
mlir::Block *currBlock = builder.getBlock();
451+
mlir::Block *labelBlock = currBlock;
452+
453+
if (!currBlock->empty()) {
454+
{
455+
mlir::OpBuilder::InsertionGuard guard(builder);
456+
labelBlock = builder.createBlock(builder.getBlock()->getParent());
457+
}
458+
builder.create<cir::BrOp>(getLoc(d.getSourceRange()), labelBlock);
459+
}
460+
461+
builder.setInsertionPointToEnd(labelBlock);
462+
builder.create<cir::LabelOp>(getLoc(d.getSourceRange()), d.getName());
463+
builder.setInsertionPointToEnd(labelBlock);
464+
465+
// FIXME: emit debug info for labels, incrementProfileCounter
466+
assert(!cir::MissingFeatures::ehstackBranches());
467+
assert(!cir::MissingFeatures::incrementProfileCounter());
468+
assert(!cir::MissingFeatures::generateDebugInfo());
469+
return mlir::success();
470+
}
471+
432472
mlir::LogicalResult CIRGenFunction::emitBreakStmt(const clang::BreakStmt &s) {
433473
builder.createBreak(getLoc(s.getBreakLoc()));
434474

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,19 @@ LogicalResult cir::ShiftOp::verify() {
17841784
return mlir::success();
17851785
}
17861786

1787+
//===----------------------------------------------------------------------===//
1788+
// LabelOp Definitions
1789+
//===----------------------------------------------------------------------===//
1790+
1791+
LogicalResult cir::LabelOp::verify() {
1792+
mlir::Operation *op = getOperation();
1793+
mlir::Block *blk = op->getBlock();
1794+
if (&blk->front() != op)
1795+
return emitError() << "must be the first operation in a block";
1796+
1797+
return mlir::success();
1798+
}
1799+
17871800
//===----------------------------------------------------------------------===//
17881801
// UnaryOp
17891802
//===----------------------------------------------------------------------===//

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
4747
Block *block = op.getOperation()->getBlock();
4848
Block *dest = op.getDest();
4949

50-
assert(!cir::MissingFeatures::labelOp());
51-
50+
if (isa<cir::LabelOp>(dest->front()))
51+
return failure();
5252
// Single edge between blocks: merge it.
5353
if (block->getNumSuccessors() == 1 &&
5454
dest->getSinglePredecessor() == block) {

clang/test/CIR/CodeGen/label.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
5+
6+
void label() {
7+
labelA:
8+
return;
9+
}
10+
11+
// CIR: cir.func no_proto dso_local @label
12+
// CIR: cir.label "labelA"
13+
// CIR: cir.return
14+
15+
// Note: We are not lowering to LLVM IR via CIR at this stage because that
16+
// process depends on the GotoSolver.
17+
18+
// OGCG: define dso_local void @label
19+
// OGCG: br label %labelA
20+
// OGCG: labelA:
21+
// OGCG: ret void
22+
23+
void multiple_labels() {
24+
labelB:
25+
labelC:
26+
return;
27+
}
28+
29+
// CIR: cir.func no_proto dso_local @multiple_labels
30+
// CIR: cir.label "labelB"
31+
// CIR: cir.br ^bb1
32+
// CIR: ^bb1: // pred: ^bb0
33+
// CIR: cir.label "labelC"
34+
// CIR: cir.return
35+
36+
// OGCG: define dso_local void @multiple_labels
37+
// OGCG: br label %labelB
38+
// OGCG: labelB:
39+
// OGCG: br label %labelC
40+
// OGCG: labelC:
41+
// OGCG: ret void
42+
43+
void label_in_if(int cond) {
44+
if (cond) {
45+
labelD:
46+
cond++;
47+
}
48+
}
49+
50+
// CIR: cir.func dso_local @label_in_if
51+
// CIR: cir.if {{.*}} {
52+
// CIR: cir.label "labelD"
53+
// CIR: [[LOAD:%.*]] = cir.load align(4) [[COND:%.*]] : !cir.ptr<!s32i>, !s32i
54+
// CIR: [[INC:%.*]] = cir.unary(inc, %3) nsw : !s32i, !s32i
55+
// CIR: cir.store align(4) [[INC]], [[COND]] : !s32i, !cir.ptr<!s32i>
56+
// CIR: }
57+
// CIR: cir.return
58+
59+
// OGCG: define dso_local void @label_in_if
60+
// OGCG: if.then:
61+
// OGCG: br label %labelD
62+
// OGCG: labelD:
63+
// OGCG: [[LOAD:%.*]] = load i32, ptr [[COND:%.*]], align 4
64+
// OGCG: [[INC:%.*]] = add nsw i32 %1, 1
65+
// OGCG: store i32 [[INC]], ptr [[COND]], align 4
66+
// OGCG: br label %if.end
67+
// OGCG: if.end:
68+
// OGCG: ret void
69+
70+
void after_return() {
71+
return;
72+
label:
73+
}
74+
75+
// CIR: cir.func no_proto dso_local @after_return
76+
// CIR: cir.br ^bb1
77+
// CIR: ^bb1: // 2 preds: ^bb0, ^bb2
78+
// CIR: cir.return
79+
// CIR: ^bb2: // no predecessors
80+
// CIR: cir.label "label"
81+
// CIR: cir.br ^bb1
82+
83+
// OGCG: define dso_local void @after_return
84+
// OGCG: br label %label
85+
// OGCG: label:
86+
// OGCG: ret void
87+
88+
89+
void after_unreachable() {
90+
__builtin_unreachable();
91+
label:
92+
}
93+
94+
// CIR: cir.func no_proto dso_local @after_unreachable
95+
// CIR: cir.unreachable
96+
// CIR: ^bb1:
97+
// CIR: cir.label "label"
98+
// CIR: cir.return
99+
100+
// OGCG: define dso_local void @after_unreachable
101+
// OGCG: unreachable
102+
// OGCG: label:
103+
// OGCG: ret void

clang/test/CIR/IR/invalid-label.cir

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: cir-opt %s -verify-diagnostics -split-input-file
2+
3+
!s32i = !cir.int<s, 32>
4+
5+
module {
6+
// expected-error@+3 {{must be the first operation in a block}}
7+
cir.func @error(){
8+
%0 = cir.const #cir.int<0> : !s32i
9+
cir.label "label"
10+
cir.return
11+
}
12+
}

clang/test/CIR/IR/label.cir

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: cir-opt %s | FileCheck %s
2+
3+
!s32i = !cir.int<s, 32>
4+
5+
module {
6+
cir.func @label() {
7+
cir.label "label"
8+
cir.return
9+
}
10+
11+
cir.func @label2() {
12+
%0 = cir.const #cir.int<0> : !s32i
13+
cir.br ^bb1
14+
^bb1: // pred: ^bb0
15+
cir.label "label2"
16+
cir.return
17+
}
18+
}
19+
20+
// CHECK: cir.func @label
21+
// CHECK-NEXT: cir.label "label"
22+
23+
// CHECK: cir.func @label2
24+
// CHECK: cir.br ^bb1
25+
// CHECK-NEXT: ^bb1: // pred: ^bb0
26+
// CHECK-NEXT: cir.label "label2"

0 commit comments

Comments
 (0)