Skip to content

Commit

Permalink
[CIR][CIRGen] Partially support statement expressions return values
Browse files Browse the repository at this point in the history
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-source-id: 59cb541c953907bfa4c645e5f9ec8ac36908fd46
Pull Request resolved: #314
  • Loading branch information
sitio-couto committed Nov 15, 2023
1 parent e8d283e commit 44d42d5
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 9 deletions.
7 changes: 6 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
// 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");
Expand Down
12 changes: 11 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "Address.h"
#include "CIRDataLayout.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
Expand Down Expand Up @@ -285,7 +286,16 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
}
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) {
Expand Down
58 changes: 51 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Expr>(ExprResult)) {
if (const auto *LS = dyn_cast<LabelStmt>(ExprResult))
llvm_unreachable("labels are NYI");
else if (const auto *AS = dyn_cast<AttributedStmt>(ExprResult))
llvm_unreachable("statement attributes are NYI");
else
llvm_unreachable("Unknown value statement");
}

const Expr *E = cast<Expr>(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,
Expand All @@ -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<mlir::cir::ScopeOp>(
auto scopeOp = builder.create<mlir::cir::ScopeOp>(
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<mlir::cir::PointerType>() &&
"expected pointer type");
retAlloca = Address(result, getPointerSize());
}

return retAlloca;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 37 additions & 0 deletions clang/test/CIR/CodeGen/stmt-expr.c
Original file line number Diff line number Diff line change
@@ -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 <!s32i>, ["tmp"]
// CHECK: %[[#V7:]] = cir.load %{{.+}} : cir.ptr <!s32i>, !s32i
// CHECK: cir.store %[[#V7]], %[[#V6]] : !s32i, cir.ptr <!s32i>
// CHECK: cir.yield %[[#V6]] : !cir.ptr<!s32i>
// 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 <!ty_22S22>
// CHECK: cir.scope {
// CHECK: %[[#VAR:]] = cir.alloca !ty_22S22, cir.ptr <!ty_22S22>
// [...]
// CHECK: cir.copy %[[#VAR]] to %[[#RET]] : !cir.ptr<!ty_22S22>
// 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.

0 comments on commit 44d42d5

Please sign in to comment.