diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h index 31186f4a8e1f..3213c6a633bc 100644 --- a/clang/lib/CIR/CodeGen/Address.h +++ b/clang/lib/CIR/CodeGen/Address.h @@ -110,6 +110,13 @@ class Address { PointerAndKnownNonNull.setInt(true); return *this; } + + /// Get the operation which defines this address. + mlir::Operation *getDefiningOp() const { + if (!isValid()) + return nullptr; + return getPointer().getDefiningOp(); + } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 26d81622f54c..f31cb4b3fca2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -796,6 +796,18 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { mlir::Value v) { return create(loc, v); } + + // TODO(cir): Change this to hoist alloca to the parent *scope* instead. + /// Move alloca operation to the parent region. + void hoistAllocaToParentRegion(mlir::cir::AllocaOp alloca) { + auto &block = alloca->getParentOp()->getParentRegion()->front(); + const auto allocas = block.getOps(); + if (allocas.empty()) { + alloca->moveBefore(&block, block.begin()); + } else { + alloca->moveAfter(*std::prev(allocas.end())); + } + } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index d2ff0de5c6f2..47b11b9fa39d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1029,6 +1029,13 @@ RValue CIRGenFunction::buildCallExpr(const clang::CallExpr *E, return buildCall(E->getCallee()->getType(), callee, E, ReturnValue); } +LValue CIRGenFunction::buildStmtExprLValue(const StmtExpr *E) { + // Can only get l-value for message expression returning aggregate type + RValue RV = buildAnyExprToTemp(E); + return makeAddrLValue(RV.getAggregateAddress(), E->getType(), + AlignmentSource::Decl); +} + RValue CIRGenFunction::buildCall(clang::QualType CalleeType, const CIRGenCallee &OrigCallee, const clang::CallExpr *E, @@ -2165,6 +2172,8 @@ LValue CIRGenFunction::buildLValue(const Expr *E) { case Expr::ObjCPropertyRefExprClass: llvm_unreachable("cannot emit a property reference directly"); + case Expr::StmtExprClass: + return buildStmtExprLValue(cast(E)); } return LValue::makeAddr(Address::invalid(), E->getType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp index 3837144ef975..e4a9923a055d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp @@ -203,7 +203,12 @@ class AggExprEmitter : public StmtVisitor { // Operators. void VisitCastExpr(CastExpr *E); void VisitCallExpr(const CallExpr *E); - void VisitStmtExpr(const StmtExpr *E) { llvm_unreachable("NYI"); } + + void VisitStmtExpr(const StmtExpr *E) { + assert(!UnimplementedFeature::stmtExprEvaluation() && "NYI"); + CGF.buildCompoundStmt(*E->getSubStmt(), /*getLast=*/true, Dest); + } + void VisitBinaryOperator(const BinaryOperator *E) { llvm_unreachable("NYI"); } void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *E) { llvm_unreachable("NYI"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 6103570bb34d..880e47f6efb4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "Address.h" #include "CIRDataLayout.h" #include "CIRGenFunction.h" #include "CIRGenModule.h" @@ -291,7 +292,24 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCastExpr(CastExpr *E); mlir::Value VisitCallExpr(const CallExpr *E); - mlir::Value VisitStmtExpr(StmtExpr *E) { llvm_unreachable("NYI"); } + + mlir::Value VisitStmtExpr(StmtExpr *E) { + assert(!UnimplementedFeature::stmtExprEvaluation() && "NYI"); + Address retAlloca = + CGF.buildCompoundStmt(*E->getSubStmt(), !E->getType()->isVoidType()); + if (!retAlloca.isValid()) + return {}; + + // FIXME(cir): This is a work around the ScopeOp builder. If we build the + // ScopeOp before its body, we would be able to create the retAlloca + // direclty in the parent scope removing the need to hoist it. + assert(retAlloca.getDefiningOp() && "expected a alloca op"); + CGF.getBuilder().hoistAllocaToParentRegion( + cast(retAlloca.getDefiningOp())); + + return CGF.buildLoadOfScalar(CGF.makeAddrLValue(retAlloca, E->getType()), + E->getExprLoc()); + } // Unary Operators. mlir::Value VisitUnaryPostDec(const UnaryOperator *E) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 2d56b33dc823..d74a978e4193 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -744,6 +744,8 @@ class CIRGenFunction : public CIRGenTypeCache { void checkTargetFeatures(const CallExpr *E, const FunctionDecl *TargetDecl); void checkTargetFeatures(SourceLocation Loc, const FunctionDecl *TargetDecl); + LValue buildStmtExprLValue(const StmtExpr *E); + /// Generate a call of the given function, expecting the given /// result type, and using the given argument list which specifies both the /// LLVM arguments and the types they were derived from. diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index f683f7509876..801fc73b179d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -13,6 +13,10 @@ #include "Address.h" #include "CIRGenFunction.h" #include "mlir/IR/Value.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Stmt.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" using namespace cir; using namespace clang; @@ -21,11 +25,42 @@ using namespace mlir::cir; Address CIRGenFunction::buildCompoundStmtWithoutScope(const CompoundStmt &S, bool getLast, AggValueSlot slot) { - for (auto *CurStmt : S.body()) - if (buildStmt(CurStmt, /*useCurrentScope=*/false).failed()) - return Address::invalid(); + const Stmt *ExprResult = S.getStmtExprResult(); + assert((!getLast || (getLast && ExprResult)) && + "If getLast is true then the CompoundStmt must have a StmtExprResult"); - return Address::invalid(); + Address retAlloca = Address::invalid(); + + for (auto *CurStmt : S.body()) { + if (getLast && ExprResult == CurStmt) { + while (!isa(ExprResult)) { + if (const auto *LS = dyn_cast(ExprResult)) + llvm_unreachable("labels are NYI"); + else if (const auto *AS = dyn_cast(ExprResult)) + llvm_unreachable("statement attributes are NYI"); + else + llvm_unreachable("Unknown value statement"); + } + + const Expr *E = cast(ExprResult); + QualType exprTy = E->getType(); + if (hasAggregateEvaluationKind(exprTy)) { + buildAggExpr(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. + retAlloca = CreateMemTemp(exprTy, getLoc(E->getSourceRange())); + buildAnyExprToMem(E, retAlloca, Qualifiers(), + /*IsInit*/ false); + } + } else { + if (buildStmt(CurStmt, /*useCurrentScope=*/false).failed()) + llvm_unreachable("failed to build statement"); + } + } + + return retAlloca; } Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast, @@ -37,9 +72,9 @@ Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast, auto scopeLoc = getLoc(S.getSourceRange()); builder.create( scopeLoc, /*scopeBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { + [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) { LexicalScope lexScope{*this, loc, builder.getInsertionBlock()}; - retAlloca = buildCompoundStmtWithoutScope(S); + retAlloca = buildCompoundStmtWithoutScope(S, getLast, slot); }); return retAlloca; diff --git a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h index ee3d643dd136..1e5d1dfe7526 100644 --- a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h +++ b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h @@ -144,6 +144,7 @@ struct UnimplementedFeature { static bool metaDataNode() { return false; } static bool isSEHTryScope() { return false; } static bool emitScalarRangeCheck() { return false; } + static bool stmtExprEvaluation() { return false; } }; } // namespace cir diff --git a/clang/test/CIR/CodeGen/stmt-expr.c b/clang/test/CIR/CodeGen/stmt-expr.c new file mode 100644 index 000000000000..8029358887e0 --- /dev/null +++ b/clang/test/CIR/CodeGen/stmt-expr.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +// Yields void. +void test1() { ({ }); } +// CHECK: @test1 +// CHECK: cir.scope { +// CHECK-NOT: cir.yield +// CHECK: } + +// Yields an out-of-scope scalar. +void test2() { ({int x = 3; x; }); } +// CHECK: @test2 +// CHECK: %[[#RETVAL:]] = cir.alloca !s32i, cir.ptr +// CHECK: cir.scope { +// CHECK: %[[#VAR:]] = cir.alloca !s32i, cir.ptr , ["x", init] +// [...] +// CHECK: %[[#TMP:]] = cir.load %[[#VAR]] : cir.ptr , !s32i +// CHECK: cir.store %[[#TMP]], %[[#RETVAL]] : !s32i, cir.ptr +// CHECK: } +// CHECK: %{{.+}} = cir.load %[[#RETVAL]] : cir.ptr , !s32i + +// Yields an aggregate. +struct S { int x; }; +int test3() { return ({ struct S s = {1}; s; }).x; } +// CHECK: @test3 +// CHECK: %[[#RETVAL:]] = cir.alloca !ty_22S22, cir.ptr +// CHECK: cir.scope { +// CHECK: %[[#VAR:]] = cir.alloca !ty_22S22, cir.ptr +// [...] +// CHECK: cir.copy %[[#VAR]] to %[[#RETVAL]] : !cir.ptr +// CHECK: } +// CHECK: %[[#RETADDR:]] = cir.get_member %1[0] {name = "x"} : !cir.ptr -> !cir.ptr +// CHECK: %{{.+}} = cir.load %[[#RETADDR]] : cir.ptr , !s32i + +// Expression is wrapped in an expression attribute (just ensure it does not crash). +void test4(int x) { ({[[gsl::suppress("foo")]] x;}); } +// CHECK: @test4 + +// TODO(cir): Missing label support. +// // Expression is wrapped in a label. +// // void test5(int x) { x = ({ label: x; }); } diff --git a/clang/test/CIR/CodeGen/stmt-expr.cpp b/clang/test/CIR/CodeGen/stmt-expr.cpp new file mode 100644 index 000000000000..d9d619f70a92 --- /dev/null +++ b/clang/test/CIR/CodeGen/stmt-expr.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +class A { +public: + A(): x(0) {} + A(A &a) : x(a.x) {} + // TODO(cir): Ensure dtors are properly called. The dtor below crashes. + // ~A() {} + int x; + void Foo() {} +}; + +void test1() { + ({ + A a; + a; + }).Foo(); +} +// CHECK: @_Z5test1v +// CHECK: cir.scope { +// CHECK: %[[#RETVAL:]] = cir.alloca !ty_22A22, cir.ptr +// CHECK: cir.scope { +// CHECK: %[[#VAR:]] = cir.alloca !ty_22A22, cir.ptr , ["a", init] {alignment = 4 : i64} +// CHECK: cir.call @_ZN1AC1Ev(%[[#VAR]]) : (!cir.ptr) -> () +// CHECK: cir.call @_ZN1AC1ERS_(%[[#RETVAL]], %[[#VAR]]) : (!cir.ptr, !cir.ptr) -> () +// TODO(cir): the local VAR should be destroyed here. +// CHECK: } +// CHECK: cir.call @_ZN1A3FooEv(%[[#RETVAL]]) : (!cir.ptr) -> () +// TODO(cir): the temporary RETVAL should be destroyed here. +// CHECK: }