Skip to content

Commit f39e12a

Browse files
committed
PR34581: Don't remove an 'if (p)' guarding a call to 'operator delete(p)' under -Oz.
Summary: This transformation is correct for a builtin call to 'free(p)', but not for 'operator delete(p)'. There is no guarantee that a user replacement 'operator delete' has no effect when called on a null pointer. However, the principle behind the transformation *is* correct, and can be applied more broadly: a 'delete p' expression is permitted to unconditionally call 'operator delete(p)'. So do that in Clang under -Oz where possible. We do this whether or not 'p' has trivial destruction, since the destruction might turn out to be trivial after inlining, and even for a class-specific (but non-virtual, non-destroying, non-array) 'operator delete'. Reviewers: davide, dnsampaio, rjmccall Reviewed By: dnsampaio Subscribers: hiraditya, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D79378
1 parent 61cd264 commit f39e12a

File tree

4 files changed

+78
-12
lines changed

4 files changed

+78
-12
lines changed

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,10 +1874,13 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
18741874
}
18751875

18761876
/// Emit the code for deleting a single object.
1877-
static void EmitObjectDelete(CodeGenFunction &CGF,
1877+
/// \return \c true if we started emitting UnconditionalDeleteBlock, \c false
1878+
/// if not.
1879+
static bool EmitObjectDelete(CodeGenFunction &CGF,
18781880
const CXXDeleteExpr *DE,
18791881
Address Ptr,
1880-
QualType ElementType) {
1882+
QualType ElementType,
1883+
llvm::BasicBlock *UnconditionalDeleteBlock) {
18811884
// C++11 [expr.delete]p3:
18821885
// If the static type of the object to be deleted is different from its
18831886
// dynamic type, the static type shall be a base class of the dynamic type
@@ -1924,7 +1927,7 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
19241927
if (UseVirtualCall) {
19251928
CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
19261929
Dtor);
1927-
return;
1930+
return false;
19281931
}
19291932
}
19301933
}
@@ -1959,7 +1962,15 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
19591962
}
19601963
}
19611964

1965+
// When optimizing for size, call 'operator delete' unconditionally.
1966+
if (CGF.CGM.getCodeGenOpts().OptimizeSize > 1) {
1967+
CGF.EmitBlock(UnconditionalDeleteBlock);
1968+
CGF.PopCleanupBlock();
1969+
return true;
1970+
}
1971+
19621972
CGF.PopCleanupBlock();
1973+
return false;
19631974
}
19641975

19651976
namespace {
@@ -2036,6 +2047,12 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
20362047
Address Ptr = EmitPointerWithAlignment(Arg);
20372048

20382049
// Null check the pointer.
2050+
//
2051+
// We could avoid this null check if we can determine that the object
2052+
// destruction is trivial and doesn't require an array cookie; we can
2053+
// unconditionally perform the operator delete call in that case. For now, we
2054+
// assume that deleted pointers are null rarely enough that it's better to
2055+
// keep the branch. This might be worth revisiting for a -O0 code size win.
20392056
llvm::BasicBlock *DeleteNotNull = createBasicBlock("delete.notnull");
20402057
llvm::BasicBlock *DeleteEnd = createBasicBlock("delete.end");
20412058

@@ -2081,11 +2098,11 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
20812098

20822099
if (E->isArrayForm()) {
20832100
EmitArrayDelete(*this, E, Ptr, DeleteTy);
2101+
EmitBlock(DeleteEnd);
20842102
} else {
2085-
EmitObjectDelete(*this, E, Ptr, DeleteTy);
2103+
if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
2104+
EmitBlock(DeleteEnd);
20862105
}
2087-
2088-
EmitBlock(DeleteEnd);
20892106
}
20902107

