llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> The `valuesToReload` handling in our `RunCleanupScope::forceCleanup()` function was not taking into account cleanup scopes for deferred conditional cleanups that get created when we call `forceDeactivation()`. This was leading to a CIR verification error in cases where a deferred cleanup was used in an expression that returns a value. This change adds code to spill values ahead of the `forceDeactivation()` call when we see that there are cleanups on the deferred stack. Assisted-by: Cursor / claude-opus-4.7 --- Full diff: https://github.com/llvm/llvm-project/pull/200904.diff 2 Files Affected: - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+59-2) - (added) clang/test/CIR/CodeGen/cleanup-derived-to-base-ref.cpp (+48) ``````````diff diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3d369dc7e7294..1e9be3dc2174e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1198,9 +1198,66 @@ class CIRGenFunction : public CIRGenTypeCache { void forceCleanup(ArrayRef<mlir::Value *> valuesToReload = {}) { assert(performCleanup && "Already forced cleanup"); cgf.didCallStackSave = oldDidCallStackSave; + + // forceDeactivate() can pop cleanup scopes that were pushed with + // deferred deactivation, which moves the insertion point out of the + // cleanup body region. Any caller value defined inside such a body + // would no longer dominate uses past the scope. The downstream + // popCleanupBlocks() handles the spill for any cleanups it pops + // itself, but it cannot help with cleanups that forceDeactivate has + // already popped. Spill those values here, while the insertion point + // is still inside the body, so we can reload them after all popping + // is done. We only spill values whose defining op lives inside a + // cir.cleanup.scope, since values defined outside any cleanup scope + // (e.g. allocas in the entry block) already dominate the post-scope + // insertion point. + const bool hasPendingDeactivations = + cgf.deferredDeactivationCleanupStack.size() > + deactivateCleanups.oldDeactivateCleanupStackSize; + + llvm::SmallVector<Address> tempAllocas; + bool didSpillAny = false; + if (hasPendingDeactivations) { + tempAllocas.reserve(valuesToReload.size()); + for (mlir::Value *valPtr : valuesToReload) { + mlir::Value val = *valPtr; + if (!val || !val.getDefiningOp() || + !val.getDefiningOp()->getParentOfType<cir::CleanupScopeOp>()) { + tempAllocas.push_back(Address::invalid()); + continue; + } + Address temp = cgf.createDefaultAlignTempAlloca( + val.getType(), val.getLoc(), "tmp.exprcleanup"); + tempAllocas.push_back(temp); + cgf.builder.createStore(val.getLoc(), val, temp); + didSpillAny = true; + } + } + deactivateCleanups.forceDeactivate(); - cgf.popCleanupBlocks(cleanupStackDepth, lifetimeExtendedCleanupStackSize, - valuesToReload); + // If we already spilled some of the caller's values, don't ask + // popCleanupBlocks to spill them again. Values we did not pre-spill + // are not inside any cir.cleanup.scope, so they cannot be invalidated + // by either forceDeactivate's or popCleanupBlocks's pops (both only + // pop cir.cleanup.scope ops); they already dominate the post-scope + // insertion point on their own. + if (didSpillAny) { + cgf.popCleanupBlocks(cleanupStackDepth, + lifetimeExtendedCleanupStackSize); + + // Reload the spilled values now that all cleanup popping (and + // promotion of any lifetime-extended cleanups onto the EH stack) is + // done. + for (auto [addr, valPtr] : llvm::zip(tempAllocas, valuesToReload)) { + if (!addr.isValid()) + continue; + *valPtr = cgf.builder.createLoad(valPtr->getLoc(), addr); + } + } else { + cgf.popCleanupBlocks(cleanupStackDepth, + lifetimeExtendedCleanupStackSize, valuesToReload); + } + performCleanup = false; cgf.currentCleanupStackDepth = oldCleanupStackDepth; } diff --git a/clang/test/CIR/CodeGen/cleanup-derived-to-base-ref.cpp b/clang/test/CIR/CodeGen/cleanup-derived-to-base-ref.cpp new file mode 100644 index 0000000000000..8197f849a3d2e --- /dev/null +++ b/clang/test/CIR/CodeGen/cleanup-derived-to-base-ref.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fcxx-exceptions -fexceptions -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fcxx-exceptions -fexceptions -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM + +struct Base {}; +struct Derived : Base { + ~Derived(); +}; + +Derived make_derived(); + +void bind_base_ref_to_derived_temp() { + const Base &r = make_derived(); +} + +// CIR-LABEL: cir.func {{.*}}@_Z29bind_base_ref_to_derived_tempv +// CIR: %[[REF_TMP:.*]] = cir.alloca !rec_Derived, !cir.ptr<!rec_Derived>, ["ref.tmp0"] +// CIR: %[[R:.*]] = cir.alloca !cir.ptr<!rec_Base>, !cir.ptr<!cir.ptr<!rec_Base>>, ["r", init, const] +// CIR: %[[SPILL:.*]] = cir.alloca !cir.ptr<!rec_Base>, !cir.ptr<!cir.ptr<!rec_Base>>, ["tmp.exprcleanup"] +// CIR: cir.call @_Z12make_derivedv() +// CIR: cir.cleanup.scope { +// CIR: %[[BASE:.*]] = cir.base_class_addr %[[REF_TMP]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR: cir.store {{.*}} %[[BASE]], %[[SPILL]] +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: cir.call @_ZN7DerivedD1Ev(%[[REF_TMP]]) +// CIR: cir.yield +// CIR: } +// CIR: cir.cleanup.scope { +// CIR: %[[RELOAD:.*]] = cir.load {{.*}} %[[SPILL]] : !cir.ptr<!cir.ptr<!rec_Base>>, !cir.ptr<!rec_Base> +// CIR: cir.store {{.*}} %[[RELOAD]], %[[R]] +// CIR: cir.yield +// CIR: } cleanup all { +// CIR: cir.call @_ZN7DerivedD1Ev(%[[REF_TMP]]) +// CIR: cir.yield +// CIR: } +// CIR: cir.return + +// LLVM-LABEL: define {{.*}}@_Z29bind_base_ref_to_derived_tempv() +// LLVM: %[[REF_TMP:.*]] = alloca %struct.Derived +// LLVM: %[[R:.*]] = alloca ptr +// LLVM: %[[SPILL:.*]] = alloca ptr +// LLVM: store ptr %[[REF_TMP]], ptr %[[SPILL]] +// LLVM: %[[RELOAD:.*]] = load ptr, ptr %[[SPILL]] +// LLVM: store ptr %[[RELOAD]], ptr %[[R]] +// LLVM: call void @_ZN7DerivedD1Ev(ptr {{.*}} %[[REF_TMP]]) +// LLVM: ret void `````````` </details> https://github.com/llvm/llvm-project/pull/200904 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
