This patch fixes a couple of places where memprof-related metadata (!memprof and !callsite) were being dropped, and one place where PGO metadata (!prof) was being dropped. All were due to instances of combineMetadata() being invoked. That function drops all metadata not in the list provided by the client, and also drops any not in its switch statement. Memprof metadata needed a case in the combineMetadata switch statement. For now we simply keep the metadata of the instruction being kept, which doesn't retain all the profile information when two calls with memprof metadata are being combined, but at least retains some. For the memprof metadata being dropped during call CSE, add memprof and callsite metadata to the list of known ids in combineMetadataForCSE. Neither memprof nor regular prof metadata were in the list of known ids for the callsite in MemCpyOptimizer, which was added to combine AA metadata after optimization of byval arguments fed by memcpy instructions, and similar types of optimizations of memcpy uses. There is one other callsite of combineMetadata, but it is only invoked on load instructions, which do not carry these types of metadata.
918 lines
37 KiB
LLVM
918 lines
37 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -passes=memcpyopt,dse -S -verify-memoryssa | FileCheck %s
|
|
|
|
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
|
target triple = "i686-apple-darwin9"
|
|
|
|
%0 = type { x86_fp80, x86_fp80 }
|
|
%1 = type { i32, i32 }
|
|
|
|
@C = external constant [0 x i8]
|
|
|
|
declare void @llvm.memcpy.p1.p0.i64(ptr addrspace(1) nocapture, ptr nocapture, i64, i1) nounwind
|
|
declare void @llvm.memcpy.p0.p1.i64(ptr nocapture, ptr addrspace(1) nocapture, i64, i1) nounwind
|
|
declare void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) nocapture, ptr addrspace(1) nocapture, i64, i1) nounwind
|
|
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind
|
|
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
|
|
declare void @llvm.memcpy.inline.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind
|
|
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1) nounwind
|
|
|
|
; Check that one of the memcpy's are removed.
|
|
;; FIXME: PR 8643 We should be able to eliminate the last memcpy here.
|
|
define void @test1(ptr sret(%0) %agg.result, x86_fp80 %z.0, x86_fp80 %z.1) nounwind {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[TMP2:%.*]] = alloca [[TMP0:%.*]], align 16
|
|
; CHECK-NEXT: [[MEMTMP:%.*]] = alloca [[TMP0]], align 16
|
|
; CHECK-NEXT: [[TMP5:%.*]] = fsub x86_fp80 0xK80000000000000000000, [[Z_1:%.*]]
|
|
; CHECK-NEXT: call void @ccoshl(ptr sret([[TMP0]]) [[TMP2]], x86_fp80 [[TMP5]], x86_fp80 [[Z_0:%.*]]) #[[ATTR2:[0-9]+]]
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[AGG_RESULT:%.*]], ptr align 16 [[TMP2]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%tmp2 = alloca %0
|
|
%memtmp = alloca %0, align 16
|
|
%tmp5 = fsub x86_fp80 0xK80000000000000000000, %z.1
|
|
call void @ccoshl(ptr sret(%0) %memtmp, x86_fp80 %tmp5, x86_fp80 %z.0) nounwind
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %tmp2, ptr align 16 %memtmp, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 16 %tmp2, i32 32, i1 false)
|
|
ret void
|
|
}
|
|
|
|
declare void @ccoshl(ptr nocapture sret(%0), x86_fp80, x86_fp80) nounwind
|
|
|
|
|
|
; The intermediate alloca and one of the memcpy's should be eliminated, the
|
|
; other should be related with a memmove.
|
|
define void @test2(ptr %P, ptr %Q) nounwind {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; The intermediate alloca and one of the memcpy's should be eliminated, the
|
|
; other should be related with a memcpy.
|
|
define void @test2_constant(ptr %Q) nounwind {
|
|
; CHECK-LABEL: @test2_constant(
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 @C, i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 @C, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; The intermediate alloca and one of the memcpy's should be eliminated, the
|
|
; other should be related with a memcpy.
|
|
define void @test2_memcpy(ptr noalias %P, ptr noalias %Q) nounwind {
|
|
; CHECK-LABEL: @test2_memcpy(
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; Same as @test2_memcpy, but the remaining memcpy should remain non-inline even
|
|
; if the one eliminated was inline.
|
|
define void @test3_memcpy(ptr noalias %P, ptr noalias %Q) nounwind {
|
|
; CHECK-LABEL: @test3_memcpy(
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; Same as @test2_memcpy, but the remaining memcpy should remain inline even
|
|
; if the one eliminated was not inline.
|
|
define void @test4_memcpy(ptr noalias %P, ptr noalias %Q) nounwind {
|
|
; CHECK-LABEL: @test4_memcpy(
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; Same as @test2_memcpy, and the inline-ness should be preserved.
|
|
define void @test5_memcpy(ptr noalias %P, ptr noalias %Q) nounwind {
|
|
; CHECK-LABEL: @test5_memcpy(
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca %0, align 16
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
|
|
}
|
|
|
|
; Similar to test5_memcpy, but without noalias; check that memcpy.inline is not folded into memmove.
|
|
define void @test6_memcpy(ptr %src, ptr %dest) nounwind {
|
|
; CHECK-LABEL: @test6_memcpy(
|
|
; CHECK-NEXT: [[TMP:%.*]] = alloca [16 x i8], align 1
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 [[TMP]], ptr align 1 [[SRC:%.*]], i32 16, i1 false)
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 [[DEST:%.*]], ptr align 1 [[TMP]], i32 16, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca [16 x i8], align 1
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %tmp, ptr align 1 %src, i32 16, i1 false)
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %dest, ptr align 1 %tmp, i32 16, i1 false)
|
|
ret void
|
|
}
|
|
|
|
; When forwarding to memcpy(arg+1, arg+1), we don't need to create this memcpy.
|
|
define void @test6_memcpy_forward_back(ptr %arg) nounwind {
|
|
; CHECK-LABEL: @test6_memcpy_forward_back(
|
|
; CHECK-NEXT: [[DEST:%.*]] = getelementptr inbounds i8, ptr [[ARG:%.*]], i64 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca [16 x i8], align 1
|
|
%src = getelementptr inbounds i8, ptr %arg, i64 1
|
|
%dest = getelementptr inbounds i8, ptr %arg, i64 1
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %tmp, ptr align 1 %src, i32 16, i1 false)
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %dest, ptr align 1 %tmp, i32 16, i1 false)
|
|
ret void
|
|
}
|
|
|
|
; We have to retain this `memcpy(arg+2, arg+1)` forwarding.
|
|
define void @test6_memcpy_forward_not_back(ptr %arg) nounwind {
|
|
; CHECK-LABEL: @test6_memcpy_forward_not_back(
|
|
; CHECK-NEXT: [[TMP:%.*]] = alloca [16 x i8], align 1
|
|
; CHECK-NEXT: [[SRC:%.*]] = getelementptr inbounds i8, ptr [[ARG:%.*]], i64 1
|
|
; CHECK-NEXT: [[DEST:%.*]] = getelementptr inbounds i8, ptr [[ARG]], i64 2
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 [[TMP]], ptr align 1 [[SRC]], i32 16, i1 false)
|
|
; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 [[DEST]], ptr align 1 [[TMP]], i32 16, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca [16 x i8], align 1
|
|
%src = getelementptr inbounds i8, ptr %arg, i64 1
|
|
%dest = getelementptr inbounds i8, ptr %arg, i64 2
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %tmp, ptr align 1 %src, i32 16, i1 false)
|
|
call void @llvm.memcpy.inline.p0.p0.i32(ptr align 1 %dest, ptr align 1 %tmp, i32 16, i1 false)
|
|
ret void
|
|
}
|
|
|
|
@x = external global %0
|
|
|
|
define void @test3(ptr noalias writable sret(%0) %agg.result) nounwind {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: [[X_0:%.*]] = alloca [[TMP0:%.*]], align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[AGG_RESULT:%.*]], ptr align 16 @x, i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%x.0 = alloca %0
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %x.0, ptr align 16 @x, i32 32, i1 false)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 16 %x.0, i32 32, i1 false)
|
|
ret void
|
|
}
|
|
|
|
|
|
; PR8644
|
|
define void @test4(ptr %P) {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[P:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%A = alloca %1
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %A, ptr align 4 %P, i64 8, i1 false)
|
|
call void @test4a(ptr align 1 byval(i8) %A)
|
|
ret void
|
|
}
|
|
|
|
; Make sure we don't remove the memcpy if the source address space doesn't match the byval argument
|
|
define void @test4_addrspace(ptr addrspace(1) %P) {
|
|
; CHECK-LABEL: @test4_addrspace(
|
|
; CHECK-NEXT: [[A1:%.*]] = alloca [[TMP1:%.*]], align 8
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 4 [[A1]], ptr addrspace(1) align 4 [[P:%.*]], i64 8, i1 false)
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[A1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a1 = alloca %1
|
|
call void @llvm.memcpy.p0.p1.i64(ptr align 4 %a1, ptr addrspace(1) align 4 %P, i64 8, i1 false)
|
|
call void @test4a(ptr align 1 byval(i8) %a1)
|
|
ret void
|
|
}
|
|
|
|
define void @test4_write_between(ptr %P) {
|
|
; CHECK-LABEL: @test4_write_between(
|
|
; CHECK-NEXT: [[A1:%.*]] = alloca [[TMP1:%.*]], align 8
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A1]], ptr align 4 [[P:%.*]], i64 8, i1 false)
|
|
; CHECK-NEXT: store i8 0, ptr [[A1]], align 1
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[A1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a1 = alloca %1
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a1, ptr align 4 %P, i64 8, i1 false)
|
|
store i8 0, ptr %a1
|
|
call void @test4a(ptr align 1 byval(i8) %a1)
|
|
ret void
|
|
}
|
|
|
|
define i8 @test4_read_between(ptr %P) {
|
|
; CHECK-LABEL: @test4_read_between(
|
|
; CHECK-NEXT: [[A1:%.*]] = alloca [[TMP1:%.*]], align 8
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A1]], ptr align 4 [[P:%.*]], i64 8, i1 false)
|
|
; CHECK-NEXT: [[X:%.*]] = load i8, ptr [[A1]], align 1
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[P]])
|
|
; CHECK-NEXT: ret i8 [[X]]
|
|
;
|
|
%a1 = alloca %1
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a1, ptr align 4 %P, i64 8, i1 false)
|
|
%x = load i8, ptr %a1
|
|
call void @test4a(ptr align 1 byval(i8) %a1)
|
|
ret i8 %x
|
|
}
|
|
|
|
define void @test4_non_local(ptr %P, i1 %c) {
|
|
; CHECK-LABEL: @test4_non_local(
|
|
; CHECK-NEXT: br i1 [[C:%.*]], label [[CALL:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: call:
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[P:%.*]])
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a1 = alloca %1
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a1, ptr align 4 %P, i64 8, i1 false)
|
|
br i1 %c, label %call, label %exit
|
|
|
|
call:
|
|
call void @test4a(ptr align 1 byval(i8) %a1)
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
declare void @test4a(ptr align 1 byval(i8))
|
|
|
|
%struct.S = type { i128, [4 x i8]}
|
|
|
|
@sS = external global %struct.S, align 16
|
|
|
|
declare void @test5a(ptr align 16 byval(%struct.S)) nounwind ssp
|
|
|
|
|
|
; rdar://8713376 - This memcpy can't be eliminated.
|
|
define i32 @test5(i32 %x) nounwind ssp {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[Y:%.*]] = alloca [[STRUCT_S:%.*]], align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[Y]], ptr align 16 @sS, i64 32, i1 false)
|
|
; CHECK-NEXT: [[A:%.*]] = getelementptr [[STRUCT_S]], ptr [[Y]], i64 0, i32 1, i64 0
|
|
; CHECK-NEXT: store i8 4, ptr [[A]], align 1
|
|
; CHECK-NEXT: call void @test5a(ptr byval([[STRUCT_S]]) align 16 [[Y]])
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%y = alloca %struct.S, align 16
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 16 %y, ptr align 16 @sS, i64 32, i1 false)
|
|
%a = getelementptr %struct.S, ptr %y, i64 0, i32 1, i64 0
|
|
store i8 4, ptr %a
|
|
call void @test5a(ptr align 16 byval(%struct.S) %y)
|
|
ret i32 0
|
|
}
|
|
|
|
;; Noop memcpy should be zapped.
|
|
define void @test6(ptr %P) {
|
|
; CHECK-LABEL: @test6(
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %P, ptr align 4 %P, i64 8, i1 false)
|
|
ret void
|
|
}
|
|
|
|
|
|
; PR9794 - Should forward memcpy into byval argument even though the memcpy
|
|
; isn't itself 8 byte aligned.
|
|
%struct.p = type { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }
|
|
|
|
define i32 @test7(ptr nocapture align 8 byval(%struct.p) %q) nounwind ssp {
|
|
; CHECK-LABEL: @test7(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[CALL:%.*]] = call i32 @g(ptr byval([[STRUCT_P:%.*]]) align 8 [[Q:%.*]]) #[[ATTR2]]
|
|
; CHECK-NEXT: ret i32 [[CALL]]
|
|
;
|
|
entry:
|
|
%agg.tmp = alloca %struct.p, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %q, i64 48, i1 false)
|
|
%call = call i32 @g(ptr align 8 byval(%struct.p) %agg.tmp) nounwind
|
|
ret i32 %call
|
|
}
|
|
|
|
declare i32 @g(ptr align 8 byval(%struct.p))
|
|
|
|
|
|
; PR11142 - When looking for a memcpy-memcpy dependency, don't get stuck on
|
|
; instructions between the memcpy's that only affect the destination pointer.
|
|
@test8.str = internal constant [7 x i8] c"ABCDEF\00"
|
|
|
|
define void @test8() {
|
|
; CHECK-LABEL: @test8(
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%A = tail call ptr @malloc(i32 10)
|
|
%B = getelementptr inbounds i8, ptr %A, i64 2
|
|
tail call void @llvm.memcpy.p0.p0.i32(ptr %B, ptr @test8.str, i32 7, i1 false)
|
|
%C = tail call ptr @malloc(i32 10)
|
|
%D = getelementptr inbounds i8, ptr %C, i64 2
|
|
tail call void @llvm.memcpy.p0.p0.i32(ptr %D, ptr %B, i32 7, i1 false)
|
|
ret void
|
|
}
|
|
|
|
declare noalias ptr @malloc(i32) willreturn allockind("alloc,uninitialized") allocsize(0)
|
|
|
|
; rdar://11341081
|
|
%struct.big = type { [50 x i32] }
|
|
|
|
define void @test9_addrspacecast() nounwind ssp uwtable {
|
|
; CHECK-LABEL: @test9_addrspacecast(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_BIG:%.*]], align 4
|
|
; CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_BIG]], align 4
|
|
; CHECK-NEXT: call void @f1(ptr sret([[STRUCT_BIG]]) [[B]])
|
|
; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr [[B]] to ptr addrspace(1)
|
|
; CHECK-NEXT: [[TMP1:%.*]] = addrspacecast ptr [[TMP]] to ptr addrspace(1)
|
|
; CHECK-NEXT: call void @f2(ptr [[B]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%b = alloca %struct.big, align 4
|
|
%tmp = alloca %struct.big, align 4
|
|
call void @f1(ptr sret(%struct.big) %tmp)
|
|
%0 = addrspacecast ptr %b to ptr addrspace(1)
|
|
%1 = addrspacecast ptr %tmp to ptr addrspace(1)
|
|
call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) align 4 %0, ptr addrspace(1) align 4 %1, i64 200, i1 false)
|
|
call void @f2(ptr %b)
|
|
ret void
|
|
}
|
|
|
|
define void @test9() nounwind ssp uwtable {
|
|
; CHECK-LABEL: @test9(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_BIG:%.*]], align 4
|
|
; CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_BIG]], align 4
|
|
; CHECK-NEXT: call void @f1(ptr sret([[STRUCT_BIG]]) [[B]])
|
|
; CHECK-NEXT: call void @f2(ptr [[B]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%b = alloca %struct.big, align 4
|
|
%tmp = alloca %struct.big, align 4
|
|
call void @f1(ptr sret(%struct.big) %tmp)
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b, ptr align 4 %tmp, i64 200, i1 false)
|
|
call void @f2(ptr %b)
|
|
ret void
|
|
}
|
|
|
|
; rdar://14073661.
|
|
; Test10 triggered assertion when the compiler try to get the size of the
|
|
; opaque type of *x, where the x is the formal argument with attribute 'sret'.
|
|
|
|
%opaque = type opaque
|
|
declare void @foo(ptr noalias nocapture)
|
|
|
|
define void @test10(ptr noalias nocapture sret(%opaque) %x, i32 %y) {
|
|
; CHECK-LABEL: @test10(
|
|
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: store i32 [[Y:%.*]], ptr [[A]], align 4
|
|
; CHECK-NEXT: call void @foo(ptr noalias nocapture [[A]])
|
|
; CHECK-NEXT: [[C:%.*]] = load i32, ptr [[A]], align 4
|
|
; CHECK-NEXT: store i32 [[C]], ptr [[X:%.*]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a = alloca i32, align 4
|
|
store i32 %y, ptr %a
|
|
call void @foo(ptr noalias nocapture %a)
|
|
%c = load i32, ptr %a
|
|
store i32 %c, ptr %x
|
|
ret void
|
|
}
|
|
|
|
; don't create new addressspacecasts when we don't know they're safe for the target
|
|
define void @test11(ptr addrspace(1) nocapture dereferenceable(80) %P) {
|
|
; CHECK-LABEL: @test11(
|
|
; CHECK-NEXT: call void @llvm.memset.p1.i64(ptr addrspace(1) align 4 [[P:%.*]], i8 0, i64 80, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%A = alloca [20 x i32], align 4
|
|
call void @llvm.memset.p0.i64(ptr align 4 %A, i8 0, i64 80, i1 false)
|
|
call void @llvm.memcpy.p1.p0.i64(ptr addrspace(1) align 4 %P, ptr align 4 %A, i64 80, i1 false)
|
|
ret void
|
|
}
|
|
|
|
declare void @f1(ptr nocapture sret(%struct.big))
|
|
declare void @f2(ptr)
|
|
|
|
declare void @f(ptr)
|
|
declare void @f_byval(ptr byval(i32))
|
|
declare void @f_full_readonly(ptr nocapture noalias readonly)
|
|
|
|
define void @immut_param(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param(
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because dest may be captured.
|
|
define void @immut_param_maycapture(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_maycapture(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because dest may be aliased.
|
|
define void @immut_param_mayalias(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_mayalias(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @f(ptr [[VAL1]])
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @f(ptr %val1) ; escape
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can remove memcpy because alloca does not escape, so lack of noalias on the
|
|
; argument doesn't matter.
|
|
define void @immut_param_unescaped_alloca(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_unescaped_alloca(
|
|
; CHECK-NEXT: call void @f(ptr nocapture readonly align 4 [[VAL:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can remove memcpy because the function is argmem: read, so there cannot be
|
|
; a write to the escaped pointer.
|
|
define void @immut_param_memory_argmem_read(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_memory_argmem_read(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @f(ptr [[VAL1]])
|
|
; CHECK-NEXT: call void @f(ptr nocapture readonly align 4 [[VAL:%.*]]) #[[ATTR6:[0-9]+]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @f(ptr %val1) ; escape
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture readonly %val1) memory(argmem: read)
|
|
ret void
|
|
}
|
|
|
|
; Can remove memcpy because the function is argmem: read, so there cannot be
|
|
; a write to the escaped pointer. The readonly on the argument is redundant in
|
|
; this case.
|
|
define void @immut_param_memory_argmem_read_no_readonly(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_memory_argmem_read_no_readonly(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @f(ptr [[VAL1]])
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr nocapture align 4 [[VAL1]]) #[[ATTR6]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @f(ptr %val1) ; escape
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture %val1) memory(argmem: read)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because dest may be written.
|
|
define void @immut_param_maywrite(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_maywrite(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr align 4 nocapture noalias %val1)
|
|
ret void
|
|
}
|
|
|
|
define void @immut_param_readonly(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_readonly(
|
|
; CHECK-NEXT: call void @f_full_readonly(ptr align 4 [[VAL:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f_full_readonly(ptr align 4 %val1)
|
|
ret void
|
|
}
|
|
|
|
define void @immut_param_no_align(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_no_align(
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
@gp = external constant [0 x i8]
|
|
; Can't remove memcpy because dest is not unescaped alloca, so cpying is meaningfull.
|
|
define void @immut_param_global(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_global(
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 @gp, ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 @gp)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 @gp, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture align 4 noalias readonly @gp)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy for VLA because of unknown size and alignment.
|
|
define void @immut_param_vla(ptr align 4 noalias %val, i64 %n) {
|
|
; CHECK-LABEL: @immut_param_vla(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca ptr, i64 [[N:%.*]], align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca ptr, i64 %n
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy for scalable vector, because of memcpy size sufficiency is unknown
|
|
define void @immut_param_scalable_vector(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_scalable_vector(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca <vscale x 2 x i32>, align 8
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 2, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca <vscale x 2 x i32>
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 2, i1 false)
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because dst is modified between call and memcpy
|
|
define void @immut_param_modified_dst(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_modified_dst(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: store i32 13, ptr [[VAL1]], align 4
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
store i32 13, ptr %val1
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because src is modified between call and memcpy
|
|
define void @immut_param_modified_src(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_modified_src(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: store i32 13, ptr [[VAL]], align 4
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
store i32 13, ptr %val
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because memcpy is volatile
|
|
define void @immut_param_volatile(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_volatile(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 true)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 true)
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy because address spaces are different.
|
|
define void @immut_param_different_addrespace(ptr addrspace(1) align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_different_addrespace(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 4 [[VAL1]], ptr addrspace(1) align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p1.i64(ptr align 4 %val1, ptr addrspace(1) align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture align 4 noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
define void @immut_param_bigger_align(ptr align 16 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_bigger_align(
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy if we remove, the bigger alignment couldn't replaced by smaller one.
|
|
define void @immut_param_smaller_align(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @immut_param_smaller_align(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[VAL1]], ptr [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 16
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 16 %val1, ptr %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
define void @immut_param_enforced_alignment() {
|
|
; CHECK-LABEL: @immut_param_enforced_alignment(
|
|
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val = alloca i8, align 1
|
|
store i32 42, ptr %val
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr %val1, ptr %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy, because if the %val directly passed to @f,
|
|
; alignment of ptr to f's argument will be different.
|
|
define void @immut_invalid_align_branched(i1 %c, ptr noalias %val) {
|
|
; CHECK-LABEL: @immut_invalid_align_branched(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca [4 x i8], align 4
|
|
; CHECK-NEXT: [[VAL2:%.*]] = alloca [16 x i8], align 16
|
|
; CHECK-NEXT: [[VAL3:%.*]] = select i1 [[C:%.*]], ptr [[VAL1]], ptr [[VAL2]]
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL3]], ptr align 4 [[VAL:%.*]], i64 4, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL3]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca [4 x i8], align 4
|
|
%val2 = alloca [16 x i8], align 16
|
|
%val3 = select i1 %c, ptr %val1, ptr %val2
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val3, ptr align 4 %val, i64 4, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val3)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy, because alias might modify the src.
|
|
define void @immut_but_alias_src(ptr %val) {
|
|
; CHECK-LABEL: @immut_but_alias_src(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val1)
|
|
ret void
|
|
}
|
|
|
|
define void @immut_unescaped_alloca() {
|
|
; CHECK-LABEL: @immut_unescaped_alloca(
|
|
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
|
|
; CHECK-NEXT: call void @f_full_readonly(ptr [[VAL]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val = alloca i8, align 4
|
|
store i32 42, ptr %val
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @f_full_readonly(ptr %val1)
|
|
ret void
|
|
}
|
|
|
|
; Can't remove memcpy, because alloca src is modified
|
|
define void @immut_unescaped_alloca_modified() {
|
|
; CHECK-LABEL: @immut_unescaped_alloca_modified(
|
|
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL]], i64 1, i1 false)
|
|
; CHECK-NEXT: call void @f_full_readonly(ptr [[VAL1]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val = alloca i8, align 4
|
|
store i32 42, ptr %val
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
store i32 13, ptr %val
|
|
call void @f_full_readonly(ptr %val1)
|
|
ret void
|
|
}
|
|
|
|
; TODO: Remove memcpy
|
|
define void @immut_valid_align_branched(i1 %c, ptr noalias align 4 %val) {
|
|
; CHECK-LABEL: @immut_valid_align_branched(
|
|
; CHECK-NEXT: [[VAL1:%.*]] = alloca [4 x i8], align 4
|
|
; CHECK-NEXT: [[VAL2:%.*]] = alloca [16 x i8], align 4
|
|
; CHECK-NEXT: [[VAL3:%.*]] = select i1 [[C:%.*]], ptr [[VAL1]], ptr [[VAL2]]
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL3]], ptr align 4 [[VAL:%.*]], i64 4, i1 false)
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL3]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca [4 x i8], align 4
|
|
%val2 = alloca [16 x i8], align 4
|
|
%val3 = select i1 %c, ptr %val1, ptr %val2
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val3, ptr align 4 %val, i64 4, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %val3)
|
|
ret void
|
|
}
|
|
|
|
; Merge/drop noalias metadata when replacing parameter.
|
|
define void @immut_param_noalias_metadata(ptr align 4 byval(i32) %ptr) {
|
|
; CHECK-LABEL: @immut_param_noalias_metadata(
|
|
; CHECK-NEXT: store i32 1, ptr [[PTR:%.*]], align 4, !noalias [[META0:![0-9]+]]
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[PTR]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca i32, align 4
|
|
store i32 1, ptr %ptr, !noalias !2
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp, ptr align 4 %ptr, i64 4, i1 false)
|
|
call void @f(ptr nocapture noalias readonly %tmp), !alias.scope !2
|
|
ret void
|
|
}
|
|
|
|
define void @byval_param_noalias_metadata(ptr align 4 byval(i32) %ptr) {
|
|
; CHECK-LABEL: @byval_param_noalias_metadata(
|
|
; CHECK-NEXT: store i32 1, ptr [[PTR:%.*]], align 4, !noalias [[META0]]
|
|
; CHECK-NEXT: call void @f_byval(ptr byval(i32) align 4 [[PTR]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca i32, align 4
|
|
store i32 1, ptr %ptr, !noalias !2
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp, ptr align 4 %ptr, i64 4, i1 false)
|
|
call void @f_byval(ptr align 4 byval(i32) %tmp), !alias.scope !2
|
|
ret void
|
|
}
|
|
|
|
define void @byval_param_profile_metadata(ptr align 4 byval(i32) %ptr) {
|
|
; CHECK-LABEL: @byval_param_profile_metadata(
|
|
; CHECK-NEXT: store i32 1, ptr [[PTR2:%.*]], align 4
|
|
; CHECK-NEXT: call void @f_byval(ptr byval(i32) align 4 [[PTR2]]), !prof [[PROF3:![0-9]+]], !memprof [[META4:![0-9]+]], !callsite [[META7:![0-9]+]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%tmp = alloca i32, align 4
|
|
store i32 1, ptr %ptr
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp, ptr align 4 %ptr, i64 4, i1 false)
|
|
call void @f_byval(ptr align 4 byval(i32) %tmp), !memprof !3, !callsite !6, !prof !7
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_memory_none(ptr %p, ptr %p2, i64 %size) {
|
|
; CHECK-LABEL: @memcpy_memory_none(
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[SIZE:%.*]], i1 false) #[[ATTR7:[0-9]+]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %size, i1 false) memory(none)
|
|
ret void
|
|
}
|
|
|
|
declare void @do_something()
|
|
declare void @capture(ptr)
|
|
|
|
define void @memcpy_memcpy_escape_before(ptr noalias %P, ptr noalias %Q) {
|
|
; CHECK-LABEL: @memcpy_memcpy_escape_before(
|
|
; CHECK-NEXT: [[MEMTMP:%.*]] = alloca [32 x i8], align 16
|
|
; CHECK-NEXT: call void @capture(ptr [[MEMTMP]])
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[MEMTMP]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[MEMTMP]], i32 32, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca [32 x i8], align 16
|
|
call void @capture(ptr %memtmp)
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @do_something()
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_memcpy_escape_after1(ptr noalias %P, ptr noalias %Q) {
|
|
; CHECK-LABEL: @memcpy_memcpy_escape_after1(
|
|
; CHECK-NEXT: [[MEMTMP:%.*]] = alloca [32 x i8], align 16
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[MEMTMP]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P]], i32 32, i1 false)
|
|
; CHECK-NEXT: call void @capture(ptr [[MEMTMP]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca [32 x i8], align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @do_something()
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
call void @capture(ptr %memtmp)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_memcpy_escape_after2(ptr noalias %P, ptr noalias %Q) {
|
|
; CHECK-LABEL: @memcpy_memcpy_escape_after2(
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[Q:%.*]], ptr align 16 [[P:%.*]], i32 32, i1 false)
|
|
; CHECK-NEXT: call void @capture(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%memtmp = alloca [32 x i8], align 16
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %memtmp, ptr align 16 %P, i32 32, i1 false)
|
|
call void @do_something()
|
|
call void @llvm.memcpy.p0.p0.i32(ptr align 16 %Q, ptr align 16 %memtmp, i32 32, i1 false)
|
|
call void @capture(ptr %P)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_byval_escape_after(ptr noalias %P) {
|
|
; CHECK-LABEL: @memcpy_byval_escape_after(
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @test4a(ptr byval(i8) align 1 [[P:%.*]])
|
|
; CHECK-NEXT: call void @capture(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%A = alloca [8 x i8]
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %A, ptr align 4 %P, i64 8, i1 false)
|
|
call void @do_something()
|
|
call void @test4a(ptr align 1 byval(i8) %A)
|
|
call void @capture(ptr %P)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_immut_escape_after(ptr align 4 noalias %val) {
|
|
; CHECK-LABEL: @memcpy_immut_escape_after(
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL:%.*]])
|
|
; CHECK-NEXT: call void @capture(ptr [[VAL]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%val1 = alloca i8, align 4
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
|
|
call void @do_something()
|
|
call void @f(ptr align 4 nocapture noalias readonly %val1)
|
|
call void @capture(ptr %val)
|
|
ret void
|
|
}
|
|
|
|
!0 = !{!0}
|
|
!1 = !{!1, !0}
|
|
!2 = !{!1}
|
|
!3 = !{!4}
|
|
!4 = !{!5, !"cold"}
|
|
!5 = !{i64 123, i64 456}
|
|
!6 = !{i64 123}
|
|
!7 = !{!"branch_weights", i32 10}
|