Skip to content

Commit 64b9410

Browse files
authored
[CIR] Upstream support for emitting ignored statements (llvm#130869)
This adds support for emitting ClangIR for statements whose value is ignored. The test case being added (CIR/CodeGen/basic.c) tests a few more things. The "f1" test case is the only part that's immediately relevant to this change, but the other cases were part of the same test in the incubator and they are supported so I brought in the entire test.
1 parent cba9dc6 commit 64b9410

File tree

5 files changed

+271
-14
lines changed

5 files changed

+271
-14
lines changed

clang/include/clang/CIR/MissingFeatures.h

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct MissingFeatures {
8383
static bool emitNullabilityCheck() { return false; }
8484
static bool astVarDeclInterface() { return false; }
8585
static bool stackSaveOp() { return false; }
86+
static bool aggValueSlot() { return false; }
8687
};
8788

8889
} // namespace cir

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,34 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
165165
return LValue();
166166
}
167167

168+
/// Emit code to compute the specified expression which
169+
/// can have any type. The result is returned as an RValue struct.
170+
RValue CIRGenFunction::emitAnyExpr(const Expr *e) {
171+
switch (CIRGenFunction::getEvaluationKind(e->getType())) {
172+
case cir::TEK_Scalar:
173+
return RValue::get(emitScalarExpr(e));
174+
case cir::TEK_Complex:
175+
cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: complex type");
176+
return RValue::get(nullptr);
177+
case cir::TEK_Aggregate:
178+
cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: aggregate type");
179+
return RValue::get(nullptr);
180+
}
181+
llvm_unreachable("bad evaluation kind");
182+
}
183+
184+
/// Emit code to compute the specified expression, ignoring the result.
185+
void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
186+
if (e->isPRValue()) {
187+
assert(!cir::MissingFeatures::aggValueSlot());
188+
emitAnyExpr(e);
189+
return;
190+
}
191+
192+
// Just emit it as an l-value and drop the result.
193+
emitLValue(e);
194+
}
195+
168196
mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
169197
mlir::Location loc,
170198
CharUnits alignment) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

+10
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ class CIRGenFunction : public CIRGenTypeCache {
154154

155155
const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); }
156156

157+
/// Emit code to compute the specified expression which can have any type. The
158+
/// result is returned as an RValue struct. If this is an aggregate
159+
/// expression, the aggloc/agglocvolatile arguments indicate where the result
160+
/// should be returned.
161+
RValue emitAnyExpr(const clang::Expr *e);
162+
157163
void finishFunction(SourceLocation endLoc);
158164
mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
159165

@@ -170,6 +176,10 @@ class CIRGenFunction : public CIRGenTypeCache {
170176

171177
void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
172178

179+
/// Emit code to compute the specified expression,
180+
/// ignoring the result.
181+
void emitIgnoredExpr(const clang::Expr *e);
182+
173183
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
174184

175185
mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s);

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

+158-14
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,160 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
5555
if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope)))
5656
return mlir::success();
5757

