Skip to content

Commit 4c0aea8

Browse files
committed
[CIR] Implement Statement Expressions
This patch adds support for statement expressions. It also changes emitCompoundStmt and emitCompoundStmtWithoutScope to accept an Address that the optional result is written to. This allows the creation of the alloca ahead of the creation of the scope which saves us from hoisting the alloca to its parent scope.
1 parent e56ae96 commit 4c0aea8

File tree

5 files changed

+272
-10
lines changed

5 files changed

+272
-10
lines changed

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
6969
void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
7070

7171
void VisitCallExpr(const CallExpr *e);
72+
void VisitStmtExpr(const StmtExpr *e) {
73+
CIRGenFunction::StmtExprEvaluation eval(cgf);
74+
Address retAlloca =
75+
cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
76+
cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca, dest);
77+
}
7278

7379
void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); }
7480

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,21 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
185185
mlir::Value VisitCastExpr(CastExpr *e);
186186
mlir::Value VisitCallExpr(const CallExpr *e);
187187

188+
mlir::Value VisitStmtExpr(StmtExpr *e) {
189+
CIRGenFunction::StmtExprEvaluation eval(cgf);
190+
if (e->getType()->isVoidType()) {
191+
cgf.emitCompoundStmt(*e->getSubStmt());
192+
return {};
193+
}
194+
195+
Address retAlloca =
196+
cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
197+
cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca);
198+
199+
return cgf.emitLoadOfScalar(cgf.makeAddrLValue(retAlloca, e->getType()),
200+
e->getExprLoc());
201+
}
202+
188203
mlir::Value VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
189204
if (e->getBase()->getType()->isVectorType()) {
190205
assert(!cir::MissingFeatures::scalableVectors());

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,9 +1157,14 @@ class CIRGenFunction : public CIRGenTypeCache {
11571157
LValue emitScalarCompoundAssignWithComplex(const CompoundAssignOperator *e,
11581158
mlir::Value &result);
11591159

1160-
void emitCompoundStmt(const clang::CompoundStmt &s);
1160+
Address emitCompoundStmt(const clang::CompoundStmt &s,
1161+
Address *lastValue = nullptr,
1162+
AggValueSlot slot = AggValueSlot::ignored());
11611163

1162-
void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
1164+
Address
1165+
emitCompoundStmtWithoutScope(const clang::CompoundStmt &s,
1166+
Address *lastValue = nullptr,
1167+
AggValueSlot slot = AggValueSlot::ignored());
11631168

11641169
void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false);
11651170
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
@@ -1396,6 +1401,27 @@ class CIRGenFunction : public CIRGenTypeCache {
13961401
// we know if a temporary should be destroyed conditionally.
13971402
ConditionalEvaluation *outermostConditional = nullptr;
13981403

1404+
/// An RAII object to record that we're evaluating a statement
1405+
/// expression.
1406+
class StmtExprEvaluation {
1407+
CIRGenFunction &cgf;
1408+
1409+
/// We have to save the outermost conditional: cleanups in a
1410+
/// statement expression aren't conditional just because the
1411+
/// StmtExpr is.
1412+
ConditionalEvaluation *savedOutermostConditional;
1413+
1414+
public:
1415+
StmtExprEvaluation(CIRGenFunction &cgf)
1416+
: cgf(cgf), savedOutermostConditional(cgf.outermostConditional) {
1417+
cgf.outermostConditional = nullptr;
1418+
}
1419+
1420+
~StmtExprEvaluation() {
1421+
cgf.outermostConditional = savedOutermostConditional;
1422+
}
1423+
};
1424+
13991425
template <typename FuncTy>
14001426
ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e,
14011427
const FuncTy &branchGenFunc);

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "CIRGenFunction.h"
1515

1616
#include "mlir/IR/Builders.h"
17+
#include "mlir/IR/Location.h"
1718
#include "clang/AST/ExprCXX.h"
1819
#include "clang/AST/Stmt.h"
1920
#include "clang/AST/StmtOpenACC.h"
@@ -23,16 +24,62 @@ using namespace clang;
2324
using namespace clang::CIRGen;
2425
using namespace cir;
2526

26-
void CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s) {
27-
for (auto *curStmt : s.body()) {
28-
if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
29-
getCIRGenModule().errorNYI(curStmt->getSourceRange(),
30-
std::string("emitCompoundStmtWithoutScope: ") +
31-
curStmt->getStmtClassName());
27+
Address CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s,
28+
Address *lastValue,
29+
AggValueSlot slot) {
30+
const Stmt *exprResult = s.getStmtExprResult();
31+
assert((!lastValue || (lastValue && exprResult)) &&
32+
"If lastValue is not null then the CompoundStmt must have a "
33+
"StmtExprResult");
34+
35+
Address retAlloca = Address::invalid();
36+
37+
for (Stmt *curStmt : s.body()) {
38+
// We have to special case labels here. They are statements, but when put
39+
// at the end of a statement expression, they yield the value of their
40+
// subexpression. Handle this by walking through all labels we encounter,
41+
// emitting them before we evaluate the subexpr.
42+
// Similar issues arise for attributed statements.
43+
if (lastValue && exprResult == curStmt) {
44+
while (!isa<Expr>(exprResult)) {
45+
if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
46+
if (emitLabel(*ls->getDecl()).failed())
47+
return Address::invalid();
48+
exprResult = ls->getSubStmt();
49+
} else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
50+
// FIXME: Update this if we ever have attributes that affect the
51+
// semantics of an expression.
52+
exprResult = as->getSubStmt();
53+
} else {
54+
llvm_unreachable("Unknown value statement");
55+
}
56+
}
57+
58+
const Expr *e = cast<Expr>(exprResult);
59+
QualType exprTy = e->getType();
60+
if (hasAggregateEvaluationKind(exprTy)) {
61+
emitAggExpr(e, slot);
62+
} else {
63+
// We can't return an RValue here because there might be cleanups at
64+
// the end of the StmtExpr. Because of that, we have to emit the result
65+
// here into a temporary alloca.
66+
emitAnyExprToMem(e, *lastValue, Qualifiers(),
67+
/*IsInit*/ false);
68+
}
69+
} else {
70+
if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
71+
return Address::invalid();
72+
}
3273
}
74+
75+
return retAlloca;
3376
}
3477

