Skip to content

[CIR] Implement Statement Expressions #153677

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 8 commits into from
Aug 19, 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
41 changes: 23 additions & 18 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1170,28 +1170,33 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf,
return;
}

CXXDestructorDecl *referenceTemporaryDtor = nullptr;
if (const clang::RecordType *rt = e->getType()
->getBaseElementTypeUnsafe()
->getAs<clang::RecordType>()) {
// Get the destructor for the reference temporary.
auto *classDecl =
cast<CXXRecordDecl>(rt->getOriginalDecl()->getDefinitionOrSelf());
if (!classDecl->hasTrivialDestructor())
referenceTemporaryDtor =
classDecl->getDefinitionOrSelf()->getDestructor();
}

if (!referenceTemporaryDtor)
const QualType::DestructionKind dk = e->getType().isDestructedType();
if (dk == QualType::DK_none)
return;

// Call the destructor for the temporary.
switch (m->getStorageDuration()) {
case SD_Static:
case SD_Thread:
cgf.cgm.errorNYI(e->getSourceRange(),
"pushTemporaryCleanup: static/thread storage duration");
return;
case SD_Thread: {
CXXDestructorDecl *referenceTemporaryDtor = nullptr;
if (const clang::RecordType *rt = e->getType()
->getBaseElementTypeUnsafe()
->getAs<clang::RecordType>()) {
// Get the destructor for the reference temporary.
if (const auto *classDecl = dyn_cast<CXXRecordDecl>(
rt->getOriginalDecl()->getDefinitionOrSelf())) {
if (!classDecl->hasTrivialDestructor())
referenceTemporaryDtor =
classDecl->getDefinitionOrSelf()->getDestructor();
}
}

if (!referenceTemporaryDtor)
return;

cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: static/thread "
"storage duration with destructors");
break;
}

case SD_FullExpression:
cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }

void VisitCallExpr(const CallExpr *e);
void VisitStmtExpr(const StmtExpr *e) {
CIRGenFunction::StmtExprEvaluation eval(cgf);
Address retAlloca =
cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
(void)cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca, dest);
}

void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); }

Expand Down
15 changes: 15 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitCastExpr(CastExpr *e);
mlir::Value VisitCallExpr(const CallExpr *e);

mlir::Value VisitStmtExpr(StmtExpr *e) {
CIRGenFunction::StmtExprEvaluation eval(cgf);
if (e->getType()->isVoidType()) {
(void)cgf.emitCompoundStmt(*e->getSubStmt());
return {};
}

Address retAlloca =
cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
(void)cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca);

return cgf.emitLoadOfScalar(cgf.makeAddrLValue(retAlloca, e->getType()),
e->getExprLoc());
}

mlir::Value VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
if (e->getBase()->getType()->isVectorType()) {
assert(!cir::MissingFeatures::scalableVectors());
Expand Down
8 changes: 2 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,13 +490,10 @@ mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
// We start with function level scope for variables.
SymTableScopeTy varScope(symbolTable);

auto result = mlir::LogicalResult::success();
if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body))
emitCompoundStmtWithoutScope(*block);
else
result = emitStmt(body, /*useCurrentScope=*/true);
return emitCompoundStmtWithoutScope(*block);

return result;
return emitStmt(body, /*useCurrentScope=*/true);
}

