diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index ffbdcd4e1d8de..39b0fef62dcaa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1170,28 +1170,33 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf, return; } - CXXDestructorDecl *referenceTemporaryDtor = nullptr; - if (const clang::RecordType *rt = e->getType() - ->getBaseElementTypeUnsafe() - ->getAs()) { - // Get the destructor for the reference temporary. - auto *classDecl = - cast(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()) { + // Get the destructor for the reference temporary. + if (const auto *classDecl = dyn_cast( + 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(), diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 6b6ac701e6867..2a3c98c458256 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -69,6 +69,12 @@ class AggExprEmitter : public StmtVisitor { void Visit(Expr *e) { StmtVisitor::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); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 8649bab91ce8e..a23bdfc02ad2d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -185,6 +185,21 @@ class ScalarExprEmitter : public StmtVisitor { 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()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 917afa8e78021..706c14ae962a8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -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(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) { @@ -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 { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 122989a26b4a8..6e3f5b1f4afac 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -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); @@ -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 ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e, const FuncTy &branchGenFunc); diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 16e03f68ba5d0..3b0eabe92284b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -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" @@ -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(exprResult)) { + if (const auto *ls = dyn_cast(exprResult)) { + if (cgf.emitLabel(*ls->getDecl()).failed()) + return mlir::failure(); + exprResult = ls->getSubStmt(); + } else if (const auto *as = dyn_cast(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(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()); @@ -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) { @@ -249,10 +301,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s, return emitDeclStmt(cast(*s)); case Stmt::CompoundStmtClass: if (useCurrentScope) - emitCompoundStmtWithoutScope(cast(*s)); - else - emitCompoundStmt(cast(*s)); - break; + return emitCompoundStmtWithoutScope(cast(*s)); + return emitCompoundStmt(cast(*s)); case Stmt::GotoStmtClass: return emitGotoStmt(cast(*s)); case Stmt::ContinueStmtClass: diff --git a/clang/test/CIR/CodeGen/statement-exprs.c b/clang/test/CIR/CodeGen/statement-exprs.c new file mode 100644 index 0000000000000..927a868336b59 --- /dev/null +++ b/clang/test/CIR/CodeGen/statement-exprs.c @@ -0,0 +1,274 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +int f19(void) { + return ({ 3;;4;; }); +} + +// CIR: cir.func dso_local @f19() -> !s32i +// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: %[[TMP:.+]] = cir.alloca !s32i, !cir.ptr, ["tmp"] +// CIR: cir.scope { +// CIR: %[[C3:.+]] = cir.const #cir.int<3> : !s32i +// CIR: %[[C4:.+]] = cir.const #cir.int<4> : !s32i +// CIR: cir.store {{.*}} %[[C4]], %[[TMP]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[TMP_VAL:.+]] = cir.load {{.*}} %[[TMP]] : !cir.ptr, !s32i +// CIR: cir.store %[[TMP_VAL]], %[[RETVAL]] : !s32i, !cir.ptr +// CIR: %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr, !s32i +// CIR: cir.return %[[RES]] : !s32i + +// LLVM: define dso_local i32 @f19() +// LLVM: %[[VAR1:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR2:.+]] = alloca i32, i64 1 +// LLVM: br label %[[LBL3:.+]] +// LLVM: [[LBL3]]: +// LLVM: store i32 4, ptr %[[VAR2]] +// LLVM: br label %[[LBL4:.+]] +// LLVM: [[LBL4]]: +// LLVM: %[[V1:.+]] = load i32, ptr %[[VAR2]] +// LLVM: store i32 %[[V1]], ptr %[[VAR1]] +// LLVM: %[[RES:.+]] = load i32, ptr %[[VAR1]] +// LLVM: ret i32 %[[RES]] + +// OGCG: define dso_local i32 @f19() +// OGCG: entry: +// OGCG: %[[TMP:.+]] = alloca i32 +// OGCG: store i32 4, ptr %[[TMP]] +// OGCG: %[[TMP_VAL:.+]] = load i32, ptr %[[TMP]] +// OGCG: ret i32 %[[TMP_VAL]] + + +int nested(void) { + ({123;}); + { + int bar = 987; + return ({ ({ int asdf = 123; asdf; }); ({9999;}); }); + } +} + +// CIR: cir.func dso_local @nested() -> !s32i +// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: %[[TMP_OUTER:.+]] = cir.alloca !s32i, !cir.ptr, ["tmp"] +// CIR: cir.scope { +// CIR: %[[C123_OUTER:.+]] = cir.const #cir.int<123> : !s32i +// CIR: cir.store {{.*}} %[[C123_OUTER]], %[[TMP_OUTER]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[LOAD_TMP_OUTER:.+]] = cir.load {{.*}} %[[TMP_OUTER]] : !cir.ptr, !s32i +// CIR: cir.scope { +// CIR: %[[BAR:.+]] = cir.alloca !s32i, !cir.ptr, ["bar", init] +// CIR: %[[TMP_BARRET:.+]] = cir.alloca !s32i, !cir.ptr, ["tmp"] +// CIR: %[[C987:.+]] = cir.const #cir.int<987> : !s32i +// CIR: cir.store {{.*}} %[[C987]], %[[BAR]] : !s32i, !cir.ptr +// CIR: cir.scope { +// CIR: %[[TMP1:.+]] = cir.alloca !s32i, !cir.ptr, ["tmp"] +// CIR: %[[TMP2:.+]] = cir.alloca !s32i, !cir.ptr, ["tmp"] +// CIR: cir.scope { +// CIR: %[[ASDF:.+]] = cir.alloca !s32i, !cir.ptr, ["asdf", init] +// CIR: %[[C123_INNER:.+]] = cir.const #cir.int<123> : !s32i +// CIR: cir.store {{.*}} %[[C123_INNER]], %[[ASDF]] : !s32i, !cir.ptr +// CIR: %[[LOAD_ASDF:.+]] = cir.load {{.*}} %[[ASDF]] : !cir.ptr, !s32i +// CIR: cir.store {{.*}} %[[LOAD_ASDF]], %[[TMP1]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[V1:.+]] = cir.load {{.*}} %[[TMP1]] : !cir.ptr, !s32i +// CIR: cir.scope { +// CIR: %[[C9999:.+]] = cir.const #cir.int<9999> : !s32i +// CIR: cir.store {{.*}} %[[C9999]], %[[TMP2]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[V2:.+]] = cir.load {{.*}} %[[TMP2]] : !cir.ptr, !s32i +// CIR: cir.store {{.*}} %[[V2]], %[[TMP_BARRET]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[BARRET_VAL:.+]] = cir.load {{.*}} %[[TMP_BARRET]] : !cir.ptr, !s32i +// CIR: cir.store %[[BARRET_VAL]], %[[RETVAL]] : !s32i, !cir.ptr +// CIR: %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr, !s32i +// CIR: cir.return %[[RES]] : !s32i +// CIR: } +// CIR: %[[FINAL_RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr, !s32i +// CIR: cir.return %[[FINAL_RES]] : !s32i + +// LLVM: define dso_local i32 @nested() +// LLVM: %[[VAR1:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR2:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR3:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR4:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR5:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR6:.+]] = alloca i32, i64 1 +// LLVM: %[[VAR7:.+]] = alloca i32, i64 1 +// LLVM: br label %[[LBL8:.+]] +// LLVM: [[LBL8]]: +// LLVM: store i32 123, ptr %[[VAR7]] +// LLVM: br label %[[LBL9:.+]] +// LLVM: [[LBL9]]: +// LLVM: br label %[[LBL10:.+]] +// LLVM: [[LBL10]]: +// LLVM: store i32 987, ptr %[[VAR1]] +// LLVM: br label %[[LBL11:.+]] +// LLVM: [[LBL11]]: +// LLVM: br label %[[LBL12:.+]] +// LLVM: [[LBL12]]: +// LLVM: store i32 123, ptr %[[VAR5]] +// LLVM: %[[V1:.+]] = load i32, ptr %[[VAR5]] +// LLVM: store i32 %[[V1]], ptr %[[VAR3]] +// LLVM: br label %[[LBL14:.+]] +// LLVM: [[LBL14]]: +// LLVM: br label %[[LBL15:.+]] +// LLVM: [[LBL15]]: +// LLVM: store i32 9999, ptr %[[VAR4]] +// LLVM: br label %[[LBL16:.+]] +// LLVM: [[LBL16]]: +// LLVM: %[[V2:.+]] = load i32, ptr %[[VAR4]] +// LLVM: store i32 %[[V2]], ptr %[[VAR2]] +// LLVM: br label %[[LBL18:.+]] +// LLVM: [[LBL18]]: +// LLVM: %[[V3:.+]] = load i32, ptr %[[VAR2]] +// LLVM: store i32 %[[V3]], ptr %[[VAR6]] +// LLVM: %[[RES:.+]] = load i32, ptr %[[VAR6]] +// LLVM: ret i32 %[[RES]] + +// OGCG: define dso_local i32 @nested() +// OGCG: entry: +// OGCG: %[[TMP_OUTER:.+]] = alloca i32 +// OGCG: %[[BAR:.+]] = alloca i32 +// OGCG: %[[ASDF:.+]] = alloca i32 +// OGCG: %[[TMP1:.+]] = alloca i32 +// OGCG: %[[TMP2:.+]] = alloca i32 +// OGCG: %[[TMP3:.+]] = alloca i32 +// OGCG: store i32 123, ptr %[[TMP_OUTER]] +// OGCG: %[[OUTER_VAL:.+]] = load i32, ptr %[[TMP_OUTER]] +// OGCG: store i32 987, ptr %[[BAR]] +// OGCG: store i32 123, ptr %[[ASDF]] +// OGCG: %[[ASDF_VAL:.+]] = load i32, ptr %[[ASDF]] +// OGCG: store i32 %[[ASDF_VAL]], ptr %[[TMP1]] +// OGCG: %[[TMP1_VAL:.+]] = load i32, ptr %[[TMP1]] +// OGCG: store i32 9999, ptr %[[TMP3]] +// OGCG: %[[TMP3_VAL:.+]] = load i32, ptr %[[TMP3]] +// OGCG: store i32 %[[TMP3_VAL]], ptr %[[TMP2]] +// OGCG: %[[RES:.+]] = load i32, ptr %[[TMP2]] +// OGCG: ret i32 %[[RES]] + +void empty() { + return ({;;;;}); +} + +// CIR: cir.func no_proto dso_local @empty() +// CIR-NEXT: cir.return + +// LLVM: define dso_local void @empty() +// LLVM: ret void +// LLVM: } + +// OGCG: define dso_local void @empty() +// OGCG: ret void +// OGCG: } + +void empty2() { ({ }); } + +// CIR: @empty2 +// CIR-NEXT: cir.return + +// LLVM: @empty2() +// LLVM: ret void +// LLVM: } + +// OGCG: @empty2() +// OGCG: ret void +// OGCG: } + + +// Yields an out-of-scope scalar. +void test2() { ({int x = 3; x; }); } +// CIR: @test2 +// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr +// CIR: cir.scope { +// CIR: %[[VAR:.+]] = cir.alloca !s32i, !cir.ptr, ["x", init] +// [...] +// CIR: %[[TMP:.+]] = cir.load{{.*}} %[[VAR]] : !cir.ptr, !s32i +// CIR: cir.store{{.*}} %[[TMP]], %[[RETVAL]] : !s32i, !cir.ptr +// CIR: } +// CIR: %{{.+}} = cir.load{{.*}} %[[RETVAL]] : !cir.ptr, !s32i + +// LLVM: define dso_local void @test2() +// LLVM: %[[X:.+]] = alloca i32, i64 1 +// LLVM: %[[TMP:.+]] = alloca i32, i64 1 +// LLVM: br label %[[LBL3:.+]] +// LLVM: [[LBL3]]: +// LLVM: store i32 3, ptr %[[X]] +// LLVM: %[[X_VAL:.+]] = load i32, ptr %[[X]] +// LLVM: store i32 %[[X_VAL]], ptr %[[TMP]] +// LLVM: br label %[[LBL5:.+]] +// LLVM: [[LBL5]]: +// LLVM: ret void + +// OGCG: define dso_local void @test2() +// OGCG: entry: +// OGCG: %[[X:.+]] = alloca i32 +// OGCG: %[[TMP:.+]] = alloca i32 +// OGCG: store i32 3, ptr %[[X]] +// OGCG: %[[X_VAL:.+]] = load i32, ptr %[[X]] +// OGCG: store i32 %[[X_VAL]], ptr %[[TMP]] +// OGCG: %[[TMP_VAL:.+]] = load i32, ptr %[[TMP]] +// OGCG: ret void + +// Yields an aggregate. +struct S { int x; }; +int test3() { return ({ struct S s = {1}; s; }).x; } +// CIR: cir.func no_proto dso_local @test3() -> !s32i +// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: %[[YIELDVAL:.+]] = cir.scope { +// CIR: %[[REF_TMP0:.+]] = cir.alloca !rec_S, !cir.ptr, ["ref.tmp0"] +// CIR: %[[TMP:.+]] = cir.alloca !rec_S, !cir.ptr, ["tmp"] +// CIR: cir.scope { +// CIR: %[[S:.+]] = cir.alloca !rec_S, !cir.ptr, ["s", init] +// CIR: %[[GEP_X_S:.+]] = cir.get_member %[[S]][0] {name = "x"} : !cir.ptr -> !cir.ptr +// CIR: %[[C1:.+]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store {{.*}} %[[C1]], %[[GEP_X_S]] : !s32i, !cir.ptr +// CIR: } +// CIR: %[[GEP_X_TMP:.+]] = cir.get_member %[[REF_TMP0]][0] {name = "x"} : !cir.ptr -> !cir.ptr +// CIR: %[[XVAL:.+]] = cir.load {{.*}} %[[GEP_X_TMP]] : !cir.ptr, !s32i +// CIR: cir.yield %[[XVAL]] : !s32i +// CIR: } : !s32i +// CIR: cir.store %[[YIELDVAL]], %[[RETVAL]] : !s32i, !cir.ptr +// CIR: %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr, !s32i +// CIR: cir.return %[[RES]] : !s32i + +// LLVM: define dso_local i32 @test3() +// LLVM: %[[VAR1:.+]] = alloca %struct.S, i64 1 +// LLVM: %[[VAR2:.+]] = alloca %struct.S, i64 1 +// LLVM: %[[VAR3:.+]] = alloca %struct.S, i64 1 +// LLVM: %[[VAR4:.+]] = alloca i32, i64 1 +// LLVM: br label %[[LBL5:.+]] +// LLVM: [[LBL5]]: +// LLVM: br label %[[LBL6:.+]] +// LLVM: [[LBL6]]: +// LLVM: %[[GEP_S:.+]] = getelementptr %struct.S, ptr %[[VAR3]], i32 0, i32 0 +// LLVM: store i32 1, ptr %[[GEP_S]] +// LLVM: br label %[[LBL8:.+]] +// LLVM: [[LBL8]]: +// LLVM: %[[GEP_VAR1:.+]] = getelementptr %struct.S, ptr %[[VAR1]], i32 0, i32 0 +// LLVM: %[[LOAD_X:.+]] = load i32, ptr %[[GEP_VAR1]] +// LLVM: br label %[[LBL11:.+]] +// LLVM: [[LBL11]]: +// LLVM: %[[PHI:.+]] = phi i32 [ %[[LOAD_X]], %[[LBL8]] ] +// LLVM: store i32 %[[PHI]], ptr %[[VAR4]] +// LLVM: %[[RES:.+]] = load i32, ptr %[[VAR4]] +// LLVM: ret i32 %[[RES]] + +// OGCG: define dso_local i32 @test3() +// OGCG: entry: +// OGCG: %[[REF_TMP:.+]] = alloca %struct.S +// OGCG: %[[S:.+]] = alloca %struct.S +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[S]], ptr align 4 @__const.test3.s, i64 4, i1 false) +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[REF_TMP]], ptr align 4 %[[S]], i64 4, i1 false) +// OGCG: %[[GEP:.+]] = getelementptr inbounds nuw %struct.S, ptr %[[REF_TMP]], i32 0, i32 0 +// OGCG: %[[XVAL:.+]] = load i32, ptr %[[GEP]] +// OGCG: ret i32 %[[XVAL]] + +// Expression is wrapped in an expression attribute (just ensure it does not crash). +void test4(int x) { ({[[gsl::suppress("foo")]] x;}); } +// CIR: @test4 +// LLVM: @test4 +// OGCG: @test4 diff --git a/clang/test/CIR/CodeGen/stmt-expr.cpp b/clang/test/CIR/CodeGen/stmt-expr.cpp new file mode 100644 index 0000000000000..9e3911f638ba7 --- /dev/null +++ b/clang/test/CIR/CodeGen/stmt-expr.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +class A { +public: + A(): x(0) {} + A(A &a) : x(a.x) {} + int x; + void Foo() {} +}; + +void test1() { + ({ + A a; + a; + }).Foo(); +} + +// CIR: cir.func dso_local @_Z5test1v() +// CIR: cir.scope { +// CIR: %[[REF_TMP0:.+]] = cir.alloca !rec_A, !cir.ptr, ["ref.tmp0"] +// CIR: %[[TMP:.+]] = cir.alloca !rec_A, !cir.ptr, ["tmp"] +// CIR: cir.scope { +// CIR: %[[A:.+]] = cir.alloca !rec_A, !cir.ptr, ["a", init] +// CIR: cir.call @_ZN1AC2Ev(%[[A]]) : (!cir.ptr) -> () +// CIR: cir.call @_ZN1AC2ERS_(%[[REF_TMP0]], %[[A]]) : (!cir.ptr, !cir.ptr) -> () +// CIR: } +// CIR: cir.call @_ZN1A3FooEv(%[[REF_TMP0]]) : (!cir.ptr) -> () +// CIR: } +// CIR: cir.return + +// LLVM: define dso_local void @_Z5test1v() +// LLVM: %[[VAR1:.+]] = alloca %class.A, i64 1 +// LLVM: %[[VAR2:.+]] = alloca %class.A, i64 1 +// LLVM: %[[VAR3:.+]] = alloca %class.A, i64 1 +// LLVM: br label %[[LBL4:.+]] +// LLVM: [[LBL4]]: +// LLVM: br label %[[LBL5:.+]] +// LLVM: [[LBL5]]: +// LLVM: call void @_ZN1AC2Ev(ptr %[[VAR3]]) +// LLVM: call void @_ZN1AC2ERS_(ptr %[[VAR1]], ptr %[[VAR3]]) +// LLVM: br label %[[LBL6:.+]] +// LLVM: [[LBL6]]: +// LLVM: call void @_ZN1A3FooEv(ptr %[[VAR1]]) +// LLVM: br label %[[LBL7:.+]] +// LLVM: [[LBL7]]: +// LLVM: ret void + +// OGCG: define dso_local void @_Z5test1v() +// OGCG: entry: +// OGCG: %[[REF_TMP:.+]] = alloca %class.A +// OGCG: %[[A:.+]] = alloca %class.A +// OGCG: call void @_ZN1AC2Ev(ptr {{.*}} %[[A]]) +// OGCG: call void @_ZN1AC2ERS_(ptr {{.*}} %[[REF_TMP]], ptr {{.*}} %[[A]]) +// OGCG: call void @_ZN1A3FooEv(ptr {{.*}} %[[REF_TMP]]) +// OGCG: ret void + +struct with_dtor { + ~with_dtor(); +}; + +void cleanup() { + ({ with_dtor wd; }); +} + +// CIR: cir.func dso_local @_Z7cleanupv() +// CIR: cir.scope { +// CIR: %[[WD:.+]] = cir.alloca !rec_with_dtor, !cir.ptr, ["wd"] +// CIR: cir.call @_ZN9with_dtorD1Ev(%[[WD]]) nothrow : (!cir.ptr) -> () +// CIR: } +// CIR: cir.return + +// LLVM: define dso_local void @_Z7cleanupv() +// LLVM: %[[WD:.+]] = alloca %struct.with_dtor, i64 1 +// LLVM: br label %[[LBL2:.+]] +// LLVM: [[LBL2]]: +// LLVM: call void @_ZN9with_dtorD1Ev(ptr %[[WD]]) +// LLVM: br label %[[LBL3:.+]] +// LLVM: [[LBL3]]: +// LLVM: ret void + +// OGCG: define dso_local void @_Z7cleanupv() +// OGCG: entry: +// OGCG: %[[WD:.+]] = alloca %struct.with_dtor +// OGCG: call void @_ZN9with_dtorD1Ev(ptr {{.*}} %[[WD]]) +// OGCG: ret void diff --git a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp b/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp index da45aca13e7f9..e6d33aeb3c7e5 100644 --- a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp +++ b/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp @@ -2,8 +2,7 @@ void HelloWorld(int *A, int *B, int *C, int N) { -// expected-error@+2{{ClangIR code gen Not Yet Implemented: OpenACC Atomic Construct}} -// expected-error@+1{{ClangIR code gen Not Yet Implemented: emitCompoundStmtWithoutScope: OpenACCAtomicConstruct}} +// expected-error@+1{{ClangIR code gen Not Yet Implemented: OpenACC Atomic Construct}} #pragma acc atomic N = N + 1;