35-
void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
78+
Address CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
79+
Address *lastValue,
80+
AggValueSlot slot) {
81+
Address retAlloca = Address::invalid();
82+
3683
// Add local scope to track new declared variables.
3784
SymTableScopeTy varScope(symbolTable);
3885
mlir::Location scopeLoc = getLoc(s.getSourceRange());
@@ -45,8 +92,10 @@ void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
4592
mlir::OpBuilder::InsertionGuard guard(builder);
4693
builder.restoreInsertionPoint(scopeInsPt);
4794
LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
48-
emitCompoundStmtWithoutScope(s);
95+
retAlloca = emitCompoundStmtWithoutScope(s, lastValue, slot);
4996
}
97+
98+
return retAlloca;
5099
}
51100

52101
void CIRGenFunction::emitStopPoint(const Stmt *s) {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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 -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
int f19(void) {
9+
return ({ 3;;4;; });
10+
}
11+
12+
// CIR: cir.func dso_local @f19() -> !s32i
13+
// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
14+
// CIR: %[[TMP:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
15+
// CIR: cir.scope {
16+
// CIR: %[[C3:.+]] = cir.const #cir.int<3> : !s32i
17+
// CIR: %[[C4:.+]] = cir.const #cir.int<4> : !s32i
18+
// CIR: cir.store {{.*}} %[[C4]], %[[TMP]] : !s32i, !cir.ptr<!s32i>
19+
// CIR: }
20+
// CIR: %[[TMP_VAL:.+]] = cir.load {{.*}} %[[TMP]] : !cir.ptr<!s32i>, !s32i
21+
// CIR: cir.store %[[TMP_VAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
22+
// CIR: %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
23+
// CIR: cir.return %[[RES]] : !s32i
24+
25+
// LLVM: define dso_local i32 @f19()
26+
// LLVM: %[[VAR1:.+]] = alloca i32, i64 1
27+
// LLVM: %[[VAR2:.+]] = alloca i32, i64 1
28+
// LLVM: br label %[[LBL3:.+]]
29+
// LLVM: [[LBL3]]:
30+
// LLVM: store i32 4, ptr %[[VAR2]]
31+
// LLVM: br label %[[LBL4:.+]]
32+
// LLVM: [[LBL4]]:
33+
// LLVM: %[[V1:.+]] = load i32, ptr %[[VAR2]]
34+
// LLVM: store i32 %[[V1]], ptr %[[VAR1]]
35+
// LLVM: %[[RES:.+]] = load i32, ptr %[[VAR1]]
36+
// LLVM: ret i32 %[[RES]]
37+
38+
// OGCG: define dso_local i32 @f19()
39+
// OGCG: entry:
40+
// OGCG: %[[TMP:.+]] = alloca i32
41+
// OGCG: store i32 4, ptr %[[TMP]]
42+
// OGCG: %[[TMP_VAL:.+]] = load i32, ptr %[[TMP]]
43+
// OGCG: ret i32 %[[TMP_VAL]]
44+
45+
46+
int nested(void) {
47+
({123;});
48+
{
49+
int bar = 987;
50+
return ({ ({ int asdf = 123; asdf; }); ({9999;}); });
51+
}
52+
}
53+
54+
// CIR: cir.func dso_local @nested() -> !s32i
55+
// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
56+
// CIR: %[[TMP_OUTER:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
57+
// CIR: cir.scope {
58+
// CIR: %[[C123_OUTER:.+]] = cir.const #cir.int<123> : !s32i
59+
// CIR: cir.store {{.*}} %[[C123_OUTER]], %[[TMP_OUTER]] : !s32i, !cir.ptr<!s32i>
60+
// CIR: }
61+
// CIR: %[[LOAD_TMP_OUTER:.+]] = cir.load {{.*}} %[[TMP_OUTER]] : !cir.ptr<!s32i>, !s32i
62+
// CIR: cir.scope {
63+
// CIR: %[[BAR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["bar", init]
64+
// CIR: %[[TMP_BARRET:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
65+
// CIR: %[[C987:.+]] = cir.const #cir.int<987> : !s32i
66+
// CIR: cir.store {{.*}} %[[C987]], %[[BAR]] : !s32i, !cir.ptr<!s32i>
67+
// CIR: cir.scope {
68+
// CIR: %[[TMP1:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
69+
// CIR: %[[TMP2:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
70+
// CIR: cir.scope {
71+
// CIR: %[[ASDF:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["asdf", init]
72+
// CIR: %[[C123_INNER:.+]] = cir.const #cir.int<123> : !s32i
73+
// CIR: cir.store {{.*}} %[[C123_INNER]], %[[ASDF]] : !s32i, !cir.ptr<!s32i>
74+
// CIR: %[[LOAD_ASDF:.+]] = cir.load {{.*}} %[[ASDF]] : !cir.ptr<!s32i>, !s32i
75+
// CIR: cir.store {{.*}} %[[LOAD_ASDF]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
76+
// CIR: }
77+
// CIR: %[[V1:.+]] = cir.load {{.*}} %[[TMP1]] : !cir.ptr<!s32i>, !s32i
78+
// CIR: cir.scope {
79+
// CIR: %[[C9999:.+]] = cir.const #cir.int<9999> : !s32i
80+
// CIR: cir.store {{.*}} %[[C9999]], %[[TMP2]] : !s32i, !cir.ptr<!s32i>
81+
// CIR: }
82+
// CIR: %[[V2:.+]] = cir.load {{.*}} %[[TMP2]] : !cir.ptr<!s32i>, !s32i
83+
// CIR: cir.store {{.*}} %[[V2]], %[[TMP_BARRET]] : !s32i, !cir.ptr<!s32i>
84+
// CIR: }
85+
// CIR: %[[BARRET_VAL:.+]] = cir.load {{.*}} %[[TMP_BARRET]] : !cir.ptr<!s32i>, !s32i
86+
// CIR: cir.store %[[BARRET_VAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
87+
// CIR: %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
88+
// CIR: cir.return %[[RES]] : !s32i
89+
// CIR: }
90+
// CIR: %[[FINAL_RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
91+
// CIR: cir.return %[[FINAL_RES]] : !s32i
92+
93+
// LLVM: define dso_local i32 @nested()
94+
// LLVM: %[[VAR1:.+]] = alloca i32, i64 1
95+
// LLVM: %[[VAR2:.+]] = alloca i32, i64 1
96+
// LLVM: %[[VAR3:.+]] = alloca i32, i64 1
97+
// LLVM: %[[VAR4:.+]] = alloca i32, i64 1
98+
// LLVM: %[[VAR5:.+]] = alloca i32, i64 1
99+
// LLVM: %[[VAR6:.+]] = alloca i32, i64 1
100+
// LLVM: %[[VAR7:.+]] = alloca i32, i64 1
101+
// LLVM: br label %[[LBL8:.+]]
102+
// LLVM: [[LBL8]]:
103+
// LLVM: store i32 123, ptr %[[VAR7]]
104+
// LLVM: br label %[[LBL9:.+]]
105+
// LLVM: [[LBL9]]:
106+
// LLVM: br label %[[LBL10:.+]]
107+
// LLVM: [[LBL10]]:
108+
// LLVM: store i32 987, ptr %[[VAR1]]
109+
// LLVM: br label %[[LBL11:.+]]
110+
// LLVM: [[LBL11]]:
111+
// LLVM: br label %[[LBL12:.+]]
112+
// LLVM: [[LBL12]]:
113+
// LLVM: store i32 123, ptr %[[VAR5]]
114+
// LLVM: %[[V1:.+]] = load i32, ptr %[[VAR5]]
115+
// LLVM: store i32 %[[V1]], ptr %[[VAR3]]
116+
// LLVM: br label %[[LBL14:.+]]
117+
// LLVM: [[LBL14]]:
118+
// LLVM: br label %[[LBL15:.+]]
119+
// LLVM: [[LBL15]]:
120+
// LLVM: store i32 9999, ptr %[[VAR4]]
121+
// LLVM: br label %[[LBL16:.+]]
122+
// LLVM: [[LBL16]]:
123+
// LLVM: %[[V2:.+]] = load i32, ptr %[[VAR4]]
124+
// LLVM: store i32 %[[V2]], ptr %[[VAR2]]
125+
// LLVM: br label %[[LBL18:.+]]
126+
// LLVM: [[LBL18]]:
127+
// LLVM: %[[V3:.+]] = load i32, ptr %[[VAR2]]
128+
// LLVM: store i32 %[[V3]], ptr %[[VAR6]]
129+
// LLVM: %[[RES:.+]] = load i32, ptr %[[VAR6]]
130+
// LLVM: ret i32 %[[RES]]
131+
132+
// OGCG: define dso_local i32 @nested()
133+
// OGCG: entry:
134+
// OGCG: %[[TMP_OUTER:.+]] = alloca i32
135+
// OGCG: %[[BAR:.+]] = alloca i32
136+
// OGCG: %[[ASDF:.+]] = alloca i32
137+
// OGCG: %[[TMP1:.+]] = alloca i32
138+
// OGCG: %[[TMP2:.+]] = alloca i32
139+
// OGCG: %[[TMP3:.+]] = alloca i32
140+
// OGCG: store i32 123, ptr %[[TMP_OUTER]]
141+
// OGCG: %[[OUTER_VAL:.+]] = load i32, ptr %[[TMP_OUTER]]
142+
// OGCG: store i32 987, ptr %[[BAR]]
143+
// OGCG: store i32 123, ptr %[[ASDF]]
144+
// OGCG: %[[ASDF_VAL:.+]] = load i32, ptr %[[ASDF]]
145+
// OGCG: store i32 %[[ASDF_VAL]], ptr %[[TMP1]]
146+
// OGCG: %[[TMP1_VAL:.+]] = load i32, ptr %[[TMP1]]
147+
// OGCG: store i32 9999, ptr %[[TMP3]]
148+
// OGCG: %[[TMP3_VAL:.+]] = load i32, ptr %[[TMP3]]
149+
// OGCG: store i32 %[[TMP3_VAL]], ptr %[[TMP2]]
150+
// OGCG: %[[RES:.+]] = load i32, ptr %[[TMP2]]
151+
// OGCG: ret i32 %[[RES]]
152+
153+
void empty() {
154+
return ({;;;;});
155+
}
156+
157+
// CIR: cir.func no_proto dso_local @empty()
158+
// CIR-NEXT: cir.return
159+
160+
// LLVM: define dso_local void @empty()
161+
// LLVM: ret void
162+
// LLVM: }
163+
164+
// OGCG: define dso_local void @empty()
165+
// OGCG: ret void
166+
// OGCG: }

0 commit comments

Comments
 (0)