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: 7 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,18 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
mlir::Value v) {
return create<mlir::cir::StackRestoreOp>(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<mlir::cir::AllocaOp>();
if (allocas.empty()) {
alloca->moveBefore(&block, block.begin());
} else {
alloca->moveAfter(*std::prev(allocas.end()));
}
}
};

} // namespace cir
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -2163,6 +2170,8 @@ LValue CIRGenFunction::buildLValue(const Expr *E) {

case Expr::ObjCPropertyRefExprClass:
llvm_unreachable("cannot emit a property reference directly");
case Expr::StmtExprClass:
return buildStmtExprLValue(cast<StmtExpr>(E));
}

return LValue::makeAddr(Address::invalid(), E->getType());
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,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
20 changes: 19 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 @@ -291,7 +292,24 @@ 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 {};

// 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<mlir::cir::AllocaOp>(retAlloca.getDefiningOp()));

return CGF.buildLoadOfScalar(CGF.makeAddrLValue(retAlloca, E->getType()),
E->getExprLoc());
}

// Unary Operators.
mlir::Value VisitUnaryPostDec(const UnaryOperator *E) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
47 changes: 41 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<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 {
if (buildStmt(CurStmt, /*useCurrentScope=*/false).failed())
llvm_unreachable("failed to build statement");
}
}

return retAlloca;
}

Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast,
Expand All @@ -37,9 +72,9 @@ Address CIRGenFunction::buildCompoundStmt(const CompoundStmt &S, bool getLast,
auto scopeLoc = getLoc(S.getSourceRange());
builder.create<mlir::cir::ScopeOp>(
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;
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 @@ -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

Expand Down
42 changes: 42 additions & 0 deletions clang/test/CIR/CodeGen/stmt-expr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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 out-of-scope scalar.
void test2() { ({int x = 3; x; }); }
// CHECK: @test2
// CHECK: %[[#RETVAL:]] = cir.alloca !s32i, cir.ptr <!s32i>
// CHECK: cir.scope {
// CHECK: %[[#VAR:]] = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init]
// [...]
// CHECK: %[[#TMP:]] = cir.load %[[#VAR]] : cir.ptr <!s32i>, !s32i
// CHECK: cir.store %[[#TMP]], %[[#RETVAL]] : !s32i, cir.ptr <!s32i>
// CHECK: }
// CHECK: %{{.+}} = cir.load %[[#RETVAL]] : cir.ptr <!s32i>, !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 <!ty_22S22>
// CHECK: cir.scope {
// CHECK: %[[#VAR:]] = cir.alloca !ty_22S22, cir.ptr <!ty_22S22>
// [...]
// CHECK: cir.copy %[[#VAR]] to %[[#RETVAL]] : !cir.ptr<!ty_22S22>
// CHECK: }
// CHECK: %[[#RETADDR:]] = cir.get_member %1[0] {name = "x"} : !cir.ptr<!ty_22S22> -> !cir.ptr<!s32i>
// CHECK: %{{.+}} = cir.load %[[#RETADDR]] : cir.ptr <!s32i>, !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; }); }
31 changes: 31 additions & 0 deletions clang/test/CIR/CodeGen/stmt-expr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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:
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 <!ty_22A22>
// CHECK: cir.scope {
// CHECK: %[[#VAR:]] = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["a", init] {alignment = 4 : i64}
// CHECK: cir.call @_ZN1AC1Ev(%[[#VAR]]) : (!cir.ptr<!ty_22A22>) -> ()
// CHECK: cir.call @_ZN1AC1ERS_(%[[#RETVAL]], %[[#VAR]]) : (!cir.ptr<!ty_22A22>, !cir.ptr<!ty_22A22>) -> ()
// TODO(cir): the local VAR should be destroyed here.
// CHECK: }
// CHECK: cir.call @_ZN1A3FooEv(%[[#RETVAL]]) : (!cir.ptr<!ty_22A22>) -> ()
// TODO(cir): the temporary RETVAL should be destroyed here.
// CHECK: }