Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct MissingFeatures {
static bool lowerAggregateLoadStore() { return false; }
static bool lowerModeOptLevel() { return false; }
static bool maybeHandleStaticInExternC() { return false; }
static bool mergeAllConstants() { return false; }
static bool metaDataNode() { return false; }
static bool moduleNameHash() { return false; }
static bool msabi() { return false; }
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ class Address {
}

clang::CharUnits getAlignment() const { return alignment; }

/// Get the operation which defines this address.
mlir::Operation *getDefiningOp() const {
if (!isValid())
return nullptr;
return getPointer().getDefiningOp();
}

template <typename OpTy> OpTy getDefiningOp() const {
return mlir::dyn_cast_or_null<OpTy>(getDefiningOp());
}
};

} // namespace clang::CIRGen
Expand Down
37 changes: 21 additions & 16 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,27 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
assert(!cir::MissingFeatures::sanitizers());
}

namespace {
struct DestroyObject final : EHScopeStack::Cleanup {
DestroyObject(Address addr, QualType type,
CIRGenFunction::Destroyer *destroyer)
: addr(addr), type(type), destroyer(destroyer) {}

Address addr;
QualType type;
CIRGenFunction::Destroyer *destroyer;

void emit(CIRGenFunction &cgf) override {
cgf.emitDestroy(addr, type, destroyer);
}
};
} // namespace

void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
QualType type, Destroyer *destroyer) {
pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
}

/// Destroys all the elements of the given array, beginning from last to first.
/// The array cannot be zero-length.
///
Expand Down Expand Up @@ -736,22 +757,6 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
llvm_unreachable("Unknown DestructionKind");
}

namespace {
struct DestroyObject final : EHScopeStack::Cleanup {
DestroyObject(Address addr, QualType type,
CIRGenFunction::Destroyer *destroyer)
: addr(addr), type(type), destroyer(destroyer) {}

Address addr;
QualType type;
CIRGenFunction::Destroyer *destroyer;

void emit(CIRGenFunction &cgf) override {
cgf.emitDestroy(addr, type, destroyer);
}
};
} // namespace

/// Enter a destroy cleanup for the given local variable.
void CIRGenFunction::emitAutoVarTypeCleanup(
const CIRGenFunction::AutoVarEmission &emission,
Expand Down
145 changes: 145 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,151 @@ void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address location,
llvm_unreachable("bad evaluation kind");
}

static Address createReferenceTemporary(CIRGenFunction &cgf,
const MaterializeTemporaryExpr *m,
const Expr *inner) {
// TODO(cir): cgf.getTargetHooks();
switch (m->getStorageDuration()) {
case SD_FullExpression:
case SD_Automatic: {
QualType ty = inner->getType();

assert(!cir::MissingFeatures::mergeAllConstants());

// The temporary memory should be created in the same scope as the extending
// declaration of the temporary materialization expression.
cir::AllocaOp extDeclAlloca;
if (const ValueDecl *extDecl = m->getExtendingDecl()) {
auto extDeclAddrIter = cgf.localDeclMap.find(extDecl);
if (extDeclAddrIter != cgf.localDeclMap.end())
extDeclAlloca = extDeclAddrIter->second.getDefiningOp<cir::AllocaOp>();
}
mlir::OpBuilder::InsertPoint ip;
if (extDeclAlloca)
ip = {extDeclAlloca->getBlock(), extDeclAlloca->getIterator()};
return cgf.createMemTemp(ty, cgf.getLoc(m->getSourceRange()),
cgf.getCounterRefTmpAsString(), /*alloca=*/nullptr,
ip);
}
case SD_Thread:
case SD_Static: {
cgf.cgm.errorNYI(
m->getSourceRange(),
"createReferenceTemporary: static/thread storage duration");
return Address::invalid();
}

case SD_Dynamic:
llvm_unreachable("temporary can't have dynamic storage duration");
}
llvm_unreachable("unknown storage duration");
}

