Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::StoreOp::create(*this, loc, val, dst, isVolatile, align, order);
}

cir::StoreOp createFlagStore(mlir::Location loc, bool val, mlir::Value dst) {
mlir::Value flag = getBool(val, loc);
return CIRBaseBuilderTy::createStore(loc, flag, dst);
}

[[nodiscard]] cir::GlobalOp createGlobal(mlir::ModuleOp mlirModule,
mlir::Location loc,
mlir::StringRef name,
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ struct MissingFeatures {
static bool countedBySize() { return false; }
static bool cgFPOptionsRAII() { return false; }
static bool checkBitfieldClipping() { return false; }
static bool cleanupDestroyNRVOVariable() { return false; }
static bool cirgenABIInfo() { return false; }
static bool cleanupAfterErrorDiags() { return false; }
static bool cleanupAppendInsts() { return false; }
Expand Down
52 changes: 49 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,22 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,
if (const RecordDecl *rd = ty->getAsRecordDecl()) {
if (const auto *cxxrd = dyn_cast<CXXRecordDecl>(rd);
(cxxrd && !cxxrd->hasTrivialDestructor()) ||
rd->isNonTrivialToPrimitiveDestroy())
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: set NRVO flag");
rd->isNonTrivialToPrimitiveDestroy()) {
// In LLVM: Create a flag that is used to indicate when the NRVO was
// applied to this variable. Set it to zero to indicate that NRVO was
// not applied. For now, use the same approach for CIRGen until we can
// be sure it's worth doing something more aggressive.
cir::ConstantOp falseNVRO = builder.getFalse(loc);
Address nrvoFlag = createTempAlloca(falseNVRO.getType(),
CharUnits::One(), loc, "nrvo",
/*ArraySize=*/nullptr, &address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*ArraySize=*/nullptr, &address);
/*arraySize=*/nullptr, &address);

assert(builder.getInsertionBlock());
builder.createStore(loc, falseNVRO, nrvoFlag);

// Record the NRVO flag for this variable.
nrvoFlags[&d] = nrvoFlag.getPointer();
emission.nrvoFlag = nrvoFlag.getPointer();
}
}
} else {
// A normal fixed sized variable becomes an alloca in the entry block,
Expand Down Expand Up @@ -809,6 +823,35 @@ struct DestroyObject final : EHScopeStack::Cleanup {
}
};

template <class Derived> struct DestroyNRVOVariable : EHScopeStack::Cleanup {
DestroyNRVOVariable(Address addr, QualType type, mlir::Value nrvoFlag)
: nrvoFlag(nrvoFlag), addr(addr), ty(type) {}

mlir::Value nrvoFlag;
Address addr;
QualType ty;

void emit(CIRGenFunction &cgf) override {
assert(!cir::MissingFeatures::cleanupDestroyNRVOVariable());
}

virtual ~DestroyNRVOVariable() = default;
};

struct DestroyNRVOVariableCXX final
: DestroyNRVOVariable<DestroyNRVOVariableCXX> {
DestroyNRVOVariableCXX(Address addr, QualType type,
const CXXDestructorDecl *dtor, mlir::Value nrvoFlag)
: DestroyNRVOVariable<DestroyNRVOVariableCXX>(addr, type, nrvoFlag),
dtor(dtor) {}

const CXXDestructorDecl *dtor;

void emitDestructorCall(CIRGenFunction &cgf) {
assert(!cir::MissingFeatures::cleanupDestroyNRVOVariable());
}
};

