Skip to content
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

[CIR][CIRGen] Partially support statement expressions return values #314

Merged
merged 8 commits into from
Jan 10, 2024
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
54 changes: 54 additions & 0 deletions clang/test/CIR/CodeGen/stmt-expr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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: } : !cir.ptr<!s32i>

// Yields an out-of-scope scalar.
void test3() { ({ int x = 3; x;}); }
// CHECK: @test3
// CHECK: %{{.+}} = cir.scope {
// CHECK: %[[#V2:]] = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init]
// CHECK: %[[#V3:]] = cir.alloca !s32i, cir.ptr <!s32i>, ["tmp"]
// [...]
// CHECK: %[[#V5:]] = cir.load %[[#V2]] : cir.ptr <!s32i>, !s32i
// CHECK: cir.store %[[#V5]], %[[#V3]] : !s32i, cir.ptr <!s32i>
// CHECK: cir.yield %[[#V3]] : !cir.ptr<!s32i>
// CHECK: } : !cir.ptr<!s32i>

// 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: }

// Expression is wrapped in an expression attribute.
void test5(int x) {
({[[gsl::suppress("foo")]] x;});
}
// CHECK: %{{.+}} = cir.scope {
// [...]
// CHECK: } : !cir.ptr<!s32i>

// TODO(cir): Missing label support.
// // Expression is wrapped in a label.
// // void test5(int x) { x = ({ label: x; }); }
24 changes: 24 additions & 0 deletions clang/test/CIR/CodeGen/stmt-expr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

class A {
public:
void Foo() {}
};

// Statement expression result must be returned by value.
// The local var a should be copied into a temporary and this temporary should
// be used to call the Foo method.
void test1() {
A a;
({a;}).Foo();
}
// CHECK: @_Z5test1v
// CHECK: %0 = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["a"]
// CHECK: cir.scope {
// CHECK: %1 = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["ref.tmp0"]
// CHECK: cir.scope {
// CHECK: cir.call @_ZN1AC1ERKS_(%1, %0) : (!cir.ptr<!ty_22A22>, !cir.ptr<!ty_22A22>) -> ()
// CHECK: }
// CHECK: cir.call @_ZN1A3FooEv(%1) : (!cir.ptr<!ty_22A22>) -> ()
// CHECK: }