| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 5 |
| ; RUN: opt < %s -S -passes='function(loop-versioning-licm,loop-mssa(licm))' | FileCheck %s |
| |
| target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| ; In these tests we have a loop where we can calculate the bounds of some memory |
| ; accesses but not others. |
| |
| ; Load from a gep whose bounds can't be calculated as the offset is loaded from memory |
| define void @gep_loaded_offset(ptr %p, ptr %q, ptr %r, i32 %n) { |
| ; CHECK-LABEL: define void @gep_loaded_offset( |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[R:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[WHILE_BODY_LVER_CHECK:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[N]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 |
| ; CHECK-NEXT: [[TMP2:%.*]] = shl nuw nsw i64 [[TMP1]], 2 |
| ; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 4 |
| ; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]] |
| ; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[R]], i64 8 |
| ; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[P]], [[SCEVGEP1]] |
| ; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[R]], [[SCEVGEP]] |
| ; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]] |
| ; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label %[[WHILE_BODY_PH_LVER_ORIG:.*]], label %[[WHILE_BODY_PH:.*]] |
| ; CHECK: [[WHILE_BODY_PH_LVER_ORIG]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY_LVER_ORIG:.*]] |
| ; CHECK: [[WHILE_BODY_LVER_ORIG]]: |
| ; CHECK-NEXT: [[N_ADDR:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[N]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[P_ADDR:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[P]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR]], -1 |
| ; CHECK-NEXT: [[RVAL:%.*]] = load i64, ptr [[R]], align 4 |
| ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[Q]], i64 [[RVAL]] |
| ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 |
| ; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[P_ADDR]], i64 4 |
| ; CHECK-NEXT: store i32 [[VAL]], ptr [[P_ADDR]], align 4 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY_LVER_ORIG]], !llvm.loop [[LOOP0:![0-9]+]] |
| ; CHECK: [[WHILE_BODY_PH]]: |
| ; CHECK-NEXT: [[RVAL1:%.*]] = load i64, ptr [[R]], align 4, !alias.scope [[META2:![0-9]+]] |
| ; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, ptr [[Q]], i64 [[RVAL1]] |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_ADDR1:%.*]] = phi i32 [ [[DEC1:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[P_ADDR1:%.*]] = phi ptr [ [[INCDEC_PTR1:%.*]], %[[WHILE_BODY]] ], [ [[P]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[DEC1]] = add nsw i32 [[N_ADDR1]], -1 |
| ; CHECK-NEXT: [[VAL1:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4 |
| ; CHECK-NEXT: [[INCDEC_PTR1]] = getelementptr inbounds nuw i8, ptr [[P_ADDR1]], i64 4 |
| ; CHECK-NEXT: store i32 [[VAL1]], ptr [[P_ADDR1]], align 4, !alias.scope [[META5:![0-9]+]], !noalias [[META2]] |
| ; CHECK-NEXT: [[TOBOOL_NOT1:%.*]] = icmp eq i32 [[DEC1]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT1]], label %[[WHILE_END_LOOPEXIT2:.*]], label %[[WHILE_BODY]], !llvm.loop [[LOOP7:![0-9]+]] |
| ; CHECK: [[WHILE_END_LOOPEXIT]]: |
| ; CHECK-NEXT: br label %[[WHILE_END:.*]] |
| ; CHECK: [[WHILE_END_LOOPEXIT2]]: |
| ; CHECK-NEXT: br label %[[WHILE_END]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n.addr = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %p.addr = phi ptr [ %incdec.ptr, %while.body ], [ %p, %entry ] |
| %dec = add nsw i32 %n.addr, -1 |
| %rval = load i64, ptr %r, align 4 |
| %arrayidx = getelementptr inbounds i32, ptr %q, i64 %rval |
| %val = load i32, ptr %arrayidx, align 4 |
| %incdec.ptr = getelementptr inbounds nuw i8, ptr %p.addr, i64 4 |
| store i32 %val, ptr %p.addr, align 4 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; As above but with a store to the loaded address. This should prevent the loop |
| ; from being versioned, as we wouldn't be able to do any code motion. |
| define void @gep_loaded_offset_with_store(ptr %p, ptr %q, ptr %r, i32 %n) { |
| ; CHECK-LABEL: define void @gep_loaded_offset_with_store( |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[R:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_ADDR:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[ENTRY]] ] |
| ; CHECK-NEXT: [[P_ADDR:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY]] ], [ [[P]], %[[ENTRY]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR]], -1 |
| ; CHECK-NEXT: [[RVAL:%.*]] = load i64, ptr [[R]], align 4 |
| ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[Q]], i64 [[RVAL]] |
| ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 |
| ; CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4 |
| ; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[P_ADDR]], i64 4 |
| ; CHECK-NEXT: store i32 [[VAL]], ptr [[P_ADDR]], align 4 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_BODY]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n.addr = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %p.addr = phi ptr [ %incdec.ptr, %while.body ], [ %p, %entry ] |
| %dec = add nsw i32 %n.addr, -1 |
| %rval = load i64, ptr %r, align 4 |
| %arrayidx = getelementptr inbounds i32, ptr %q, i64 %rval |
| %val = load i32, ptr %arrayidx, align 4 |
| store i32 0, ptr %arrayidx, align 4 |
| %incdec.ptr = getelementptr inbounds nuw i8, ptr %p.addr, i64 4 |
| store i32 %val, ptr %p.addr, align 4 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; Load from a gep whose bounds can't be calculated as the pointer is loaded from memory |
| define void @gep_loaded_base(ptr %p, ptr %q, ptr %r, i32 %n) { |
| ; CHECK-LABEL: define void @gep_loaded_base( |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[R:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[WHILE_BODY_LVER_CHECK:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[N]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 |
| ; CHECK-NEXT: [[TMP2:%.*]] = shl nuw nsw i64 [[TMP1]], 2 |
| ; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 4 |
| ; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]] |
| ; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[R]], i64 8 |
| ; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[P]], [[SCEVGEP1]] |
| ; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[R]], [[SCEVGEP]] |
| ; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]] |
| ; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label %[[WHILE_BODY_PH_LVER_ORIG:.*]], label %[[WHILE_BODY_PH:.*]] |
| ; CHECK: [[WHILE_BODY_PH_LVER_ORIG]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY_LVER_ORIG:.*]] |
| ; CHECK: [[WHILE_BODY_LVER_ORIG]]: |
| ; CHECK-NEXT: [[N_ADDR:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[N]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[P_ADDR:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[P]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR]], -1 |
| ; CHECK-NEXT: [[RVAL:%.*]] = load ptr, ptr [[R]], align 4 |
| ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[RVAL]], i64 0 |
| ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 |
| ; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[P_ADDR]], i64 4 |
| ; CHECK-NEXT: store i32 [[VAL]], ptr [[P_ADDR]], align 4 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY_LVER_ORIG]], !llvm.loop [[LOOP9:![0-9]+]] |
| ; CHECK: [[WHILE_BODY_PH]]: |
| ; CHECK-NEXT: [[RVAL1:%.*]] = load ptr, ptr [[R]], align 4, !alias.scope [[META10:![0-9]+]] |
| ; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, ptr [[RVAL1]], i64 0 |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_ADDR1:%.*]] = phi i32 [ [[DEC1:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[P_ADDR1:%.*]] = phi ptr [ [[INCDEC_PTR1:%.*]], %[[WHILE_BODY]] ], [ [[P]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[DEC1]] = add nsw i32 [[N_ADDR1]], -1 |
| ; CHECK-NEXT: [[VAL1:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4 |
| ; CHECK-NEXT: [[INCDEC_PTR1]] = getelementptr inbounds nuw i8, ptr [[P_ADDR1]], i64 4 |
| ; CHECK-NEXT: store i32 [[VAL1]], ptr [[P_ADDR1]], align 4, !alias.scope [[META13:![0-9]+]], !noalias [[META10]] |
| ; CHECK-NEXT: [[TOBOOL_NOT1:%.*]] = icmp eq i32 [[DEC1]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT1]], label %[[WHILE_END_LOOPEXIT2:.*]], label %[[WHILE_BODY]], !llvm.loop [[LOOP15:![0-9]+]] |
| ; CHECK: [[WHILE_END_LOOPEXIT]]: |
| ; CHECK-NEXT: br label %[[WHILE_END:.*]] |
| ; CHECK: [[WHILE_END_LOOPEXIT2]]: |
| ; CHECK-NEXT: br label %[[WHILE_END]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n.addr = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %p.addr = phi ptr [ %incdec.ptr, %while.body ], [ %p, %entry ] |
| %dec = add nsw i32 %n.addr, -1 |
| %rval = load ptr, ptr %r, align 4 |
| %arrayidx = getelementptr inbounds i32, ptr %rval, i64 0 |
| %val = load i32, ptr %arrayidx, align 4 |
| %incdec.ptr = getelementptr inbounds nuw i8, ptr %p.addr, i64 4 |
| store i32 %val, ptr %p.addr, align 4 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; Load from a gep with an offset that scalar evolution can't describe |
| define void @gep_strange_offset(ptr %p, ptr %q, ptr %r, i32 %n) { |
| ; CHECK-LABEL: define void @gep_strange_offset( |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[R:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[WHILE_BODY_LVER_CHECK:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[N]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 |
| ; CHECK-NEXT: [[TMP2:%.*]] = shl nuw nsw i64 [[TMP1]], 2 |
| ; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 4 |
| ; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP3]] |
| ; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[Q]], i64 4 |
| ; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[P]], [[SCEVGEP1]] |
| ; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[Q]], [[SCEVGEP]] |
| ; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]] |
| ; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label %[[WHILE_BODY_PH_LVER_ORIG:.*]], label %[[WHILE_BODY_PH:.*]] |
| ; CHECK: [[WHILE_BODY_PH_LVER_ORIG]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY_LVER_ORIG:.*]] |
| ; CHECK: [[WHILE_BODY_LVER_ORIG]]: |
| ; CHECK-NEXT: [[N_ADDR:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[N]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[P_ADDR:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[P]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_ADDR]], -1 |
| ; CHECK-NEXT: [[QVAL:%.*]] = load i32, ptr [[Q]], align 4 |
| ; CHECK-NEXT: [[REM:%.*]] = srem i32 [[DEC]], 2 |
| ; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[REM]] to i64 |
| ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[R]], i64 [[IDXPROM]] |
| ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 |
| ; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[VAL]], [[QVAL]] |
| ; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[P_ADDR]], i64 4 |
| ; CHECK-NEXT: store i32 [[ADD]], ptr [[P_ADDR]], align 4 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY_LVER_ORIG]], !llvm.loop [[LOOP16:![0-9]+]] |
| ; CHECK: [[WHILE_BODY_PH]]: |
| ; CHECK-NEXT: [[QVAL1:%.*]] = load i32, ptr [[Q]], align 4, !alias.scope [[META17:![0-9]+]] |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_ADDR1:%.*]] = phi i32 [ [[DEC1:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[P_ADDR1:%.*]] = phi ptr [ [[INCDEC_PTR1:%.*]], %[[WHILE_BODY]] ], [ [[P]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[DEC1]] = add nsw i32 [[N_ADDR1]], -1 |
| ; CHECK-NEXT: [[REM1:%.*]] = srem i32 [[DEC1]], 2 |
| ; CHECK-NEXT: [[IDXPROM1:%.*]] = sext i32 [[REM1]] to i64 |
| ; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, ptr [[R]], i64 [[IDXPROM1]] |
| ; CHECK-NEXT: [[VAL1:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4 |
| ; CHECK-NEXT: [[ADD1:%.*]] = add nsw i32 [[VAL1]], [[QVAL1]] |
| ; CHECK-NEXT: [[INCDEC_PTR1]] = getelementptr inbounds nuw i8, ptr [[P_ADDR1]], i64 4 |
| ; CHECK-NEXT: store i32 [[ADD1]], ptr [[P_ADDR1]], align 4, !alias.scope [[META20:![0-9]+]], !noalias [[META17]] |
| ; CHECK-NEXT: [[TOBOOL_NOT1:%.*]] = icmp eq i32 [[DEC1]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT1]], label %[[WHILE_END_LOOPEXIT2:.*]], label %[[WHILE_BODY]], !llvm.loop [[LOOP22:![0-9]+]] |
| ; CHECK: [[WHILE_END_LOOPEXIT]]: |
| ; CHECK-NEXT: br label %[[WHILE_END:.*]] |
| ; CHECK: [[WHILE_END_LOOPEXIT2]]: |
| ; CHECK-NEXT: br label %[[WHILE_END]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n.addr = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %p.addr = phi ptr [ %incdec.ptr, %while.body ], [ %p, %entry ] |
| %dec = add nsw i32 %n.addr, -1 |
| %qval = load i32, ptr %q, align 4 |
| %rem = srem i32 %dec, 2 |
| %idxprom = sext i32 %rem to i64 |
| %arrayidx = getelementptr inbounds i32, ptr %r, i64 %idxprom |
| %val = load i32, ptr %arrayidx, align 4 |
| %add = add nsw i32 %val, %qval |
| %incdec.ptr = getelementptr inbounds nuw i8, ptr %p.addr, i64 4 |
| store i32 %add, ptr %p.addr, align 4 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; A memcpy-like loop where the source address is loaded from a pointer |
| define void @memcpy_load_src(ptr %dst, ptr %src, i32 %n) { |
| ; CHECK-LABEL: define void @memcpy_load_src( |
| ; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[WHILE_BODY_LVER_CHECK:.*:]] |
| ; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[SRC]], i64 8 |
| ; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[N]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 |
| ; CHECK-NEXT: [[TMP2:%.*]] = add nuw nsw i64 [[TMP1]], 1 |
| ; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[DST]], i64 [[TMP2]] |
| ; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[SRC]], [[SCEVGEP1]] |
| ; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[DST]], [[SCEVGEP]] |
| ; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]] |
| ; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label %[[WHILE_BODY_PH_LVER_ORIG:.*]], label %[[WHILE_BODY_PH:.*]] |
| ; CHECK: [[WHILE_BODY_PH_LVER_ORIG]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY_LVER_ORIG:.*]] |
| ; CHECK: [[WHILE_BODY_LVER_ORIG]]: |
| ; CHECK-NEXT: [[N_VAL:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[N]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[DST_VAL:%.*]] = phi ptr [ [[DST_VAL_NEXT:%.*]], %[[WHILE_BODY_LVER_ORIG]] ], [ [[DST]], %[[WHILE_BODY_PH_LVER_ORIG]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_VAL]], -1 |
| ; CHECK-NEXT: [[SRC_VAL:%.*]] = load ptr, ptr [[SRC]], align 8 |
| ; CHECK-NEXT: [[SRC_VAL_NEXT:%.*]] = getelementptr inbounds nuw i8, ptr [[SRC_VAL]], i64 1 |
| ; CHECK-NEXT: [[DST_VAL_NEXT]] = getelementptr inbounds nuw i8, ptr [[DST_VAL]], i64 1 |
| ; CHECK-NEXT: store ptr [[SRC_VAL_NEXT]], ptr [[SRC]], align 8 |
| ; CHECK-NEXT: [[VAL:%.*]] = load i8, ptr [[SRC_VAL]], align 1 |
| ; CHECK-NEXT: store i8 [[VAL]], ptr [[DST_VAL]], align 1 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY_LVER_ORIG]], !llvm.loop [[LOOP23:![0-9]+]] |
| ; CHECK: [[WHILE_BODY_PH]]: |
| ; CHECK-NEXT: [[SRC_PROMOTED:%.*]] = load ptr, ptr [[SRC]], align 8, !alias.scope [[META24:![0-9]+]], !noalias [[META27:![0-9]+]] |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[SRC_VAL_NEXT3:%.*]] = phi ptr [ [[SRC_VAL_NEXT1:%.*]], %[[WHILE_BODY]] ], [ [[SRC_PROMOTED]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[N_VAL1:%.*]] = phi i32 [ [[DEC1:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[DST_VAL1:%.*]] = phi ptr [ [[DST_VAL_NEXT1:%.*]], %[[WHILE_BODY]] ], [ [[DST]], %[[WHILE_BODY_PH]] ] |
| ; CHECK-NEXT: [[DEC1]] = add nsw i32 [[N_VAL1]], -1 |
| ; CHECK-NEXT: [[SRC_VAL_NEXT1]] = getelementptr inbounds nuw i8, ptr [[SRC_VAL_NEXT3]], i64 1 |
| ; CHECK-NEXT: [[DST_VAL_NEXT1]] = getelementptr inbounds nuw i8, ptr [[DST_VAL1]], i64 1 |
| ; CHECK-NEXT: store ptr [[SRC_VAL_NEXT1]], ptr [[SRC]], align 8, !alias.scope [[META24]], !noalias [[META27]] |
| ; CHECK-NEXT: [[VAL1:%.*]] = load i8, ptr [[SRC_VAL_NEXT3]], align 1 |
| ; CHECK-NEXT: store i8 [[VAL1]], ptr [[DST_VAL1]], align 1, !alias.scope [[META27]] |
| ; CHECK-NEXT: [[TOBOOL_NOT1:%.*]] = icmp eq i32 [[DEC1]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT1]], label %[[WHILE_END_LOOPEXIT2:.*]], label %[[WHILE_BODY]], !llvm.loop [[LOOP29:![0-9]+]] |
| ; CHECK: [[WHILE_END_LOOPEXIT]]: |
| ; CHECK-NEXT: br label %[[WHILE_END:.*]] |
| ; CHECK: [[WHILE_END_LOOPEXIT2]]: |
| ; CHECK-NEXT: br label %[[WHILE_END]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n_val = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %dst_val = phi ptr [ %dst_val.next, %while.body ], [ %dst, %entry ] |
| %dec = add nsw i32 %n_val, -1 |
| %src_val = load ptr, ptr %src, align 8 |
| %src_val.next = getelementptr inbounds nuw i8, ptr %src_val, i64 1 |
| %dst_val.next = getelementptr inbounds nuw i8, ptr %dst_val, i64 1 |
| store ptr %src_val.next, ptr %src, align 8 |
| %val = load i8, ptr %src_val, align 1 |
| store i8 %val, ptr %dst_val, align 1 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; A memcpy-like loop where the destination address is loaded from a pointer |
| ; FIXME: We could hoist the load of the destination address, but doing the |
| ; bounds check of the store through that pointer itself requires using the |
| ; hoisted load. |
| define void @memcpy_load_dst(ptr %dst, ptr %src, i32 %n) { |
| ; CHECK-LABEL: define void @memcpy_load_dst( |
| ; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_VAL:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[ENTRY]] ] |
| ; CHECK-NEXT: [[SRC_VAL:%.*]] = phi ptr [ [[SRC_VAL_NEXT:%.*]], %[[WHILE_BODY]] ], [ [[SRC]], %[[ENTRY]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_VAL]], -1 |
| ; CHECK-NEXT: [[DST_VAL:%.*]] = load ptr, ptr [[DST]], align 8 |
| ; CHECK-NEXT: [[SRC_VAL_NEXT]] = getelementptr inbounds nuw i8, ptr [[SRC_VAL]], i64 1 |
| ; CHECK-NEXT: [[DST_VAL_NEXT:%.*]] = getelementptr inbounds nuw i8, ptr [[DST_VAL]], i64 1 |
| ; CHECK-NEXT: store ptr [[DST_VAL_NEXT]], ptr [[DST]], align 8 |
| ; CHECK-NEXT: [[VAL:%.*]] = load i8, ptr [[SRC_VAL]], align 1 |
| ; CHECK-NEXT: store i8 [[VAL]], ptr [[DST_VAL]], align 1 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_BODY]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n_val = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %src_val = phi ptr [ %src_val.next, %while.body ], [ %src, %entry ] |
| %dec = add nsw i32 %n_val, -1 |
| %dst_val = load ptr, ptr %dst, align 8 |
| %src_val.next = getelementptr inbounds nuw i8, ptr %src_val, i64 1 |
| %dst_val.next = getelementptr inbounds nuw i8, ptr %dst_val, i64 1 |
| store ptr %dst_val.next, ptr %dst, align 8 |
| %val = load i8, ptr %src_val, align 1 |
| store i8 %val, ptr %dst_val, align 1 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |
| |
| ; A memcpy-like loop where both the source and destination pointers are loaded from pointers |
| ; FIXME: We could hoist the loads of both addresses, but doing the bounds check |
| ; of the store through the destination address itself requires using the hoisted |
| ; load. |
| define void @memcpy_load_src_dst(ptr %dst, ptr %src, i32 %n) { |
| ; CHECK-LABEL: define void @memcpy_load_src_dst( |
| ; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]], i32 [[N:%.*]]) { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: [[N_VAL:%.*]] = phi i32 [ [[DEC:%.*]], %[[WHILE_BODY]] ], [ [[N]], %[[ENTRY]] ] |
| ; CHECK-NEXT: [[DEC]] = add nsw i32 [[N_VAL]], -1 |
| ; CHECK-NEXT: [[SRC_VAL:%.*]] = load ptr, ptr [[SRC]], align 8 |
| ; CHECK-NEXT: [[DST_VAL:%.*]] = load ptr, ptr [[DST]], align 8 |
| ; CHECK-NEXT: [[SRC_VAL_NEXT:%.*]] = getelementptr inbounds nuw i8, ptr [[SRC_VAL]], i64 1 |
| ; CHECK-NEXT: [[DST_VAL_NEXT:%.*]] = getelementptr inbounds nuw i8, ptr [[DST_VAL]], i64 1 |
| ; CHECK-NEXT: store ptr [[SRC_VAL_NEXT]], ptr [[SRC]], align 8 |
| ; CHECK-NEXT: store ptr [[DST_VAL_NEXT]], ptr [[DST]], align 8 |
| ; CHECK-NEXT: [[VAL:%.*]] = load i8, ptr [[SRC_VAL]], align 1 |
| ; CHECK-NEXT: store i8 [[VAL]], ptr [[DST_VAL]], align 1 |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[DEC]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_BODY]] |
| ; CHECK: [[WHILE_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %while.body |
| |
| while.body: |
| %n_val = phi i32 [ %dec, %while.body ], [ %n, %entry ] |
| %dec = add nsw i32 %n_val, -1 |
| %src_val = load ptr, ptr %src, align 8 |
| %dst_val = load ptr, ptr %dst, align 8 |
| %src_val.next = getelementptr inbounds nuw i8, ptr %src_val, i64 1 |
| %dst_val.next = getelementptr inbounds nuw i8, ptr %dst_val, i64 1 |
| store ptr %src_val.next, ptr %src, align 8 |
| store ptr %dst_val.next, ptr %dst, align 8 |
| %val = load i8, ptr %src_val, align 1 |
| store i8 %val, ptr %dst_val, align 1 |
| %tobool.not = icmp eq i32 %dec, 0 |
| br i1 %tobool.not, label %while.end, label %while.body |
| |
| while.end: |
| ret void |
| } |