static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
Expand Down Expand Up @@ -561,7 +558,6 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
emitImplicitAssignmentOperatorBody(args);
} else if (body) {
if (mlir::failed(emitFunctionBody(body))) {
fn.erase();
return nullptr;
}
} else {
Expand Down
30 changes: 28 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,9 +1174,14 @@ class CIRGenFunction : public CIRGenTypeCache {
LValue emitScalarCompoundAssignWithComplex(const CompoundAssignOperator *e,
mlir::Value &result);

void emitCompoundStmt(const clang::CompoundStmt &s);
mlir::LogicalResult
emitCompoundStmt(const clang::CompoundStmt &s, Address *lastValue = nullptr,
AggValueSlot slot = AggValueSlot::ignored());

void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
mlir::LogicalResult
emitCompoundStmtWithoutScope(const clang::CompoundStmt &s,
Address *lastValue = nullptr,
AggValueSlot slot = AggValueSlot::ignored());

void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false);
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
Expand Down Expand Up @@ -1413,6 +1418,27 @@ class CIRGenFunction : public CIRGenTypeCache {
// we know if a temporary should be destroyed conditionally.
ConditionalEvaluation *outermostConditional = nullptr;

/// An RAII object to record that we're evaluating a statement
/// expression.
class StmtExprEvaluation {
CIRGenFunction &cgf;

/// We have to save the outermost conditional: cleanups in a
/// statement expression aren't conditional just because the
/// StmtExpr is.
ConditionalEvaluation *savedOutermostConditional;

public:
StmtExprEvaluation(CIRGenFunction &cgf)
: cgf(cgf), savedOutermostConditional(cgf.outermostConditional) {
cgf.outermostConditional = nullptr;
}

~StmtExprEvaluation() {
cgf.outermostConditional = savedOutermostConditional;
}
};

template <typename FuncTy>
ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e,
const FuncTy &branchGenFunc);
Expand Down
84 changes: 67 additions & 17 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "CIRGenFunction.h"

#include "mlir/IR/Builders.h"
#include "mlir/IR/Location.h"
#include "mlir/Support/LLVM.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtOpenACC.h"
Expand All @@ -23,16 +25,68 @@ 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(),
std::string("emitCompoundStmtWithoutScope: ") +
curStmt->getStmtClassName());
static mlir::LogicalResult emitStmtWithResult(CIRGenFunction &cgf,
const Stmt *exprResult,
AggValueSlot slot,
Address *lastValue) {
// We have to special case labels here. They are statements, but when put
// at the end of a statement expression, they yield the value of their
// subexpression. Handle this by walking through all labels we encounter,
// emitting them before we evaluate the subexpr.
// Similar issues arise for attributed statements.
while (!isa<Expr>(exprResult)) {
if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
if (cgf.emitLabel(*ls->getDecl()).failed())
return mlir::failure();
exprResult = ls->getSubStmt();
} else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
// FIXME: Update this if we ever have attributes that affect the
// semantics of an expression.
exprResult = as->getSubStmt();
} else {
llvm_unreachable("Unknown value statement");
}
}

const Expr *e = cast<Expr>(exprResult);
QualType exprTy = e->getType();
if (cgf.hasAggregateEvaluationKind(exprTy)) {
cgf.emitAggExpr(e, slot);
} else {
// We can't return an RValue here because there might be cleanups at
// the end of the StmtExpr. Because of that, we have to emit the result
// here into a temporary alloca.
cgf.emitAnyExprToMem(e, *lastValue, Qualifiers(),
/*IsInit*/ false);
}

return mlir::success();
}

mlir::LogicalResult CIRGenFunction::emitCompoundStmtWithoutScope(
const CompoundStmt &s, Address *lastValue, AggValueSlot slot) {
mlir::LogicalResult result = mlir::success();
const Stmt *exprResult = s.getStmtExprResult();
assert((!lastValue || (lastValue && exprResult)) &&
"If lastValue is not null then the CompoundStmt must have a "
"StmtExprResult");

for (const Stmt *curStmt : s.body()) {
const bool saveResult = lastValue && exprResult == curStmt;
if (saveResult) {
if (emitStmtWithResult(*this, exprResult, slot, lastValue).failed())
result = mlir::failure();
} else {
if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
result = mlir::failure();
}
}
return result;
}

void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
mlir::LogicalResult CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
Address *lastValue,
AggValueSlot slot) {
// Add local scope to track new declared variables.
SymTableScopeTy varScope(symbolTable);
mlir::Location scopeLoc = getLoc(s.getSourceRange());
Expand All @@ -41,12 +95,10 @@ void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
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);
}
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(scopeInsPt);
LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
return emitCompoundStmtWithoutScope(s, lastValue, slot);
}

void CIRGenFunction::emitStopPoint(const Stmt *s) {
Expand Down Expand Up @@ -249,10 +301,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
return emitDeclStmt(cast<DeclStmt>(*s));
case Stmt::CompoundStmtClass:
if (useCurrentScope)
emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
else
emitCompoundStmt(cast<CompoundStmt>(*s));
break;
return emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
return emitCompoundStmt(cast<CompoundStmt>(*s));
case Stmt::GotoStmtClass:
return emitGotoStmt(cast<GotoStmt>(*s));
case Stmt::ContinueStmtClass:
Expand Down
Loading