Skip to content

Commit 916218c

Browse files
[CIR] Upstream GotoOp (#153701)
This PR upstreams `GotoOp`. It moves some tests from the `goto` test file to the `label` test file, and adds verify logic to `FuncOp`. The gotosSolver, required for lowering, will be implemented in a future PR.
1 parent 60aa0d4 commit 916218c

File tree

8 files changed

+358
-5
lines changed

8 files changed

+358
-5
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
504504
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
505505
auto last =
506506
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
507-
// TODO: Add LabelOp missing feature here
508-
return mlir::isa<cir::AllocaOp>(&op);
507+
return mlir::isa<cir::AllocaOp, cir::LabelOp>(&op);
509508
});
510509

511510
if (last != block->rend())

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

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

1063+
//===----------------------------------------------------------------------===//
1064+
// GotoOp
1065+
//===----------------------------------------------------------------------===//
1066+
1067+
def CIR_GotoOp : CIR_Op<"goto", [Terminator]> {
1068+
let description = [{
1069+
1070+
Transfers control to the specified `label`. This requires a corresponding
1071+
`cir.label` to exist and is used by to represent source level `goto`s
1072+
that jump across region boundaries. Alternatively, `cir.br` is used to
1073+
construct goto's that don't violate such boundaries.
1074+
1075+
`cir.goto` is completely symbolic (i.e. it "jumps" on a label that isn't
1076+
yet materialized) and should be taken into account by passes and analysis
1077+
when deciding if it's safe to make some assumptions about a given region
1078+
or basic block.
1079+
1080+
Example:
1081+
```C++
1082+
int test(int x) {
1083+
if (x)
1084+
goto label;
1085+
{
1086+
x = 10;
1087+
label:
1088+
return x;
1089+
}
1090+
}
1091+
```
1092+
1093+
```mlir
1094+
cir.scope { // REGION #1
1095+
%2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
1096+
%3 = cir.cast(int_to_bool, %2 : !s32i), !cir.bool
1097+
cir.if %3 {
1098+
cir.goto "label"
1099+
}
1100+
}
1101+
cir.scope { // REGION #2
1102+
%2 = cir.const #cir.int<10> : !s32i
1103+
cir.store %2, %0 : !s32i, !cir.ptr<!s32i>
1104+
cir.br ^bb1
1105+
^bb1: // pred: ^bb0
1106+
cir.label "label"
1107+
%3 = cir.load %0 : !cir.ptr<!s32i>, !s32i
1108+
cir.store %3, %1 : !s32i, !cir.ptr<!s32i>
1109+
%4 = cir.load %1 : !cir.ptr<!s32i>, !s32i
1110+
cir.return %4 : !s32i
1111+
}
1112+
cir.unreachable
1113+
```
1114+
}];
1115+
let arguments = (ins StrAttr:$label);
1116+
let assemblyFormat = [{ $label attr-dict }];
1117+
}
1118+
10631119
//===----------------------------------------------------------------------===//
10641120
// LabelOp
10651121
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,8 @@ class CIRGenFunction : public CIRGenTypeCache {
11201120

11211121
mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
11221122

1123+
mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &s);
1124+
11231125
void emitImplicitAssignmentOperatorBody(FunctionArgList &args);
11241126

11251127
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
252252
else
253253
emitCompoundStmt(cast<CompoundStmt>(*s));
254254
break;
255+
case Stmt::GotoStmtClass:
256+
return emitGotoStmt(cast<GotoStmt>(*s));
255257
case Stmt::ContinueStmtClass:
256258
return emitContinueStmt(cast<ContinueStmt>(*s));
257259

@@ -435,6 +437,24 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
435437
return mlir::success();
436438
}
437439

440+
mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) {
441+
// FIXME: LLVM codegen inserts emit a stop point here for debug info
442+
// sake when the insertion point is available, but doesn't do
443+
// anything special when there isn't. We haven't implemented debug
444+
// info support just yet, look at this again once we have it.
445+
assert(!cir::MissingFeatures::generateDebugInfo());
446+
447+
cir::GotoOp::create(builder, getLoc(s.getSourceRange()),
448+
s.getLabel()->getName());
449+
450+
// A goto marks the end of a block, create a new one for codegen after
451+
// emitGotoStmt can resume building in that block.
452+
// Insert the new block to continue codegen after goto.
453+
builder.createBlock(builder.getBlock()->getParent());
454+
455+
return mlir::success();
456+
}
457+
438458
mlir::LogicalResult
439459
CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {
440460
builder.createContinue(getLoc(s.getContinueLoc()));

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc"
2323
#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc"
2424
#include "clang/CIR/MissingFeatures.h"
25+
#include "llvm/ADT/SetOperations.h"
26+
#include "llvm/ADT/SmallSet.h"
2527
#include "llvm/Support/LogicalResult.h"
2628

2729
#include <numeric>
@@ -1647,9 +1649,28 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
16471649
}
16481650
}
16491651