static void pushTemporaryCleanup(CIRGenFunction &cgf,
const MaterializeTemporaryExpr *m,
const Expr *e, Address referenceTemporary) {
// Objective-C++ ARC:
// If we are binding a reference to a temporary that has ownership, we
// need to perform retain/release operations on the temporary.
//
// FIXME(ogcg): This should be looking at e, not m.
if (m->getType().getObjCLifetime()) {
cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: ObjCLifetime");
return;
}

CXXDestructorDecl *referenceTemporaryDtor = nullptr;
if (const clang::RecordType *rt = e->getType()
->getBaseElementTypeUnsafe()
->getAs<clang::RecordType>()) {
// Get the destructor for the reference temporary.
auto *classDecl = cast<CXXRecordDecl>(rt->getDecl());
if (!classDecl->hasTrivialDestructor())
referenceTemporaryDtor = classDecl->getDestructor();
}

if (!referenceTemporaryDtor)
return;

// Call the destructor for the temporary.
switch (m->getStorageDuration()) {
case SD_Static:
case SD_Thread:
cgf.cgm.errorNYI(e->getSourceRange(),
"pushTemporaryCleanup: static/thread storage duration");
return;

case SD_FullExpression:
cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),
CIRGenFunction::destroyCXXObject);
break;

case SD_Automatic:
cgf.cgm.errorNYI(e->getSourceRange(),
"pushTemporaryCleanup: automatic storage duration");
break;

case SD_Dynamic:
llvm_unreachable("temporary cannot have dynamic storage duration");
}
}

LValue CIRGenFunction::emitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *m) {
const Expr *e = m->getSubExpr();

assert((!m->getExtendingDecl() || !isa<VarDecl>(m->getExtendingDecl()) ||
!cast<VarDecl>(m->getExtendingDecl())->isARCPseudoStrong()) &&
"Reference should never be pseudo-strong!");

// FIXME: ideally this would use emitAnyExprToMem, however, we cannot do so
// as that will cause the lifetime adjustment to be lost for ARC
auto ownership = m->getType().getObjCLifetime();
if (ownership != Qualifiers::OCL_None &&
ownership != Qualifiers::OCL_ExplicitNone) {
cgm.errorNYI(e->getSourceRange(),
"emitMaterializeTemporaryExpr: ObjCLifetime");
return {};
}

SmallVector<const Expr *, 2> commaLHSs;
SmallVector<SubobjectAdjustment, 2> adjustments;
e = e->skipRValueSubobjectAdjustments(commaLHSs, adjustments);

for (const Expr *ignored : commaLHSs)
emitIgnoredExpr(ignored);

if (isa<OpaqueValueExpr>(e)) {
cgm.errorNYI(e->getSourceRange(),
"emitMaterializeTemporaryExpr: OpaqueValueExpr");
return {};
}

// Create and initialize the reference temporary.
Address object = createReferenceTemporary(*this, m, e);

if (auto var = object.getPointer().getDefiningOp<cir::GlobalOp>()) {
// TODO(cir): add something akin to stripPointerCasts() to ptr above
cgm.errorNYI(e->getSourceRange(), "emitMaterializeTemporaryExpr: GlobalOp");
return {};
} else {
assert(!cir::MissingFeatures::emitLifetimeMarkers());
emitAnyExprToMem(e, object, Qualifiers(), /*isInitializer=*/true);
}
pushTemporaryCleanup(*this, m, e, object);

// Perform derived-to-base casts and/or field accesses, to get from the
// temporary object we created (and, potentially, for which we extended
// the lifetime) to the subobject we're binding the reference to.
if (!adjustments.empty()) {
cgm.errorNYI(e->getSourceRange(),
"emitMaterializeTemporaryExpr: Adjustments");
return {};
}

return makeAddrLValue(object, m->getType(), AlignmentSource::Decl);
}

LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) {
if (e->isFileScope()) {
cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope");
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {

mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }

mlir::Value VisitExprWithCleanups(ExprWithCleanups *e);
mlir::Value VisitCXXNewExpr(const CXXNewExpr *e) {
return cgf.emitCXXNewExpr(e);
}
Expand Down Expand Up @@ -1217,6 +1218,29 @@ mlir::Value ScalarExprEmitter::emitCompoundAssign(
return emitLoadOfLValue(lhs, e->getExprLoc());
}

mlir::Value ScalarExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
mlir::OpBuilder &builder = cgf.builder;

auto scope = cir::ScopeOp::create(
builder, scopeLoc,
/*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Type &yieldTy, mlir::Location loc) {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
builder.getInsertionBlock()};
mlir::Value scopeYieldVal = Visit(e->getSubExpr());
if (scopeYieldVal) {
// Defend against dominance problems caused by jumps out of expression
// evaluation through the shared cleanup block.
lexScope.forceCleanup();
cir::YieldOp::create(builder, loc, scopeYieldVal);
yieldTy = scopeYieldVal.getType();
}
});

return scope.getNumResults() > 0 ? scope->getResult(0) : nullptr;
}

} // namespace

LValue
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
case Expr::CXXDynamicCastExprClass:
case Expr::ImplicitCastExprClass:
return emitCastLValue(cast<CastExpr>(e));
case Expr::MaterializeTemporaryExprClass:
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
}
}

Expand All @@ -811,6 +813,10 @@ static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) {
return std::string(out.str());
}

std::string CIRGenFunction::getCounterRefTmpAsString() {
return getVersionedTmpName("ref.tmp", counterRefTmp++);
}

std::string CIRGenFunction::getCounterAggTmpAsString() {
return getVersionedTmpName("agg.tmp", counterAggTmp++);
}
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,9 @@ class CIRGenFunction : public CIRGenTypeCache {
};

/// Hold counters for incrementally naming temporaries
unsigned counterRefTmp = 0;
unsigned counterAggTmp = 0;
std::string getCounterRefTmpAsString();
std::string getCounterAggTmpAsString();

/// Helpers to convert Clang's SourceLocation to a MLIR Location.
Expand Down Expand Up @@ -604,6 +606,19 @@ class CIRGenFunction : public CIRGenTypeCache {
void popCleanupBlocks(size_t oldCleanupStackDepth);
void popCleanupBlock();

/// Push a cleanup to be run at the end of the current full-expression. Safe
/// against the possibility that we're currently inside a
/// conditionally-evaluated expression.
template <class T, class... As>
void pushFullExprCleanup(CleanupKind kind, As... a) {
// If we're not in a conditional branch, or if none of the
// arguments requires saving, then use the unconditional cleanup.
if (!isInConditionalBranch())
return ehStack.pushCleanup<T>(kind, a...);

cgm.errorNYI("pushFullExprCleanup in conditional branch");
}

/// Enters a new scope for capturing cleanups, all of which
/// will be executed once the scope is exited.
class RunCleanupsScope {
Expand All @@ -619,6 +634,7 @@ class CIRGenFunction : public CIRGenTypeCache {
protected:
CIRGenFunction &cgf;

public:
/// Enter a new cleanup scope.
explicit RunCleanupsScope(CIRGenFunction &cgf)
: performCleanup(true), cgf(cgf) {
Expand Down Expand Up @@ -801,6 +817,9 @@ class CIRGenFunction : public CIRGenTypeCache {

static Destroyer destroyCXXObject;

void pushDestroy(CleanupKind kind, Address addr, QualType type,
Destroyer *destroyer);

Destroyer *getDestroyer(clang::QualType::DestructionKind kind);

/// ----------------------
Expand Down Expand Up @@ -1136,6 +1155,8 @@ class CIRGenFunction : public CIRGenTypeCache {
const clang::FieldDecl *field,
llvm::StringRef fieldName);

LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);

LValue emitMemberExpr(const MemberExpr *e);

/// Given an expression with a pointer type, emit the value and compute our
Expand Down
14 changes: 14 additions & 0 deletions clang/test/CIR/CodeGen/cleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,17 @@ void test_cleanup_nested() {
// CHECK: }
// CHECK: cir.call @_ZN5StrukD1Ev(%[[OUTER]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
// CHECK: cir.return

void use_ref(const Struk &);

void test_expr_with_cleanup() {
use_ref(Struk{});
}

// CHECK: cir.func{{.*}} @_Z22test_expr_with_cleanupv()
// CHECK: cir.scope {
// CHECK: %[[S:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>
// CHECK: cir.call @_Z7use_refRK5Struk(%[[S]])
// CHECK: cir.call @_ZN5StrukD1Ev(%[[S]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
// CHECK: }
// CHECK: cir.return