Skip to content

Commit dedcf88

Browse files
authored
[CIR] Implement NRVO variable cleanup (#170774)
This implements the cleanup handling for C++ NRVO variables. Because exception handling is still incomplete, this doesn't behave any differently with exceptions enabled yet, but the NRVO-specific code for that case is trivial.
1 parent 96c733e commit dedcf88

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
331331
return cir::StoreOp::create(*this, loc, val, dst, isVolatile, align, order);
332332
}
333333

334+
/// Emit a load from an boolean flag variable.
335+
cir::LoadOp createFlagLoad(mlir::Location loc, mlir::Value addr) {
336+
mlir::Type boolTy = getBoolTy();
337+
if (boolTy != mlir::cast<cir::PointerType>(addr.getType()).getPointee())
338+
addr = createPtrBitcast(addr, boolTy);
339+
return createLoad(loc, addr, /*isVolatile=*/false, /*alignment=*/1);
340+
}
341+
334342
cir::StoreOp createFlagStore(mlir::Location loc, bool val, mlir::Value dst) {
335343
mlir::Value flag = getBool(val, loc);
336344
return CIRBaseBuilderTy::createStore(loc, flag, dst);

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ struct MissingFeatures {
227227
static bool countedBySize() { return false; }
228228
static bool cgFPOptionsRAII() { return false; }
229229
static bool checkBitfieldClipping() { return false; }
230-
static bool cleanupDestroyNRVOVariable() { return false; }
231230
static bool cirgenABIInfo() { return false; }
232231
static bool cleanupAfterErrorDiags() { return false; }
233232
static bool cleanupAppendInsts() { return false; }

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,
106106
cir::ConstantOp falseNVRO = builder.getFalse(loc);
107107
Address nrvoFlag = createTempAlloca(falseNVRO.getType(),
108108
CharUnits::One(), loc, "nrvo",
109-
/*arraySize=*/nullptr, &address);
109+
/*arraySize=*/nullptr);
110110
assert(builder.getInsertionBlock());
111111
builder.createStore(loc, falseNVRO, nrvoFlag);
112112

@@ -835,7 +835,24 @@ template <class Derived> struct DestroyNRVOVariable : EHScopeStack::Cleanup {
835835
QualType ty;
836836

837837
void emit(CIRGenFunction &cgf, Flags flags) override {
838-
assert(!cir::MissingFeatures::cleanupDestroyNRVOVariable());
838+
// Along the exceptions path we always execute the dtor.
839+
bool nrvo = flags.isForNormalCleanup() && nrvoFlag;
840+
841+
CIRGenBuilderTy &builder = cgf.getBuilder();
842+
mlir::OpBuilder::InsertionGuard guard(builder);
843+
if (nrvo) {
844+
// If we exited via NRVO, we skip the destructor call.
845+
mlir::Location loc = addr.getPointer().getLoc();
846+
mlir::Value didNRVO = builder.createFlagLoad(loc, nrvoFlag);
847+
mlir::Value notNRVO = builder.createNot(didNRVO);
848+
cir::IfOp::create(builder, loc, notNRVO, /*withElseRegion=*/false,
849+
[&](mlir::OpBuilder &b, mlir::Location) {
850+
static_cast<Derived *>(this)->emitDestructorCall(cgf);
851+
builder.createYield(loc);
852+
});
853+
} else {
854+
static_cast<Derived *>(this)->emitDestructorCall(cgf);
855+
}
839856
}
840857

841858
virtual ~DestroyNRVOVariable() = default;
@@ -851,7 +868,9 @@ struct DestroyNRVOVariableCXX final
851868
const CXXDestructorDecl *dtor;
852869

853870
void emitDestructorCall(CIRGenFunction &cgf) {
854-
assert(!cir::MissingFeatures::cleanupDestroyNRVOVariable());
871+
cgf.emitCXXDestructorCall(dtor, Dtor_Complete,
872+
/*forVirtualBase=*/false,
873+
/*delegating=*/false, addr, ty);
855874
}
856875
};
857876

clang/test/CIR/CodeGen/nrvo.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ NonTrivial test_nrvo() {
7272
// CIR: cir.call @_Z10maybeThrowv() : () -> ()
7373
// CIR: %[[TRUE:.*]] = cir.const #true
7474
// CIR: cir.store{{.*}} %[[TRUE]], %[[NRVO_FLAG]]
75+
// CIR: %[[NRVO_FLAG_VAL:.*]] = cir.load{{.*}} %[[NRVO_FLAG]]
76+
// CIR: %[[NOT_NRVO_VAL:.*]] = cir.unary(not, %[[NRVO_FLAG_VAL]])
77+
// CIR: cir.if %[[NOT_NRVO_VAL]] {
78+
// CIR: cir.call @_ZN10NonTrivialD1Ev(%[[RESULT]])
79+
// CIR: }
7580
// CIR: %[[RET:.*]] = cir.load %[[RESULT]]
7681
// CIR: cir.return %[[RET]]
7782

@@ -81,6 +86,14 @@ NonTrivial test_nrvo() {
8186
// LLVM: store i8 0, ptr %[[NRVO_FLAG]]
8287
// LLVM: call void @_Z10maybeThrowv()
8388
// LLVM: store i8 1, ptr %[[NRVO_FLAG]]
89+
// LLVM: %[[NRVO_VAL:.*]] = load i8, ptr %[[NRVO_FLAG]]
90+
// LLVM: %[[NRVO_VAL_TRUNC:.*]] = trunc i8 %[[NRVO_VAL]] to i1
91+
// LLVM: %[[NOT_NRVO_VAL:.*]] = xor i1 %[[NRVO_VAL_TRUNC]], true
92+
// LLVM: br i1 %[[NOT_NRVO_VAL]], label %[[NRVO_UNUSED:.*]], label %[[NRVO_USED:.*]]
93+
// LLVM: [[NRVO_UNUSED]]:
94+
// LLVM: call void @_ZN10NonTrivialD1Ev(ptr %[[RESULT]])
95+
// LLVM: br label %[[NRVO_USED]]
96+
// LLVM: [[NRVO_USED]]:
8497
// LLVM: %[[RET:.*]] = load %struct.NonTrivial, ptr %[[RESULT]]
8598
// LLVM: ret %struct.NonTrivial %[[RET]]
8699

0 commit comments

Comments
 (0)