blob: f503e2344f952efdfdc00d57aa9664c6b2e6a2ad [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Emit Stmt nodes as CIR code.
//
//===----------------------------------------------------------------------===//
#include "CIRGenBuilder.h"
#include "CIRGenFunction.h"
#include "mlir/IR/Builders.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
using namespace clang::CIRGen;
using namespace cir;
void CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s) {
for (auto *curStmt : s.body()) {
if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
getCIRGenModule().errorNYI(curStmt->getSourceRange(), "statement");
}
}
void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
mlir::Location scopeLoc = getLoc(s.getSourceRange());
mlir::OpBuilder::InsertPoint scopeInsPt;
builder.create<cir::ScopeOp>(
scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) {
scopeInsPt = b.saveInsertionPoint();
});
{
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(scopeInsPt);
LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
emitCompoundStmtWithoutScope(s);
}
}
void CIRGenFunction::emitStopPoint(const Stmt *s) {
assert(!cir::MissingFeatures::generateDebugInfo());
}
// Build CIR for a statement. useCurrentScope should be true if no new scopes
// need to be created when finding a compound statement.
mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
bool useCurrentScope,
ArrayRef<const Attr *> attr) {
if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope)))
return mlir::success();
switch (s->getStmtClass()) {
case Stmt::NoStmtClass:
case Stmt::CXXCatchStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHFinallyStmtClass:
case Stmt::MSDependentExistsStmtClass:
llvm_unreachable("invalid statement class to emit generically");
case Stmt::BreakStmtClass:
case Stmt::NullStmtClass:
case Stmt::CompoundStmtClass:
case Stmt::ContinueStmtClass:
case Stmt::DeclStmtClass:
case Stmt::ReturnStmtClass:
llvm_unreachable("should have emitted these statements as simple");
#define STMT(Type, Base)
#define ABSTRACT_STMT(Op)
#define EXPR(Type, Base) case Stmt::Type##Class:
#include "clang/AST/StmtNodes.inc"
{
// Remember the block we came in on.
mlir::Block *incoming = builder.getInsertionBlock();
assert(incoming && "expression emission must have an insertion point");
emitIgnoredExpr(cast<Expr>(s));
mlir::Block *outgoing = builder.getInsertionBlock();
assert(outgoing && "expression emission cleared block!");
return mlir::success();
}
case Stmt::IfStmtClass:
return emitIfStmt(cast<IfStmt>(*s));
case Stmt::ForStmtClass:
return emitForStmt(cast<ForStmt>(*s));
case Stmt::WhileStmtClass:
return emitWhileStmt(cast<WhileStmt>(*s));
case Stmt::DoStmtClass:
return emitDoStmt(cast<DoStmt>(*s));
case Stmt::OpenACCComputeConstructClass:
return emitOpenACCComputeConstruct(cast<OpenACCComputeConstruct>(*s));
case Stmt::OpenACCLoopConstructClass:
return emitOpenACCLoopConstruct(cast<OpenACCLoopConstruct>(*s));
case Stmt::OpenACCCombinedConstructClass:
return emitOpenACCCombinedConstruct(cast<OpenACCCombinedConstruct>(*s));
case Stmt::OpenACCDataConstructClass:
return emitOpenACCDataConstruct(cast<OpenACCDataConstruct>(*s));
case Stmt::OpenACCEnterDataConstructClass:
return emitOpenACCEnterDataConstruct(cast<OpenACCEnterDataConstruct>(*s));
case Stmt::OpenACCExitDataConstructClass:
return emitOpenACCExitDataConstruct(cast<OpenACCExitDataConstruct>(*s));
case Stmt::OpenACCHostDataConstructClass:
return emitOpenACCHostDataConstruct(cast<OpenACCHostDataConstruct>(*s));
case Stmt::OpenACCWaitConstructClass:
return emitOpenACCWaitConstruct(cast<OpenACCWaitConstruct>(*s));
case Stmt::OpenACCInitConstructClass:
return emitOpenACCInitConstruct(cast<OpenACCInitConstruct>(*s));
case Stmt::OpenACCShutdownConstructClass:
return emitOpenACCShutdownConstruct(cast<OpenACCShutdownConstruct>(*s));
case Stmt::OpenACCSetConstructClass:
return emitOpenACCSetConstruct(cast<OpenACCSetConstruct>(*s));
case Stmt::OpenACCUpdateConstructClass:
return emitOpenACCUpdateConstruct(cast<OpenACCUpdateConstruct>(*s));
case Stmt::OpenACCCacheConstructClass:
return emitOpenACCCacheConstruct(cast<OpenACCCacheConstruct>(*s));
case Stmt::OpenACCAtomicConstructClass:
return emitOpenACCAtomicConstruct(cast<OpenACCAtomicConstruct>(*s));
case Stmt::OMPScopeDirectiveClass:
case Stmt::OMPErrorDirectiveClass:
case Stmt::LabelStmtClass:
case Stmt::AttributedStmtClass:
case Stmt::GotoStmtClass:
case Stmt::DefaultStmtClass:
case Stmt::CaseStmtClass:
case Stmt::SEHLeaveStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::SwitchStmtClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoreturnStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXForRangeStmtClass:
case Stmt::IndirectGotoStmtClass:
case Stmt::GCCAsmStmtClass:
case Stmt::MSAsmStmtClass:
case Stmt::OMPParallelDirectiveClass:
case Stmt::OMPTaskwaitDirectiveClass:
case Stmt::OMPTaskyieldDirectiveClass:
case Stmt::OMPBarrierDirectiveClass:
case Stmt::CapturedStmtClass:
case Stmt::ObjCAtTryStmtClass:
case Stmt::ObjCAtThrowStmtClass:
case Stmt::ObjCAtSynchronizedStmtClass:
case Stmt::ObjCForCollectionStmtClass:
case Stmt::ObjCAutoreleasePoolStmtClass:
case Stmt::SEHTryStmtClass:
case Stmt::OMPMetaDirectiveClass:
case Stmt::OMPCanonicalLoopClass:
case Stmt::OMPSimdDirectiveClass:
case Stmt::OMPTileDirectiveClass:
case Stmt::OMPUnrollDirectiveClass:
case Stmt::OMPForDirectiveClass:
case Stmt::OMPForSimdDirectiveClass:
case Stmt::OMPSectionsDirectiveClass:
case Stmt::OMPSectionDirectiveClass:
case Stmt::OMPSingleDirectiveClass:
case Stmt::OMPMasterDirectiveClass:
case Stmt::OMPCriticalDirectiveClass:
case Stmt::OMPParallelForDirectiveClass:
case Stmt::OMPParallelForSimdDirectiveClass:
case Stmt::OMPParallelMasterDirectiveClass:
case Stmt::OMPParallelSectionsDirectiveClass:
case Stmt::OMPTaskDirectiveClass:
case Stmt::OMPTaskgroupDirectiveClass:
case Stmt::OMPFlushDirectiveClass:
case Stmt::OMPDepobjDirectiveClass:
case Stmt::OMPScanDirectiveClass:
case Stmt::OMPOrderedDirectiveClass:
case Stmt::OMPAtomicDirectiveClass:
case Stmt::OMPTargetDirectiveClass:
case Stmt::OMPTeamsDirectiveClass:
case Stmt::OMPCancellationPointDirectiveClass:
case Stmt::OMPCancelDirectiveClass:
case Stmt::OMPTargetDataDirectiveClass:
case Stmt::OMPTargetEnterDataDirectiveClass:
case Stmt::OMPTargetExitDataDirectiveClass:
case Stmt::OMPTargetParallelDirectiveClass:
case Stmt::OMPTargetParallelForDirectiveClass:
case Stmt::OMPTaskLoopDirectiveClass:
case Stmt::OMPTaskLoopSimdDirectiveClass:
case Stmt::OMPMaskedTaskLoopDirectiveClass:
case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
case Stmt::OMPMasterTaskLoopDirectiveClass:
case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
case Stmt::OMPParallelGenericLoopDirectiveClass:
case Stmt::OMPParallelMaskedDirectiveClass:
case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
case Stmt::OMPDistributeDirectiveClass:
case Stmt::OMPDistributeParallelForDirectiveClass:
case Stmt::OMPDistributeParallelForSimdDirectiveClass:
case Stmt::OMPDistributeSimdDirectiveClass:
case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
case Stmt::OMPTargetParallelForSimdDirectiveClass:
case Stmt::OMPTargetSimdDirectiveClass:
case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
case Stmt::OMPTargetUpdateDirectiveClass:
case Stmt::OMPTeamsDistributeDirectiveClass:
case Stmt::OMPTeamsDistributeSimdDirectiveClass:
case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
case Stmt::OMPTeamsGenericLoopDirectiveClass:
case Stmt::OMPTargetTeamsDirectiveClass:
case Stmt::OMPTargetTeamsDistributeDirectiveClass:
case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
case Stmt::OMPInteropDirectiveClass:
case Stmt::OMPDispatchDirectiveClass:
case Stmt::OMPGenericLoopDirectiveClass:
case Stmt::OMPReverseDirectiveClass:
case Stmt::OMPInterchangeDirectiveClass:
case Stmt::OMPAssumeDirectiveClass:
case Stmt::OMPMaskedDirectiveClass:
case Stmt::OMPStripeDirectiveClass:
case Stmt::ObjCAtCatchStmtClass:
case Stmt::ObjCAtFinallyStmtClass:
cgm.errorNYI(s->getSourceRange(),
std::string("emitStmt: ") + s->getStmtClassName());
return mlir::failure();
}
llvm_unreachable("Unexpected statement class");
}
mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
bool useCurrentScope) {
switch (s->getStmtClass()) {
default:
return mlir::failure();
case Stmt::DeclStmtClass:
return emitDeclStmt(cast<DeclStmt>(*s));
case Stmt::CompoundStmtClass:
if (useCurrentScope)
emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
else
emitCompoundStmt(cast<CompoundStmt>(*s));
break;
case Stmt::ContinueStmtClass:
return emitContinueStmt(cast<ContinueStmt>(*s));
// NullStmt doesn't need any handling, but we need to say we handled it.
case Stmt::NullStmtClass:
break;
case Stmt::BreakStmtClass:
return emitBreakStmt(cast<BreakStmt>(*s));
case Stmt::ReturnStmtClass:
return emitReturnStmt(cast<ReturnStmt>(*s));
}
return mlir::success();
}
// Add a terminating yield on a body region if no other terminators are used.
static void terminateBody(CIRGenBuilderTy &builder, mlir::Region &r,
mlir::Location loc) {
if (r.empty())
return;
SmallVector<mlir::Block *, 4> eraseBlocks;
unsigned numBlocks = r.getBlocks().size();
for (auto &block : r.getBlocks()) {
// Already cleanup after return operations, which might create
// empty blocks if emitted as last stmt.
if (numBlocks != 1 && block.empty() && block.hasNoPredecessors() &&
block.hasNoSuccessors())
eraseBlocks.push_back(&block);
if (block.empty() ||
!block.back().hasTrait<mlir::OpTrait::IsTerminator>()) {
mlir::OpBuilder::InsertionGuard guardCase(builder);
builder.setInsertionPointToEnd(&block);
builder.createYield(loc);
}
}
for (auto *b : eraseBlocks)
b->erase();
}
mlir::LogicalResult CIRGenFunction::emitIfStmt(const IfStmt &s) {
mlir::LogicalResult res = mlir::success();
// The else branch of a consteval if statement is always the only branch
// that can be runtime evaluated.
const Stmt *constevalExecuted;
if (s.isConsteval()) {
constevalExecuted = s.isNegatedConsteval() ? s.getThen() : s.getElse();
if (!constevalExecuted) {
// No runtime code execution required
return res;
}
}
// C99 6.8.4.1: The first substatement is executed if the expression
// compares unequal to 0. The condition must be a scalar type.
auto ifStmtBuilder = [&]() -> mlir::LogicalResult {
if (s.isConsteval())
return emitStmt(constevalExecuted, /*useCurrentScope=*/true);
if (s.getInit())
if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())
return mlir::failure();
if (s.getConditionVariable())
emitDecl(*s.getConditionVariable());
// If the condition folds to a constant and this is an 'if constexpr',
// we simplify it early in CIRGen to avoid emitting the full 'if'.
bool condConstant;
if (constantFoldsToBool(s.getCond(), condConstant, s.isConstexpr())) {
if (s.isConstexpr()) {
// Handle "if constexpr" explicitly here to avoid generating some
// ill-formed code since in CIR the "if" is no longer simplified
// in this lambda like in Clang but postponed to other MLIR
// passes.
if (const Stmt *executed = condConstant ? s.getThen() : s.getElse())
return emitStmt(executed, /*useCurrentScope=*/true);
// There is nothing to execute at runtime.
// TODO(cir): there is still an empty cir.scope generated by the caller.
return mlir::success();
}
}
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
assert(!cir::MissingFeatures::incrementProfileCounter());
return emitIfOnBoolExpr(s.getCond(), s.getThen(), s.getElse());
};
// TODO: Add a new scoped symbol table.
// LexicalScope ConditionScope(*this, S.getCond()->getSourceRange());
// The if scope contains the full source range for IfStmt.
mlir::Location scopeLoc = getLoc(s.getSourceRange());
builder.create<cir::ScopeOp>(
scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
LexicalScope lexScope{*this, scopeLoc, builder.getInsertionBlock()};
res = ifStmtBuilder();
});
return res;
}
mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {
assert(builder.getInsertionBlock() && "expected valid insertion point");
for (const Decl *I : s.decls())
emitDecl(*I);
return mlir::success();
}
mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
mlir::Location loc = getLoc(s.getSourceRange());
const Expr *rv = s.getRetValue();
if (getContext().getLangOpts().ElideConstructors && s.getNRVOCandidate() &&
s.getNRVOCandidate()->isNRVOVariable()) {
getCIRGenModule().errorNYI(s.getSourceRange(),
"named return value optimization");
} else if (!rv) {
// No return expression. Do nothing.
} else if (rv->getType()->isVoidType()) {
// Make sure not to return anything, but evaluate the expression
// for side effects.
if (rv) {
emitAnyExpr(rv);
}
} else if (cast<FunctionDecl>(curGD.getDecl())
->getReturnType()
->isReferenceType()) {
getCIRGenModule().errorNYI(s.getSourceRange(),
"function return type that is a reference");
} else {
mlir::Value value = nullptr;
switch (CIRGenFunction::getEvaluationKind(rv->getType())) {
case cir::TEK_Scalar:
value = emitScalarExpr(rv);
if (value) { // Change this to an assert once emitScalarExpr is complete
builder.CIRBaseBuilderTy::createStore(loc, value, *fnRetAlloca);
}
break;
default:
getCIRGenModule().errorNYI(s.getSourceRange(),
"non-scalar function return type");
break;
}
}
auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc);
builder.create<cir::BrOp>(loc, retBlock);
builder.createBlock(builder.getBlock()->getParent());
return mlir::success();
}
mlir::LogicalResult
CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {
builder.createContinue(getLoc(s.getContinueLoc()));
// Insert the new block to continue codegen after the continue statement.
builder.createBlock(builder.getBlock()->getParent());
return mlir::success();
}
mlir::LogicalResult CIRGenFunction::emitBreakStmt(const clang::BreakStmt &s) {
builder.createBreak(getLoc(s.getBreakLoc()));
// Insert the new block to continue codegen after the break statement.
builder.createBlock(builder.getBlock()->getParent());
return mlir::success();
}
mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {
cir::ForOp forOp;
// TODO: pass in an array of attributes.
auto forStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
// Evaluate the first part before the loop.
if (s.getInit())
if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())
return mlir::failure();
assert(!cir::MissingFeatures::loopInfoStack());
// In the classic codegen, if there are any cleanups between here and the
// loop-exit scope, a block is created to stage the loop exit. We probably
// already do the right thing because of ScopeOp, but we need more testing
// to be sure we handle all cases.
assert(!cir::MissingFeatures::requiresCleanups());
forOp = builder.createFor(
getLoc(s.getSourceRange()),
/*condBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
assert(!cir::MissingFeatures::createProfileWeightsForLoop());
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
mlir::Value condVal;
if (s.getCond()) {
// If the for statement has a condition scope,
// emit the local variable declaration.
if (s.getConditionVariable())
emitDecl(*s.getConditionVariable());
// C99 6.8.5p2/p4: The first substatement is executed if the
// expression compares unequal to 0. The condition must be a
// scalar type.
condVal = evaluateExprAsBool(s.getCond());
} else {
cir::BoolType boolTy = cir::BoolType::get(b.getContext());
condVal = b.create<cir::ConstantOp>(
loc, boolTy, cir::BoolAttr::get(b.getContext(), boolTy, true));
}
builder.createCondition(condVal);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
// The scope of the for loop body is nested within the scope of the
// for loop's init-statement and condition.
if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
loopRes = mlir::failure();
emitStopPoint(&s);
},
/*stepBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
if (s.getInc())
if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed())
loopRes = mlir::failure();
builder.createYield(loc);
});
return loopRes;
};
auto res = mlir::success();
auto scopeLoc = getLoc(s.getSourceRange());
builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
LexicalScope lexScope{
*this, loc, builder.getInsertionBlock()};
res = forStmtBuilder();
});
if (res.failed())
return res;
terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc()));
return mlir::success();
}
mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {
cir::DoWhileOp doWhileOp;
// TODO: pass in array of attributes.
auto doStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
assert(!cir::MissingFeatures::loopInfoStack());
// From LLVM: if there are any cleanups between here and the loop-exit
// scope, create a block to stage a loop exit along.
// We probably already do the right thing because of ScopeOp, but make
// sure we handle all cases.
assert(!cir::MissingFeatures::requiresCleanups());
doWhileOp = builder.createDoWhile(
getLoc(s.getSourceRange()),
/*condBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
assert(!cir::MissingFeatures::createProfileWeightsForLoop());
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
// C99 6.8.5p2/p4: The first substatement is executed if the
// expression compares unequal to 0. The condition must be a
// scalar type.
mlir::Value condVal = evaluateExprAsBool(s.getCond());
builder.createCondition(condVal);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
// The scope of the do-while loop body is a nested scope.
if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
loopRes = mlir::failure();
emitStopPoint(&s);
});
return loopRes;
};
mlir::LogicalResult res = mlir::success();
mlir::Location scopeLoc = getLoc(s.getSourceRange());
builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
LexicalScope lexScope{
*this, loc, builder.getInsertionBlock()};
res = doStmtBuilder();
});
if (res.failed())
return res;
terminateBody(builder, doWhileOp.getBody(), getLoc(s.getEndLoc()));
return mlir::success();
}
mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) {
cir::WhileOp whileOp;
// TODO: pass in array of attributes.
auto whileStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
assert(!cir::MissingFeatures::loopInfoStack());
// From LLVM: if there are any cleanups between here and the loop-exit
// scope, create a block to stage a loop exit along.
// We probably already do the right thing because of ScopeOp, but make
// sure we handle all cases.
assert(!cir::MissingFeatures::requiresCleanups());
whileOp = builder.createWhile(
getLoc(s.getSourceRange()),
/*condBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
assert(!cir::MissingFeatures::createProfileWeightsForLoop());
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
mlir::Value condVal;
// If the for statement has a condition scope,
// emit the local variable declaration.
if (s.getConditionVariable())
emitDecl(*s.getConditionVariable());
// C99 6.8.5p2/p4: The first substatement is executed if the
// expression compares unequal to 0. The condition must be a
// scalar type.
condVal = evaluateExprAsBool(s.getCond());
builder.createCondition(condVal);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
// The scope of the while loop body is a nested scope.
if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
loopRes = mlir::failure();
emitStopPoint(&s);
});
return loopRes;
};
mlir::LogicalResult res = mlir::success();
mlir::Location scopeLoc = getLoc(s.getSourceRange());
builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
LexicalScope lexScope{
*this, loc, builder.getInsertionBlock()};
res = whileStmtBuilder();
});
if (res.failed())
return res;
terminateBody(builder, whileOp.getBody(), getLoc(s.getEndLoc()));
return mlir::success();
}