struct CallStackRestore final : EHScopeStack::Cleanup {
Address stack;
CallStackRestore(Address stack) : stack(stack) {}
Expand Down Expand Up @@ -965,7 +1008,10 @@ void CIRGenFunction::emitAutoVarTypeCleanup(
// If there's an NRVO flag on the emission, we need a different
// cleanup.
if (emission.nrvoFlag) {
cgm.errorNYI(var->getSourceRange(), "emitAutoVarTypeCleanup: NRVO");
assert(!type->isArrayType());
CXXDestructorDecl *dtor = type->getAsCXXRecordDecl()->getDestructor();
ehStack.pushCleanup<DestroyNRVOVariableCXX>(cleanupKind, addr, type, dtor,
emission.nrvoFlag);
return;
}
// Otherwise, this is handled below.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class CIRGenFunction : public CIRGenTypeCache {

GlobalDecl curSEHParent;

/// A mapping from NRVO variables to the flags used to indicate
/// when the NRVO has been applied to this variable.
llvm::DenseMap<const VarDecl *, mlir::Value> nrvoFlags;

llvm::DenseMap<const clang::ValueDecl *, clang::FieldDecl *>
lambdaCaptureFields;
clang::FieldDecl *lambdaThisCaptureField = nullptr;
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,14 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
if (getContext().getLangOpts().ElideConstructors && s.getNRVOCandidate() &&
s.getNRVOCandidate()->isNRVOVariable()) {
assert(!cir::MissingFeatures::openMP());
assert(!cir::MissingFeatures::nrvo());
// Apply the named return value optimization for this return statement,
// which means doing nothing: the appropriate result has already been
// constructed into the NRVO variable.

// If there is an NRVO flag for this variable, set it to 1 into indicate
// that the cleanup code should not destroy the variable.
if (auto nrvoFlag = nrvoFlags[s.getNRVOCandidate()])
builder.createFlagStore(loc, true, nrvoFlag);
} else if (!rv) {
// No return expression. Do nothing.
} else if (rv->getType()->isVoidType()) {
Expand Down
49 changes: 49 additions & 0 deletions clang/test/CIR/CodeGen/nrvo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,52 @@ struct S f1() {
// OGCG-NEXT: call void @_ZN1SC1Ev(ptr {{.*}} %[[RETVAL]])
// OGCG-NEXT: %[[RET:.*]] = load i64, ptr %[[RETVAL]]
// OGCG-NEXT: ret i64 %[[RET]]

struct NonTrivial {
~NonTrivial();
};

void maybeThrow();

NonTrivial test_nrvo() {
NonTrivial result;
maybeThrow();
return result;
}

// TODO(cir): Handle normal cleanup properly.

// CIR: cir.func {{.*}} @_Z9test_nrvov()
// CIR: %[[RESULT:.*]] = cir.alloca !rec_NonTrivial, !cir.ptr<!rec_NonTrivial>, ["__retval"]
// CIR: %[[NRVO_FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["nrvo"]
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store{{.*}} %[[FALSE]], %[[NRVO_FLAG]]
// CIR: cir.call @_Z10maybeThrowv() : () -> ()
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store{{.*}} %[[TRUE]], %[[NRVO_FLAG]]
// CIR: %[[RET:.*]] = cir.load %[[RESULT]]
// CIR: cir.return %[[RET]]

// LLVM: define {{.*}} %struct.NonTrivial @_Z9test_nrvov()
// LLVM: %[[RESULT:.*]] = alloca %struct.NonTrivial
// LLVM: %[[NRVO_FLAG:.*]] = alloca i8
// LLVM: store i8 0, ptr %[[NRVO_FLAG]]
// LLVM: call void @_Z10maybeThrowv()
// LLVM: store i8 1, ptr %[[NRVO_FLAG]]
// LLVM: %[[RET:.*]] = load %struct.NonTrivial, ptr %[[RESULT]]
// LLVM: ret %struct.NonTrivial %[[RET]]

// OGCG: define {{.*}} void @_Z9test_nrvov(ptr {{.*}} sret(%struct.NonTrivial) {{.*}} %[[RESULT:.*]])
// OGCG: %[[RESULT_ADDR:.*]] = alloca ptr
// OGCG: %[[NRVO_FLAG:.*]] = alloca i1, align 1
// OGCG: store ptr %[[RESULT]], ptr %[[RESULT_ADDR]]
// OGCG: store i1 false, ptr %[[NRVO_FLAG]]
// OGCG: call void @_Z10maybeThrowv()
// OGCG: store i1 true, ptr %[[NRVO_FLAG]]
// OGCG: %[[NRVO_VAL:.*]] = load i1, ptr %[[NRVO_FLAG]]
// OGCG: br i1 %[[NRVO_VAL]], label %[[SKIPDTOR:.*]], label %[[NRVO_UNUSED:.*]]
// OGCG: [[NRVO_UNUSED]]:
// OGCG: call void @_ZN10NonTrivialD1Ev(ptr {{.*}} %[[RESULT]])
// OGCG: br label %[[SKIPDTOR]]
// OGCG: [[SKIPDTOR]]:
// OGCG: ret void
Loading