From 9969458b1673648394fce3f50f3f4a84071c7ca7 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 6 Jul 2012 23:09:25 +0000 Subject: [PATCH] teach instcombine to remove allocated buffers even if there are stores, memcpy/memmove/memset, and objectsize users. This means we can do cheap DSE for heap memory. Nothing is done if the pointer excapes or has a load. The churn in the tests is mostly due to objectsize, since we want to make sure we don't delete the malloc call before evaluating the objectsize (otherwise it becomes -1/0) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159876 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../InstCombine/InstructionCombining.cpp | 27 +++++++++- test/Transforms/InstCombine/badmalloc.ll | 23 +++++++- test/Transforms/InstCombine/invoke.ll | 18 +++++++ .../InstCombine/malloc-free-delete.ll | 53 +++++++++++++++++-- test/Transforms/InstCombine/objsize-64.ll | 10 ++-- test/Transforms/InstCombine/objsize.ll | 14 ++--- 6 files changed, 128 insertions(+), 17 deletions(-) diff --git a/lib/Transforms/InstCombine/InstructionCombining.cpp b/lib/Transforms/InstCombine/InstructionCombining.cpp index c5124bf7b28..bcf69183bba 100644 --- a/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1137,12 +1137,29 @@ static bool IsOnlyNullComparedAndFreed(Value *V, SmallVectorImpl &Users, } } if (IntrinsicInst *II = dyn_cast(U)) { - if (II->getIntrinsicID() == Intrinsic::lifetime_start || - II->getIntrinsicID() == Intrinsic::lifetime_end) { + switch (II->getIntrinsicID()) { + default: return false; + case Intrinsic::memmove: + case Intrinsic::memcpy: + case Intrinsic::memset: { + MemIntrinsic *MI = cast(II); + if (MI->isVolatile() || MI->getRawDest() != V) + return false; + } + // fall through + case Intrinsic::objectsize: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: Users.push_back(II); continue; } } + if (StoreInst *SI = dyn_cast(U)) { + if (SI->isVolatile() || SI->getPointerOperand() != V) + return false; + Users.push_back(SI); + continue; + } return false; } return true; @@ -1164,6 +1181,12 @@ Instruction *InstCombiner::visitMalloc(Instruction &MI) { C->isFalseWhenEqual())); } else if (isa(I) || isa(I)) { ReplaceInstUsesWith(*I, UndefValue::get(I->getType())); + } else if (IntrinsicInst *II = dyn_cast(I)) { + if (II->getIntrinsicID() == Intrinsic::objectsize) { + ConstantInt *CI = cast(II->getArgOperand(1)); + uint64_t DontKnow = CI->isZero() ? -1ULL : 0; + ReplaceInstUsesWith(*I, ConstantInt::get(I->getType(), DontKnow)); + } } EraseInstFromFunction(*I); } diff --git a/test/Transforms/InstCombine/badmalloc.ll b/test/Transforms/InstCombine/badmalloc.ll index f5a623dc77c..3abe28aede5 100644 --- a/test/Transforms/InstCombine/badmalloc.ll +++ b/test/Transforms/InstCombine/badmalloc.ll @@ -16,5 +16,26 @@ define i1 @test1() { ret i1 %B ; CHECK: @test1 -; CHECK: ret i1 %B +; CHECK: ret i1 false +} + +; CHECK: @test2 +define noalias i8* @test2() nounwind { +entry: +; CHECK: @malloc + %A = call noalias i8* @malloc(i64 4) nounwind +; CHECK: icmp eq + %tobool = icmp eq i8* %A, null +; CHECK: br i1 + br i1 %tobool, label %return, label %if.end + +if.end: +; CHECK: store + store i8 7, i8* %A + br label %return + +return: +; CHECK: phi + %retval.0 = phi i8* [ %A, %if.end ], [ null, %entry ] + ret i8* %retval.0 } diff --git a/test/Transforms/InstCombine/invoke.ll b/test/Transforms/InstCombine/invoke.ll index ea3564dae03..04eaf86a287 100644 --- a/test/Transforms/InstCombine/invoke.ll +++ b/test/Transforms/InstCombine/invoke.ll @@ -4,6 +4,7 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 declare i32 @__gxx_personality_v0(...) declare void @__cxa_call_unexpected(i8*) declare i64 @llvm.objectsize.i64(i8*, i1) nounwind readonly +declare i8* @_Znwm(i64) ; CHECK: @f1 @@ -45,3 +46,20 @@ lpad: tail call void @__cxa_call_unexpected(i8* %2) noreturn nounwind unreachable } + +; CHECK: @f3 +define void @f3() nounwind uwtable ssp { +; CHECK: invoke void @llvm.donothing() + %call = invoke noalias i8* @_Znwm(i64 13) + to label %invoke.cont unwind label %lpad + +invoke.cont: + ret void + +lpad: + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + filter [0 x i8*] zeroinitializer + %2 = extractvalue { i8*, i32 } %1, 0 + tail call void @__cxa_call_unexpected(i8* %2) noreturn nounwind + unreachable +} diff --git a/test/Transforms/InstCombine/malloc-free-delete.ll b/test/Transforms/InstCombine/malloc-free-delete.ll index eae973df0a5..4e3217dc2d9 100644 --- a/test/Transforms/InstCombine/malloc-free-delete.ll +++ b/test/Transforms/InstCombine/malloc-free-delete.ll @@ -1,17 +1,17 @@ ; RUN: opt < %s -instcombine -S | FileCheck %s ; PR1201 define i32 @main(i32 %argc, i8** %argv) { +; CHECK: @main %c_19 = alloca i8* %malloc_206 = tail call i8* @malloc(i32 mul (i32 ptrtoint (i8* getelementptr (i8* null, i32 1) to i32), i32 10)) store i8* %malloc_206, i8** %c_19 %tmp_207 = load i8** %c_19 tail call void @free(i8* %tmp_207) ret i32 0 -; CHECK-NOT: malloc -; CHECK-NOT: free -; CHECK: ret i32 0 +; CHECK-NEXT: ret i32 0 } +declare noalias i8* @calloc(i32, i32) nounwind declare noalias i8* @malloc(i32) declare void @free(i8*) @@ -26,13 +26,24 @@ define i1 @foo() { declare void @llvm.lifetime.start(i64, i8*) declare void @llvm.lifetime.end(i64, i8*) +declare i64 @llvm.objectsize.i64(i8*, i1) +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind +declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind +declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i32, i1) nounwind -define void @test3() { +define void @test3(i8* %src) { ; CHECK: @test3 ; CHECK-NEXT: ret void %a = call noalias i8* @malloc(i32 10) call void @llvm.lifetime.start(i64 10, i8* %a) call void @llvm.lifetime.end(i64 10, i8* %a) + %size = call i64 @llvm.objectsize.i64(i8* %a, i1 true) + store i8 42, i8* %a + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %src, i32 32, i32 1, i1 false) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %src, i32 32, i32 1, i1 false) + call void @llvm.memset.p0i8.i32(i8* %a, i8 5, i32 32, i32 1, i1 false) + %alloc2 = call noalias i8* @calloc(i32 5, i32 7) nounwind + %z = icmp ne i8* %alloc2, null ret void } @@ -46,3 +57,37 @@ define void @test4() { call void @free(i8* %C) ret void } + +; CHECK: @test5 +define void @test5(i8* %ptr, i8** %esc) { +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call i8* @malloc +; CHECK-NEXT: call void @llvm.memcpy +; CHECK-NEXT: call void @llvm.memmove +; CHECK-NEXT: store +; CHECK-NEXT: call void @llvm.memcpy +; CHECK-NEXT: call void @llvm.memmove +; CHECK-NEXT: call void @llvm.memset +; CHECK-NEXT: store volatile +; CHECK-NEXT: ret + %a = call i8* @malloc(i32 700) + %b = call i8* @malloc(i32 700) + %c = call i8* @malloc(i32 700) + %d = call i8* @malloc(i32 700) + %e = call i8* @malloc(i32 700) + %f = call i8* @malloc(i32 700) + %g = call i8* @malloc(i32 700) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr, i8* %a, i32 32, i32 1, i1 false) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %b, i32 32, i32 1, i1 false) + store i8* %c, i8** %esc + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %d, i8* %ptr, i32 32, i32 1, i1 true) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %e, i8* %ptr, i32 32, i32 1, i1 true) + call void @llvm.memset.p0i8.i32(i8* %f, i8 5, i32 32, i32 1, i1 true) + store volatile i8 4, i8* %g + ret void +} diff --git a/test/Transforms/InstCombine/objsize-64.ll b/test/Transforms/InstCombine/objsize-64.ll index 4993063d1cc..530e1234b4a 100644 --- a/test/Transforms/InstCombine/objsize-64.ll +++ b/test/Transforms/InstCombine/objsize-64.ll @@ -8,23 +8,25 @@ declare void @__cxa_call_unexpected(i8*) declare i64 @llvm.objectsize.i64(i8*, i1) nounwind readonly ; CHECK: @f1 -define i64 @f1() { +define i64 @f1(i8 **%esc) { %call = call i8* @malloc(i32 4) + store i8* %call, i8** %esc %size = call i64 @llvm.objectsize.i64(i8* %call, i1 false) -; CHECK-NEXT: ret i64 4 +; CHECK: ret i64 4 ret i64 %size } ; CHECK: @f2 -define i64 @f2() nounwind uwtable ssp { +define i64 @f2(i8** %esc) nounwind uwtable ssp { entry: -; CHECK: invoke void @llvm.donothing() +; CHECK: invoke noalias i8* @_Znwm(i64 13) %call = invoke noalias i8* @_Znwm(i64 13) to label %invoke.cont unwind label %lpad invoke.cont: ; CHECK: ret i64 13 + store i8* %call, i8** %esc %0 = tail call i64 @llvm.objectsize.i64(i8* %call, i1 false) ret i64 %0 diff --git a/test/Transforms/InstCombine/objsize.ll b/test/Transforms/InstCombine/objsize.ll index 2d2d6c286df..ac488faea37 100644 --- a/test/Transforms/InstCombine/objsize.ll +++ b/test/Transforms/InstCombine/objsize.ll @@ -121,7 +121,7 @@ entry: ; rdar://7782496 @s = external global i8* -define void @test5(i32 %n) nounwind ssp { +define i8* @test5(i32 %n) nounwind ssp { ; CHECK: @test5 entry: %0 = tail call noalias i8* @malloc(i32 20) nounwind @@ -130,7 +130,7 @@ entry: ; CHECK-NOT: @llvm.objectsize ; CHECK: @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* %1, i32 10, i32 1, i1 false) %3 = tail call i8* @__memcpy_chk(i8* %0, i8* %2, i32 10, i32 %1) nounwind - ret void + ret i8* %0 } define void @test6(i32 %n) nounwind ssp { @@ -149,22 +149,24 @@ declare i8* @__memset_chk(i8*, i32, i32, i32) nounwind declare noalias i8* @malloc(i32) nounwind -define i32 @test7() { +define i32 @test7(i8** %esc) { ; CHECK: @test7 %alloc = call noalias i8* @malloc(i32 48) nounwind + store i8* %alloc, i8** %esc %gep = getelementptr inbounds i8* %alloc, i32 16 %objsize = call i32 @llvm.objectsize.i32(i8* %gep, i1 false) nounwind readonly -; CHECK-NEXT: ret i32 32 +; CHECK: ret i32 32 ret i32 %objsize } declare noalias i8* @calloc(i32, i32) nounwind -define i32 @test8() { +define i32 @test8(i8** %esc) { ; CHECK: @test8 %alloc = call noalias i8* @calloc(i32 5, i32 7) nounwind + store i8* %alloc, i8** %esc %gep = getelementptr inbounds i8* %alloc, i32 5 %objsize = call i32 @llvm.objectsize.i32(i8* %gep, i1 false) nounwind readonly -; CHECK-NEXT: ret i32 30 +; CHECK: ret i32 30 ret i32 %objsize }