From cd27a2d71d4529d2d8258e12f3bac7c00415a135 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Tue, 14 Nov 2023 21:45:38 -0300 Subject: [PATCH] [CIR][CIRGen] Partially support statement expressions return values Adds support for GCC statement expressions return values by leveraging the `cir.scope` operation return value and aggregate copies. This does not implement the full semantics of statement expressions. [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp | 7 ++- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 12 +++- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 58 ++++++++++++++++--- .../CodeGen/UnimplementedFeatureGuarding.h | 1 + clang/test/CIR/CodeGen/stmt-expr.c | 37 ++++++++++++ 5 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 clang/test/CIR/CodeGen/stmt-expr.c diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp index 761537534409..e05969ec04ba 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp @@ -131,7 +131,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 96d69c2ebb08..92ba8b5b7777 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" @@ -285,7 +286,16 @@ 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 {}; + 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/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 46aea77418ba..a50e6acc9308 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -13,6 +13,9 @@ #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" using namespace cir; using namespace clang; @@ -21,11 +24,41 @@ 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 { + assert(buildStmt(CurStmt, /*useCurrentScope=*/false).succeeded()); + } + } + + return retAlloca; } Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast, @@ -35,14 +68,25 @@ Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast, // Add local scope to track new declared variables. SymTableScopeTy varScope(symbolTable); auto scopeLoc = getLoc(S.getSourceRange()); - builder.create( + auto scopeOp = builder.create( scopeLoc, /*scopeBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { + [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) { LexicalScopeContext lexScope{loc, builder.getInsertionBlock()}; LexicalScopeGuard lexScopeGuard{*this, &lexScope}; - retAlloca = buildCompoundStmtWithoutScope(S); + retAlloca = buildCompoundStmtWithoutScope(S, getLast, slot); + if (getLast && retAlloca.isValid()) { + lexScopeGuard.setRetVal(retAlloca.getPointer()); + type = retAlloca.getPointer().getType(); + } }); + // Get address object from CIR pointer. + if (const mlir::Value result = scopeOp.getResults()) { + assert(result.getType().isa() && + "expected pointer type"); + retAlloca = Address(result, getPointerSize()); + } + return retAlloca; } diff --git a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h index 5a857a2db39f..b03664c40798 100644 --- a/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h +++ b/clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h @@ -139,6 +139,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..0acbb2505ed3 --- /dev/null +++ b/clang/test/CIR/CodeGen/stmt-expr.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -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 l-value. +void test2(int x) { ({ x;}); } +// CHECK: @test2 +// CHECK: %{{.+}} = cir.scope { +// CHECK: %[[#V6:]] = cir.alloca !s32i, cir.ptr , ["tmp"] +// CHECK: %[[#V7:]] = cir.load %{{.+}} : cir.ptr , !s32i +// CHECK: cir.store %[[#V7]], %[[#V6]] : !s32i, cir.ptr +// CHECK: cir.yield %[[#V6]] : !cir.ptr +// CHECK: } + +// Yields an aggregate. +struct S { int x; }; +void test4() { ({ struct S s = {1}; s; }); } +// CHECK: @test4 +// CHECK: %[[#RET:]] = cir.alloca !ty_22S22, cir.ptr +// CHECK: cir.scope { +// CHECK: %[[#VAR:]] = cir.alloca !ty_22S22, cir.ptr +// [...] +// CHECK: cir.copy %[[#VAR]] to %[[#RET]] : !cir.ptr +// CHECK: } + +// TODO(cir): Missing label support. +// // Expression is wrapped in a label. +// // void test5(int x) { x = ({ label: x; }); } + +// TODO(cir): Can't think of an example for this. +// // Expression is wrapped in an expression attribute.