| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -passes=memcpyopt -S %s | FileCheck %s |
| |
| declare void @init(ptr nocapture sret(i64) align 8) |
| |
| declare i1 @check(ptr readonly byval(i64) align 8) readonly argmemonly |
| |
| declare void @clobber(ptr) argmemonly |
| |
| declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) |
| declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) |
| declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) |
| |
| ; %a.2's lifetime ends before the call to @check. Cannot replace |
| ; %a.1 with %a.2 in the call to @check. |
| define i1 @alloca_forwarding_lifetime_end_clobber() { |
| ; CHECK-LABEL: @alloca_forwarding_lifetime_end_clobber( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]]) |
| ; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1 |
| ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false) |
| ; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[A_2]]) |
| ; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_1]]) |
| ; CHECK-NEXT: ret i1 [[CALL]] |
| ; |
| entry: |
| %a.1 = alloca i64, align 8 |
| %a.2 = alloca i64, align 8 |
| call void @llvm.lifetime.start.p0(i64 8, ptr %a.2) |
| call void @init(ptr sret(i64) align 8 %a.2) |
| store i8 0, ptr %a.2 |
| call void @llvm.memcpy.p0.p0.i64(ptr %a.1, ptr %a.2, i64 8, i1 false) |
| call void @llvm.lifetime.end.p0(i64 8, ptr %a.2) |
| ;call void @clobber(ptr %a.2) |
| %call = call i1 @check(ptr byval(i64) align 8 %a.1) |
| ret i1 %call |
| } |
| |
| ; There is a call clobbering %a.2 before the call to @check. Cannot replace |
| ; %a.1 with %a.2 in the call to @check. |
| define i1 @alloca_forwarding_call_clobber() { |
| ; CHECK-LABEL: @alloca_forwarding_call_clobber( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]]) |
| ; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1 |
| ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false) |
| ; CHECK-NEXT: call void @clobber(ptr [[A_2]]) |
| ; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_1]]) |
| ; CHECK-NEXT: ret i1 [[CALL]] |
| ; |
| entry: |
| %a.1 = alloca i64, align 8 |
| %a.2 = alloca i64, align 8 |
| call void @llvm.lifetime.start.p0(i64 8, ptr %a.2) |
| call void @init(ptr sret(i64) align 8 %a.2) |
| store i8 0, ptr %a.2 |
| call void @llvm.memcpy.p0.p0.i64(ptr %a.1, ptr %a.2, i64 8, i1 false) |
| call void @clobber(ptr %a.2) |
| %call = call i1 @check(ptr byval(i64) align 8 %a.1) |
| ret i1 %call |
| } |
| |
| define i1 @alloca_forwarding_call_clobber_after() { |
| ; CHECK-LABEL: @alloca_forwarding_call_clobber_after( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]]) |
| ; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1 |
| ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false) |
| ; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: call void @clobber(ptr [[A_2]]) |
| ; CHECK-NEXT: ret i1 [[CALL]] |
| ; |
| entry: |
| %a.1 = alloca i64, align 8 |
| %a.2 = alloca i64, align 8 |
| call void @llvm.lifetime.start.p0(i64 8, ptr %a.2) |
| call void @init(ptr sret(i64) align 8 %a.2) |
| store i8 0, ptr %a.2 |
| call void @llvm.memcpy.p0.p0.i64(ptr %a.1, ptr %a.2, i64 8, i1 false) |
| %call = call i1 @check(ptr byval(i64) align 8 %a.1) |
| call void @clobber(ptr %a.2) |
| ret i1 %call |
| } |
| |
| define i1 @alloca_forwarding_unrelated_call_noclobber() { |
| ; CHECK-LABEL: @alloca_forwarding_unrelated_call_noclobber( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A_3:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]]) |
| ; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1 |
| ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false) |
| ; CHECK-NEXT: call void @clobber(ptr [[A_3]]) |
| ; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_2]]) |
| ; CHECK-NEXT: ret i1 [[CALL]] |
| ; |
| entry: |
| %a.1 = alloca i64, align 8 |
| %a.2 = alloca i64, align 8 |
| %a.3 = alloca i64, align 8 |
| call void @llvm.lifetime.start.p0(i64 8, ptr %a.2) |
| call void @init(ptr sret(i64) align 8 %a.2) |
| store i8 0, ptr %a.2 |
| call void @llvm.memcpy.p0.p0.i64(ptr %a.1, ptr %a.2, i64 8, i1 false) |
| call void @clobber(ptr %a.3) |
| %call = call i1 @check(ptr byval(i64) align 8 %a.1) |
| ret i1 %call |
| } |
| |