Skip to content

[CIR] Upstream support for range-based for loops #138176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,41 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
emitLValue(e);
}

Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) {
assert(e->getType()->isArrayType() &&
"Array to pointer decay must have array source type!");

// Expressions of array type can't be bitfields or vector elements.
LValue lv = emitLValue(e);
Address addr = lv.getAddress();

// If the array type was an incomplete type, we need to make sure
// the decay ends up being the right type.
auto lvalueAddrTy = mlir::cast<cir::PointerType>(addr.getPointer().getType());

if (e->getType()->isVariableArrayType())
return addr;

auto pointeeTy = mlir::cast<cir::ArrayType>(lvalueAddrTy.getPointee());

mlir::Type arrayTy = convertType(e->getType());
assert(mlir::isa<cir::ArrayType>(arrayTy) && "expected array");
assert(pointeeTy == arrayTy);

// The result of this decay conversion points to an array element within the
// base lvalue. However, since TBAA currently does not support representing
// accesses to elements of member arrays, we conservatively represent accesses
// to the pointee object as if it had no any base lvalue specified.
// TODO: Support TBAA for member arrays.
QualType eltType = e->getType()->castAsArrayTypeUnsafe()->getElementType();
assert(!cir::MissingFeatures::opTBAA());

mlir::Value ptr = builder.maybeBuildArrayDecay(
cgm.getLoc(e->getSourceRange()), addr.getPointer(),
convertTypeForMem(eltType));
return Address(ptr, addr.getAlignment());
}

/// Emit an `if` on a boolean condition, filling `then` and `else` into
/// appropriated regions.
mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,9 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return v;
}

case CK_ArrayToPointerDecay:
return cgf.emitArrayToPointerDecay(subExpr).getPointer();

case CK_NullToPointer: {
if (mustVisitNullValue(subExpr))
cgf.emitIgnoredExpr(subExpr);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ class CIRGenFunction : public CIRGenTypeCache {

LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);

Address emitArrayToPointerDecay(const Expr *array);

AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);

/// Emit code and set up symbol table for a variable declaration with auto,
Expand Down Expand Up @@ -485,6 +487,10 @@ class CIRGenFunction : public CIRGenTypeCache {
LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e);

mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);

mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s,
llvm::ArrayRef<const Attr *> attrs);

mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);

/// Emit an expression as an initializer for an object (variable, field, etc.)
Expand Down
80 changes: 79 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
return emitWhileStmt(cast<WhileStmt>(*s));
case Stmt::DoStmtClass:
return emitDoStmt(cast<DoStmt>(*s));
case Stmt::CXXForRangeStmtClass:
return emitCXXForRangeStmt(cast<CXXForRangeStmt>(*s), attr);
case Stmt::OpenACCComputeConstructClass:
return emitOpenACCComputeConstruct(cast<OpenACCComputeConstruct>(*s));
case Stmt::OpenACCLoopConstructClass:
Expand Down Expand Up @@ -137,7 +139,6 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoreturnStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXForRangeStmtClass:
case Stmt::IndirectGotoStmtClass:
case Stmt::GCCAsmStmtClass:
case Stmt::MSAsmStmtClass:
Expand Down Expand Up @@ -547,6 +548,83 @@ mlir::LogicalResult CIRGenFunction::emitSwitchCase(const SwitchCase &s,
llvm_unreachable("expect case or default stmt");
}

mlir::LogicalResult
CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s,
ArrayRef<const Attr *> forAttrs) {
cir::ForOp forOp;

// TODO(cir): pass in array of attributes.
auto forStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
// Evaluate the first pieces before the loop.
if (s.getInit())
if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())
return mlir::failure();
if (emitStmt(s.getRangeStmt(), /*useCurrentScope=*/true).failed())
return mlir::failure();
if (emitStmt(s.getBeginStmt(), /*useCurrentScope=*/true).failed())
return mlir::failure();
if (emitStmt(s.getEndStmt(), /*useCurrentScope=*/true).failed())
return mlir::failure();

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());

forOp = builder.createFor(
getLoc(s.getSourceRange()),
/*condBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
assert(!cir::MissingFeatures::createProfileWeightsForLoop());
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
mlir::Value condVal = evaluateExprAsBool(s.getCond());
builder.createCondition(condVal);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
// https://en.cppreference.com/w/cpp/language/for
// In C++ the scope of the init-statement and the scope of
// statement are one and the same.
bool useCurrentScope = true;
if (emitStmt(s.getLoopVarStmt(), useCurrentScope).failed())
loopRes = mlir::failure();
if (emitStmt(s.getBody(), useCurrentScope).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;
};

mlir::LogicalResult res = mlir::success();
mlir::Location scopeLoc = getLoc(s.getSourceRange());
builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
// Create a cleanup scope for the condition
// variable cleanups. Logical equivalent from
// LLVM codegn for LexicalScope
// ConditionScope(*this, S.getSourceRange())...
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::emitForStmt(const ForStmt &s) {
cir::ForOp forOp;

Expand Down
Loading