Skip to content

Commit 12aaa85

Browse files
smeenaitstellar
authored andcommitted
[InstCombine] Remove attributes after hoisting free above null check
If the parameter had been annotated as nonnull because of the null check, we want to remove the attribute, since it may no longer apply and could result in miscompiles if left. Similarly, we also want to remove undef-implying attributes, since they may not apply anymore either. Fixes PR52110. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D111515 (cherry picked from commit 6404f4b)
1 parent 35df3f9 commit 12aaa85

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,6 +2843,26 @@ static Instruction *tryToMoveFreeBeforeNullTest(CallInst &FI,
28432843
}
28442844
assert(FreeInstrBB->size() == 1 &&
28452845
"Only the branch instruction should remain");
2846+
2847+
// Now that we've moved the call to free before the NULL check, we have to
2848+
// remove any attributes on its parameter that imply it's non-null, because
2849+
// those attributes might have only been valid because of the NULL check, and
2850+
// we can get miscompiles if we keep them. This is conservative if non-null is
2851+
// also implied by something other than the NULL check, but it's guaranteed to
2852+
// be correct, and the conservativeness won't matter in practice, since the
2853+
// attributes are irrelevant for the call to free itself and the pointer
2854+
// shouldn't be used after the call.
2855+
AttributeList Attrs = FI.getAttributes();
2856+
Attrs = Attrs.removeParamAttribute(FI.getContext(), 0, Attribute::NonNull);
2857+
Attribute Dereferenceable = Attrs.getParamAttr(0, Attribute::Dereferenceable);
2858+
if (Dereferenceable.isValid()) {
2859+
uint64_t Bytes = Dereferenceable.getDereferenceableBytes();
2860+
Attrs = Attrs.removeParamAttribute(FI.getContext(), 0,
2861+
Attribute::Dereferenceable);
2862+
Attrs = Attrs.addDereferenceableOrNullParamAttr(FI.getContext(), 0, Bytes);
2863+
}
2864+
FI.setAttributes(Attrs);
2865+
28462866
return &FI;
28472867
}
28482868

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -instcombine -S | FileCheck %s
3+
4+
declare noalias i8* @calloc(i32, i32) nounwind
5+
declare noalias i8* @malloc(i32)
6+
declare noalias i8* @aligned_alloc(i32, i32)
7+
declare void @free(i8*)
8+
9+
;; Test that nonnull-implying attributes on the parameter are adjusted when the
10+
;; call is moved, since they may no longer be valid and result in miscompiles if
11+
;; kept unchanged.
12+
define void @test_nonnull_free_move(i8* %foo) minsize {
13+
; CHECK-LABEL: @test_nonnull_free_move(
14+
; CHECK-NEXT: entry:
15+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
16+
; CHECK-NEXT: tail call void @free(i8* [[FOO]])
17+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
18+
; CHECK: if.then:
19+
; CHECK-NEXT: br label [[IF_END]]
20+
; CHECK: if.end:
21+
; CHECK-NEXT: ret void
22+
;
23+
entry:
24+
%tobool = icmp eq i8* %foo, null
25+
br i1 %tobool, label %if.end, label %if.then
26+
27+
if.then: ; preds = %entry
28+
tail call void @free(i8* nonnull %foo)
29+
br label %if.end
30+
31+
if.end: ; preds = %entry, %if.then
32+
ret void
33+
}
34+
35+
define void @test_dereferenceable_free_move(i8* %foo) minsize {
36+
; CHECK-LABEL: @test_dereferenceable_free_move(
37+
; CHECK-NEXT: entry:
38+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
39+
; CHECK-NEXT: tail call void @free(i8* dereferenceable_or_null(4) [[FOO]])
40+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
41+
; CHECK: if.then:
42+
; CHECK-NEXT: br label [[IF_END]]
43+
; CHECK: if.end:
44+
; CHECK-NEXT: ret void
45+
;
46+
entry:
47+
%tobool = icmp eq i8* %foo, null
48+
br i1 %tobool, label %if.end, label %if.then
49+
50+
if.then: ; preds = %entry
51+
tail call void @free(i8* dereferenceable(4) %foo)
52+
br label %if.end
53+
54+
if.end: ; preds = %entry, %if.then
55+
ret void
56+
}
57+
58+
define void @test_nonnull_dereferenceable_free_move(i8* %foo) minsize {
59+
; CHECK-LABEL: @test_nonnull_dereferenceable_free_move(
60+
; CHECK-NEXT: entry:
61+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
62+
; CHECK-NEXT: tail call void @free(i8* dereferenceable_or_null(16) [[FOO]])
63+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
64+
; CHECK: if.then:
65+
; CHECK-NEXT: br label [[IF_END]]
66+
; CHECK: if.end:
67+
; CHECK-NEXT: ret void
68+
;
69+
entry:
70+
%tobool = icmp eq i8* %foo, null
71+
br i1 %tobool, label %if.end, label %if.then
72+
73+
if.then: ; preds = %entry
74+
tail call void @free(i8* nonnull dereferenceable(16) %foo)
75+
br label %if.end
76+
77+
if.end: ; preds = %entry, %if.then
78+
ret void
79+
}

0 commit comments

Comments
 (0)