58-
// Only a subset of simple statements are supported at the moment. When more
59-
// kinds of statements are supported, a
60-
// switch (s->getStmtClass()) {
61-
// will be added here.
62-
return mlir::failure();
58+
switch (s->getStmtClass()) {
59+
60+
#define STMT(Type, Base)
61+
#define ABSTRACT_STMT(Op)
62+
#define EXPR(Type, Base) case Stmt::Type##Class:
63+
#include "clang/AST/StmtNodes.inc"
64+
{
65+
// Remember the block we came in on.
66+
mlir::Block *incoming = builder.getInsertionBlock();
67+
assert(incoming && "expression emission must have an insertion point");
68+
69+
emitIgnoredExpr(cast<Expr>(s));
70+
71+
mlir::Block *outgoing = builder.getInsertionBlock();
72+
assert(outgoing && "expression emission cleared block!");
73+
return mlir::success();
74+
}
75+
76+
case Stmt::OMPScopeDirectiveClass:
77+
case Stmt::OMPErrorDirectiveClass:
78+
case Stmt::NoStmtClass:
79+
case Stmt::CXXCatchStmtClass:
80+
case Stmt::SEHExceptStmtClass:
81+
case Stmt::SEHFinallyStmtClass:
82+
case Stmt::MSDependentExistsStmtClass:
83+
case Stmt::NullStmtClass:
84+
case Stmt::CompoundStmtClass:
85+
case Stmt::DeclStmtClass:
86+
case Stmt::LabelStmtClass:
87+
case Stmt::AttributedStmtClass:
88+
case Stmt::GotoStmtClass:
89+
case Stmt::BreakStmtClass:
90+
case Stmt::ContinueStmtClass:
91+
case Stmt::DefaultStmtClass:
92+
case Stmt::CaseStmtClass:
93+
case Stmt::SEHLeaveStmtClass:
94+
case Stmt::SYCLKernelCallStmtClass:
95+
case Stmt::IfStmtClass:
96+
case Stmt::SwitchStmtClass:
97+
case Stmt::ForStmtClass:
98+
case Stmt::WhileStmtClass:
99+
case Stmt::DoStmtClass:
100+
case Stmt::CoroutineBodyStmtClass:
101+
case Stmt::CoreturnStmtClass:
102+
case Stmt::CXXTryStmtClass:
103+
case Stmt::CXXForRangeStmtClass:
104+
case Stmt::IndirectGotoStmtClass:
105+
case Stmt::ReturnStmtClass:
106+
case Stmt::GCCAsmStmtClass:
107+
case Stmt::MSAsmStmtClass:
108+
case Stmt::OMPParallelDirectiveClass:
109+
case Stmt::OMPTaskwaitDirectiveClass:
110+
case Stmt::OMPTaskyieldDirectiveClass:
111+
case Stmt::OMPBarrierDirectiveClass:
112+
case Stmt::CapturedStmtClass:
113+
case Stmt::ObjCAtTryStmtClass:
114+
case Stmt::ObjCAtThrowStmtClass:
115+
case Stmt::ObjCAtSynchronizedStmtClass:
116+
case Stmt::ObjCForCollectionStmtClass:
117+
case Stmt::ObjCAutoreleasePoolStmtClass:
118+
case Stmt::SEHTryStmtClass:
119+
case Stmt::OMPMetaDirectiveClass:
120+
case Stmt::OMPCanonicalLoopClass:
121+
case Stmt::OMPSimdDirectiveClass:
122+
case Stmt::OMPTileDirectiveClass:
123+
case Stmt::OMPUnrollDirectiveClass:
124+
case Stmt::OMPForDirectiveClass:
125+
case Stmt::OMPForSimdDirectiveClass:
126+
case Stmt::OMPSectionsDirectiveClass:
127+
case Stmt::OMPSectionDirectiveClass:
128+
case Stmt::OMPSingleDirectiveClass:
129+
case Stmt::OMPMasterDirectiveClass:
130+
case Stmt::OMPCriticalDirectiveClass:
131+
case Stmt::OMPParallelForDirectiveClass:
132+
case Stmt::OMPParallelForSimdDirectiveClass:
133+
case Stmt::OMPParallelMasterDirectiveClass:
134+
case Stmt::OMPParallelSectionsDirectiveClass:
135+
case Stmt::OMPTaskDirectiveClass:
136+
case Stmt::OMPTaskgroupDirectiveClass:
137+
case Stmt::OMPFlushDirectiveClass:
138+
case Stmt::OMPDepobjDirectiveClass:
139+
case Stmt::OMPScanDirectiveClass:
140+
case Stmt::OMPOrderedDirectiveClass:
141+
case Stmt::OMPAtomicDirectiveClass:
142+
case Stmt::OMPTargetDirectiveClass:
143+
case Stmt::OMPTeamsDirectiveClass:
144+
case Stmt::OMPCancellationPointDirectiveClass:
145+
case Stmt::OMPCancelDirectiveClass:
146+
case Stmt::OMPTargetDataDirectiveClass:
147+
case Stmt::OMPTargetEnterDataDirectiveClass:
148+
case Stmt::OMPTargetExitDataDirectiveClass:
149+
case Stmt::OMPTargetParallelDirectiveClass:
150+
case Stmt::OMPTargetParallelForDirectiveClass:
151+
case Stmt::OMPTaskLoopDirectiveClass:
152+
case Stmt::OMPTaskLoopSimdDirectiveClass:
153+
case Stmt::OMPMaskedTaskLoopDirectiveClass:
154+
case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
155+
case Stmt::OMPMasterTaskLoopDirectiveClass:
156+
case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
157+
case Stmt::OMPParallelGenericLoopDirectiveClass:
158+
case Stmt::OMPParallelMaskedDirectiveClass:
159+
case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
160+
case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
161+
case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
162+
case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
163+
case Stmt::OMPDistributeDirectiveClass:
164+
case Stmt::OMPDistributeParallelForDirectiveClass:
165+
case Stmt::OMPDistributeParallelForSimdDirectiveClass:
166+
case Stmt::OMPDistributeSimdDirectiveClass:
167+
case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
168+
case Stmt::OMPTargetParallelForSimdDirectiveClass:
169+
case Stmt::OMPTargetSimdDirectiveClass:
170+
case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
171+
case Stmt::OMPTargetUpdateDirectiveClass:
172+
case Stmt::OMPTeamsDistributeDirectiveClass:
173+
case Stmt::OMPTeamsDistributeSimdDirectiveClass:
174+
case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
175+
case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
176+
case Stmt::OMPTeamsGenericLoopDirectiveClass:
177+
case Stmt::OMPTargetTeamsDirectiveClass:
178+
case Stmt::OMPTargetTeamsDistributeDirectiveClass:
179+
case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
180+
case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
181+
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
182+
case Stmt::OMPInteropDirectiveClass:
183+
case Stmt::OMPDispatchDirectiveClass:
184+
case Stmt::OMPGenericLoopDirectiveClass:
185+
case Stmt::OMPReverseDirectiveClass:
186+
case Stmt::OMPInterchangeDirectiveClass:
187+
case Stmt::OMPAssumeDirectiveClass:
188+
case Stmt::OMPMaskedDirectiveClass:
189+
case Stmt::OMPStripeDirectiveClass:
190+
case Stmt::OpenACCComputeConstructClass:
191+
case Stmt::OpenACCLoopConstructClass:
192+
case Stmt::OpenACCCombinedConstructClass:
193+
case Stmt::OpenACCDataConstructClass:
194+
case Stmt::OpenACCEnterDataConstructClass:
195+
case Stmt::OpenACCExitDataConstructClass:
196+
case Stmt::OpenACCHostDataConstructClass:
197+
case Stmt::OpenACCWaitConstructClass:
198+
case Stmt::OpenACCInitConstructClass:
199+
case Stmt::OpenACCShutdownConstructClass:
200+
case Stmt::OpenACCSetConstructClass:
201+
case Stmt::OpenACCUpdateConstructClass:
202+
case Stmt::OpenACCCacheConstructClass:
203+
case Stmt::OpenACCAtomicConstructClass:
204+
case Stmt::ObjCAtCatchStmtClass:
205+
case Stmt::ObjCAtFinallyStmtClass:
206+
cgm.errorNYI(s->getSourceRange(),
207+
std::string("emitStmt: ") + s->getStmtClassName());
208+
return mlir::failure();
209+
}
210+
211+
llvm_unreachable("Unexpected statement class");
63212
}
64213

