summaryrefslogtreecommitdiffstats
path: root/clang/lib/CodeGen/CGExprAgg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CodeGen/CGExprAgg.cpp')
-rw-r--r--clang/lib/CodeGen/CGExprAgg.cpp98
1 files changed, 51 insertions, 47 deletions
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 394336ee122..5613f8d9700 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -37,23 +37,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
AggValueSlot Dest;
bool IsResultUnused;
- /// We want to use 'dest' as the return slot except under two
- /// conditions:
- /// - The destination slot requires garbage collection, so we
- /// need to use the GC API.
- /// - The destination slot is potentially aliased.
- bool shouldUseDestForReturnSlot() const {
- return !(Dest.requiresGCollection() || Dest.isPotentiallyAliased());
- }
-
- ReturnValueSlot getReturnValueSlot() const {
- if (!shouldUseDestForReturnSlot())
- return ReturnValueSlot();
-
- return ReturnValueSlot(Dest.getAddress(), Dest.isVolatile(),
- IsResultUnused);
- }
-
AggValueSlot EnsureSlot(QualType T) {
if (!Dest.isIgnored()) return Dest;
return CGF.CreateAggTemp(T, "agg.tmp.ensured");
@@ -63,6 +46,15 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Dest = CGF.CreateAggTemp(T, "agg.tmp.ensured");
}
+ // Calls `Fn` with a valid return value slot, potentially creating a temporary
+ // to do so. If a temporary is created, an appropriate copy into `Dest` will
+ // be emitted.
+ //
+ // The given function should take a ReturnValueSlot, and return an RValue that
+ // points to said slot.
+ void withReturnValueSlot(const Expr *E,
+ llvm::function_ref<RValue(ReturnValueSlot)> Fn);
+
public:
AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest, bool IsResultUnused)
: CGF(cgf), Builder(CGF.Builder), Dest(Dest),
@@ -242,34 +234,44 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) {
return Record->hasObjectMember();
}
-/// \brief Perform the final move to DestPtr if for some reason
-/// getReturnValueSlot() didn't use it directly.
-///
-/// The idea is that you do something like this:
-/// RValue Result = EmitSomething(..., getReturnValueSlot());
-/// EmitMoveFromReturnSlot(E, Result);
-///
-/// If nothing interferes, this will cause the result to be emitted
-/// directly into the return value slot. Otherwise, a final move
-/// will be performed.
-void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) {
- // Push destructor if the result is ignored and the type is a C struct that
- // is non-trivial to destroy.
- QualType Ty = E->getType();
- if (Dest.isIgnored() &&
- Ty.isDestructedType() == QualType::DK_nontrivial_c_struct)
- CGF.pushDestroy(Ty.isDestructedType(), src.getAggregateAddress(), Ty);
-
- if (shouldUseDestForReturnSlot()) {
- // Logically, Dest.getAddr() should equal Src.getAggregateAddr().
- // The possibility of undef rvalues complicates that a lot,
- // though, so we can't really assert.
- return;
+void AggExprEmitter::withReturnValueSlot(
+ const Expr *E, llvm::function_ref<RValue(ReturnValueSlot)> EmitCall) {
+ QualType RetTy = E->getType();
+ bool RequiresDestruction =
+ Dest.isIgnored() &&
+ RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct;
+
+ // If it makes no observable difference, save a memcpy + temporary.
+ //
+ // We need to always provide our own temporary if destruction is required.
+ // Otherwise, EmitCall will emit its own, notice that it's "unused", and end
+ // its lifetime before we have the chance to emit a proper destructor call.
+ bool UseTemp = Dest.isPotentiallyAliased() || Dest.requiresGCollection() ||
+ (RequiresDestruction && !Dest.getAddress().isValid());
+
+ Address RetAddr = Address::invalid();
+ if (!UseTemp) {
+ RetAddr = Dest.getAddress();
+ } else {
+ RetAddr = CGF.CreateMemTemp(RetTy);
+ uint64_t Size =
+ CGF.CGM.getDataLayout().getTypeAllocSize(CGF.ConvertTypeForMem(RetTy));
+ if (llvm::Value *LifetimeSizePtr =
+ CGF.EmitLifetimeStart(Size, RetAddr.getPointer()))
+ CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
+ NormalEHLifetimeMarker, RetAddr, LifetimeSizePtr);
}
- // Otherwise, copy from there to the destination.
- assert(Dest.getPointer() != src.getAggregatePointer());
- EmitFinalDestCopy(E->getType(), src);
+ RValue Src =
+ EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), IsResultUnused));
+
+ if (RequiresDestruction)
+ CGF.pushDestroy(RetTy.isDestructedType(), Src.getAggregateAddress(), RetTy);
+
+ if (UseTemp) {
+ assert(Dest.getPointer() != Src.getAggregatePointer());
+ EmitFinalDestCopy(E->getType(), Src);
+ }
}
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
@@ -828,13 +830,15 @@ void AggExprEmitter::VisitCallExpr(const CallExpr *E) {
return;
}
- RValue RV = CGF.EmitCallExpr(E, getReturnValueSlot());
- EmitMoveFromReturnSlot(E, RV);
+ withReturnValueSlot(E, [&](ReturnValueSlot Slot) {
+ return CGF.EmitCallExpr(E, Slot);
+ });
}
void AggExprEmitter::VisitObjCMessageExpr(ObjCMessageExpr *E) {
- RValue RV = CGF.EmitObjCMessageExpr(E, getReturnValueSlot());
- EmitMoveFromReturnSlot(E, RV);
+ withReturnValueSlot(E, [&](ReturnValueSlot Slot) {
+ return CGF.EmitObjCMessageExpr(E, Slot);
+ });
}
void AggExprEmitter::VisitBinComma(const BinaryOperator *E) {
OpenPOWER on IntegriCloud