1650-
// TODO(CIR): The properties of functions that require verification haven't
1651-
// been implemented yet.
1652-
mlir::LogicalResult cir::FuncOp::verify() { return success(); }
1652+
mlir::LogicalResult cir::FuncOp::verify() {
1653+
1654+
llvm::SmallSet<llvm::StringRef, 16> labels;
1655+
llvm::SmallSet<llvm::StringRef, 16> gotos;
1656+
1657+
getOperation()->walk([&](mlir::Operation *op) {
1658+
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
1659+
labels.insert(lab.getLabel());
1660+
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
1661+
gotos.insert(goTo.getLabel());
1662+
}
1663+
});
1664+
1665+
if (!labels.empty() || !gotos.empty()) {
1666+
llvm::SmallSet<llvm::StringRef, 16> mismatched =
1667+
llvm::set_difference(gotos, labels);
1668+
1669+
if (!mismatched.empty())
1670+
return emitOpError() << "goto/label mismatch";
1671+
}
1672+
return success();
1673+
}
16531674

16541675
//===----------------------------------------------------------------------===//
16551676
// BinOp

clang/test/CIR/CodeGen/goto.cpp

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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+
int shouldNotGenBranchRet(int x) {
7+
if (x > 5)
8+
goto err;
9+
return 0;
10+
err:
11+
return -1;
12+
}
13+
// CIR: cir.func dso_local @_Z21shouldNotGenBranchReti
14+
// CIR: cir.if {{.*}} {
15+
// CIR: cir.goto "err"
16+
// CIR: }
17+
// CIR: [[ZERO:%.*]] = cir.const #cir.int<0> : !s32i
18+
// CIR: cir.store [[ZERO]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i>
19+
// CIR: cir.br ^bb1
20+
// CIR: ^bb1:
21+
// CIR: [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
22+
// CIR: cir.return [[RET]] : !s32i
23+
// CIR: ^bb2:
24+
// CIR: cir.label "err"
25+
// CIR: [[ONE:%.*]] = cir.const #cir.int<1> : !s32i
26+
// CIR: [[MINUS:%.*]] = cir.unary(minus, [[ONE]]) nsw : !s32i, !s32i
27+
// CIR: cir.store [[MINUS]], [[RETVAL]] : !s32i, !cir.ptr<!s32i>
28+
// CIR: cir.br ^bb1
29+
30+
// OGCG: define dso_local noundef i32 @_Z21shouldNotGenBranchReti
31+
// OGCG: if.then:
32+
// OGCG: br label %err
33+
// OGCG: if.end:
34+
// OGCG: br label %return
35+
// OGCG: err:
36+
// OGCG: br label %return
37+
// OGCG: return:
38+
39+
int shouldGenBranch(int x) {
40+
if (x > 5)
41+
goto err;
42+
x++;
43+
err:
44+
return -1;
45+
}
46+
// CIR: cir.func dso_local @_Z15shouldGenBranchi
47+
// CIR: cir.if {{.*}} {
48+
// CIR: cir.goto "err"
49+
// CIR: }
50+
// CIR: cir.br ^bb1
51+
// CIR: ^bb1:
52+
// CIR: cir.label "err"
53+
54+
// OGCG: define dso_local noundef i32 @_Z15shouldGenBranchi
55+
// OGCG: if.then:
56+
// OGCG: br label %err
57+
// OGCG: if.end:
58+
// OGCG: br label %err
59+
// OGCG: err:
60+
// OGCG: ret
61+
62+
void severalLabelsInARow(int a) {
63+
int b = a;
64+
goto end1;
65+
b = b + 1;
66+
goto end2;
67+
end1:
68+
end2:
69+
b = b + 2;
70+
}
71+
// CIR: cir.func dso_local @_Z19severalLabelsInARowi
72+
// CIR: cir.goto "end1"
73+
// CIR: ^bb[[#BLK1:]]
74+
// CIR: cir.goto "end2"
75+
// CIR: ^bb[[#BLK2:]]:
76+
// CIR: cir.label "end1"
77+
// CIR: cir.br ^bb[[#BLK3:]]
78+
// CIR: ^bb[[#BLK3]]:
79+
// CIR: cir.label "end2"
80+
81+
// OGCG: define dso_local void @_Z19severalLabelsInARowi
82+
// OGCG: br label %end1
83+
// OGCG: end1:
84+
// OGCG: br label %end2
85+
// OGCG: end2:
86+
// OGCG: ret
87+
88+
void severalGotosInARow(int a) {
89+
int b = a;
90+
goto end;
91+
goto end;
92+
end:
93+
b = b + 2;
94+
}
95+
// CIR: cir.func dso_local @_Z18severalGotosInARowi
96+
// CIR: cir.goto "end"
97+
// CIR: ^bb[[#BLK1:]]:
98+
// CIR: cir.goto "end"
99+
// CIR: ^bb[[#BLK2:]]:
100+
// CIR: cir.label "end"
101+
102+
// OGCG: define dso_local void @_Z18severalGotosInARowi(i32 noundef %a) #0 {
103+
// OGCG: br label %end
104+
// OGCG: end:
105+
// OGCG: ret void
106+
107+
extern "C" void action1();
108+
extern "C" void action2();
109+
extern "C" void multiple_non_case(int v) {
110+
switch (v) {
111+
default:
112+
action1();
113+
l2:
114+
action2();
115+
break;
116+
}
117+
}
118+
119+
// CIR: cir.func dso_local @multiple_non_case
120+
// CIR: cir.switch
121+
// CIR: cir.case(default, []) {
122+
// CIR: cir.call @action1()
123+
// CIR: cir.br ^[[BB1:[a-zA-Z0-9]+]]
124+
// CIR: ^[[BB1]]:
125+
// CIR: cir.label
126+
// CIR: cir.call @action2()
127+
// CIR: cir.break
128+
129+
// OGCG: define dso_local void @multiple_non_case
130+
// OGCG: sw.default:
131+
// OGCG: call void @action1()
132+
// OGCG: br label %l2
133+
// OGCG: l2:
134+
// OGCG: call void @action2()
135+
// OGCG: br label [[BREAK:%.*]]
136+
137+
extern "C" void case_follow_label(int v) {
138+
switch (v) {
139+
case 1:
140+
label:
141+
case 2:
142+
action1();
143+
break;
144+
default:
145+
action2();
146+
goto label;
147+
}
148+
}
149+
150+
// CIR: cir.func dso_local @case_follow_label
151+
// CIR: cir.switch
152+
// CIR: cir.case(equal, [#cir.int<1> : !s32i]) {
153+
// CIR: cir.label "label"
154+
// CIR: cir.case(equal, [#cir.int<2> : !s32i]) {
155+
// CIR: cir.call @action1()
156+
// CIR: cir.break
157+
// CIR: cir.case(default, []) {
158+
// CIR: cir.call @action2()
159+
// CIR: cir.goto "label"
160+
161+
// OGCG: define dso_local void @case_follow_label
162+
// OGCG: sw.bb:
163+
// OGCG: br label %label
164+
// OGCG: label:
165+
// OGCG: br label %sw.bb1
166+
// OGCG: sw.bb1:
167+
// OGCG: call void @action1()
168+
// OGCG: br label %sw.epilog
169+
// OGCG: sw.default:
170+
// OGCG: call void @action2()
171+
// OGCG: br label %label
172+
// OGCG: sw.epilog:
173+
// OGCG: ret void
174+
175+
extern "C" void default_follow_label(int v) {
176+
switch (v) {
177+
case 1:
178+
case 2:
179+
action1();
180+
break;
181+
label:
182+
default:
183+
action2();
184+
goto label;
185+
}
186+
}
187+
188+
// CIR: cir.func dso_local @default_follow_label
189+
// CIR: cir.switch
190+
// CIR: cir.case(equal, [#cir.int<1> : !s32i]) {
191+
// CIR: cir.yield
192+
// CIR: cir.case(equal, [#cir.int<2> : !s32i]) {
193+
// CIR: cir.call @action1()
194+
// CIR: cir.break
195+
// CIR: cir.label "label"
196+
// CIR: cir.case(default, []) {
197+
// CIR: cir.call @action2()
198+
// CIR: cir.goto "label"
199+
200+
// OGCG: define dso_local void @default_follow_label
201+
// OGCG: sw.bb:
202+
// OGCG: call void @action1()
203+
// OGCG: br label %sw.epilog
204+
// OGCG: label:
205+
// OGCG: br label %sw.default
206+
// OGCG: sw.default:
207+
// OGCG: call void @action2()
208+
// OGCG: br label %label
209+
// OGCG: sw.epilog:
210+
// OGCG: ret void

0 commit comments

Comments
 (0)