llvm/llvm/test/Transforms/LICM/hoist-alloc.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=licm -use-dereferenceable-at-point-semantics=0 < %s | FileCheck %s
; RUN: opt -S -passes=licm -use-dereferenceable-at-point-semantics=1 < %s | FileCheck %s

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

declare void @unknown()
declare void @init(ptr nocapture)
declare void @use(i8)

define i8 @test_sink_alloca() {
; CHECK-LABEL: @test_sink_alloca(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [32 x i8], align 1
; CHECK-NEXT:    call void @init(ptr [[A]])
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[ADDR_LE:%.*]] = getelementptr i8, ptr [[A]], i32 31
; CHECK-NEXT:    [[RES_LE:%.*]] = load i8, ptr [[ADDR_LE]], align 1
; CHECK-NEXT:    ret i8 [[RES_LE]]
;
entry:
  %a = alloca [32 x i8]
  call void @init(ptr %a)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a, i32 31
  %res = load i8, ptr %addr
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  ret i8 %res
}

define i8 @test_hoist_alloca() {
; CHECK-LABEL: @test_hoist_alloca(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [32 x i8], align 1
; CHECK-NEXT:    call void @init(ptr [[A]])
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A]], i32 31
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
; CHECK-NEXT:    ret i8 [[RES_LCSSA]]
;
entry:
  %a = alloca [32 x i8]
  call void @init(ptr %a)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  ret i8 %res
}

; The attributes listed here are a) inferred by -O3 from the names
; and b) required for a standalone test.  We're very inconsistent about
; which decisions we drive from TLI vs assume attributes have been infered.
declare void @free(ptr nocapture)
declare noalias ptr @malloc(i64)

define i8 @test_sink_malloc() {
; CHECK-LABEL: @test_sink_malloc(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @malloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[ADDR_LE:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    [[RES_LE:%.*]] = load i8, ptr [[ADDR_LE]], align 1
; CHECK-NEXT:    call void @free(ptr [[A_RAW]])
; CHECK-NEXT:    ret i8 [[RES_LE]]
;
entry:
  ; Mark as nonnull to simplify test
  %a.raw = call nonnull ptr @malloc(i64 32)
  call void @init(ptr %a.raw)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  call void @free(ptr %a.raw)
  ret i8 %res
}

; TODO: We can hoist the load in this case, but only once we have
; some form of context sensitive free analysis.
define i8 @test_hoist_malloc() {
; CHECK-LABEL: @test_hoist_malloc(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @malloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
; CHECK-NEXT:    call void @free(ptr [[A_RAW]])
; CHECK-NEXT:    ret i8 [[RES_LCSSA]]
;
entry:
  %a.raw = call nonnull ptr @malloc(i64 32)
  call void @init(ptr %a.raw)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  call void @free(ptr %a.raw)
  ret i8 %res
}

define i8 @test_hoist_malloc_leak() nofree nosync {
; CHECK-LABEL: @test_hoist_malloc_leak(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @malloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
; CHECK-NEXT:    ret i8 [[RES_LCSSA]]
;
entry:
  %a.raw = call nonnull ptr @malloc(i64 32)
  call void @init(ptr %a.raw)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  ret i8 %res
}

; In this case, we can't hoist the load out of the loop as the memory it
; accesses may have been conditionally freed in a manner correlated with
; whether the load is reached in the loop.
define void @test_hoist_malloc_cond_free(i1 %c) {
; CHECK-LABEL: @test_hoist_malloc_cond_free(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @malloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    br i1 [[C:%.*]], label [[COND_FREE:%.*]], label [[PREHEADER:%.*]]
; CHECK:       cond.free:
; CHECK-NEXT:    call void @free(ptr [[A_RAW]])
; CHECK-NEXT:    br label [[PREHEADER]]
; CHECK:       preheader:
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[PREHEADER]] ]
; CHECK-NEXT:    br i1 [[C]], label [[FOR_END:%.*]], label [[LOOP_LATCH]]
; CHECK:       loop.latch:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    ret void
;
entry:
  %a.raw = call nonnull ptr @malloc(i64 32)
  call void @init(ptr %a.raw)
  br i1 %c, label %cond.free, label %preheader
cond.free:
  call void @free(ptr %a.raw)
  br label %preheader
preheader:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %loop.latch ], [ 0, %preheader ]
  br i1 %c, label %for.end, label %loop.latch

loop.latch:
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  ret void
}

define i8 @test_sink_malloc_cond_free(i1 %c) {
; CHECK-LABEL: @test_sink_malloc_cond_free(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @malloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    br i1 [[C:%.*]], label [[COND_FREE:%.*]], label [[PREHEADER:%.*]]
; CHECK:       cond.free:
; CHECK-NEXT:    call void @free(ptr [[A_RAW]])
; CHECK-NEXT:    br label [[PREHEADER]]
; CHECK:       preheader:
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[PREHEADER]] ]
; CHECK-NEXT:    br i1 [[C]], label [[FOR_END_SPLIT_LOOP_EXIT1:%.*]], label [[LOOP_LATCH]]
; CHECK:       loop.latch:
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END_SPLIT_LOOP_EXIT:%.*]], label [[FOR_BODY]]
; CHECK:       for.end.split.loop.exit:
; CHECK-NEXT:    [[ADDR_LE:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    [[RES_LE:%.*]] = load i8, ptr [[ADDR_LE]], align 1
; CHECK-NEXT:    br label [[FOR_END:%.*]]
; CHECK:       for.end.split.loop.exit1:
; CHECK-NEXT:    [[PHI_PH2:%.*]] = phi i8 [ 0, [[FOR_BODY]] ]
; CHECK-NEXT:    br label [[FOR_END]]
; CHECK:       for.end:
; CHECK-NEXT:    [[PHI:%.*]] = phi i8 [ [[RES_LE]], [[FOR_END_SPLIT_LOOP_EXIT]] ], [ [[PHI_PH2]], [[FOR_END_SPLIT_LOOP_EXIT1]] ]
; CHECK-NEXT:    ret i8 [[PHI]]
;
entry:
  %a.raw = call nonnull ptr @malloc(i64 32)
  call void @init(ptr %a.raw)
  br i1 %c, label %cond.free, label %preheader
cond.free:
  call void @free(ptr %a.raw)
  br label %preheader
preheader:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %loop.latch ], [ 0, %preheader ]
  br i1 %c, label %for.end, label %loop.latch

loop.latch:
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  %phi = phi i8 [%res, %loop.latch], [0, %for.body]
  ret i8 %phi
}