65214
mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
@@ -106,16 +255,11 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
106255
// this section will do nothing. But for now a ReturnOp is necessary.
107256
builder.create<ReturnOp>(loc);
108257
} else if (rv->getType()->isVoidType()) {
109-
// No return value. Emit the return expression for its side effects.
110-
// TODO(CIR): Once emitAnyExpr(e) has been upstreamed, get rid of the check
111-
// and just call emitAnyExpr(rv) here.
112-
if (CIRGenFunction::hasScalarEvaluationKind(rv->getType())) {
113-
emitScalarExpr(rv);
114-
} else {
115-
getCIRGenModule().errorNYI(s.getSourceRange(),
116-
"non-scalar function return type");
258+
// Make sure not to return anything, but evaluate the expression
259+
// for side effects.
260+
if (rv) {
261+
emitAnyExpr(rv);
117262
}
118-
builder.create<ReturnOp>(loc);
119263
} else if (fnRetTy->isReferenceType()) {
120264
getCIRGenModule().errorNYI(s.getSourceRange(),
121265
"function return type that is a reference");

clang/test/CIR/CodeGen/basic.c

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -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 -Wno-unused-value -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 -Wno-unused-value -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
int f1(int i);
9+
10+
int f1(int i) {
11+
i;
12+
return i;
13+
}
14+
15+
// CIR: module
16+
// CIR-NEXT: cir.func @f1(%arg0: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
17+
// CIR-NEXT: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
18+
// CIR-NEXT: cir.store %arg0, %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
19+
// CIR-NEXT: %[[I_IGNORED:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
20+
// CIR-NEXT: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
21+
// CIR-NEXT: cir.return %[[I]] : !cir.int<s, 32>
22+
23+
// LLVM: define i32 @f1(i32 %[[I:.*]])
24+
// LLVM-NEXT: %[[I_PTR:.*]] = alloca i32, i64 1, align 4
25+
// LLVM-NEXT: store i32 %[[I]], ptr %[[I_PTR]], align 4
26+
// LLVM-NEXT: %[[I_IGNORED:.*]] = load i32, ptr %[[I_PTR]], align 4
27+
// LLVM-NEXT: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
28+
// LLVM-NEXT: ret i32 %[[I]]
29+
30+
// OGCG: define{{.*}} i32 @f1(i32 noundef %[[I:.*]])
31+
// OGCG-NEXT: entry:
32+
// OGCG-NEXT: %[[I_PTR:.*]] = alloca i32, align 4
33+
// OGCG-NEXT: store i32 %[[I]], ptr %[[I_PTR]], align 4
34+
// OGCG-NEXT: %[[I_IGNORED:.*]] = load i32, ptr %[[I_PTR]], align 4
35+
// OGCG-NEXT: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
36+
// OGCG-NEXT: ret i32 %[[I]]
37+
38+
int f2(void) { return 3; }
39+
40+
// CIR: cir.func @f2() -> !cir.int<s, 32>
41+
// CIR-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !cir.int<s, 32>
42+
// CIR-NEXT: cir.return %[[THREE]] : !cir.int<s, 32>
43+
44+
// LLVM: define i32 @f2()
45+
// LLVM-NEXT: ret i32 3
46+
47+
// OGCG: define{{.*}} i32 @f2()
48+
// OGCG-NEXT: entry:
49+
// OGCG-NEXT: ret i32 3
50+
51+
int f3(void) {
52+
int i = 3;
53+
return i;
54+
}
55+
56+
// CIR: cir.func @f3() -> !cir.int<s, 32>
57+
// CIR-NEXT: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
58+
// CIR-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !cir.int<s, 32>
59+
// CIR-NEXT: cir.store %[[THREE]], %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
60+
// CIR-NEXT: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
61+
// CIR-NEXT: cir.return %[[I]] : !cir.int<s, 32>
62+
63+
// LLVM: define i32 @f3()
64+
// LLVM-NEXT: %[[I_PTR:.*]] = alloca i32, i64 1, align 4
65+
// LLVM-NEXT: store i32 3, ptr %[[I_PTR]], align 4
66+
// LLVM-NEXT: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
67+
// LLVM-NEXT: ret i32 %[[I]]
68+
69+
// OGCG: define{{.*}} i32 @f3
70+
// OGCG-NEXT: entry:
71+
// OGCG-NEXT: %[[I_PTR:.*]] = alloca i32, align 4
72+
// OGCG-NEXT: store i32 3, ptr %[[I_PTR]], align 4
73+
// OGCG-NEXT: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
74+
// OGCG-NEXT: ret i32 %[[I]]

0 commit comments

Comments
 (0)