Path: blob/main/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
213799 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Emit Stmt nodes as CIR code.9//10//===----------------------------------------------------------------------===//1112#include "CIRGenBuilder.h"13#include "CIRGenFunction.h"1415#include "mlir/IR/Builders.h"16#include "clang/AST/ExprCXX.h"17#include "clang/AST/Stmt.h"18#include "clang/AST/StmtOpenACC.h"19#include "clang/CIR/MissingFeatures.h"2021using namespace clang;22using namespace clang::CIRGen;23using namespace cir;2425void CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s) {26for (auto *curStmt : s.body()) {27if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())28getCIRGenModule().errorNYI(curStmt->getSourceRange(),29std::string("emitCompoundStmtWithoutScope: ") +30curStmt->getStmtClassName());31}32}3334void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {35mlir::Location scopeLoc = getLoc(s.getSourceRange());36mlir::OpBuilder::InsertPoint scopeInsPt;37builder.create<cir::ScopeOp>(38scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) {39scopeInsPt = b.saveInsertionPoint();40});41{42mlir::OpBuilder::InsertionGuard guard(builder);43builder.restoreInsertionPoint(scopeInsPt);44LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());45emitCompoundStmtWithoutScope(s);46}47}4849void CIRGenFunction::emitStopPoint(const Stmt *s) {50assert(!cir::MissingFeatures::generateDebugInfo());51}5253// Build CIR for a statement. useCurrentScope should be true if no new scopes54// need to be created when finding a compound statement.55mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,56bool useCurrentScope,57ArrayRef<const Attr *> attr) {58if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope)))59return mlir::success();6061switch (s->getStmtClass()) {62case Stmt::NoStmtClass:63case Stmt::CXXCatchStmtClass:64case Stmt::SEHExceptStmtClass:65case Stmt::SEHFinallyStmtClass:66case Stmt::MSDependentExistsStmtClass:67llvm_unreachable("invalid statement class to emit generically");68case Stmt::BreakStmtClass:69case Stmt::NullStmtClass:70case Stmt::CompoundStmtClass:71case Stmt::ContinueStmtClass:72case Stmt::DeclStmtClass:73case Stmt::ReturnStmtClass:74llvm_unreachable("should have emitted these statements as simple");7576#define STMT(Type, Base)77#define ABSTRACT_STMT(Op)78#define EXPR(Type, Base) case Stmt::Type##Class:79#include "clang/AST/StmtNodes.inc"80{81// Remember the block we came in on.82mlir::Block *incoming = builder.getInsertionBlock();83assert(incoming && "expression emission must have an insertion point");8485emitIgnoredExpr(cast<Expr>(s));8687mlir::Block *outgoing = builder.getInsertionBlock();88assert(outgoing && "expression emission cleared block!");89return mlir::success();90}91case Stmt::IfStmtClass:92return emitIfStmt(cast<IfStmt>(*s));93case Stmt::SwitchStmtClass:94return emitSwitchStmt(cast<SwitchStmt>(*s));95case Stmt::ForStmtClass:96return emitForStmt(cast<ForStmt>(*s));97case Stmt::WhileStmtClass:98return emitWhileStmt(cast<WhileStmt>(*s));99case Stmt::DoStmtClass:100return emitDoStmt(cast<DoStmt>(*s));101case Stmt::CXXForRangeStmtClass:102return emitCXXForRangeStmt(cast<CXXForRangeStmt>(*s), attr);103case Stmt::OpenACCComputeConstructClass:104return emitOpenACCComputeConstruct(cast<OpenACCComputeConstruct>(*s));105case Stmt::OpenACCLoopConstructClass:106return emitOpenACCLoopConstruct(cast<OpenACCLoopConstruct>(*s));107case Stmt::OpenACCCombinedConstructClass:108return emitOpenACCCombinedConstruct(cast<OpenACCCombinedConstruct>(*s));109case Stmt::OpenACCDataConstructClass:110return emitOpenACCDataConstruct(cast<OpenACCDataConstruct>(*s));111case Stmt::OpenACCEnterDataConstructClass:112return emitOpenACCEnterDataConstruct(cast<OpenACCEnterDataConstruct>(*s));113case Stmt::OpenACCExitDataConstructClass:114return emitOpenACCExitDataConstruct(cast<OpenACCExitDataConstruct>(*s));115case Stmt::OpenACCHostDataConstructClass:116return emitOpenACCHostDataConstruct(cast<OpenACCHostDataConstruct>(*s));117case Stmt::OpenACCWaitConstructClass:118return emitOpenACCWaitConstruct(cast<OpenACCWaitConstruct>(*s));119case Stmt::OpenACCInitConstructClass:120return emitOpenACCInitConstruct(cast<OpenACCInitConstruct>(*s));121case Stmt::OpenACCShutdownConstructClass:122return emitOpenACCShutdownConstruct(cast<OpenACCShutdownConstruct>(*s));123case Stmt::OpenACCSetConstructClass:124return emitOpenACCSetConstruct(cast<OpenACCSetConstruct>(*s));125case Stmt::OpenACCUpdateConstructClass:126return emitOpenACCUpdateConstruct(cast<OpenACCUpdateConstruct>(*s));127case Stmt::OpenACCCacheConstructClass:128return emitOpenACCCacheConstruct(cast<OpenACCCacheConstruct>(*s));129case Stmt::OpenACCAtomicConstructClass:130return emitOpenACCAtomicConstruct(cast<OpenACCAtomicConstruct>(*s));131case Stmt::OMPScopeDirectiveClass:132case Stmt::OMPErrorDirectiveClass:133case Stmt::LabelStmtClass:134case Stmt::AttributedStmtClass:135case Stmt::GotoStmtClass:136case Stmt::DefaultStmtClass:137case Stmt::CaseStmtClass:138case Stmt::SEHLeaveStmtClass:139case Stmt::SYCLKernelCallStmtClass:140case Stmt::CoroutineBodyStmtClass:141case Stmt::CoreturnStmtClass:142case Stmt::CXXTryStmtClass:143case Stmt::IndirectGotoStmtClass:144case Stmt::GCCAsmStmtClass:145case Stmt::MSAsmStmtClass:146case Stmt::OMPParallelDirectiveClass:147case Stmt::OMPTaskwaitDirectiveClass:148case Stmt::OMPTaskyieldDirectiveClass:149case Stmt::OMPBarrierDirectiveClass:150case Stmt::CapturedStmtClass:151case Stmt::ObjCAtTryStmtClass:152case Stmt::ObjCAtThrowStmtClass:153case Stmt::ObjCAtSynchronizedStmtClass:154case Stmt::ObjCForCollectionStmtClass:155case Stmt::ObjCAutoreleasePoolStmtClass:156case Stmt::SEHTryStmtClass:157case Stmt::OMPMetaDirectiveClass:158case Stmt::OMPCanonicalLoopClass:159case Stmt::OMPSimdDirectiveClass:160case Stmt::OMPTileDirectiveClass:161case Stmt::OMPUnrollDirectiveClass:162case Stmt::OMPForDirectiveClass:163case Stmt::OMPForSimdDirectiveClass:164case Stmt::OMPSectionsDirectiveClass:165case Stmt::OMPSectionDirectiveClass:166case Stmt::OMPSingleDirectiveClass:167case Stmt::OMPMasterDirectiveClass:168case Stmt::OMPCriticalDirectiveClass:169case Stmt::OMPParallelForDirectiveClass:170case Stmt::OMPParallelForSimdDirectiveClass:171case Stmt::OMPParallelMasterDirectiveClass:172case Stmt::OMPParallelSectionsDirectiveClass:173case Stmt::OMPTaskDirectiveClass:174case Stmt::OMPTaskgroupDirectiveClass:175case Stmt::OMPFlushDirectiveClass:176case Stmt::OMPDepobjDirectiveClass:177case Stmt::OMPScanDirectiveClass:178case Stmt::OMPOrderedDirectiveClass:179case Stmt::OMPAtomicDirectiveClass:180case Stmt::OMPTargetDirectiveClass:181case Stmt::OMPTeamsDirectiveClass:182case Stmt::OMPCancellationPointDirectiveClass:183case Stmt::OMPCancelDirectiveClass:184case Stmt::OMPTargetDataDirectiveClass:185case Stmt::OMPTargetEnterDataDirectiveClass:186case Stmt::OMPTargetExitDataDirectiveClass:187case Stmt::OMPTargetParallelDirectiveClass:188case Stmt::OMPTargetParallelForDirectiveClass:189case Stmt::OMPTaskLoopDirectiveClass:190case Stmt::OMPTaskLoopSimdDirectiveClass:191case Stmt::OMPMaskedTaskLoopDirectiveClass:192case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:193case Stmt::OMPMasterTaskLoopDirectiveClass:194case Stmt::OMPMasterTaskLoopSimdDirectiveClass:195case Stmt::OMPParallelGenericLoopDirectiveClass:196case Stmt::OMPParallelMaskedDirectiveClass:197case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:198case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:199case Stmt::OMPParallelMasterTaskLoopDirectiveClass:200case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:201case Stmt::OMPDistributeDirectiveClass:202case Stmt::OMPDistributeParallelForDirectiveClass:203case Stmt::OMPDistributeParallelForSimdDirectiveClass:204case Stmt::OMPDistributeSimdDirectiveClass:205case Stmt::OMPTargetParallelGenericLoopDirectiveClass:206case Stmt::OMPTargetParallelForSimdDirectiveClass:207case Stmt::OMPTargetSimdDirectiveClass:208case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:209case Stmt::OMPTargetUpdateDirectiveClass:210case Stmt::OMPTeamsDistributeDirectiveClass:211case Stmt::OMPTeamsDistributeSimdDirectiveClass:212case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:213case Stmt::OMPTeamsDistributeParallelForDirectiveClass:214case Stmt::OMPTeamsGenericLoopDirectiveClass:215case Stmt::OMPTargetTeamsDirectiveClass:216case Stmt::OMPTargetTeamsDistributeDirectiveClass:217case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:218case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:219case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:220case Stmt::OMPInteropDirectiveClass:221case Stmt::OMPDispatchDirectiveClass:222case Stmt::OMPGenericLoopDirectiveClass:223case Stmt::OMPReverseDirectiveClass:224case Stmt::OMPInterchangeDirectiveClass:225case Stmt::OMPAssumeDirectiveClass:226case Stmt::OMPMaskedDirectiveClass:227case Stmt::OMPStripeDirectiveClass:228case Stmt::ObjCAtCatchStmtClass:229case Stmt::ObjCAtFinallyStmtClass:230cgm.errorNYI(s->getSourceRange(),231std::string("emitStmt: ") + s->getStmtClassName());232return mlir::failure();233}234235llvm_unreachable("Unexpected statement class");236}237238mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,239bool useCurrentScope) {240switch (s->getStmtClass()) {241default:242return mlir::failure();243case Stmt::DeclStmtClass:244return emitDeclStmt(cast<DeclStmt>(*s));245case Stmt::CompoundStmtClass:246if (useCurrentScope)247emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));248else249emitCompoundStmt(cast<CompoundStmt>(*s));250break;251case Stmt::ContinueStmtClass:252return emitContinueStmt(cast<ContinueStmt>(*s));253254// NullStmt doesn't need any handling, but we need to say we handled it.255case Stmt::NullStmtClass:256break;257case Stmt::CaseStmtClass:258case Stmt::DefaultStmtClass:259// If we reached here, we must not handling a switch case in the top level.260return emitSwitchCase(cast<SwitchCase>(*s),261/*buildingTopLevelCase=*/false);262break;263264case Stmt::BreakStmtClass:265return emitBreakStmt(cast<BreakStmt>(*s));266case Stmt::ReturnStmtClass:267return emitReturnStmt(cast<ReturnStmt>(*s));268}269270return mlir::success();271}272273// Add a terminating yield on a body region if no other terminators are used.274static void terminateBody(CIRGenBuilderTy &builder, mlir::Region &r,275mlir::Location loc) {276if (r.empty())277return;278279SmallVector<mlir::Block *, 4> eraseBlocks;280unsigned numBlocks = r.getBlocks().size();281for (auto &block : r.getBlocks()) {282// Already cleanup after return operations, which might create283// empty blocks if emitted as last stmt.284if (numBlocks != 1 && block.empty() && block.hasNoPredecessors() &&285block.hasNoSuccessors())286eraseBlocks.push_back(&block);287288if (block.empty() ||289!block.back().hasTrait<mlir::OpTrait::IsTerminator>()) {290mlir::OpBuilder::InsertionGuard guardCase(builder);291builder.setInsertionPointToEnd(&block);292builder.createYield(loc);293}294}295296for (auto *b : eraseBlocks)297b->erase();298}299300mlir::LogicalResult CIRGenFunction::emitIfStmt(const IfStmt &s) {301mlir::LogicalResult res = mlir::success();302// The else branch of a consteval if statement is always the only branch303// that can be runtime evaluated.304const Stmt *constevalExecuted;305if (s.isConsteval()) {306constevalExecuted = s.isNegatedConsteval() ? s.getThen() : s.getElse();307if (!constevalExecuted) {308// No runtime code execution required309return res;310}311}312313// C99 6.8.4.1: The first substatement is executed if the expression314// compares unequal to 0. The condition must be a scalar type.315auto ifStmtBuilder = [&]() -> mlir::LogicalResult {316if (s.isConsteval())317return emitStmt(constevalExecuted, /*useCurrentScope=*/true);318319if (s.getInit())320if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())321return mlir::failure();322323if (s.getConditionVariable())324emitDecl(*s.getConditionVariable());325326// If the condition folds to a constant and this is an 'if constexpr',327// we simplify it early in CIRGen to avoid emitting the full 'if'.328bool condConstant;329if (constantFoldsToBool(s.getCond(), condConstant, s.isConstexpr())) {330if (s.isConstexpr()) {331// Handle "if constexpr" explicitly here to avoid generating some332// ill-formed code since in CIR the "if" is no longer simplified333// in this lambda like in Clang but postponed to other MLIR334// passes.335if (const Stmt *executed = condConstant ? s.getThen() : s.getElse())336return emitStmt(executed, /*useCurrentScope=*/true);337// There is nothing to execute at runtime.338// TODO(cir): there is still an empty cir.scope generated by the caller.339return mlir::success();340}341}342343assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());344assert(!cir::MissingFeatures::incrementProfileCounter());345return emitIfOnBoolExpr(s.getCond(), s.getThen(), s.getElse());346};347348// TODO: Add a new scoped symbol table.349// LexicalScope ConditionScope(*this, S.getCond()->getSourceRange());350// The if scope contains the full source range for IfStmt.351mlir::Location scopeLoc = getLoc(s.getSourceRange());352builder.create<cir::ScopeOp>(353scopeLoc, /*scopeBuilder=*/354[&](mlir::OpBuilder &b, mlir::Location loc) {355LexicalScope lexScope{*this, scopeLoc, builder.getInsertionBlock()};356res = ifStmtBuilder();357});358359return res;360}361362mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {363assert(builder.getInsertionBlock() && "expected valid insertion point");364365for (const Decl *I : s.decls())366emitDecl(*I);367368return mlir::success();369}370371mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {372mlir::Location loc = getLoc(s.getSourceRange());373const Expr *rv = s.getRetValue();374375if (getContext().getLangOpts().ElideConstructors && s.getNRVOCandidate() &&376s.getNRVOCandidate()->isNRVOVariable()) {377getCIRGenModule().errorNYI(s.getSourceRange(),378"named return value optimization");379} else if (!rv) {380// No return expression. Do nothing.381} else if (rv->getType()->isVoidType()) {382// Make sure not to return anything, but evaluate the expression383// for side effects.384if (rv) {385emitAnyExpr(rv);386}387} else if (cast<FunctionDecl>(curGD.getDecl())388->getReturnType()389->isReferenceType()) {390// If this function returns a reference, take the address of the391// expression rather than the value.392RValue result = emitReferenceBindingToExpr(rv);393builder.CIRBaseBuilderTy::createStore(loc, result.getValue(), *fnRetAlloca);394} else {395mlir::Value value = nullptr;396switch (CIRGenFunction::getEvaluationKind(rv->getType())) {397case cir::TEK_Scalar:398value = emitScalarExpr(rv);399if (value) { // Change this to an assert once emitScalarExpr is complete400builder.CIRBaseBuilderTy::createStore(loc, value, *fnRetAlloca);401}402break;403default:404getCIRGenModule().errorNYI(s.getSourceRange(),405"non-scalar function return type");406break;407}408}409410auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc);411builder.create<cir::BrOp>(loc, retBlock);412builder.createBlock(builder.getBlock()->getParent());413414return mlir::success();415}416417mlir::LogicalResult418CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {419builder.createContinue(getLoc(s.getContinueLoc()));420421// Insert the new block to continue codegen after the continue statement.422builder.createBlock(builder.getBlock()->getParent());423424return mlir::success();425}426427mlir::LogicalResult CIRGenFunction::emitBreakStmt(const clang::BreakStmt &s) {428builder.createBreak(getLoc(s.getBreakLoc()));429430// Insert the new block to continue codegen after the break statement.431builder.createBlock(builder.getBlock()->getParent());432433return mlir::success();434}435436template <typename T>437mlir::LogicalResult438CIRGenFunction::emitCaseDefaultCascade(const T *stmt, mlir::Type condType,439mlir::ArrayAttr value, CaseOpKind kind,440bool buildingTopLevelCase) {441442assert((isa<CaseStmt, DefaultStmt>(stmt)) &&443"only case or default stmt go here");444445mlir::LogicalResult result = mlir::success();446447mlir::Location loc = getLoc(stmt->getBeginLoc());448449enum class SubStmtKind { Case, Default, Other };450SubStmtKind subStmtKind = SubStmtKind::Other;451const Stmt *sub = stmt->getSubStmt();452453mlir::OpBuilder::InsertPoint insertPoint;454builder.create<CaseOp>(loc, value, kind, insertPoint);455456{457mlir::OpBuilder::InsertionGuard guardSwitch(builder);458builder.restoreInsertionPoint(insertPoint);459460if (isa<DefaultStmt>(sub) && isa<CaseStmt>(stmt)) {461subStmtKind = SubStmtKind::Default;462builder.createYield(loc);463} else if (isa<CaseStmt>(sub) && isa<DefaultStmt, CaseStmt>(stmt)) {464subStmtKind = SubStmtKind::Case;465builder.createYield(loc);466} else {467result = emitStmt(sub, /*useCurrentScope=*/!isa<CompoundStmt>(sub));468}469470insertPoint = builder.saveInsertionPoint();471}472473// If the substmt is default stmt or case stmt, try to handle the special case474// to make it into the simple form. e.g.475//476// swtich () {477// case 1:478// default:479// ...480// }481//482// we prefer generating483//484// cir.switch() {485// cir.case(equal, 1) {486// cir.yield487// }488// cir.case(default) {489// ...490// }491// }492//493// than494//495// cir.switch() {496// cir.case(equal, 1) {497// cir.case(default) {498// ...499// }500// }501// }502//503// We don't need to revert this if we find the current switch can't be in504// simple form later since the conversion itself should be harmless.505if (subStmtKind == SubStmtKind::Case) {506result = emitCaseStmt(*cast<CaseStmt>(sub), condType, buildingTopLevelCase);507} else if (subStmtKind == SubStmtKind::Default) {508result = emitDefaultStmt(*cast<DefaultStmt>(sub), condType,509buildingTopLevelCase);510} else if (buildingTopLevelCase) {511// If we're building a top level case, try to restore the insert point to512// the case we're building, then we can attach more random stmts to the513// case to make generating `cir.switch` operation to be a simple form.514builder.restoreInsertionPoint(insertPoint);515}516517return result;518}519520mlir::LogicalResult CIRGenFunction::emitCaseStmt(const CaseStmt &s,521mlir::Type condType,522bool buildingTopLevelCase) {523cir::CaseOpKind kind;524mlir::ArrayAttr value;525llvm::APSInt intVal = s.getLHS()->EvaluateKnownConstInt(getContext());526527// If the case statement has an RHS value, it is representing a GNU528// case range statement, where LHS is the beginning of the range529// and RHS is the end of the range.530if (const Expr *rhs = s.getRHS()) {531llvm::APSInt endVal = rhs->EvaluateKnownConstInt(getContext());532value = builder.getArrayAttr({cir::IntAttr::get(condType, intVal),533cir::IntAttr::get(condType, endVal)});534kind = cir::CaseOpKind::Range;535} else {536value = builder.getArrayAttr({cir::IntAttr::get(condType, intVal)});537kind = cir::CaseOpKind::Equal;538}539540return emitCaseDefaultCascade(&s, condType, value, kind,541buildingTopLevelCase);542}543544mlir::LogicalResult CIRGenFunction::emitDefaultStmt(const clang::DefaultStmt &s,545mlir::Type condType,546bool buildingTopLevelCase) {547return emitCaseDefaultCascade(&s, condType, builder.getArrayAttr({}),548cir::CaseOpKind::Default, buildingTopLevelCase);549}550551mlir::LogicalResult CIRGenFunction::emitSwitchCase(const SwitchCase &s,552bool buildingTopLevelCase) {553assert(!condTypeStack.empty() &&554"build switch case without specifying the type of the condition");555556if (s.getStmtClass() == Stmt::CaseStmtClass)557return emitCaseStmt(cast<CaseStmt>(s), condTypeStack.back(),558buildingTopLevelCase);559560if (s.getStmtClass() == Stmt::DefaultStmtClass)561return emitDefaultStmt(cast<DefaultStmt>(s), condTypeStack.back(),562buildingTopLevelCase);563564llvm_unreachable("expect case or default stmt");565}566567mlir::LogicalResult568CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s,569ArrayRef<const Attr *> forAttrs) {570cir::ForOp forOp;571572// TODO(cir): pass in array of attributes.573auto forStmtBuilder = [&]() -> mlir::LogicalResult {574mlir::LogicalResult loopRes = mlir::success();575// Evaluate the first pieces before the loop.576if (s.getInit())577if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())578return mlir::failure();579if (emitStmt(s.getRangeStmt(), /*useCurrentScope=*/true).failed())580return mlir::failure();581if (emitStmt(s.getBeginStmt(), /*useCurrentScope=*/true).failed())582return mlir::failure();583if (emitStmt(s.getEndStmt(), /*useCurrentScope=*/true).failed())584return mlir::failure();585586assert(!cir::MissingFeatures::loopInfoStack());587// From LLVM: if there are any cleanups between here and the loop-exit588// scope, create a block to stage a loop exit along.589// We probably already do the right thing because of ScopeOp, but make590// sure we handle all cases.591assert(!cir::MissingFeatures::requiresCleanups());592593forOp = builder.createFor(594getLoc(s.getSourceRange()),595/*condBuilder=*/596[&](mlir::OpBuilder &b, mlir::Location loc) {597assert(!cir::MissingFeatures::createProfileWeightsForLoop());598assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());599mlir::Value condVal = evaluateExprAsBool(s.getCond());600builder.createCondition(condVal);601},602/*bodyBuilder=*/603[&](mlir::OpBuilder &b, mlir::Location loc) {604// https://en.cppreference.com/w/cpp/language/for605// In C++ the scope of the init-statement and the scope of606// statement are one and the same.607bool useCurrentScope = true;608if (emitStmt(s.getLoopVarStmt(), useCurrentScope).failed())609loopRes = mlir::failure();610if (emitStmt(s.getBody(), useCurrentScope).failed())611loopRes = mlir::failure();612emitStopPoint(&s);613},614/*stepBuilder=*/615[&](mlir::OpBuilder &b, mlir::Location loc) {616if (s.getInc())617if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed())618loopRes = mlir::failure();619builder.createYield(loc);620});621return loopRes;622};623624mlir::LogicalResult res = mlir::success();625mlir::Location scopeLoc = getLoc(s.getSourceRange());626builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/627[&](mlir::OpBuilder &b, mlir::Location loc) {628// Create a cleanup scope for the condition629// variable cleanups. Logical equivalent from630// LLVM codegn for LexicalScope631// ConditionScope(*this, S.getSourceRange())...632LexicalScope lexScope{633*this, loc, builder.getInsertionBlock()};634res = forStmtBuilder();635});636637if (res.failed())638return res;639640terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc()));641return mlir::success();642}643644mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {645cir::ForOp forOp;646647// TODO: pass in an array of attributes.648auto forStmtBuilder = [&]() -> mlir::LogicalResult {649mlir::LogicalResult loopRes = mlir::success();650// Evaluate the first part before the loop.651if (s.getInit())652if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())653return mlir::failure();654assert(!cir::MissingFeatures::loopInfoStack());655// In the classic codegen, if there are any cleanups between here and the656// loop-exit scope, a block is created to stage the loop exit. We probably657// already do the right thing because of ScopeOp, but we need more testing658// to be sure we handle all cases.659assert(!cir::MissingFeatures::requiresCleanups());660661forOp = builder.createFor(662getLoc(s.getSourceRange()),663/*condBuilder=*/664[&](mlir::OpBuilder &b, mlir::Location loc) {665assert(!cir::MissingFeatures::createProfileWeightsForLoop());666assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());667mlir::Value condVal;668if (s.getCond()) {669// If the for statement has a condition scope,670// emit the local variable declaration.671if (s.getConditionVariable())672emitDecl(*s.getConditionVariable());673// C99 6.8.5p2/p4: The first substatement is executed if the674// expression compares unequal to 0. The condition must be a675// scalar type.676condVal = evaluateExprAsBool(s.getCond());677} else {678condVal = b.create<cir::ConstantOp>(loc, builder.getTrueAttr());679}680builder.createCondition(condVal);681},682/*bodyBuilder=*/683[&](mlir::OpBuilder &b, mlir::Location loc) {684// The scope of the for loop body is nested within the scope of the685// for loop's init-statement and condition.686if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())687loopRes = mlir::failure();688emitStopPoint(&s);689},690/*stepBuilder=*/691[&](mlir::OpBuilder &b, mlir::Location loc) {692if (s.getInc())693if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed())694loopRes = mlir::failure();695builder.createYield(loc);696});697return loopRes;698};699700auto res = mlir::success();701auto scopeLoc = getLoc(s.getSourceRange());702builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/703[&](mlir::OpBuilder &b, mlir::Location loc) {704LexicalScope lexScope{705*this, loc, builder.getInsertionBlock()};706res = forStmtBuilder();707});708709if (res.failed())710return res;711712terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc()));713return mlir::success();714}715716mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {717cir::DoWhileOp doWhileOp;718719// TODO: pass in array of attributes.720auto doStmtBuilder = [&]() -> mlir::LogicalResult {721mlir::LogicalResult loopRes = mlir::success();722assert(!cir::MissingFeatures::loopInfoStack());723// From LLVM: if there are any cleanups between here and the loop-exit724// scope, create a block to stage a loop exit along.725// We probably already do the right thing because of ScopeOp, but make726// sure we handle all cases.727assert(!cir::MissingFeatures::requiresCleanups());728729doWhileOp = builder.createDoWhile(730getLoc(s.getSourceRange()),731/*condBuilder=*/732[&](mlir::OpBuilder &b, mlir::Location loc) {733assert(!cir::MissingFeatures::createProfileWeightsForLoop());734assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());735// C99 6.8.5p2/p4: The first substatement is executed if the736// expression compares unequal to 0. The condition must be a737// scalar type.738mlir::Value condVal = evaluateExprAsBool(s.getCond());739builder.createCondition(condVal);740},741/*bodyBuilder=*/742[&](mlir::OpBuilder &b, mlir::Location loc) {743// The scope of the do-while loop body is a nested scope.744if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())745loopRes = mlir::failure();746emitStopPoint(&s);747});748return loopRes;749};750751mlir::LogicalResult res = mlir::success();752mlir::Location scopeLoc = getLoc(s.getSourceRange());753builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/754[&](mlir::OpBuilder &b, mlir::Location loc) {755LexicalScope lexScope{756*this, loc, builder.getInsertionBlock()};757res = doStmtBuilder();758});759760if (res.failed())761return res;762763terminateBody(builder, doWhileOp.getBody(), getLoc(s.getEndLoc()));764return mlir::success();765}766767mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) {768cir::WhileOp whileOp;769770// TODO: pass in array of attributes.771auto whileStmtBuilder = [&]() -> mlir::LogicalResult {772mlir::LogicalResult loopRes = mlir::success();773assert(!cir::MissingFeatures::loopInfoStack());774// From LLVM: if there are any cleanups between here and the loop-exit775// scope, create a block to stage a loop exit along.776// We probably already do the right thing because of ScopeOp, but make777// sure we handle all cases.778assert(!cir::MissingFeatures::requiresCleanups());779780whileOp = builder.createWhile(781getLoc(s.getSourceRange()),782/*condBuilder=*/783[&](mlir::OpBuilder &b, mlir::Location loc) {784assert(!cir::MissingFeatures::createProfileWeightsForLoop());785assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());786mlir::Value condVal;787// If the for statement has a condition scope,788// emit the local variable declaration.789if (s.getConditionVariable())790emitDecl(*s.getConditionVariable());791// C99 6.8.5p2/p4: The first substatement is executed if the792// expression compares unequal to 0. The condition must be a793// scalar type.794condVal = evaluateExprAsBool(s.getCond());795builder.createCondition(condVal);796},797/*bodyBuilder=*/798[&](mlir::OpBuilder &b, mlir::Location loc) {799// The scope of the while loop body is a nested scope.800if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())801loopRes = mlir::failure();802emitStopPoint(&s);803});804return loopRes;805};806807mlir::LogicalResult res = mlir::success();808mlir::Location scopeLoc = getLoc(s.getSourceRange());809builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/810[&](mlir::OpBuilder &b, mlir::Location loc) {811LexicalScope lexScope{812*this, loc, builder.getInsertionBlock()};813res = whileStmtBuilder();814});815816if (res.failed())817return res;818819terminateBody(builder, whileOp.getBody(), getLoc(s.getEndLoc()));820return mlir::success();821}822823mlir::LogicalResult CIRGenFunction::emitSwitchBody(const Stmt *s) {824// It is rare but legal if the switch body is not a compound stmt. e.g.,825//826// switch(a)827// while(...) {828// case1829// ...830// case2831// ...832// }833if (!isa<CompoundStmt>(s))834return emitStmt(s, /*useCurrentScope=*/true);835836auto *compoundStmt = cast<CompoundStmt>(s);837838mlir::Block *swtichBlock = builder.getBlock();839for (auto *c : compoundStmt->body()) {840if (auto *switchCase = dyn_cast<SwitchCase>(c)) {841builder.setInsertionPointToEnd(swtichBlock);842// Reset insert point automatically, so that we can attach following843// random stmt to the region of previous built case op to try to make844// the being generated `cir.switch` to be in simple form.845if (mlir::failed(846emitSwitchCase(*switchCase, /*buildingTopLevelCase=*/true)))847return mlir::failure();848849continue;850}851852// Otherwise, just build the statements in the nearest case region.853if (mlir::failed(emitStmt(c, /*useCurrentScope=*/!isa<CompoundStmt>(c))))854return mlir::failure();855}856857return mlir::success();858}859860mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {861// TODO: LLVM codegen does some early optimization to fold the condition and862// only emit live cases. CIR should use MLIR to achieve similar things,863// nothing to be done here.864// if (ConstantFoldsToSimpleInteger(S.getCond(), ConstantCondValue))...865assert(!cir::MissingFeatures::constantFoldSwitchStatement());866867SwitchOp swop;868auto switchStmtBuilder = [&]() -> mlir::LogicalResult {869if (s.getInit())870if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())871return mlir::failure();872873if (s.getConditionVariable())874emitDecl(*s.getConditionVariable());875876mlir::Value condV = emitScalarExpr(s.getCond());877878// TODO: PGO and likelihood (e.g. PGO.haveRegionCounts())879assert(!cir::MissingFeatures::pgoUse());880assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());881// TODO: if the switch has a condition wrapped by __builtin_unpredictable?882assert(!cir::MissingFeatures::insertBuiltinUnpredictable());883884mlir::LogicalResult res = mlir::success();885swop = builder.create<SwitchOp>(886getLoc(s.getBeginLoc()), condV,887/*switchBuilder=*/888[&](mlir::OpBuilder &b, mlir::Location loc, mlir::OperationState &os) {889curLexScope->setAsSwitch();890891condTypeStack.push_back(condV.getType());892893res = emitSwitchBody(s.getBody());894895condTypeStack.pop_back();896});897898return res;899};900901// The switch scope contains the full source range for SwitchStmt.902mlir::Location scopeLoc = getLoc(s.getSourceRange());903mlir::LogicalResult res = mlir::success();904builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/905[&](mlir::OpBuilder &b, mlir::Location loc) {906LexicalScope lexScope{907*this, loc, builder.getInsertionBlock()};908res = switchStmtBuilder();909});910911llvm::SmallVector<CaseOp> cases;912swop.collectCases(cases);913for (auto caseOp : cases)914terminateBody(builder, caseOp.getCaseRegion(), caseOp.getLoc());915terminateBody(builder, swop.getBody(), swop.getLoc());916917return res;918}919920921