llvm-project/llvm/test/Transforms/SROA/readonlynocapture.ll
Nikita Popov c23b4fbdbb
[IR] Remove size argument from lifetime intrinsics (#150248)
Now that #149310 has restricted lifetime intrinsics to only work on
allocas, we can also drop the explicit size argument. Instead, the size
is implied by the alloca.

This removes the ability to only mark a prefix of an alloca alive/dead.
We never used that capability, so we should remove the need to handle
that possibility everywhere (though many key places, including stack
coloring, did not actually respect this).
2025-08-08 11:09:34 +02:00

587 lines
19 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=sroa -S | FileCheck %s
declare void @callee(ptr nocapture readonly %p)
define i32 @simple() {
; CHECK-LABEL: @simple(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: ret i32 0
;
%a = alloca i32
store i32 0, ptr %a
call void @callee(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
define i32 @smallbig() {
; CHECK-LABEL: @smallbig(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i8 0, ptr [[A]], align 1
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[L1]]
;
%a = alloca i32
store i8 0, ptr %a
call void @callee(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
define i32 @twoalloc() {
; CHECK-LABEL: @twoalloc(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[R:%.*]] = add i32 0, 1
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee(ptr %a)
%l1 = load i32, ptr %a
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
define i32 @twostore() {
; CHECK-LABEL: @twostore(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 1, ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: store i32 2, ptr [[A]], align 4
; CHECK-NEXT: ret i32 2
;
%a = alloca i32
store i32 1, ptr %a
call void @callee(ptr %a)
store i32 2, ptr %a
%l = load i32, ptr %a
ret i32 %l
}
define float @differenttype() {
; CHECK-LABEL: @differenttype(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L2:%.*]] = load float, ptr [[B]], align 4
; CHECK-NEXT: ret float [[L2]]
;
%a = alloca {i32, i32}
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee(ptr %a)
%l2 = load float, ptr %b
ret float %l2
}
define i32 @twoalloc_store64(i64 %x) {
; CHECK-LABEL: @twoalloc_store64(
; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8
; CHECK-NEXT: store i64 [[X:%.*]], ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: [[L2:%.*]] = load i32, ptr [[B]], align 4
; CHECK-NEXT: ret i32 [[L2]]
;
%a = alloca i64
store i64 %x, ptr %a
call void @callee(ptr %a)
%l1 = load i32, ptr %a
%b = getelementptr i32, ptr %a, i32 1
%l2 = load i32, ptr %b
ret i32 %l2
}
define i32 @twocalls() {
; CHECK-LABEL: @twocalls(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[R:%.*]] = add i32 0, 1
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee(ptr %a)
%l1 = load i32, ptr %a
call void @callee(ptr %a)
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
define i32 @volatile() {
; CHECK-LABEL: @volatile(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store volatile i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load volatile i32, ptr [[A]], align 4
; CHECK-NEXT: [[L2:%.*]] = load i32, ptr [[B]], align 4
; CHECK-NEXT: [[R:%.*]] = add i32 [[L1]], [[L2]]
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store volatile i32 1, ptr %b
call void @callee(ptr %a)
%l1 = load volatile i32, ptr %a
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
define i32 @atomic() {
; CHECK-LABEL: @atomic(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load atomic i32, ptr [[A]] seq_cst, align 4
; CHECK-NEXT: [[R:%.*]] = add i32 [[L1]], 1
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee(ptr %a)
%l1 = load atomic i32, ptr %a seq_cst, align 4
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
define i32 @notdominating() {
; CHECK-LABEL: @notdominating(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[R:%.*]] = add i32 undef, undef
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
%b = getelementptr i32, ptr %a, i32 1
%l1 = load i32, ptr %a
%l2 = load i32, ptr %b
store i32 0, ptr %a
store i32 1, ptr %b
call void @callee(ptr %a)
%r = add i32 %l1, %l2
ret i32 %r
}
declare void @callee_notreadonly(ptr %p)
define i32 @notreadonly() {
; CHECK-LABEL: @notreadonly(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee_notreadonly(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: [[L2:%.*]] = load i32, ptr [[B]], align 4
; CHECK-NEXT: [[R:%.*]] = add i32 [[L1]], [[L2]]
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee_notreadonly(ptr %a)
%l1 = load i32, ptr %a
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
declare void @callee_multiuse(ptr nocapture readonly %p, ptr nocapture readonly %q)
define i32 @multiuse() {
; CHECK-LABEL: @multiuse(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee_multiuse(ptr [[A]], ptr [[A]])
; CHECK-NEXT: [[R:%.*]] = add i32 0, 1
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee_multiuse(ptr %a, ptr %a)
%l1 = load i32, ptr %a
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
ret i32 %r
}
define i32 @memcpyed(ptr %src) {
; CHECK-LABEL: @memcpyed(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A]], ptr [[SRC:%.*]], i64 4, i1 false)
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[L1]]
;
%a = alloca i32
store i32 0, ptr %a
call void @callee(ptr %a)
call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %src, i64 4, i1 false)
%l1 = load i32, ptr %a
ret i32 %l1
}
define ptr @memcpyedsplit(ptr %src) {
; CHECK-LABEL: @memcpyedsplit(
; CHECK-NEXT: [[A:%.*]] = alloca { i64, i64 }, align 8
; CHECK-NEXT: store i8 1, ptr [[A]], align 1
; CHECK-NEXT: [[B:%.*]] = getelementptr i64, ptr [[A]], i32 1
; CHECK-NEXT: store ptr null, ptr [[B]], align 8
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A]], ptr [[SRC:%.*]], i64 16, i1 false)
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load ptr, ptr [[B]], align 8
; CHECK-NEXT: ret ptr [[L1]]
;
%a = alloca { i64, i64 }
store i8 1, ptr %a
%b = getelementptr i64, ptr %a, i32 1
store ptr null, ptr %b
call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %src, i64 16, i1 false)
call void @callee(ptr %a)
%l1 = load ptr, ptr %b
ret ptr %l1
}
; This struct contains padding bits. The load should not be replaced by poison.
%struct.LoadImmediateInfo = type { i32 }
define void @incompletestruct(i1 %b, i1 %c) {
; CHECK-LABEL: @incompletestruct(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[LII:%.*]] = alloca [[STRUCT_LOADIMMEDIATEINFO:%.*]], align 4
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[LII]])
; CHECK-NEXT: [[BF_CLEAR4:%.*]] = and i32 undef, -262144
; CHECK-NEXT: [[BF_SET5:%.*]] = select i1 [[B:%.*]], i32 196608, i32 131072
; CHECK-NEXT: [[BF_SET12:%.*]] = or disjoint i32 [[BF_SET5]], [[BF_CLEAR4]]
; CHECK-NEXT: store i32 [[BF_SET12]], ptr [[LII]], align 4
; CHECK-NEXT: call void @callee(ptr [[LII]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[LII]])
; CHECK-NEXT: ret void
;
entry:
%LII = alloca %struct.LoadImmediateInfo, align 4
call void @llvm.lifetime.start.p0(ptr nonnull %LII)
%bf.load = load i32, ptr %LII, align 4
%bf.clear4 = and i32 %bf.load, -262144
%bf.set5 = select i1 %b, i32 196608, i32 131072
%bf.set12 = or disjoint i32 %bf.set5, %bf.clear4
store i32 %bf.set12, ptr %LII, align 4
call void @callee(ptr %LII)
call void @llvm.lifetime.end.p0(ptr nonnull %LII)
ret void
}
define void @incompletestruct_bb(i1 %b, i1 %c) {
; CHECK-LABEL: @incompletestruct_bb(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[LII:%.*]] = alloca [[STRUCT_LOADIMMEDIATEINFO:%.*]], align 4
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[LII]])
; CHECK-NEXT: [[BF_CLEAR4:%.*]] = and i32 undef, -262144
; CHECK-NEXT: [[BF_SET5:%.*]] = select i1 [[B:%.*]], i32 196608, i32 131072
; CHECK-NEXT: [[BF_SET12:%.*]] = or disjoint i32 [[BF_SET5]], [[BF_CLEAR4]]
; CHECK-NEXT: store i32 [[BF_SET12]], ptr [[LII]], align 4
; CHECK-NEXT: call void @callee(ptr [[LII]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[LII]])
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: ret void
;
entry:
%LII = alloca %struct.LoadImmediateInfo, align 4
br i1 %c, label %if.then, label %if.end
if.then: ; preds = %entry
call void @llvm.lifetime.start.p0(ptr nonnull %LII)
%bf.load = load i32, ptr %LII, align 4
%bf.clear4 = and i32 %bf.load, -262144
%bf.set5 = select i1 %b, i32 196608, i32 131072
%bf.set12 = or disjoint i32 %bf.set5, %bf.clear4
store i32 %bf.set12, ptr %LII, align 4
call void @callee(ptr %LII)
call void @llvm.lifetime.end.p0(ptr nonnull %LII)
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
define i32 @sixteenload() {
; CHECK-LABEL: @sixteenload(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[A1:%.*]] = add i32 0, 0
; CHECK-NEXT: [[A2:%.*]] = add i32 [[A1]], 0
; CHECK-NEXT: ret i32 [[A2]]
;
%a = alloca i32
store i32 0, ptr %a
call void @callee(ptr %a)
%l1 = load i32, ptr %a
%l2 = load i32, ptr %a
%l3 = load i32, ptr %a
%l4 = load i32, ptr %a
%l5 = load i32, ptr %a
%l6 = load i32, ptr %a
%l7 = load i32, ptr %a
%l8 = load i32, ptr %a
%l9 = load i32, ptr %a
%l10 = load i32, ptr %a
%l11 = load i32, ptr %a
%l12 = load i32, ptr %a
%l13 = load i32, ptr %a
%l14 = load i32, ptr %a
%l15 = load i32, ptr %a
%l16 = load i32, ptr %a
%a1 = add i32 %l1, %l2
%a2 = add i32 %a1, %l3
ret i32 %a2
}
define i32 @testcallalloca() {
; CHECK-LABEL: @testcallalloca(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void [[A]]()
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[L1]]
;
%a = alloca i32
store i32 0, ptr %a
call void %a()
%l1 = load i32, ptr %a
ret i32 %l1
}
declare void @callee_byval(ptr byval(i32) %p)
define i32 @simple_byval() {
; CHECK-LABEL: @simple_byval(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee_byval(ptr [[A]])
; CHECK-NEXT: ret i32 0
;
%a = alloca i32
store i32 0, ptr %a
call void @callee_byval(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
declare void @callee_address_only_capture(ptr readonly captures(address) %p)
define i32 @address_only_capture() {
; CHECK-LABEL: @address_only_capture(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee_address_only_capture(ptr [[A]])
; CHECK-NEXT: ret i32 0
;
%a = alloca i32
store i32 0, ptr %a
call void @callee_address_only_capture(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
declare void @callee_read_only_capture(ptr readonly captures(address, read_provenance) %p)
define i32 @read_only_capture() {
; CHECK-LABEL: @read_only_capture(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee_read_only_capture(ptr [[A]])
; CHECK-NEXT: ret i32 0
;
%a = alloca i32
store i32 0, ptr %a
call void @callee_read_only_capture(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
declare void @callee_provenance_only_capture(ptr readonly captures(provenance) %p)
; Should not be transformed, as write-provenance is captured.
define i32 @provenance_only_capture() {
; CHECK-LABEL: @provenance_only_capture(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee_provenance_only_capture(ptr [[A]])
; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[L1]]
;
%a = alloca i32
store i32 0, ptr %a
call void @callee_provenance_only_capture(ptr %a)
%l1 = load i32, ptr %a
ret i32 %l1
}
define i32 @simple_with_lifetimes() {
; CHECK-LABEL: @simple_with_lifetimes(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[A]])
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[A]])
; CHECK-NEXT: ret i32 0
;
%a = alloca i32
call void @llvm.lifetime.start(ptr %a)
store i32 0, ptr %a
call void @callee(ptr %a)
%l1 = load i32, ptr %a
call void @llvm.lifetime.end(ptr %a)
ret i32 %l1
}
define i32 @twoalloc_with_lifetimes() {
; CHECK-LABEL: @twoalloc_with_lifetimes(
; CHECK-NEXT: [[A:%.*]] = alloca { i32, i32 }, align 8
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[A]])
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[A]], i32 1
; CHECK-NEXT: store i32 1, ptr [[B]], align 4
; CHECK-NEXT: call void @callee(ptr [[A]])
; CHECK-NEXT: [[R:%.*]] = add i32 0, 1
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[A]])
; CHECK-NEXT: ret i32 [[R]]
;
%a = alloca {i32, i32}
call void @llvm.lifetime.start(ptr %a)
store i32 0, ptr %a
%b = getelementptr i32, ptr %a, i32 1
store i32 1, ptr %b
call void @callee(ptr %a)
%l1 = load i32, ptr %a
%l2 = load i32, ptr %b
%r = add i32 %l1, %l2
call void @llvm.lifetime.end(ptr %a)
ret i32 %r
}
declare void @use.i32(i32)
; We can promote the %i load, even though there is an unknown offset load
; in the loop. It is sufficient that we know all stores.
define void @load_dyn_offset(ptr %ary) {
; CHECK-LABEL: @load_dyn_offset(
; CHECK-NEXT: [[A:%.*]] = alloca { i64, [4 x i32] }, align 8
; CHECK-NEXT: store i64 0, ptr [[A]], align 4
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[GEP]], ptr [[ARY:%.*]], i64 16, i1 false)
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], [[LOOP]] ], [ 0, [[TMP0:%.*]] ]
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: store i64 [[I_NEXT]], ptr [[A]], align 4
; CHECK-NEXT: [[GEP_I:%.*]] = getelementptr i32, ptr [[GEP]], i64 [[I]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[GEP_I]], align 4
; CHECK-NEXT: call void @use.i32(i32 [[VAL]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[I_NEXT]], 6
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
%a = alloca {i64, [4 x i32]}
store i64 0, ptr %a
%gep = getelementptr i8, ptr %a, i64 8
call void @llvm.memcpy(ptr %gep, ptr %ary, i64 16, i1 false)
br label %loop
loop:
%i = load i64, ptr %a
%i.next = add i64 %i, 1
store i64 %i.next, ptr %a
%gep.i = getelementptr i32, ptr %gep, i64 %i
%val = load i32, ptr %gep.i
call void @use.i32(i32 %val)
%cmp = icmp eq i64 %i.next, 6
br i1 %cmp, label %exit, label %loop
exit:
ret void
}
; Same as previous test, but with an unknown-offset store. We can't promote in
; that case.
define void @store_dyn_offset(ptr %ary) {
; CHECK-LABEL: @store_dyn_offset(
; CHECK-NEXT: [[A:%.*]] = alloca { i64, [4 x i32] }, align 8
; CHECK-NEXT: store i64 0, ptr [[A]], align 4
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[GEP]], ptr [[ARY:%.*]], i64 16, i1 false)
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[I:%.*]] = load i64, ptr [[A]], align 4
; CHECK-NEXT: [[I_NEXT:%.*]] = add i64 [[I]], 1
; CHECK-NEXT: store i64 [[I_NEXT]], ptr [[A]], align 4
; CHECK-NEXT: [[GEP_I:%.*]] = getelementptr i32, ptr [[GEP]], i64 [[I]]
; CHECK-NEXT: [[I_TRUNC:%.*]] = trunc i64 [[I]] to i32
; CHECK-NEXT: store i32 [[I_TRUNC]], ptr [[GEP_I]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[I_NEXT]], 6
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
%a = alloca {i64, [4 x i32]}
store i64 0, ptr %a
%gep = getelementptr i8, ptr %a, i64 8
call void @llvm.memcpy(ptr %gep, ptr %ary, i64 16, i1 false)
br label %loop
loop:
%i = load i64, ptr %a
%i.next = add i64 %i, 1
store i64 %i.next, ptr %a
%gep.i = getelementptr i32, ptr %gep, i64 %i
%i.trunc = trunc i64 %i to i32
store i32 %i.trunc, ptr %gep.i
%cmp = icmp eq i64 %i.next, 6
br i1 %cmp, label %exit, label %loop
exit:
ret void
}
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)