20912108
static bool isGLValueFromPointerDeref(const Expr *E) {

clang/test/CodeGenCXX/delete.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s
1+
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,CHECK-NOSIZE
2+
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - -Oz -disable-llvm-passes | FileCheck %s --check-prefixes=CHECK,CHECK-SIZE
23

34
void t1(int *a) {
45
delete a;
@@ -9,7 +10,19 @@ struct S {
910
};
1011

1112
// POD types.
13+
14+
// CHECK-LABEL: define void @_Z2t3P1S
1215
void t3(S *s) {
16+
// CHECK: icmp {{.*}} null
17+
// CHECK: br i1
18+
19+
// CHECK: bitcast
20+
// CHECK-NEXT: call void @_ZdlPv
21+
22+
// Check the delete is inside the 'if !null' check unless we're optimizing
23+
// for size. FIXME: We could omit the branch entirely in this case.
24+
// CHECK-NOSIZE-NEXT: br
25+
// CHECK-SIZE-NEXT: ret
1326
delete s;
1427
}
1528

@@ -22,7 +35,9 @@ struct T {
2235
// CHECK-LABEL: define void @_Z2t4P1T
2336
void t4(T *t) {
2437
// CHECK: call void @_ZN1TD1Ev
25-
// CHECK-NEXT: bitcast
38+
// CHECK-NOSIZE-NEXT: bitcast
39+
// CHECK-SIZE-NEXT: br
40+
// CHECK-SIZE: bitcast
2641
// CHECK-NEXT: call void @_ZdlPv
2742
delete t;
2843
}
@@ -49,7 +64,9 @@ namespace test0 {
4964
// CHECK-LABEL: define void @_ZN5test04testEPNS_1AE(
5065
void test(A *a) {
5166
// CHECK: call void @_ZN5test01AD1Ev
52-
// CHECK-NEXT: bitcast
67+
// CHECK-NOSIZE-NEXT: bitcast
68+
// CHECK-SIZE-NEXT: br
69+
// CHECK-SIZE: bitcast
5370
// CHECK-NEXT: call void @_ZN5test01AdlEPv
5471
delete a;
5572
}

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,9 +2670,16 @@ Instruction *InstCombiner::visitFree(CallInst &FI) {
26702670
// if (foo) free(foo);
26712671
// into
26722672
// free(foo);
2673-
if (MinimizeSize)
2674-
if (Instruction *I = tryToMoveFreeBeforeNullTest(FI, DL))
2675-
return I;
2673+
//
2674+
// Note that we can only do this for 'free' and not for any flavor of
2675+
// 'operator delete'; there is no 'operator delete' symbol for which we are
2676+
// permitted to invent a call, even if we're passing in a null pointer.
2677+
if (MinimizeSize) {
2678+
LibFunc Func;
2679+
if (TLI.getLibFunc(FI, Func) && TLI.has(Func) && Func == LibFunc_free)
2680+
if (Instruction *I = tryToMoveFreeBeforeNullTest(FI, DL))
2681+
return I;
2682+
}
26762683

26772684
return nullptr;
26782685
}

llvm/test/Transforms/InstCombine/malloc-free-delete.ll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,31 @@ if.end: ; preds = %entry, %if.then
140140
ret void
141141
}
142142

143+
; Same optimization with even a builtin 'operator delete' would be
144+
; incorrect in general.
145+
; 'if (p) delete p;' cannot result in a call to 'operator delete(0)'.
146+
define void @test6a(i8* %foo) minsize {
147+
; CHECK-LABEL: @test6a(
148+
; CHECK-NEXT: entry:
149+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
150+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
151+
; CHECK: if.then:
152+
; CHECK-NEXT: tail call void @_ZdlPv(i8* [[FOO]])
153+
; CHECK-NEXT: br label [[IF_END]]
154+
; CHECK: if.end:
155+
; CHECK-NEXT: ret void
156+
entry:
157+
%tobool = icmp eq i8* %foo, null
158+
br i1 %tobool, label %if.end, label %if.then
159+
160+
if.then: ; preds = %entry
161+
tail call void @_ZdlPv(i8* %foo) builtin
162+
br label %if.end
163+
164+
if.end: ; preds = %entry, %if.then
165+
ret void
166+
}
167+
143168
declare i8* @_ZnwmRKSt9nothrow_t(i64, i8*) nobuiltin
144169
declare void @_ZdlPvRKSt9nothrow_t(i8*, i8*) nobuiltin
145170
declare i32 @__gxx_personality_v0(...)

0 commit comments

Comments
 (0)