337 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py2; RUN: opt < %s -passes=instcombine -S | FileCheck %s3; PR12014 5target datalayout = "p:32:32:32"6 7define i32 @main(i32 %argc, ptr %argv) {8; CHECK-LABEL: @main(9; CHECK-NEXT: ret i32 010;11 %c_19 = alloca ptr12 %mul = mul i32 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i32), 1013 %malloc_206 = tail call ptr @malloc(i32 %mul)14 store ptr %malloc_206, ptr %c_1915 %tmp_207 = load ptr, ptr %c_1916 tail call void @free(ptr %tmp_207)17 ret i32 018}19 20define i32 @dead_aligned_alloc(i32 %size, i32 %alignment, i8 %value) {21; CHECK-LABEL: @dead_aligned_alloc(22; CHECK-NEXT: ret i32 023;24 %aligned_allocation = tail call ptr @aligned_alloc(i32 %alignment, i32 %size)25 store i8 %value, ptr %aligned_allocation26 tail call void @free(ptr %aligned_allocation)27 ret i32 028}29 30define i1 @aligned_alloc_only_pointe(i32 %size, i32 %alignment, i8 %value) {31; CHECK-LABEL: @aligned_alloc_only_pointe(32; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call ptr @aligned_alloc(i32 [[ALIGNMENT:%.*]], i32 [[SIZE:%.*]])33; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null34; CHECK-NEXT: ret i1 [[CMP]]35;36 %aligned_allocation = tail call ptr @aligned_alloc(i32 %alignment, i32 %size)37 %cmp = icmp ne ptr %aligned_allocation, null38 ret i1 %cmp39}40 41define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_and_value_known_ok(i32 %size, i32 %alignment, i8 %value) {42; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_alignment_and_value_known_ok(43; CHECK-NEXT: ret i1 true44;45 %aligned_allocation = tail call ptr @aligned_alloc(i32 8, i32 32)46 %cmp = icmp ne ptr %aligned_allocation, null47 ret i1 %cmp48}49 50define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2(i32 %size, i32 %alignment, i8 %value) {51; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2(52; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call dereferenceable_or_null(32) ptr @aligned_alloc(i32 3, i32 32)53; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null54; CHECK-NEXT: ret i1 [[CMP]]55;56 %aligned_allocation = tail call ptr @aligned_alloc(i32 3, i32 32)57 %cmp = icmp ne ptr %aligned_allocation, null58 ret i1 %cmp59}60 61define i1 @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment(i32 %size, i32 %alignment, i8 %value) {62; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment(63; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call dereferenceable_or_null(31) ptr @aligned_alloc(i32 8, i32 31)64; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null65; CHECK-NEXT: ret i1 [[CMP]]66;67 %aligned_allocation = tail call ptr @aligned_alloc(i32 8, i32 31)68 %cmp = icmp ne ptr %aligned_allocation, null69 ret i1 %cmp70}71 72; This test uses a aligned allocation function different to @aligned_alloc,73; and should be treated as having @aligned_alloc's constraints on alignment74; and size operands.75define i1 @other_aligned_allocation_function(i32 %size, i32 %alignment, i8 %value) {76; CHECK-LABEL: @other_aligned_allocation_function(77; CHECK-NEXT: ret i1 true78;79 %aligned_allocation = tail call ptr @other_aligned_alloc(i32 %alignment, i32 %size)80 %cmp = icmp ne ptr %aligned_allocation, null81 ret i1 %cmp82}83 84declare noalias ptr @calloc(i32, i32) nounwind allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc"85declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") allocsize(0) "alloc-family"="malloc"86declare noalias ptr @aligned_alloc(i32, i32) allockind("alloc,uninitialized,aligned") allocsize(1) "alloc-family"="malloc"87declare noalias ptr @other_aligned_alloc(i32, i32) allockind("alloc,uninitialized,aligned") allocsize(1) "alloc-family"="malloc"88declare void @free(ptr) allockind("free") "alloc-family"="malloc"89 90define i1 @foo() {91; CHECK-LABEL: @foo(92; CHECK-NEXT: ret i1 false93;94 %m = call ptr @malloc(i32 1)95 %z = icmp eq ptr %m, null96 call void @free(ptr %m)97 ret i1 %z98}99 100declare void @llvm.lifetime.start.p0(ptr)101declare void @llvm.lifetime.end.p0(ptr)102declare i64 @llvm.objectsize.i64(ptr, i1)103declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind104declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind105declare void @llvm.memset.p0.i32(ptr, i8, i32, i1) nounwind106 107define void @test3(ptr %src) {108; CHECK-LABEL: @test3(109; CHECK-NEXT: ret void110;111 %a = call noalias ptr @malloc(i32 10)112 %size = call i64 @llvm.objectsize.i64(ptr %a, i1 true)113 store i8 42, ptr %a114 call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %src, i32 32, i1 false)115 call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %src, i32 32, i1 false)116 call void @llvm.memset.p0.i32(ptr %a, i8 5, i32 32, i1 false)117 %alloc2 = call noalias ptr @calloc(i32 5, i32 7) nounwind118 %z = icmp ne ptr %alloc2, null119 ret void120}121 122;; This used to crash.123define void @test4() {124; CHECK-LABEL: @test4(125; CHECK-NEXT: ret void126;127 %A = call ptr @malloc(i32 16000)128 call void @free(ptr %A)129 ret void130}131 132define void @test5(ptr %ptr, ptr %esc) {133; CHECK-LABEL: @test5(134; CHECK-NEXT: [[C:%.*]] = call dereferenceable_or_null(700) ptr @malloc(i32 700)135; CHECK-NEXT: [[D:%.*]] = call dereferenceable_or_null(700) ptr @malloc(i32 700)136; CHECK-NEXT: [[E:%.*]] = call dereferenceable_or_null(700) ptr @malloc(i32 700)137; CHECK-NEXT: [[F:%.*]] = call dereferenceable_or_null(700) ptr @malloc(i32 700)138; CHECK-NEXT: [[G:%.*]] = call dereferenceable_or_null(700) ptr @malloc(i32 700)139; CHECK-NEXT: store ptr [[C]], ptr [[ESC:%.*]], align 4140; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr [[D]], ptr [[PTR:%.*]], i32 32, i1 true)141; CHECK-NEXT: call void @llvm.memmove.p0.p0.i32(ptr [[E]], ptr [[PTR]], i32 32, i1 true)142; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr [[F]], i8 5, i32 32, i1 true)143; CHECK-NEXT: store volatile i8 4, ptr [[G]], align 1144; CHECK-NEXT: ret void145;146 %a = call ptr @malloc(i32 700)147 %b = call ptr @malloc(i32 700)148 %c = call ptr @malloc(i32 700)149 %d = call ptr @malloc(i32 700)150 %e = call ptr @malloc(i32 700)151 %f = call ptr @malloc(i32 700)152 %g = call ptr @malloc(i32 700)153 call void @llvm.memcpy.p0.p0.i32(ptr %ptr, ptr %a, i32 32, i1 false)154 call void @llvm.memmove.p0.p0.i32(ptr %ptr, ptr %b, i32 32, i1 false)155 store ptr %c, ptr %esc156 call void @llvm.memcpy.p0.p0.i32(ptr %d, ptr %ptr, i32 32, i1 true)157 call void @llvm.memmove.p0.p0.i32(ptr %e, ptr %ptr, i32 32, i1 true)158 call void @llvm.memset.p0.i32(ptr %f, i8 5, i32 32, i1 true)159 store volatile i8 4, ptr %g160 ret void161}162 163;; When a basic block contains only a call to free and this block is accessed164;; through a test of the argument of free against null, move the call in the165;; predecessor block.166;; Using simplifycfg will remove the empty basic block and the branch operation167;; Then, performing a dead elimination will remove the comparison.168;; This is what happens with -O1 and upper.169define void @test6(ptr %foo) minsize {170; CHECK-LABEL: @test6(171; CHECK-NEXT: entry:172; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[FOO:%.*]], null173; CHECK-NEXT: tail call void @free(ptr [[FOO]])174; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]175; CHECK: if.then:176; CHECK-NEXT: br label [[IF_END]]177; CHECK: if.end:178; CHECK-NEXT: ret void179;180;; Call to free moved181;; Block is now empty and may be simplified by simplifycfg182entry:183 %tobool = icmp eq ptr %foo, null184 br i1 %tobool, label %if.end, label %if.then185 186if.then: ; preds = %entry187 tail call void @free(ptr %foo)188 br label %if.end189 190if.end: ; preds = %entry, %if.then191 ret void192}193 194;; Check that the optimization that moves a call to free in its predecessor195;; block (see test6) also happens when noop casts are involved.196define void @test12(ptr %foo) minsize {197; CHECK-LABEL: @test12(198; CHECK-NEXT: entry:199; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[FOO:%.*]], null200; CHECK-NEXT: tail call void @free(ptr [[FOO]])201; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]202; CHECK: if.then:203; CHECK-NEXT: br label [[IF_END]]204; CHECK: if.end:205; CHECK-NEXT: ret void206;207;; Everything before the call to free should have been moved as well.208;; Call to free moved209;; Block is now empty and may be simplified by simplifycfg210entry:211 %tobool = icmp eq ptr %foo, null212 br i1 %tobool, label %if.end, label %if.then213 214if.then: ; preds = %entry215 tail call void @free(ptr %foo)216 br label %if.end217 218if.end: ; preds = %entry, %if.then219 ret void220}221 222;; Test that nonnull-implying attributes on the parameter are adjusted when the223;; call is moved, since they may no longer be valid and result in miscompiles if224;; kept unchanged.225define void @test_nonnull_free_move(ptr %foo) minsize {226; CHECK-LABEL: @test_nonnull_free_move(227; CHECK-NEXT: entry:228; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[FOO:%.*]], null229; CHECK-NEXT: tail call void @free(ptr [[FOO]])230; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]231; CHECK: if.then:232; CHECK-NEXT: br label [[IF_END]]233; CHECK: if.end:234; CHECK-NEXT: ret void235;236entry:237 %tobool = icmp eq ptr %foo, null238 br i1 %tobool, label %if.end, label %if.then239 240if.then: ; preds = %entry241 tail call void @free(ptr nonnull %foo)242 br label %if.end243 244if.end: ; preds = %entry, %if.then245 ret void246}247 248define void @test_dereferenceable_free_move(ptr %foo) minsize {249; CHECK-LABEL: @test_dereferenceable_free_move(250; CHECK-NEXT: entry:251; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[FOO:%.*]], null252; CHECK-NEXT: tail call void @free(ptr dereferenceable_or_null(4) [[FOO]])253; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]254; CHECK: if.then:255; CHECK-NEXT: br label [[IF_END]]256; CHECK: if.end:257; CHECK-NEXT: ret void258;259entry:260 %tobool = icmp eq ptr %foo, null261 br i1 %tobool, label %if.end, label %if.then262 263if.then: ; preds = %entry264 tail call void @free(ptr dereferenceable(4) %foo)265 br label %if.end266 267if.end: ; preds = %entry, %if.then268 ret void269}270 271define void @test_nonnull_dereferenceable_free_move(ptr %foo) minsize {272; CHECK-LABEL: @test_nonnull_dereferenceable_free_move(273; CHECK-NEXT: entry:274; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[FOO:%.*]], null275; CHECK-NEXT: tail call void @free(ptr dereferenceable_or_null(16) [[FOO]])276; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]277; CHECK: if.then:278; CHECK-NEXT: br label [[IF_END]]279; CHECK: if.end:280; CHECK-NEXT: ret void281;282entry:283 %tobool = icmp eq ptr %foo, null284 br i1 %tobool, label %if.end, label %if.then285 286if.then: ; preds = %entry287 tail call void @free(ptr nonnull dereferenceable(16) %foo)288 br label %if.end289 290if.end: ; preds = %entry, %if.then291 ret void292}293 294; The next four tests cover the semantics of the nofree attributes. These295; are thought to be legal transforms, but an implementation thereof has296; been reverted once due to difficult to isolate fallout.297 298; TODO: Freeing a no-free pointer -> %foo must be null299define void @test13(ptr nofree %foo) {300; CHECK-LABEL: @test13(301; CHECK-NEXT: call void @free(ptr [[FOO:%.*]])302; CHECK-NEXT: ret void303;304 call void @free(ptr %foo)305 ret void306}307 308; TODO: Freeing a no-free pointer -> %foo must be null309define void @test14(ptr %foo) nofree {310; CHECK-LABEL: @test14(311; CHECK-NEXT: call void @free(ptr [[FOO:%.*]])312; CHECK-NEXT: ret void313;314 call void @free(ptr %foo)315 ret void316}317 318; TODO: free call marked no-free -> %foo must be null319define void @test15(ptr %foo) {320; CHECK-LABEL: @test15(321; CHECK-NEXT: call void @free(ptr [[FOO:%.*]]) #[[ATTR8:[0-9]+]]322; CHECK-NEXT: ret void323;324 call void @free(ptr %foo) nofree325 ret void326}327 328; TODO: freeing a nonnull nofree pointer -> full UB329define void @test16(ptr nonnull nofree %foo) {330; CHECK-LABEL: @test16(331; CHECK-NEXT: call void @free(ptr [[FOO:%.*]])332; CHECK-NEXT: ret void333;334 call void @free(ptr %foo)335 ret void336}337