Skip to content

Commit 6404f4b

Browse files
committed
[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
1 parent 6c76d01 commit 6404f4b

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2814,6 +2814,26 @@ static Instruction *tryToMoveFreeBeforeNullTest(CallInst &FI,
28142814
}
28152815
assert(FreeInstrBB->size() == 1 &&
28162816
"Only the branch instruction should remain");
2817+
2818+
// Now that we've moved the call to free before the NULL check, we have to
2819+
// remove any attributes on its parameter that imply it's non-null, because
2820+
// those attributes might have only been valid because of the NULL check, and
2821+
// we can get miscompiles if we keep them. This is conservative if non-null is
2822+
// also implied by something other than the NULL check, but it's guaranteed to
2823+
// be correct, and the conservativeness won't matter in practice, since the
2824+
// attributes are irrelevant for the call to free itself and the pointer
2825+
// shouldn't be used after the call.
2826+
AttributeList Attrs = FI.getAttributes();
2827+
Attrs = Attrs.removeParamAttribute(FI.getContext(), 0, Attribute::NonNull);
2828+
Attribute Dereferenceable = Attrs.getParamAttr(0, Attribute::Dereferenceable);
2829+
if (Dereferenceable.isValid()) {
2830+
uint64_t Bytes = Dereferenceable.getDereferenceableBytes();
2831+
Attrs = Attrs.removeParamAttribute(FI.getContext(), 0,
2832+
Attribute::Dereferenceable);
2833+
Attrs = Attrs.addDereferenceableOrNullParamAttr(FI.getContext(), 0, Bytes);
2834+
}
2835+
FI.setAttributes(Attrs);
2836+
28172837
return &FI;
28182838
}
28192839

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,78 @@ if.end: ; preds = %entry, %if.then
170170
ret void
171171
}
172172

173+
;; Test that nonnull-implying attributes on the parameter are adjusted when the
174+
;; call is moved, since they may no longer be valid and result in miscompiles if
175+
;; kept unchanged.
176+
define void @test_nonnull_free_move(i8* %foo) minsize {
177+
; CHECK-LABEL: @test_nonnull_free_move(
178+
; CHECK-NEXT: entry:
179+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
180+
; CHECK-NEXT: tail call void @free(i8* [[FOO]])
181+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
182+
; CHECK: if.then:
183+
; CHECK-NEXT: br label [[IF_END]]
184+
; CHECK: if.end:
185+
; CHECK-NEXT: ret void
186+
;
187+
entry:
188+
%tobool = icmp eq i8* %foo, null
189+
br i1 %tobool, label %if.end, label %if.then
190+
191+
if.then: ; preds = %entry
192+
tail call void @free(i8* nonnull %foo)
193+
br label %if.end
194+
195+
if.end: ; preds = %entry, %if.then
196+
ret void
197+
}
198+
199+
define void @test_dereferenceable_free_move(i8* %foo) minsize {
200+
; CHECK-LABEL: @test_dereferenceable_free_move(
201+
; CHECK-NEXT: entry:
202+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
203+
; CHECK-NEXT: tail call void @free(i8* dereferenceable_or_null(4) [[FOO]])
204+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
205+
; CHECK: if.then:
206+
; CHECK-NEXT: br label [[IF_END]]
207+
; CHECK: if.end:
208+
; CHECK-NEXT: ret void
209+
;
210+
entry:
211+
%tobool = icmp eq i8* %foo, null
212+
br i1 %tobool, label %if.end, label %if.then
213+
214+
if.then: ; preds = %entry
215+
tail call void @free(i8* dereferenceable(4) %foo)
216+
br label %if.end
217+
218+
if.end: ; preds = %entry, %if.then
219+
ret void
220+
}
221+
222+
define void @test_nonnull_dereferenceable_free_move(i8* %foo) minsize {
223+
; CHECK-LABEL: @test_nonnull_dereferenceable_free_move(
224+
; CHECK-NEXT: entry:
225+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null
226+
; CHECK-NEXT: tail call void @free(i8* dereferenceable_or_null(16) [[FOO]])
227+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
228+
; CHECK: if.then:
229+
; CHECK-NEXT: br label [[IF_END]]
230+
; CHECK: if.end:
231+
; CHECK-NEXT: ret void
232+
;
233+
entry:
234+
%tobool = icmp eq i8* %foo, null
235+
br i1 %tobool, label %if.end, label %if.then
236+
237+
if.then: ; preds = %entry
238+
tail call void @free(i8* nonnull dereferenceable(16) %foo)
239+
br label %if.end
240+
241+
if.end: ; preds = %entry, %if.then
242+
ret void
243+
}
244+
173245
; The next four tests cover the semantics of the nofree attributes. These
174246
; are thought to be legal transforms, but an implementation thereof has
175247
; been reverted once due to difficult to isolate fallout.

0 commit comments

Comments
 (0)