1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt -S -passes=memcpyopt < %s -verify-memoryssa | FileCheck %s 3 4define i8 @read_dest_between_call_and_memcpy() { 5; CHECK-LABEL: @read_dest_between_call_and_memcpy( 6; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 7; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 8; CHECK-NEXT: store i8 1, ptr [[DEST]], align 1 9; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[SRC]], i8 0, i64 16, i1 false) 10; CHECK-NEXT: [[X:%.*]] = load i8, ptr [[DEST]], align 1 11; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DEST]], i8 0, i64 16, i1 false) 12; CHECK-NEXT: ret i8 [[X]] 13; 14 %dest = alloca [16 x i8] 15 %src = alloca [16 x i8] 16 store i8 1, ptr %dest 17 call void @llvm.memset.p0.i64(ptr %src, i8 0, i64 16, i1 false) 18 %x = load i8, ptr %dest 19 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 20 ret i8 %x 21} 22 23define i8 @read_src_between_call_and_memcpy() { 24; CHECK-LABEL: @read_src_between_call_and_memcpy( 25; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 26; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 27; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[SRC]], i8 0, i64 16, i1 false) 28; CHECK-NEXT: [[X:%.*]] = load i8, ptr [[SRC]], align 1 29; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DEST]], i8 0, i64 16, i1 false) 30; CHECK-NEXT: ret i8 [[X]] 31; 32 %dest = alloca [16 x i8] 33 %src = alloca [16 x i8] 34 call void @llvm.memset.p0.i64(ptr %src, i8 0, i64 16, i1 false) 35 %x = load i8, ptr %src 36 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 37 ret i8 %x 38} 39 40define void @write_dest_between_call_and_memcpy() { 41; CHECK-LABEL: @write_dest_between_call_and_memcpy( 42; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 43; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 44; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[SRC]], i8 0, i64 16, i1 false) 45; CHECK-NEXT: store i8 1, ptr [[DEST]], align 1 46; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DEST]], i8 0, i64 16, i1 false) 47; CHECK-NEXT: ret void 48; 49 %dest = alloca [16 x i8] 50 %src = alloca [16 x i8] 51 call void @llvm.memset.p0.i64(ptr %src, i8 0, i64 16, i1 false) 52 store i8 1, ptr %dest 53 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 54 ret void 55} 56 57define void @write_src_between_call_and_memcpy() { 58; CHECK-LABEL: @write_src_between_call_and_memcpy( 59; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 60; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[SRC]], i8 0, i64 16, i1 false) 61; CHECK-NEXT: store i8 1, ptr [[SRC]], align 1 62; CHECK-NEXT: ret void 63; 64 %dest = alloca [16 x i8] 65 %src = alloca [16 x i8] 66 call void @llvm.memset.p0.i64(ptr %src, i8 0, i64 16, i1 false) 67 store i8 1, ptr %src 68 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 69 ret void 70} 71 72define void @throw_between_call_and_mempy(ptr writable dereferenceable(16) %dest.i8) { 73; CHECK-LABEL: @throw_between_call_and_mempy( 74; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 75; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[SRC]], i8 0, i64 16, i1 false) 76; CHECK-NEXT: call void @may_throw() #[[ATTR2:[0-9]+]] 77; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DEST_I8:%.*]], i8 0, i64 16, i1 false) 78; CHECK-NEXT: ret void 79; 80 %src = alloca [16 x i8] 81 call void @llvm.memset.p0.i64(ptr %src, i8 0, i64 16, i1 false) 82 call void @may_throw() readnone 83 call void @llvm.memcpy.p0.p0.i64(ptr %dest.i8, ptr %src, i64 16, i1 false) 84 ret void 85} 86 87define void @dest_is_gep_nounwind_call() { 88; CHECK-LABEL: @dest_is_gep_nounwind_call( 89; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 90; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1 91; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], ptr [[DEST]], i64 0, i64 8 92; CHECK-NEXT: call void @accept_ptr(ptr [[DEST_I8]]) #[[ATTR3:[0-9]+]] 93; CHECK-NEXT: ret void 94; 95 %dest = alloca [16 x i8] 96 %src = alloca [8 x i8] 97 %dest.i8 = getelementptr [16 x i8], ptr %dest, i64 0, i64 8 98 call void @accept_ptr(ptr %src) nounwind 99 call void @llvm.memcpy.p0.p0.i64(ptr %dest.i8, ptr %src, i64 8, i1 false) 100 ret void 101} 102 103define void @dest_is_gep_may_throw_call() { 104; CHECK-LABEL: @dest_is_gep_may_throw_call( 105; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 106; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1 107; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], ptr [[DEST]], i64 0, i64 8 108; CHECK-NEXT: call void @accept_ptr(ptr [[DEST_I8]]) 109; CHECK-NEXT: ret void 110; 111 %dest = alloca [16 x i8] 112 %src = alloca [8 x i8] 113 %dest.i8 = getelementptr [16 x i8], ptr %dest, i64 0, i64 8 114 call void @accept_ptr(ptr %src) 115 call void @llvm.memcpy.p0.p0.i64(ptr %dest.i8, ptr %src, i64 8, i1 false) 116 ret void 117} 118 119define void @dest_is_gep_requires_movement() { 120; CHECK-LABEL: @dest_is_gep_requires_movement( 121; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 122; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1 123; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], ptr [[DEST]], i64 0, i64 8 124; CHECK-NEXT: call void @accept_ptr(ptr [[DEST_I8]]) #[[ATTR3]] 125; CHECK-NEXT: ret void 126; 127 %dest = alloca [16 x i8] 128 %src = alloca [8 x i8] 129 call void @accept_ptr(ptr %src) nounwind 130 %dest.i8 = getelementptr [16 x i8], ptr %dest, i64 0, i64 8 131 call void @llvm.memcpy.p0.p0.i64(ptr %dest.i8, ptr %src, i64 8, i1 false) 132 ret void 133} 134 135define void @capture_before_call_argmemonly() { 136; CHECK-LABEL: @capture_before_call_argmemonly( 137; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 138; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 139; CHECK-NEXT: call void @accept_ptr(ptr [[DEST]]) 140; CHECK-NEXT: call void @accept_ptr(ptr captures(none) [[DEST]]) #[[ATTR4:[0-9]+]] 141; CHECK-NEXT: ret void 142; 143 %dest = alloca [16 x i8] 144 %src = alloca [16 x i8] 145 call void @accept_ptr(ptr %dest) ; capture 146 call void @accept_ptr(ptr nocapture %src) argmemonly 147 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 148 ret void 149} 150 151define void @capture_before_call_argmemonly_nounwind() { 152; CHECK-LABEL: @capture_before_call_argmemonly_nounwind( 153; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 154; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 155; CHECK-NEXT: call void @accept_ptr(ptr [[DEST]]) 156; CHECK-NEXT: call void @accept_ptr(ptr captures(none) [[DEST]]) #[[ATTR5:[0-9]+]] 157; CHECK-NEXT: ret void 158; 159 %dest = alloca [16 x i8] 160 %src = alloca [16 x i8] 161 call void @accept_ptr(ptr %dest) ; capture 162 ; NB: argmemonly currently implies willreturn. 163 call void @accept_ptr(ptr nocapture %src) argmemonly nounwind 164 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 165 ret void 166} 167 168define void @capture_before_call_argmemonly_nounwind_willreturn() { 169; CHECK-LABEL: @capture_before_call_argmemonly_nounwind_willreturn( 170; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 171; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 172; CHECK-NEXT: call void @accept_ptr(ptr [[DEST]]) 173; CHECK-NEXT: call void @accept_ptr(ptr captures(none) [[DEST]]) #[[ATTR6:[0-9]+]] 174; CHECK-NEXT: ret void 175; 176 %dest = alloca [16 x i8] 177 %src = alloca [16 x i8] 178 call void @accept_ptr(ptr %dest) ; capture 179 call void @accept_ptr(ptr nocapture %src) argmemonly nounwind willreturn 180 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 181 ret void 182} 183 184; There is no path from the capture back to the memcpy. 185; So we are allowed to perform the call slot optimization. 186define void @capture_nopath_call(i1 %cond) { 187; CHECK-LABEL: @capture_nopath_call( 188; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1 189; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1 190; CHECK-NEXT: br i1 [[COND:%.*]], label [[CAPTURES:%.*]], label [[NOCAPTURES:%.*]] 191; CHECK: captures: 192; CHECK-NEXT: call void @accept_ptr(ptr [[DEST]]) 193; CHECK-NEXT: ret void 194; CHECK: nocaptures: 195; CHECK-NEXT: call void @accept_ptr(ptr [[DEST]]) #[[ATTR3]] 196; CHECK-NEXT: ret void 197; 198 %dest = alloca [16 x i8] 199 %src = alloca [16 x i8] 200 br i1 %cond, label %captures, label %nocaptures 201 202captures: 203 call void @accept_ptr(ptr %dest) ; capture 204 ret void 205 206nocaptures: 207 call void @accept_ptr(ptr %src) nounwind 208 call void @llvm.memcpy.p0.p0.i64(ptr %dest, ptr %src, i64 16, i1 false) 209 ret void 210} 211 212define void @source_alignment(ptr noalias writable dereferenceable(128) %dst) { 213; CHECK-LABEL: @source_alignment( 214; CHECK-NEXT: [[SRC:%.*]] = alloca [128 x i8], align 4 215; CHECK-NEXT: call void @accept_ptr(ptr captures(none) [[DST:%.*]]) #[[ATTR3]] 216; CHECK-NEXT: ret void 217; 218 %src = alloca [128 x i8], align 4 219 call void @accept_ptr(ptr nocapture %src) nounwind 220 call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dst, ptr %src, i64 128, i1 false) 221 ret void 222} 223 224define void @dest_not_writable(ptr noalias dereferenceable(128) %dst) { 225; CHECK-LABEL: @dest_not_writable( 226; CHECK-NEXT: [[SRC:%.*]] = alloca [128 x i8], align 4 227; CHECK-NEXT: call void @accept_ptr(ptr captures(none) [[SRC]]) #[[ATTR3]] 228; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[DST:%.*]], ptr [[SRC]], i64 128, i1 false) 229; CHECK-NEXT: ret void 230; 231 %src = alloca [128 x i8], align 4 232 call void @accept_ptr(ptr nocapture %src) nounwind 233 call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dst, ptr %src, i64 128, i1 false) 234 ret void 235} 236 237declare void @may_throw() 238declare void @accept_ptr(ptr) 239declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) 240declare void @llvm.memset.p0.i64(ptr, i8, i64, i1) 241