declare noalias ptr @my_alloc(i64) allocsize(0)

; We would need context sensitive reasoning about frees (which we don't
; don't currently have) to hoist the load in this example.
define i8 @test_hoist_allocsize() {
; CHECK-LABEL: @test_hoist_allocsize(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @my_alloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
; CHECK-NEXT:    call void @free(ptr [[A_RAW]])
; CHECK-NEXT:    ret i8 [[RES_LCSSA]]
;
entry:
  %a.raw = call nonnull ptr @my_alloc(i64 32)
  call void @init(ptr %a.raw)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  call void @free(ptr %a.raw)
  ret i8 %res
}

define i8 @test_hoist_allocsize_leak() nofree nosync {
; CHECK-LABEL: @test_hoist_allocsize_leak(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_RAW:%.*]] = call nonnull ptr @my_alloc(i64 32)
; CHECK-NEXT:    call void @init(ptr [[A_RAW]])
; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr i8, ptr [[A_RAW]], i32 31
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @unknown()
; CHECK-NEXT:    [[RES:%.*]] = load i8, ptr [[ADDR]], align 1
; CHECK-NEXT:    call void @use(i8 [[RES]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], 200
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; CHECK:       for.end:
; CHECK-NEXT:    [[RES_LCSSA:%.*]] = phi i8 [ [[RES]], [[FOR_BODY]] ]
; CHECK-NEXT:    ret i8 [[RES_LCSSA]]
;
entry:
  %a.raw = call nonnull ptr @my_alloc(i64 32)
  call void @init(ptr %a.raw)
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  call void @unknown() ;; may throw
  %addr = getelementptr i8, ptr %a.raw, i32 31
  %res = load i8, ptr %addr
  call void @use(i8 %res)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp eq i64 %iv.next, 200
  br i1 %exitcond, label %for.end, label %for.body

for.end:
  ret i8 %res
}