Index: llvm/test/Transforms/MemCpyOpt/memcpy.ll =================================================================== --- llvm/test/Transforms/MemCpyOpt/memcpy.ll +++ llvm/test/Transforms/MemCpyOpt/memcpy.ll @@ -391,3 +391,279 @@ declare void @f1(ptr nocapture sret(%struct.big)) declare void @f2(ptr) + +declare void @f(ptr) +declare void @f_full_readonly(ptr nocapture noalias readonly) + +; TODO: Remove memcpy, which is guaranteed to be invariant +; before and after the call because of its attributes. +define void @immut_param(ptr align 4 noalias %val) { +; CHECK-LABEL: @immut_param( +; 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 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 readonly %val1) + ret void +} + +; TODO: Remove memcpy +define void @immut_param_readonly(ptr align 4 noalias %val) { +; CHECK-LABEL: @immut_param_readonly( +; 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 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_full_readonly(ptr align 4 %val1) + ret void +} + +; TODO: Remove memcpy +define void @immut_param_no_align(ptr align 4 noalias %val) { +; CHECK-LABEL: @immut_param_no_align( +; 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 +} + +@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 , 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 + 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 +} + +; TODO: remove memcpy +define void @immut_param_bigger_align(ptr align 16 noalias %val) { +; CHECK-LABEL: @immut_param_bigger_align( +; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4 +; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[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 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 +} + +; TODO: remove memcpy. +define void @immut_param_enforced_alignment(ptr align 16 noalias %val) { +; CHECK-LABEL: @immut_param_enforced_alignment( +; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 16 +; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[VAL1]], ptr align 1 [[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 align 1 %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 +} + +; TODO: remove memcpy +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: [[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) + 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 +}