1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Emit Stmt nodes as CIR code. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "CIRGenBuilder.h" |
14 | #include "CIRGenFunction.h" |
15 | |
16 | #include "mlir/IR/Builders.h" |
17 | #include "clang/AST/ExprCXX.h" |
18 | #include "clang/AST/Stmt.h" |
19 | #include "clang/AST/StmtOpenACC.h" |
20 | #include "clang/CIR/MissingFeatures.h" |
21 | |
22 | using namespace clang; |
23 | using namespace clang::CIRGen; |
24 | using namespace cir; |
25 | |
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()); |
32 | } |
33 | } |
34 | |
35 | void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) { |
36 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
37 | mlir::OpBuilder::InsertPoint scopeInsPt; |
38 | builder.create<cir::ScopeOp>( |
39 | scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) { |
40 | scopeInsPt = b.saveInsertionPoint(); |
41 | }); |
42 | { |
43 | mlir::OpBuilder::InsertionGuard guard(builder); |
44 | builder.restoreInsertionPoint(scopeInsPt); |
45 | LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock()); |
46 | emitCompoundStmtWithoutScope(s); |
47 | } |
48 | } |
49 | |
50 | void CIRGenFunction::emitStopPoint(const Stmt *s) { |
51 | assert(!cir::MissingFeatures::generateDebugInfo()); |
52 | } |
53 | |
54 | // Build CIR for a statement. useCurrentScope should be true if no new scopes |
55 | // need to be created when finding a compound statement. |
56 | mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, |
57 | bool useCurrentScope, |
58 | ArrayRef<const Attr *> attr) { |
59 | if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope))) |
60 | return mlir::success(); |
61 | |
62 | switch (s->getStmtClass()) { |
63 | case Stmt::NoStmtClass: |
64 | case Stmt::CXXCatchStmtClass: |
65 | case Stmt::SEHExceptStmtClass: |
66 | case Stmt::SEHFinallyStmtClass: |
67 | case Stmt::MSDependentExistsStmtClass: |
68 | llvm_unreachable("invalid statement class to emit generically" ); |
69 | case Stmt::BreakStmtClass: |
70 | case Stmt::NullStmtClass: |
71 | case Stmt::CompoundStmtClass: |
72 | case Stmt::ContinueStmtClass: |
73 | case Stmt::DeclStmtClass: |
74 | case Stmt::ReturnStmtClass: |
75 | llvm_unreachable("should have emitted these statements as simple" ); |
76 | |
77 | #define STMT(Type, Base) |
78 | #define ABSTRACT_STMT(Op) |
79 | #define EXPR(Type, Base) case Stmt::Type##Class: |
80 | #include "clang/AST/StmtNodes.inc" |
81 | { |
82 | // Remember the block we came in on. |
83 | mlir::Block *incoming = builder.getInsertionBlock(); |
84 | assert(incoming && "expression emission must have an insertion point" ); |
85 | |
86 | emitIgnoredExpr(e: cast<Expr>(s)); |
87 | |
88 | mlir::Block *outgoing = builder.getInsertionBlock(); |
89 | assert(outgoing && "expression emission cleared block!" ); |
90 | return mlir::success(); |
91 | } |
92 | case Stmt::IfStmtClass: |
93 | return emitIfStmt(cast<IfStmt>(*s)); |
94 | case Stmt::SwitchStmtClass: |
95 | return emitSwitchStmt(cast<SwitchStmt>(*s)); |
96 | case Stmt::ForStmtClass: |
97 | return emitForStmt(cast<ForStmt>(*s)); |
98 | case Stmt::WhileStmtClass: |
99 | return emitWhileStmt(cast<WhileStmt>(*s)); |
100 | case Stmt::DoStmtClass: |
101 | return emitDoStmt(cast<DoStmt>(*s)); |
102 | case Stmt::CXXForRangeStmtClass: |
103 | return emitCXXForRangeStmt(cast<CXXForRangeStmt>(*s), attr); |
104 | case Stmt::OpenACCComputeConstructClass: |
105 | return emitOpenACCComputeConstruct(cast<OpenACCComputeConstruct>(*s)); |
106 | case Stmt::OpenACCLoopConstructClass: |
107 | return emitOpenACCLoopConstruct(cast<OpenACCLoopConstruct>(*s)); |
108 | case Stmt::OpenACCCombinedConstructClass: |
109 | return emitOpenACCCombinedConstruct(cast<OpenACCCombinedConstruct>(*s)); |
110 | case Stmt::OpenACCDataConstructClass: |
111 | return emitOpenACCDataConstruct(cast<OpenACCDataConstruct>(*s)); |
112 | case Stmt::OpenACCEnterDataConstructClass: |
113 | return emitOpenACCEnterDataConstruct(cast<OpenACCEnterDataConstruct>(*s)); |
114 | case Stmt::OpenACCExitDataConstructClass: |
115 | return emitOpenACCExitDataConstruct(cast<OpenACCExitDataConstruct>(*s)); |
116 | case Stmt::OpenACCHostDataConstructClass: |
117 | return emitOpenACCHostDataConstruct(cast<OpenACCHostDataConstruct>(*s)); |
118 | case Stmt::OpenACCWaitConstructClass: |
119 | return emitOpenACCWaitConstruct(cast<OpenACCWaitConstruct>(*s)); |
120 | case Stmt::OpenACCInitConstructClass: |
121 | return emitOpenACCInitConstruct(cast<OpenACCInitConstruct>(*s)); |
122 | case Stmt::OpenACCShutdownConstructClass: |
123 | return emitOpenACCShutdownConstruct(cast<OpenACCShutdownConstruct>(*s)); |
124 | case Stmt::OpenACCSetConstructClass: |
125 | return emitOpenACCSetConstruct(cast<OpenACCSetConstruct>(*s)); |
126 | case Stmt::OpenACCUpdateConstructClass: |
127 | return emitOpenACCUpdateConstruct(cast<OpenACCUpdateConstruct>(*s)); |
128 | case Stmt::OpenACCCacheConstructClass: |
129 | return emitOpenACCCacheConstruct(cast<OpenACCCacheConstruct>(*s)); |
130 | case Stmt::OpenACCAtomicConstructClass: |
131 | return emitOpenACCAtomicConstruct(cast<OpenACCAtomicConstruct>(*s)); |
132 | case Stmt::OMPScopeDirectiveClass: |
133 | case Stmt::OMPErrorDirectiveClass: |
134 | case Stmt::LabelStmtClass: |
135 | case Stmt::AttributedStmtClass: |
136 | case Stmt::GotoStmtClass: |
137 | case Stmt::DefaultStmtClass: |
138 | case Stmt::CaseStmtClass: |
139 | case Stmt::SEHLeaveStmtClass: |
140 | case Stmt::SYCLKernelCallStmtClass: |
141 | case Stmt::CoroutineBodyStmtClass: |
142 | case Stmt::CoreturnStmtClass: |
143 | case Stmt::CXXTryStmtClass: |
144 | case Stmt::IndirectGotoStmtClass: |
145 | case Stmt::GCCAsmStmtClass: |
146 | case Stmt::MSAsmStmtClass: |
147 | case Stmt::OMPParallelDirectiveClass: |
148 | case Stmt::OMPTaskwaitDirectiveClass: |
149 | case Stmt::OMPTaskyieldDirectiveClass: |
150 | case Stmt::OMPBarrierDirectiveClass: |
151 | case Stmt::CapturedStmtClass: |
152 | case Stmt::ObjCAtTryStmtClass: |
153 | case Stmt::ObjCAtThrowStmtClass: |
154 | case Stmt::ObjCAtSynchronizedStmtClass: |
155 | case Stmt::ObjCForCollectionStmtClass: |
156 | case Stmt::ObjCAutoreleasePoolStmtClass: |
157 | case Stmt::SEHTryStmtClass: |
158 | case Stmt::OMPMetaDirectiveClass: |
159 | case Stmt::OMPCanonicalLoopClass: |
160 | case Stmt::OMPSimdDirectiveClass: |
161 | case Stmt::OMPTileDirectiveClass: |
162 | case Stmt::OMPUnrollDirectiveClass: |
163 | case Stmt::OMPForDirectiveClass: |
164 | case Stmt::OMPForSimdDirectiveClass: |
165 | case Stmt::OMPSectionsDirectiveClass: |
166 | case Stmt::OMPSectionDirectiveClass: |
167 | case Stmt::OMPSingleDirectiveClass: |
168 | case Stmt::OMPMasterDirectiveClass: |
169 | case Stmt::OMPCriticalDirectiveClass: |
170 | case Stmt::OMPParallelForDirectiveClass: |
171 | case Stmt::OMPParallelForSimdDirectiveClass: |
172 | case Stmt::OMPParallelMasterDirectiveClass: |
173 | case Stmt::OMPParallelSectionsDirectiveClass: |
174 | case Stmt::OMPTaskDirectiveClass: |
175 | case Stmt::OMPTaskgroupDirectiveClass: |
176 | case Stmt::OMPFlushDirectiveClass: |
177 | case Stmt::OMPDepobjDirectiveClass: |
178 | case Stmt::OMPScanDirectiveClass: |
179 | case Stmt::OMPOrderedDirectiveClass: |
180 | case Stmt::OMPAtomicDirectiveClass: |
181 | case Stmt::OMPTargetDirectiveClass: |
182 | case Stmt::OMPTeamsDirectiveClass: |
183 | case Stmt::OMPCancellationPointDirectiveClass: |
184 | case Stmt::OMPCancelDirectiveClass: |
185 | case Stmt::OMPTargetDataDirectiveClass: |
186 | case Stmt::OMPTargetEnterDataDirectiveClass: |
187 | case Stmt::OMPTargetExitDataDirectiveClass: |
188 | case Stmt::OMPTargetParallelDirectiveClass: |
189 | case Stmt::OMPTargetParallelForDirectiveClass: |
190 | case Stmt::OMPTaskLoopDirectiveClass: |
191 | case Stmt::OMPTaskLoopSimdDirectiveClass: |
192 | case Stmt::OMPMaskedTaskLoopDirectiveClass: |
193 | case Stmt::OMPMaskedTaskLoopSimdDirectiveClass: |
194 | case Stmt::OMPMasterTaskLoopDirectiveClass: |
195 | case Stmt::OMPMasterTaskLoopSimdDirectiveClass: |
196 | case Stmt::OMPParallelGenericLoopDirectiveClass: |
197 | case Stmt::OMPParallelMaskedDirectiveClass: |
198 | case Stmt::OMPParallelMaskedTaskLoopDirectiveClass: |
199 | case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass: |
200 | case Stmt::OMPParallelMasterTaskLoopDirectiveClass: |
201 | case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: |
202 | case Stmt::OMPDistributeDirectiveClass: |
203 | case Stmt::OMPDistributeParallelForDirectiveClass: |
204 | case Stmt::OMPDistributeParallelForSimdDirectiveClass: |
205 | case Stmt::OMPDistributeSimdDirectiveClass: |
206 | case Stmt::OMPTargetParallelGenericLoopDirectiveClass: |
207 | case Stmt::OMPTargetParallelForSimdDirectiveClass: |
208 | case Stmt::OMPTargetSimdDirectiveClass: |
209 | case Stmt::OMPTargetTeamsGenericLoopDirectiveClass: |
210 | case Stmt::OMPTargetUpdateDirectiveClass: |
211 | case Stmt::OMPTeamsDistributeDirectiveClass: |
212 | case Stmt::OMPTeamsDistributeSimdDirectiveClass: |
213 | case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: |
214 | case Stmt::OMPTeamsDistributeParallelForDirectiveClass: |
215 | case Stmt::OMPTeamsGenericLoopDirectiveClass: |
216 | case Stmt::OMPTargetTeamsDirectiveClass: |
217 | case Stmt::OMPTargetTeamsDistributeDirectiveClass: |
218 | case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: |
219 | case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: |
220 | case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: |
221 | case Stmt::OMPInteropDirectiveClass: |
222 | case Stmt::OMPDispatchDirectiveClass: |
223 | case Stmt::OMPGenericLoopDirectiveClass: |
224 | case Stmt::OMPReverseDirectiveClass: |
225 | case Stmt::OMPInterchangeDirectiveClass: |
226 | case Stmt::OMPAssumeDirectiveClass: |
227 | case Stmt::OMPMaskedDirectiveClass: |
228 | case Stmt::OMPStripeDirectiveClass: |
229 | case Stmt::ObjCAtCatchStmtClass: |
230 | case Stmt::ObjCAtFinallyStmtClass: |
231 | cgm.errorNYI(s->getSourceRange(), |
232 | std::string("emitStmt: " ) + s->getStmtClassName()); |
233 | return mlir::failure(); |
234 | } |
235 | |
236 | llvm_unreachable("Unexpected statement class" ); |
237 | } |
238 | |
239 | mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s, |
240 | bool useCurrentScope) { |
241 | switch (s->getStmtClass()) { |
242 | default: |
243 | return mlir::failure(); |
244 | case Stmt::DeclStmtClass: |
245 | return emitDeclStmt(cast<DeclStmt>(*s)); |
246 | case Stmt::CompoundStmtClass: |
247 | if (useCurrentScope) |
248 | emitCompoundStmtWithoutScope(s: cast<CompoundStmt>(Val: *s)); |
249 | else |
250 | emitCompoundStmt(s: cast<CompoundStmt>(Val: *s)); |
251 | break; |
252 | case Stmt::ContinueStmtClass: |
253 | return emitContinueStmt(cast<ContinueStmt>(*s)); |
254 | |
255 | // NullStmt doesn't need any handling, but we need to say we handled it. |
256 | case Stmt::NullStmtClass: |
257 | break; |
258 | case Stmt::CaseStmtClass: |
259 | case Stmt::DefaultStmtClass: |
260 | // If we reached here, we must not handling a switch case in the top level. |
261 | return emitSwitchCase(cast<SwitchCase>(*s), |
262 | /*buildingTopLevelCase=*/false); |
263 | break; |
264 | |
265 | case Stmt::BreakStmtClass: |
266 | return emitBreakStmt(cast<BreakStmt>(*s)); |
267 | case Stmt::ReturnStmtClass: |
268 | return emitReturnStmt(cast<ReturnStmt>(*s)); |
269 | } |
270 | |
271 | return mlir::success(); |
272 | } |
273 | |
274 | // Add a terminating yield on a body region if no other terminators are used. |
275 | static void terminateBody(CIRGenBuilderTy &builder, mlir::Region &r, |
276 | mlir::Location loc) { |
277 | if (r.empty()) |
278 | return; |
279 | |
280 | SmallVector<mlir::Block *, 4> eraseBlocks; |
281 | unsigned numBlocks = r.getBlocks().size(); |
282 | for (auto &block : r.getBlocks()) { |
283 | // Already cleanup after return operations, which might create |
284 | // empty blocks if emitted as last stmt. |
285 | if (numBlocks != 1 && block.empty() && block.hasNoPredecessors() && |
286 | block.hasNoSuccessors()) |
287 | eraseBlocks.push_back(&block); |
288 | |
289 | if (block.empty() || |
290 | !block.back().hasTrait<mlir::OpTrait::IsTerminator>()) { |
291 | mlir::OpBuilder::InsertionGuard guardCase(builder); |
292 | builder.setInsertionPointToEnd(&block); |
293 | builder.createYield(loc); |
294 | } |
295 | } |
296 | |
297 | for (auto *b : eraseBlocks) |
298 | b->erase(); |
299 | } |
300 | |
301 | mlir::LogicalResult CIRGenFunction::emitIfStmt(const IfStmt &s) { |
302 | mlir::LogicalResult res = mlir::success(); |
303 | // The else branch of a consteval if statement is always the only branch |
304 | // that can be runtime evaluated. |
305 | const Stmt *constevalExecuted; |
306 | if (s.isConsteval()) { |
307 | constevalExecuted = s.isNegatedConsteval() ? s.getThen() : s.getElse(); |
308 | if (!constevalExecuted) { |
309 | // No runtime code execution required |
310 | return res; |
311 | } |
312 | } |
313 | |
314 | // C99 6.8.4.1: The first substatement is executed if the expression |
315 | // compares unequal to 0. The condition must be a scalar type. |
316 | auto ifStmtBuilder = [&]() -> mlir::LogicalResult { |
317 | if (s.isConsteval()) |
318 | return emitStmt(constevalExecuted, /*useCurrentScope=*/true); |
319 | |
320 | if (s.getInit()) |
321 | if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed()) |
322 | return mlir::failure(); |
323 | |
324 | if (s.getConditionVariable()) |
325 | emitDecl(*s.getConditionVariable()); |
326 | |
327 | // If the condition folds to a constant and this is an 'if constexpr', |
328 | // we simplify it early in CIRGen to avoid emitting the full 'if'. |
329 | bool condConstant; |
330 | if (constantFoldsToBool(s.getCond(), condConstant, s.isConstexpr())) { |
331 | if (s.isConstexpr()) { |
332 | // Handle "if constexpr" explicitly here to avoid generating some |
333 | // ill-formed code since in CIR the "if" is no longer simplified |
334 | // in this lambda like in Clang but postponed to other MLIR |
335 | // passes. |
336 | if (const Stmt *executed = condConstant ? s.getThen() : s.getElse()) |
337 | return emitStmt(executed, /*useCurrentScope=*/true); |
338 | // There is nothing to execute at runtime. |
339 | // TODO(cir): there is still an empty cir.scope generated by the caller. |
340 | return mlir::success(); |
341 | } |
342 | } |
343 | |
344 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
345 | assert(!cir::MissingFeatures::incrementProfileCounter()); |
346 | return emitIfOnBoolExpr(s.getCond(), s.getThen(), s.getElse()); |
347 | }; |
348 | |
349 | // TODO: Add a new scoped symbol table. |
350 | // LexicalScope ConditionScope(*this, S.getCond()->getSourceRange()); |
351 | // The if scope contains the full source range for IfStmt. |
352 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
353 | builder.create<cir::ScopeOp>( |
354 | scopeLoc, /*scopeBuilder=*/ |
355 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
356 | LexicalScope lexScope{*this, scopeLoc, builder.getInsertionBlock()}; |
357 | res = ifStmtBuilder(); |
358 | }); |
359 | |
360 | return res; |
361 | } |
362 | |
363 | mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) { |
364 | assert(builder.getInsertionBlock() && "expected valid insertion point" ); |
365 | |
366 | for (const Decl *I : s.decls()) |
367 | emitDecl(d: *I); |
368 | |
369 | return mlir::success(); |
370 | } |
371 | |
372 | mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) { |
373 | mlir::Location loc = getLoc(s.getSourceRange()); |
374 | const Expr *rv = s.getRetValue(); |
375 | |
376 | if (getContext().getLangOpts().ElideConstructors && s.getNRVOCandidate() && |
377 | s.getNRVOCandidate()->isNRVOVariable()) { |
378 | getCIRGenModule().errorNYI(s.getSourceRange(), |
379 | "named return value optimization" ); |
380 | } else if (!rv) { |
381 | // No return expression. Do nothing. |
382 | } else if (rv->getType()->isVoidType()) { |
383 | // Make sure not to return anything, but evaluate the expression |
384 | // for side effects. |
385 | if (rv) { |
386 | emitAnyExpr(e: rv); |
387 | } |
388 | } else if (cast<FunctionDecl>(Val: curGD.getDecl()) |
389 | ->getReturnType() |
390 | ->isReferenceType()) { |
391 | // If this function returns a reference, take the address of the |
392 | // expression rather than the value. |
393 | RValue result = emitReferenceBindingToExpr(e: rv); |
394 | builder.CIRBaseBuilderTy::createStore(loc, result.getScalarVal(), |
395 | *fnRetAlloca); |
396 | } else { |
397 | mlir::Value value = nullptr; |
398 | switch (CIRGenFunction::getEvaluationKind(type: rv->getType())) { |
399 | case cir::TEK_Scalar: |
400 | value = emitScalarExpr(rv); |
401 | if (value) { // Change this to an assert once emitScalarExpr is complete |
402 | builder.CIRBaseBuilderTy::createStore(loc, value, *fnRetAlloca); |
403 | } |
404 | break; |
405 | default: |
406 | getCIRGenModule().errorNYI(s.getSourceRange(), |
407 | "non-scalar function return type" ); |
408 | break; |
409 | } |
410 | } |
411 | |
412 | auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc); |
413 | builder.create<cir::BrOp>(loc, retBlock); |
414 | builder.createBlock(builder.getBlock()->getParent()); |
415 | |
416 | return mlir::success(); |
417 | } |
418 | |
419 | mlir::LogicalResult |
420 | CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) { |
421 | builder.createContinue(getLoc(s.getContinueLoc())); |
422 | |
423 | // Insert the new block to continue codegen after the continue statement. |
424 | builder.createBlock(builder.getBlock()->getParent()); |
425 | |
426 | return mlir::success(); |
427 | } |
428 | |
429 | mlir::LogicalResult CIRGenFunction::emitBreakStmt(const clang::BreakStmt &s) { |
430 | builder.createBreak(getLoc(s.getBreakLoc())); |
431 | |
432 | // Insert the new block to continue codegen after the break statement. |
433 | builder.createBlock(builder.getBlock()->getParent()); |
434 | |
435 | return mlir::success(); |
436 | } |
437 | |
438 | template <typename T> |
439 | mlir::LogicalResult |
440 | CIRGenFunction::emitCaseDefaultCascade(const T *stmt, mlir::Type condType, |
441 | mlir::ArrayAttr value, CaseOpKind kind, |
442 | bool buildingTopLevelCase) { |
443 | |
444 | assert((isa<CaseStmt, DefaultStmt>(stmt)) && |
445 | "only case or default stmt go here" ); |
446 | |
447 | mlir::LogicalResult result = mlir::success(); |
448 | |
449 | mlir::Location loc = getLoc(stmt->getBeginLoc()); |
450 | |
451 | enum class SubStmtKind { Case, Default, Other }; |
452 | SubStmtKind subStmtKind = SubStmtKind::Other; |
453 | const Stmt *sub = stmt->getSubStmt(); |
454 | |
455 | mlir::OpBuilder::InsertPoint insertPoint; |
456 | builder.create<CaseOp>(loc, value, kind, insertPoint); |
457 | |
458 | { |
459 | mlir::OpBuilder::InsertionGuard guardSwitch(builder); |
460 | builder.restoreInsertionPoint(insertPoint); |
461 | |
462 | if (isa<DefaultStmt>(Val: sub) && isa<CaseStmt>(stmt)) { |
463 | subStmtKind = SubStmtKind::Default; |
464 | builder.createYield(loc); |
465 | } else if (isa<CaseStmt>(Val: sub) && isa<DefaultStmt, CaseStmt>(stmt)) { |
466 | subStmtKind = SubStmtKind::Case; |
467 | builder.createYield(loc); |
468 | } else { |
469 | result = emitStmt(sub, /*useCurrentScope=*/!isa<CompoundStmt>(sub)); |
470 | } |
471 | |
472 | insertPoint = builder.saveInsertionPoint(); |
473 | } |
474 | |
475 | // If the substmt is default stmt or case stmt, try to handle the special case |
476 | // to make it into the simple form. e.g. |
477 | // |
478 | // swtich () { |
479 | // case 1: |
480 | // default: |
481 | // ... |
482 | // } |
483 | // |
484 | // we prefer generating |
485 | // |
486 | // cir.switch() { |
487 | // cir.case(equal, 1) { |
488 | // cir.yield |
489 | // } |
490 | // cir.case(default) { |
491 | // ... |
492 | // } |
493 | // } |
494 | // |
495 | // than |
496 | // |
497 | // cir.switch() { |
498 | // cir.case(equal, 1) { |
499 | // cir.case(default) { |
500 | // ... |
501 | // } |
502 | // } |
503 | // } |
504 | // |
505 | // We don't need to revert this if we find the current switch can't be in |
506 | // simple form later since the conversion itself should be harmless. |
507 | if (subStmtKind == SubStmtKind::Case) { |
508 | result = emitCaseStmt(*cast<CaseStmt>(sub), condType, buildingTopLevelCase); |
509 | } else if (subStmtKind == SubStmtKind::Default) { |
510 | result = emitDefaultStmt(*cast<DefaultStmt>(sub), condType, |
511 | buildingTopLevelCase); |
512 | } else if (buildingTopLevelCase) { |
513 | // If we're building a top level case, try to restore the insert point to |
514 | // the case we're building, then we can attach more random stmts to the |
515 | // case to make generating `cir.switch` operation to be a simple form. |
516 | builder.restoreInsertionPoint(insertPoint); |
517 | } |
518 | |
519 | return result; |
520 | } |
521 | |
522 | mlir::LogicalResult CIRGenFunction::emitCaseStmt(const CaseStmt &s, |
523 | mlir::Type condType, |
524 | bool buildingTopLevelCase) { |
525 | cir::CaseOpKind kind; |
526 | mlir::ArrayAttr value; |
527 | llvm::APSInt intVal = s.getLHS()->EvaluateKnownConstInt(Ctx: getContext()); |
528 | |
529 | // If the case statement has an RHS value, it is representing a GNU |
530 | // case range statement, where LHS is the beginning of the range |
531 | // and RHS is the end of the range. |
532 | if (const Expr *rhs = s.getRHS()) { |
533 | llvm::APSInt endVal = rhs->EvaluateKnownConstInt(Ctx: getContext()); |
534 | value = builder.getArrayAttr({cir::IntAttr::get(condType, intVal), |
535 | cir::IntAttr::get(condType, endVal)}); |
536 | kind = cir::CaseOpKind::Range; |
537 | } else { |
538 | value = builder.getArrayAttr({cir::IntAttr::get(condType, intVal)}); |
539 | kind = cir::CaseOpKind::Equal; |
540 | } |
541 | |
542 | return emitCaseDefaultCascade(&s, condType, value, kind, |
543 | buildingTopLevelCase); |
544 | } |
545 | |
546 | mlir::LogicalResult CIRGenFunction::emitDefaultStmt(const clang::DefaultStmt &s, |
547 | mlir::Type condType, |
548 | bool buildingTopLevelCase) { |
549 | return emitCaseDefaultCascade(&s, condType, builder.getArrayAttr({}), |
550 | cir::CaseOpKind::Default, buildingTopLevelCase); |
551 | } |
552 | |
553 | mlir::LogicalResult CIRGenFunction::emitSwitchCase(const SwitchCase &s, |
554 | bool buildingTopLevelCase) { |
555 | assert(!condTypeStack.empty() && |
556 | "build switch case without specifying the type of the condition" ); |
557 | |
558 | if (s.getStmtClass() == Stmt::CaseStmtClass) |
559 | return emitCaseStmt(cast<CaseStmt>(s), condTypeStack.back(), |
560 | buildingTopLevelCase); |
561 | |
562 | if (s.getStmtClass() == Stmt::DefaultStmtClass) |
563 | return emitDefaultStmt(cast<DefaultStmt>(s), condTypeStack.back(), |
564 | buildingTopLevelCase); |
565 | |
566 | llvm_unreachable("expect case or default stmt" ); |
567 | } |
568 | |
569 | mlir::LogicalResult |
570 | CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s, |
571 | ArrayRef<const Attr *> forAttrs) { |
572 | cir::ForOp forOp; |
573 | |
574 | // TODO(cir): pass in array of attributes. |
575 | auto forStmtBuilder = [&]() -> mlir::LogicalResult { |
576 | mlir::LogicalResult loopRes = mlir::success(); |
577 | // Evaluate the first pieces before the loop. |
578 | if (s.getInit()) |
579 | if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed()) |
580 | return mlir::failure(); |
581 | if (emitStmt(s.getRangeStmt(), /*useCurrentScope=*/true).failed()) |
582 | return mlir::failure(); |
583 | if (emitStmt(s.getBeginStmt(), /*useCurrentScope=*/true).failed()) |
584 | return mlir::failure(); |
585 | if (emitStmt(s.getEndStmt(), /*useCurrentScope=*/true).failed()) |
586 | return mlir::failure(); |
587 | |
588 | assert(!cir::MissingFeatures::loopInfoStack()); |
589 | // From LLVM: if there are any cleanups between here and the loop-exit |
590 | // scope, create a block to stage a loop exit along. |
591 | // We probably already do the right thing because of ScopeOp, but make |
592 | // sure we handle all cases. |
593 | assert(!cir::MissingFeatures::requiresCleanups()); |
594 | |
595 | forOp = builder.createFor( |
596 | getLoc(s.getSourceRange()), |
597 | /*condBuilder=*/ |
598 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
599 | assert(!cir::MissingFeatures::createProfileWeightsForLoop()); |
600 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
601 | mlir::Value condVal = evaluateExprAsBool(s.getCond()); |
602 | builder.createCondition(condVal); |
603 | }, |
604 | /*bodyBuilder=*/ |
605 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
606 | // https://en.cppreference.com/w/cpp/language/for |
607 | // In C++ the scope of the init-statement and the scope of |
608 | // statement are one and the same. |
609 | bool useCurrentScope = true; |
610 | if (emitStmt(s.getLoopVarStmt(), useCurrentScope).failed()) |
611 | loopRes = mlir::failure(); |
612 | if (emitStmt(s.getBody(), useCurrentScope).failed()) |
613 | loopRes = mlir::failure(); |
614 | emitStopPoint(&s); |
615 | }, |
616 | /*stepBuilder=*/ |
617 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
618 | if (s.getInc()) |
619 | if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed()) |
620 | loopRes = mlir::failure(); |
621 | builder.createYield(loc); |
622 | }); |
623 | return loopRes; |
624 | }; |
625 | |
626 | mlir::LogicalResult res = mlir::success(); |
627 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
628 | builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/ |
629 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
630 | // Create a cleanup scope for the condition |
631 | // variable cleanups. Logical equivalent from |
632 | // LLVM codegn for LexicalScope |
633 | // ConditionScope(*this, S.getSourceRange())... |
634 | LexicalScope lexScope{ |
635 | *this, loc, builder.getInsertionBlock()}; |
636 | res = forStmtBuilder(); |
637 | }); |
638 | |
639 | if (res.failed()) |
640 | return res; |
641 | |
642 | terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc())); |
643 | return mlir::success(); |
644 | } |
645 | |
646 | mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) { |
647 | cir::ForOp forOp; |
648 | |
649 | // TODO: pass in an array of attributes. |
650 | auto forStmtBuilder = [&]() -> mlir::LogicalResult { |
651 | mlir::LogicalResult loopRes = mlir::success(); |
652 | // Evaluate the first part before the loop. |
653 | if (s.getInit()) |
654 | if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed()) |
655 | return mlir::failure(); |
656 | assert(!cir::MissingFeatures::loopInfoStack()); |
657 | // In the classic codegen, if there are any cleanups between here and the |
658 | // loop-exit scope, a block is created to stage the loop exit. We probably |
659 | // already do the right thing because of ScopeOp, but we need more testing |
660 | // to be sure we handle all cases. |
661 | assert(!cir::MissingFeatures::requiresCleanups()); |
662 | |
663 | forOp = builder.createFor( |
664 | getLoc(s.getSourceRange()), |
665 | /*condBuilder=*/ |
666 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
667 | assert(!cir::MissingFeatures::createProfileWeightsForLoop()); |
668 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
669 | mlir::Value condVal; |
670 | if (s.getCond()) { |
671 | // If the for statement has a condition scope, |
672 | // emit the local variable declaration. |
673 | if (s.getConditionVariable()) |
674 | emitDecl(*s.getConditionVariable()); |
675 | // C99 6.8.5p2/p4: The first substatement is executed if the |
676 | // expression compares unequal to 0. The condition must be a |
677 | // scalar type. |
678 | condVal = evaluateExprAsBool(s.getCond()); |
679 | } else { |
680 | condVal = b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); |
681 | } |
682 | builder.createCondition(condVal); |
683 | }, |
684 | /*bodyBuilder=*/ |
685 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
686 | // The scope of the for loop body is nested within the scope of the |
687 | // for loop's init-statement and condition. |
688 | if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed()) |
689 | loopRes = mlir::failure(); |
690 | emitStopPoint(&s); |
691 | }, |
692 | /*stepBuilder=*/ |
693 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
694 | if (s.getInc()) |
695 | if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed()) |
696 | loopRes = mlir::failure(); |
697 | builder.createYield(loc); |
698 | }); |
699 | return loopRes; |
700 | }; |
701 | |
702 | auto res = mlir::success(); |
703 | auto scopeLoc = getLoc(s.getSourceRange()); |
704 | builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/ |
705 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
706 | LexicalScope lexScope{ |
707 | *this, loc, builder.getInsertionBlock()}; |
708 | res = forStmtBuilder(); |
709 | }); |
710 | |
711 | if (res.failed()) |
712 | return res; |
713 | |
714 | terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc())); |
715 | return mlir::success(); |
716 | } |
717 | |
718 | mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) { |
719 | cir::DoWhileOp doWhileOp; |
720 | |
721 | // TODO: pass in array of attributes. |
722 | auto doStmtBuilder = [&]() -> mlir::LogicalResult { |
723 | mlir::LogicalResult loopRes = mlir::success(); |
724 | assert(!cir::MissingFeatures::loopInfoStack()); |
725 | // From LLVM: if there are any cleanups between here and the loop-exit |
726 | // scope, create a block to stage a loop exit along. |
727 | // We probably already do the right thing because of ScopeOp, but make |
728 | // sure we handle all cases. |
729 | assert(!cir::MissingFeatures::requiresCleanups()); |
730 | |
731 | doWhileOp = builder.createDoWhile( |
732 | getLoc(s.getSourceRange()), |
733 | /*condBuilder=*/ |
734 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
735 | assert(!cir::MissingFeatures::createProfileWeightsForLoop()); |
736 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
737 | // C99 6.8.5p2/p4: The first substatement is executed if the |
738 | // expression compares unequal to 0. The condition must be a |
739 | // scalar type. |
740 | mlir::Value condVal = evaluateExprAsBool(s.getCond()); |
741 | builder.createCondition(condVal); |
742 | }, |
743 | /*bodyBuilder=*/ |
744 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
745 | // The scope of the do-while loop body is a nested scope. |
746 | if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed()) |
747 | loopRes = mlir::failure(); |
748 | emitStopPoint(&s); |
749 | }); |
750 | return loopRes; |
751 | }; |
752 | |
753 | mlir::LogicalResult res = mlir::success(); |
754 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
755 | builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/ |
756 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
757 | LexicalScope lexScope{ |
758 | *this, loc, builder.getInsertionBlock()}; |
759 | res = doStmtBuilder(); |
760 | }); |
761 | |
762 | if (res.failed()) |
763 | return res; |
764 | |
765 | terminateBody(builder, doWhileOp.getBody(), getLoc(s.getEndLoc())); |
766 | return mlir::success(); |
767 | } |
768 | |
769 | mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) { |
770 | cir::WhileOp whileOp; |
771 | |
772 | // TODO: pass in array of attributes. |
773 | auto whileStmtBuilder = [&]() -> mlir::LogicalResult { |
774 | mlir::LogicalResult loopRes = mlir::success(); |
775 | assert(!cir::MissingFeatures::loopInfoStack()); |
776 | // From LLVM: if there are any cleanups between here and the loop-exit |
777 | // scope, create a block to stage a loop exit along. |
778 | // We probably already do the right thing because of ScopeOp, but make |
779 | // sure we handle all cases. |
780 | assert(!cir::MissingFeatures::requiresCleanups()); |
781 | |
782 | whileOp = builder.createWhile( |
783 | getLoc(s.getSourceRange()), |
784 | /*condBuilder=*/ |
785 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
786 | assert(!cir::MissingFeatures::createProfileWeightsForLoop()); |
787 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
788 | mlir::Value condVal; |
789 | // If the for statement has a condition scope, |
790 | // emit the local variable declaration. |
791 | if (s.getConditionVariable()) |
792 | emitDecl(*s.getConditionVariable()); |
793 | // C99 6.8.5p2/p4: The first substatement is executed if the |
794 | // expression compares unequal to 0. The condition must be a |
795 | // scalar type. |
796 | condVal = evaluateExprAsBool(s.getCond()); |
797 | builder.createCondition(condVal); |
798 | }, |
799 | /*bodyBuilder=*/ |
800 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
801 | // The scope of the while loop body is a nested scope. |
802 | if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed()) |
803 | loopRes = mlir::failure(); |
804 | emitStopPoint(&s); |
805 | }); |
806 | return loopRes; |
807 | }; |
808 | |
809 | mlir::LogicalResult res = mlir::success(); |
810 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
811 | builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/ |
812 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
813 | LexicalScope lexScope{ |
814 | *this, loc, builder.getInsertionBlock()}; |
815 | res = whileStmtBuilder(); |
816 | }); |
817 | |
818 | if (res.failed()) |
819 | return res; |
820 | |
821 | terminateBody(builder, whileOp.getBody(), getLoc(s.getEndLoc())); |
822 | return mlir::success(); |
823 | } |
824 | |
825 | mlir::LogicalResult CIRGenFunction::emitSwitchBody(const Stmt *s) { |
826 | // It is rare but legal if the switch body is not a compound stmt. e.g., |
827 | // |
828 | // switch(a) |
829 | // while(...) { |
830 | // case1 |
831 | // ... |
832 | // case2 |
833 | // ... |
834 | // } |
835 | if (!isa<CompoundStmt>(s)) |
836 | return emitStmt(s, /*useCurrentScope=*/true); |
837 | |
838 | auto *compoundStmt = cast<CompoundStmt>(Val: s); |
839 | |
840 | mlir::Block *swtichBlock = builder.getBlock(); |
841 | for (auto *c : compoundStmt->body()) { |
842 | if (auto *switchCase = dyn_cast<SwitchCase>(Val: c)) { |
843 | builder.setInsertionPointToEnd(swtichBlock); |
844 | // Reset insert point automatically, so that we can attach following |
845 | // random stmt to the region of previous built case op to try to make |
846 | // the being generated `cir.switch` to be in simple form. |
847 | if (mlir::failed( |
848 | emitSwitchCase(*switchCase, /*buildingTopLevelCase=*/true))) |
849 | return mlir::failure(); |
850 | |
851 | continue; |
852 | } |
853 | |
854 | // Otherwise, just build the statements in the nearest case region. |
855 | if (mlir::failed(emitStmt(c, /*useCurrentScope=*/!isa<CompoundStmt>(c)))) |
856 | return mlir::failure(); |
857 | } |
858 | |
859 | return mlir::success(); |
860 | } |
861 | |
862 | mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) { |
863 | // TODO: LLVM codegen does some early optimization to fold the condition and |
864 | // only emit live cases. CIR should use MLIR to achieve similar things, |
865 | // nothing to be done here. |
866 | // if (ConstantFoldsToSimpleInteger(S.getCond(), ConstantCondValue))... |
867 | assert(!cir::MissingFeatures::constantFoldSwitchStatement()); |
868 | |
869 | SwitchOp swop; |
870 | auto switchStmtBuilder = [&]() -> mlir::LogicalResult { |
871 | if (s.getInit()) |
872 | if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed()) |
873 | return mlir::failure(); |
874 | |
875 | if (s.getConditionVariable()) |
876 | emitDecl(*s.getConditionVariable()); |
877 | |
878 | mlir::Value condV = emitScalarExpr(s.getCond()); |
879 | |
880 | // TODO: PGO and likelihood (e.g. PGO.haveRegionCounts()) |
881 | assert(!cir::MissingFeatures::pgoUse()); |
882 | assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic()); |
883 | // TODO: if the switch has a condition wrapped by __builtin_unpredictable? |
884 | assert(!cir::MissingFeatures::insertBuiltinUnpredictable()); |
885 | |
886 | mlir::LogicalResult res = mlir::success(); |
887 | swop = builder.create<SwitchOp>( |
888 | getLoc(s.getBeginLoc()), condV, |
889 | /*switchBuilder=*/ |
890 | [&](mlir::OpBuilder &b, mlir::Location loc, mlir::OperationState &os) { |
891 | curLexScope->setAsSwitch(); |
892 | |
893 | condTypeStack.push_back(condV.getType()); |
894 | |
895 | res = emitSwitchBody(s.getBody()); |
896 | |
897 | condTypeStack.pop_back(); |
898 | }); |
899 | |
900 | return res; |
901 | }; |
902 | |
903 | // The switch scope contains the full source range for SwitchStmt. |
904 | mlir::Location scopeLoc = getLoc(s.getSourceRange()); |
905 | mlir::LogicalResult res = mlir::success(); |
906 | builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/ |
907 | [&](mlir::OpBuilder &b, mlir::Location loc) { |
908 | LexicalScope lexScope{ |
909 | *this, loc, builder.getInsertionBlock()}; |
910 | res = switchStmtBuilder(); |
911 | }); |
912 | |
913 | llvm::SmallVector<CaseOp> cases; |
914 | swop.collectCases(cases); |
915 | for (auto caseOp : cases) |
916 | terminateBody(builder, caseOp.getCaseRegion(), caseOp.getLoc()); |
917 | terminateBody(builder, swop.getBody(), swop.getLoc()); |
918 | |
919 | return res; |
920 | } |
921 | |