240 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 42; RUN: opt < %s -passes=memcpyopt -S -verify-memoryssa | FileCheck %s3 4%buf = type [9 x i8]5 6; We can forward `memcpy` because the copy location are the same,7define void @forward_offset(ptr %src, ptr %dest) {8; CHECK-LABEL: define void @forward_offset(9; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {10; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 111; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)12; CHECK-NEXT: [[SRC_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 113; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 114; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 6, i1 false)15; CHECK-NEXT: ret void16;17 %cpy_tmp = alloca %buf, align 118 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)19 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 120 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)21 ret void22}23 24; We need to update the align value of the source of `memcpy` when forwarding.25define void @forward_offset_align(ptr %src, ptr %dest) {26; CHECK-LABEL: define void @forward_offset_align(27; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {28; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 129; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 4 [[SRC]], i64 9, i1 false)30; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 331; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 332; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 5, i1 false)33; CHECK-NEXT: ret void34;35 %cpy_tmp = alloca %buf, align 136 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 4 %src, i64 9, i1 false)37 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 338 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 5, i1 false)39 ret void40}41 42; We can change the align value to 2 when forwarding.43define void @forward_offset_align_2(ptr %src, ptr %dest) {44; CHECK-LABEL: define void @forward_offset_align_2(45; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {46; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 147; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 4 [[SRC]], i64 9, i1 false)48; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 249; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 250; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 2 [[TMP1]], i64 6, i1 false)51; CHECK-NEXT: ret void52;53 %cpy_tmp = alloca %buf, align 154 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 4 %src, i64 9, i1 false)55 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 256 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)57 ret void58}59 60; If the copy destination can be used as the copy source, we don't need to create a GEP instruction.61define void @forward_offset_without_gep(ptr %src) {62; CHECK-LABEL: define void @forward_offset_without_gep(63; CHECK-SAME: ptr [[SRC:%.*]]) {64; CHECK-NEXT: [[TMP:%.*]] = alloca [9 x i8], align 165; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[TMP]], ptr align 1 [[SRC]], i64 7, i1 false)66; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 167; CHECK-NEXT: [[DEST:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 168; CHECK-NEXT: ret void69;70 %cpy_tmp = alloca %buf, align 171 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)72 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 173 %dest = getelementptr inbounds i8, ptr %src, i64 174 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)75 ret void76}77 78; We need to create a GEP instruction when forwarding.79define void @forward_offset_with_gep(ptr %src) {80; CHECK-LABEL: define void @forward_offset_with_gep(81; CHECK-SAME: ptr [[SRC:%.*]]) {82; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 183; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)84; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 185; CHECK-NEXT: [[DEST:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 286; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 187; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 6, i1 false)88; CHECK-NEXT: ret void89;90 %cpy_tmp = alloca %buf, align 191 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)92 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 193 %dest = getelementptr inbounds i8, ptr %src, i64 294 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)95 ret void96}97 98; Make sure we pass the right parameters when calling `memcpy`.99define void @forward_offset_memcpy(ptr %src, ptr %dest) {100; CHECK-LABEL: define void @forward_offset_memcpy(101; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {102; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 1103; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)104; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 1105; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 1106; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 6, i1 false)107; CHECK-NEXT: call void @use(ptr [[DEST]])108; CHECK-NEXT: ret void109;110 %cpy_tmp = alloca %buf, align 1111 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)112 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 1113 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)114 call void @use(ptr %dest)115 ret void116}117 118; Make sure we pass the right parameters when calling `memcpy.inline`.119define void @forward_offset_memcpy_inline(ptr %src, ptr %dest) {120; CHECK-LABEL: define void @forward_offset_memcpy_inline(121; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {122; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 1123; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)124; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 1125; CHECK-NEXT: call void @llvm.memcpy.inline.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 6, i1 false)126; CHECK-NEXT: call void @use(ptr [[DEST]])127; CHECK-NEXT: ret void128;129 %cpy_tmp = alloca %buf, align 1130 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)131 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 1132 call void @llvm.memcpy.inline.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)133 call void @use(ptr %dest)134 ret void135}136 137; We can forward `memcpy` by shrinking it to the size of the `memcpy` it depends on.138define void @forward_oversize_offset(ptr %src, ptr %dest) {139; CHECK-LABEL: define void @forward_oversize_offset(140; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {141; CHECK-NEXT: [[CPY_TMP:%.*]] = alloca [9 x i8], align 1142; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[CPY_TMP]], ptr align 1 [[SRC]], i64 6, i1 false)143; CHECK-NEXT: [[CPY_TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[CPY_TMP]], i64 1144; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 1145; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 5, i1 false)146; CHECK-NEXT: ret void147;148 %cpy_tmp = alloca %buf, align 1149 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 6, i1 false)150 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 1151 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 6, i1 false)152 ret void153}154 155; We can forward `memcpy` because the write operation does not corrupt the location to be copied.156define void @forward_offset_and_store(ptr %src, ptr %dest) {157; CHECK-LABEL: define void @forward_offset_and_store(158; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {159; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 1160; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)161; CHECK-NEXT: store i8 1, ptr [[SRC]], align 1162; CHECK-NEXT: [[DEP_SRC_END:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 6163; CHECK-NEXT: store i8 1, ptr [[DEP_SRC_END]], align 1164; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 1165; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 1166; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP1]], i64 5, i1 false)167; CHECK-NEXT: ret void168;169 %cpy_tmp = alloca %buf, align 1170 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)171 store i8 1, ptr %src, align 1172 %src_end = getelementptr inbounds i8, ptr %src, i64 6173 store i8 1, ptr %src_end, align 1174 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 1175 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 5, i1 false)176 ret void177}178 179; We cannot forward `memcpy` because the write operation alters the location to be copied.180; Also, make sure we have removed the GEP instruction that was created temporarily.181define void @do_not_forward_offset_and_store(ptr %src, ptr %dest) {182; CHECK-LABEL: define void @do_not_forward_offset_and_store(183; CHECK-SAME: ptr [[SRC:%.*]], ptr [[DEST:%.*]]) {184; CHECK-NEXT: [[DEP_DEST:%.*]] = alloca [9 x i8], align 1185; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEP_DEST]], ptr align 1 [[SRC]], i64 7, i1 false)186; CHECK-NEXT: [[DEP:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 1187; CHECK-NEXT: store i8 1, ptr [[DEP]], align 1188; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[DEP_DEST]], i64 1189; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[TMP_OFFSET]], i64 5, i1 false)190; CHECK-NEXT: ret void191;192 %cpy_tmp = alloca %buf, align 1193 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %cpy_tmp, ptr align 1 %src, i64 7, i1 false)194 %src_offset = getelementptr inbounds i8, ptr %src, i64 1195 store i8 1, ptr %src_offset, align 1196 %cpy_tmp_offset = getelementptr inbounds i8, ptr %cpy_tmp, i64 1197 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %dest, ptr align 1 %cpy_tmp_offset, i64 5, i1 false)198 ret void199}200 201; Make sure we don't crash when the copy source is a constant.202@buf = external global [32 x i8]203 204define void @pr98675(ptr noalias %p1, ptr noalias %p2) {205; CHECK-LABEL: define void @pr98675(206; CHECK-SAME: ptr noalias [[P1:%.*]], ptr noalias [[P2:%.*]]) {207; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P1]], ptr @buf, i64 26, i1 false)208; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P1]], i64 10209; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P2]], ptr getelementptr inbounds (i8, ptr @buf, i64 10), i64 1, i1 false)210; CHECK-NEXT: ret void211;212 call void @llvm.memcpy.p0.p0.i64(ptr %p1, ptr @buf, i64 26, i1 false)213 %gep = getelementptr i8, ptr %p1, i64 10214 call void @llvm.memmove.p0.p0.i64(ptr %p2, ptr %gep, i64 1, i1 false)215 ret void216}217 218define void @over_offset_cpy(ptr %src) {219; CHECK-LABEL: define void @over_offset_cpy(220; CHECK-SAME: ptr [[SRC:%.*]]) {221; CHECK-NEXT: [[TMP:%.*]] = alloca [2 x i8], align 1222; CHECK-NEXT: [[DST:%.*]] = alloca i8, align 1223; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[SRC]], i64 1, i1 false)224; CHECK-NEXT: [[TMP_OFFSET:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 1225; CHECK-NEXT: ret void226;227 %tmp = alloca [2 x i8]228 %dst = alloca i8229 call void @llvm.memcpy.p0.p0.i64(ptr align 8 %tmp, ptr align 8 %src, i64 1, i1 false)230 %tmp_offset = getelementptr inbounds i8, ptr %tmp, i64 1231 call void @llvm.memcpy.p0.p0.i64(ptr align 8 %dst, ptr align 8 %tmp_offset, i64 1, i1 false)232 233 ret void234}235 236declare void @use(ptr)237 238declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)239declare void @llvm.memcpy.inline.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)240