Imported Upstream version 5.18.0.167

Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-10-20 08:25:10 +00:00
parent e19d552987
commit b084638f15
28489 changed files with 184 additions and 3866856 deletions

View File

@ -1,234 +0,0 @@
; RUN: opt < %s -S -mtriple=aarch64-none-linux-gnu -mattr=+neon -early-cse | FileCheck %s
; RUN: opt < %s -S -mtriple=aarch64-none-linux-gnu -mattr=+neon -basicaa -early-cse-memssa | FileCheck %s
; RUN: opt < %s -S -mtriple=aarch64-none-linux-gnu -mattr=+neon -passes=early-cse | FileCheck %s
; RUN: opt < %s -S -mtriple=aarch64-none-linux-gnu -mattr=+neon -aa-pipeline=basic-aa -passes=early-cse-memssa | FileCheck %s
define <4 x i32> @test_cse(i32* %a, [2 x <4 x i32>] %s.coerce, i32 %n) {
entry:
; Check that @llvm.aarch64.neon.ld2 is optimized away by Early CSE.
; CHECK-LABEL: @test_cse
; CHECK-NOT: call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%1 = bitcast <4 x i32> %s.coerce.fca.0.extract to <16 x i8>
%2 = bitcast <4 x i32> %s.coerce.fca.1.extract to <16 x i8>
%3 = bitcast <16 x i8> %1 to <4 x i32>
%4 = bitcast <16 x i8> %2 to <4 x i32>
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %4, i8* %0)
%5 = bitcast i32* %a to i8*
%vld2 = call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8* %5)
%vld2.fca.0.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 0
%vld2.fca.1.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 1
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld2.fca.0.extract, <4 x i32> %vld2.fca.0.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
define <4 x i32> @test_cse2(i32* %a, [2 x <4 x i32>] %s.coerce, i32 %n) {
entry:
; Check that the first @llvm.aarch64.neon.st2 is optimized away by Early CSE.
; CHECK-LABEL: @test_cse2
; CHECK-NOT: call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %3, i8* %0)
; CHECK: call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %s.coerce.fca.0.extract, <4 x i32> %s.coerce.fca.1.extract, i8* %0)
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%1 = bitcast <4 x i32> %s.coerce.fca.0.extract to <16 x i8>
%2 = bitcast <4 x i32> %s.coerce.fca.1.extract to <16 x i8>
%3 = bitcast <16 x i8> %1 to <4 x i32>
%4 = bitcast <16 x i8> %2 to <4 x i32>
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %3, i8* %0)
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %4, i8* %0)
%5 = bitcast i32* %a to i8*
%vld2 = call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8* %5)
%vld2.fca.0.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 0
%vld2.fca.1.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 1
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld2.fca.0.extract, <4 x i32> %vld2.fca.0.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
define <4 x i32> @test_cse3(i32* %a, [2 x <4 x i32>] %s.coerce, i32 %n) #0 {
entry:
; Check that the first @llvm.aarch64.neon.ld2 is optimized away by Early CSE.
; CHECK-LABEL: @test_cse3
; CHECK: call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8
; CHECK-NOT: call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%vld2 = call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8* %0)
%vld2.fca.0.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 0
%vld2.fca.1.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 1
%1 = bitcast i32* %a to i8*
%vld22 = call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8* %1)
%vld22.fca.0.extract = extractvalue { <4 x i32>, <4 x i32> } %vld22, 0
%vld22.fca.1.extract = extractvalue { <4 x i32>, <4 x i32> } %vld22, 1
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld2.fca.0.extract, <4 x i32> %vld22.fca.0.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
define <4 x i32> @test_nocse(i32* %a, i32* %b, [2 x <4 x i32>] %s.coerce, i32 %n) {
entry:
; Check that the store prevents @llvm.aarch64.neon.ld2 from being optimized
; away by Early CSE.
; CHECK-LABEL: @test_nocse
; CHECK: call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%1 = bitcast <4 x i32> %s.coerce.fca.0.extract to <16 x i8>
%2 = bitcast <4 x i32> %s.coerce.fca.1.extract to <16 x i8>
%3 = bitcast <16 x i8> %1 to <4 x i32>
%4 = bitcast <16 x i8> %2 to <4 x i32>
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %4, i8* %0)
store i32 0, i32* %b, align 4
%5 = bitcast i32* %a to i8*
%vld2 = call { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8* %5)
%vld2.fca.0.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 0
%vld2.fca.1.extract = extractvalue { <4 x i32>, <4 x i32> } %vld2, 1
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld2.fca.0.extract, <4 x i32> %vld2.fca.0.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
define <4 x i32> @test_nocse2(i32* %a, [2 x <4 x i32>] %s.coerce, i32 %n) {
entry:
; Check that @llvm.aarch64.neon.ld3 is not optimized away by Early CSE due
; to mismatch between st2 and ld3.
; CHECK-LABEL: @test_nocse2
; CHECK: call { <4 x i32>, <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld3.v4i32.p0i8
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%1 = bitcast <4 x i32> %s.coerce.fca.0.extract to <16 x i8>
%2 = bitcast <4 x i32> %s.coerce.fca.1.extract to <16 x i8>
%3 = bitcast <16 x i8> %1 to <4 x i32>
%4 = bitcast <16 x i8> %2 to <4 x i32>
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %4, i8* %0)
%5 = bitcast i32* %a to i8*
%vld3 = call { <4 x i32>, <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld3.v4i32.p0i8(i8* %5)
%vld3.fca.0.extract = extractvalue { <4 x i32>, <4 x i32>, <4 x i32> } %vld3, 0
%vld3.fca.2.extract = extractvalue { <4 x i32>, <4 x i32>, <4 x i32> } %vld3, 2
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld3.fca.0.extract, <4 x i32> %vld3.fca.2.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
define <4 x i32> @test_nocse3(i32* %a, [2 x <4 x i32>] %s.coerce, i32 %n) {
entry:
; Check that @llvm.aarch64.neon.st3 is not optimized away by Early CSE due to
; mismatch between st2 and st3.
; CHECK-LABEL: @test_nocse3
; CHECK: call void @llvm.aarch64.neon.st3.v4i32.p0i8
; CHECK: call void @llvm.aarch64.neon.st2.v4i32.p0i8
%s.coerce.fca.0.extract = extractvalue [2 x <4 x i32>] %s.coerce, 0
%s.coerce.fca.1.extract = extractvalue [2 x <4 x i32>] %s.coerce, 1
br label %for.cond
for.cond: ; preds = %for.body, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%res.0 = phi <4 x i32> [ undef, %entry ], [ %call, %for.body ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%0 = bitcast i32* %a to i8*
%1 = bitcast <4 x i32> %s.coerce.fca.0.extract to <16 x i8>
%2 = bitcast <4 x i32> %s.coerce.fca.1.extract to <16 x i8>
%3 = bitcast <16 x i8> %1 to <4 x i32>
%4 = bitcast <16 x i8> %2 to <4 x i32>
call void @llvm.aarch64.neon.st3.v4i32.p0i8(<4 x i32> %4, <4 x i32> %3, <4 x i32> %3, i8* %0)
call void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32> %3, <4 x i32> %3, i8* %0)
%5 = bitcast i32* %a to i8*
%vld3 = call { <4 x i32>, <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld3.v4i32.p0i8(i8* %5)
%vld3.fca.0.extract = extractvalue { <4 x i32>, <4 x i32>, <4 x i32> } %vld3, 0
%vld3.fca.1.extract = extractvalue { <4 x i32>, <4 x i32>, <4 x i32> } %vld3, 1
%call = call <4 x i32> @vaddq_s32(<4 x i32> %vld3.fca.0.extract, <4 x i32> %vld3.fca.0.extract)
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end: ; preds = %for.cond
ret <4 x i32> %res.0
}
; Function Attrs: nounwind
declare void @llvm.aarch64.neon.st2.v4i32.p0i8(<4 x i32>, <4 x i32>, i8* nocapture)
; Function Attrs: nounwind
declare void @llvm.aarch64.neon.st3.v4i32.p0i8(<4 x i32>, <4 x i32>, <4 x i32>, i8* nocapture)
; Function Attrs: nounwind readonly
declare { <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld2.v4i32.p0i8(i8*)
; Function Attrs: nounwind readonly
declare { <4 x i32>, <4 x i32>, <4 x i32> } @llvm.aarch64.neon.ld3.v4i32.p0i8(i8*)
define internal fastcc <4 x i32> @vaddq_s32(<4 x i32> %__p0, <4 x i32> %__p1) {
entry:
%add = add <4 x i32> %__p0, %__p1
ret <4 x i32> %add
}

View File

@ -1,19 +0,0 @@
; RUN: opt -S -early-cse < %s | FileCheck %s
; RUN: opt -S -basicaa -early-cse-memssa < %s | FileCheck %s
target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-gnu"
declare { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } @llvm.aarch64.neon.ld4.v4i16.p0v4i16(<4 x i16>*)
; Although the store and the ld4 are using the same pointer, the
; data can not be reused because ld4 accesses multiple elements.
define { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } @foo() {
entry:
store <4 x i16> undef, <4 x i16>* undef, align 8
%0 = call { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } @llvm.aarch64.neon.ld4.v4i16.p0v4i16(<4 x i16>* undef)
ret { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } %0
; CHECK-LABEL: @foo(
; CHECK: store
; CHECK-NEXT: call
; CHECK-NEXT: ret
}

View File

@ -1,5 +0,0 @@
config.suffixes = ['.ll']
targets = set(config.root.targets_to_build.split())
if not 'AArch64' in targets:
config.unsupported = True

View File

@ -1,260 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
; CHECK-LABEL: @test12(
define i32 @test12(i1 %B, i32* %P1, i32* %P2) {
%load0 = load i32, i32* %P1
%1 = load atomic i32, i32* %P2 seq_cst, align 4
%load1 = load i32, i32* %P1
%sel = select i1 %B, i32 %load0, i32 %load1
ret i32 %sel
; CHECK: load i32, i32* %P1
; CHECK: load i32, i32* %P1
}
; CHECK-LABEL: @test13(
; atomic to non-atomic forwarding is legal
define i32 @test13(i1 %B, i32* %P1) {
%a = load atomic i32, i32* %P1 seq_cst, align 4
%b = load i32, i32* %P1
%res = sub i32 %a, %b
ret i32 %res
; CHECK: load atomic i32, i32* %P1
; CHECK: ret i32 0
}
; CHECK-LABEL: @test14(
; atomic to unordered atomic forwarding is legal
define i32 @test14(i1 %B, i32* %P1) {
%a = load atomic i32, i32* %P1 seq_cst, align 4
%b = load atomic i32, i32* %P1 unordered, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK: load atomic i32, i32* %P1 seq_cst
; CHECK-NEXT: ret i32 0
}
; CHECK-LABEL: @test15(
; implementation restriction: can't forward to stonger
; than unordered
define i32 @test15(i1 %B, i32* %P1, i32* %P2) {
%a = load atomic i32, i32* %P1 seq_cst, align 4
%b = load atomic i32, i32* %P1 seq_cst, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK: load atomic i32, i32* %P1
; CHECK: load atomic i32, i32* %P1
}
; CHECK-LABEL: @test16(
; forwarding non-atomic to atomic is wrong! (However,
; it would be legal to use the later value in place of the
; former in this particular example. We just don't
; do that right now.)
define i32 @test16(i1 %B, i32* %P1, i32* %P2) {
%a = load i32, i32* %P1, align 4
%b = load atomic i32, i32* %P1 unordered, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK: load i32, i32* %P1
; CHECK: load atomic i32, i32* %P1
}
; Can't DSE across a full fence
define void @fence_seq_cst_store(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @fence_seq_cst_store
; CHECK: store
; CHECK: store atomic
; CHECK: store
store i32 0, i32* %P1, align 4
store atomic i32 0, i32* %P2 seq_cst, align 4
store i32 0, i32* %P1, align 4
ret void
}
; Can't DSE across a full fence
define void @fence_seq_cst(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @fence_seq_cst
; CHECK: store
; CHECK: fence seq_cst
; CHECK: store
store i32 0, i32* %P1, align 4
fence seq_cst
store i32 0, i32* %P1, align 4
ret void
}
; Can't DSE across a full fence
define void @fence_asm_sideeffect(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @fence_asm_sideeffect
; CHECK: store
; CHECK: call void asm sideeffect
; CHECK: store
store i32 0, i32* %P1, align 4
call void asm sideeffect "", ""()
store i32 0, i32* %P1, align 4
ret void
}
; Can't DSE across a full fence
define void @fence_asm_memory(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @fence_asm_memory
; CHECK: store
; CHECK: call void asm
; CHECK: store
store i32 0, i32* %P1, align 4
call void asm "", "~{memory}"()
store i32 0, i32* %P1, align 4
ret void
}
; Can't remove a volatile load
define i32 @volatile_load(i1 %B, i32* %P1, i32* %P2) {
%a = load i32, i32* %P1, align 4
%b = load volatile i32, i32* %P1, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK-LABEL: @volatile_load
; CHECK: load i32, i32* %P1
; CHECK: load volatile i32, i32* %P1
}
; Can't remove redundant volatile loads
define i32 @redundant_volatile_load(i1 %B, i32* %P1, i32* %P2) {
%a = load volatile i32, i32* %P1, align 4
%b = load volatile i32, i32* %P1, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK-LABEL: @redundant_volatile_load
; CHECK: load volatile i32, i32* %P1
; CHECK: load volatile i32, i32* %P1
; CHECK: sub
}
; Can't DSE a volatile store
define void @volatile_store(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @volatile_store
; CHECK: store volatile
; CHECK: store
store volatile i32 0, i32* %P1, align 4
store i32 3, i32* %P1, align 4
ret void
}
; Can't DSE a redundant volatile store
define void @redundant_volatile_store(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @redundant_volatile_store
; CHECK: store volatile
; CHECK: store volatile
store volatile i32 0, i32* %P1, align 4
store volatile i32 0, i32* %P1, align 4
ret void
}
; Can value forward from volatiles
define i32 @test20(i1 %B, i32* %P1, i32* %P2) {
%a = load volatile i32, i32* %P1, align 4
%b = load i32, i32* %P1, align 4
%res = sub i32 %a, %b
ret i32 %res
; CHECK-LABEL: @test20
; CHECK: load volatile i32, i32* %P1
; CHECK: ret i32 0
}
; Can DSE a non-volatile store in favor of a volatile one
; currently a missed optimization
define void @test21(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test21
; CHECK: store
; CHECK: store volatile
store i32 0, i32* %P1, align 4
store volatile i32 3, i32* %P1, align 4
ret void
}
; Can DSE a normal store in favor of a unordered one
define void @test22(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test22
; CHECK-NEXT: store atomic
store i32 0, i32* %P1, align 4
store atomic i32 3, i32* %P1 unordered, align 4
ret void
}
; Can also DSE a unordered store in favor of a normal one
define void @test23(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test23
; CHECK-NEXT: store i32 0
store atomic i32 3, i32* %P1 unordered, align 4
store i32 0, i32* %P1, align 4
ret void
}
; As an implementation limitation, can't remove ordered stores
; Note that we could remove the earlier store if we could
; represent the required ordering.
define void @test24(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test24
; CHECK-NEXT: store atomic
; CHECK-NEXT: store i32 0
store atomic i32 3, i32* %P1 release, align 4
store i32 0, i32* %P1, align 4
ret void
}
; Can't remove volatile stores - each is independently observable and
; the count of such stores is an observable program side effect.
define void @test25(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test25
; CHECK-NEXT: store volatile
; CHECK-NEXT: store volatile
store volatile i32 3, i32* %P1, align 4
store volatile i32 0, i32* %P1, align 4
ret void
}
; Can DSE a unordered store in favor of a unordered one
define void @test26(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test26
; CHECK-NEXT: store atomic i32 3, i32* %P1 unordered, align 4
; CHECK-NEXT: ret
store atomic i32 0, i32* %P1 unordered, align 4
store atomic i32 3, i32* %P1 unordered, align 4
ret void
}
; Can DSE a unordered store in favor of a ordered one,
; but current don't due to implementation limits
define void @test27(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test27
; CHECK-NEXT: store atomic i32 0, i32* %P1 unordered, align 4
; CHECK-NEXT: store atomic i32 3, i32* %P1 release, align 4
; CHECK-NEXT: ret
store atomic i32 0, i32* %P1 unordered, align 4
store atomic i32 3, i32* %P1 release, align 4
ret void
}
; Can DSE an unordered atomic store in favor of an
; ordered one, but current don't due to implementation limits
define void @test28(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test28
; CHECK-NEXT: store atomic i32 0, i32* %P1 unordered, align 4
; CHECK-NEXT: store atomic i32 3, i32* %P1 release, align 4
; CHECK-NEXT: ret
store atomic i32 0, i32* %P1 unordered, align 4
store atomic i32 3, i32* %P1 release, align 4
ret void
}
; As an implementation limitation, can't remove ordered stores
; see also: @test24
define void @test29(i1 %B, i32* %P1, i32* %P2) {
; CHECK-LABEL: @test29
; CHECK-NEXT: store atomic
; CHECK-NEXT: store atomic
store atomic i32 3, i32* %P1 release, align 4
store atomic i32 0, i32* %P1 unordered, align 4
ret void
}

View File

@ -1,293 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
; RUN: opt < %s -S -passes=early-cse | FileCheck %s
declare void @llvm.assume(i1) nounwind
; CHECK-LABEL: @test1(
define void @test1(i8 %V, i32 *%P) {
%A = bitcast i64 42 to double ;; dead
%B = add i32 4, 19 ;; constant folds
store i32 %B, i32* %P
; CHECK-NEXT: store i32 23, i32* %P
%C = zext i8 %V to i32
%D = zext i8 %V to i32 ;; CSE
store volatile i32 %C, i32* %P
store volatile i32 %D, i32* %P
; CHECK-NEXT: %C = zext i8 %V to i32
; CHECK-NEXT: store volatile i32 %C
; CHECK-NEXT: store volatile i32 %C
%E = add i32 %C, %C
%F = add i32 %C, %C
store volatile i32 %E, i32* %P
store volatile i32 %F, i32* %P
; CHECK-NEXT: %E = add i32 %C, %C
; CHECK-NEXT: store volatile i32 %E
; CHECK-NEXT: store volatile i32 %E
%G = add nuw i32 %C, %C
store volatile i32 %G, i32* %P
; CHECK-NEXT: store volatile i32 %E
ret void
}
;; Simple load value numbering.
; CHECK-LABEL: @test2(
define i32 @test2(i32 *%P) {
%V1 = load i32, i32* %P
%V2 = load i32, i32* %P
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: ret i32 0
}
; CHECK-LABEL: @test2a(
define i32 @test2a(i32 *%P, i1 %b) {
%V1 = load i32, i32* %P
tail call void @llvm.assume(i1 %b)
%V2 = load i32, i32* %P
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: ret i32 0
}
;; Cross block load value numbering.
; CHECK-LABEL: @test3(
define i32 @test3(i32 *%P, i1 %Cond) {
%V1 = load i32, i32* %P
br i1 %Cond, label %T, label %F
T:
store i32 4, i32* %P
ret i32 42
F:
%V2 = load i32, i32* %P
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: F:
; CHECK: ret i32 0
}
; CHECK-LABEL: @test3a(
define i32 @test3a(i32 *%P, i1 %Cond, i1 %b) {
%V1 = load i32, i32* %P
br i1 %Cond, label %T, label %F
T:
store i32 4, i32* %P
ret i32 42
F:
tail call void @llvm.assume(i1 %b)
%V2 = load i32, i32* %P
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: F:
; CHECK: ret i32 0
}
;; Cross block load value numbering stops when stores happen.
; CHECK-LABEL: @test4(
define i32 @test4(i32 *%P, i1 %Cond) {
%V1 = load i32, i32* %P
br i1 %Cond, label %T, label %F
T:
ret i32 42
F:
; Clobbers V1
store i32 42, i32* %P
%V2 = load i32, i32* %P
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: F:
; CHECK: ret i32 %Diff
}
declare i32 @func(i32 *%P) readonly
;; Simple call CSE'ing.
; CHECK-LABEL: @test5(
define i32 @test5(i32 *%P) {
%V1 = call i32 @func(i32* %P)
%V2 = call i32 @func(i32* %P)
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: ret i32 0
}
;; Trivial Store->load forwarding
; CHECK-LABEL: @test6(
define i32 @test6(i32 *%P) {
store i32 42, i32* %P
%V1 = load i32, i32* %P
ret i32 %V1
; CHECK: ret i32 42
}
; CHECK-LABEL: @test6a(
define i32 @test6a(i32 *%P, i1 %b) {
store i32 42, i32* %P
tail call void @llvm.assume(i1 %b)
%V1 = load i32, i32* %P
ret i32 %V1
; CHECK: ret i32 42
}
;; Trivial dead store elimination.
; CHECK-LABEL: @test7(
define void @test7(i32 *%P) {
store i32 42, i32* %P
store i32 45, i32* %P
ret void
; CHECK-NEXT: store i32 45
; CHECK-NEXT: ret void
}
;; Readnone functions aren't invalidated by stores.
; CHECK-LABEL: @test8(
define i32 @test8(i32 *%P) {
%V1 = call i32 @func(i32* %P) readnone
store i32 4, i32* %P
%V2 = call i32 @func(i32* %P) readnone
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: ret i32 0
}
;; Trivial DSE can't be performed across a readonly call. The call
;; can observe the earlier write.
; CHECK-LABEL: @test9(
define i32 @test9(i32 *%P) {
store i32 4, i32* %P
%V1 = call i32 @func(i32* %P) readonly
store i32 5, i32* %P
ret i32 %V1
; CHECK: store i32 4, i32* %P
; CHECK-NEXT: %V1 = call i32 @func(i32* %P)
; CHECK-NEXT: store i32 5, i32* %P
; CHECK-NEXT: ret i32 %V1
}
;; Trivial DSE can be performed across a readnone call.
; CHECK-LABEL: @test10
define i32 @test10(i32 *%P) {
store i32 4, i32* %P
%V1 = call i32 @func(i32* %P) readnone
store i32 5, i32* %P
ret i32 %V1
; CHECK-NEXT: %V1 = call i32 @func(i32* %P)
; CHECK-NEXT: store i32 5, i32* %P
; CHECK-NEXT: ret i32 %V1
}
;; Trivial dead store elimination - should work for an entire series of dead stores too.
; CHECK-LABEL: @test11(
define void @test11(i32 *%P) {
store i32 42, i32* %P
store i32 43, i32* %P
store i32 44, i32* %P
store i32 45, i32* %P
ret void
; CHECK-NEXT: store i32 45
; CHECK-NEXT: ret void
}
; CHECK-LABEL: @test12(
define i32 @test12(i1 %B, i32* %P1, i32* %P2) {
%load0 = load i32, i32* %P1
%1 = load atomic i32, i32* %P2 seq_cst, align 4
%load1 = load i32, i32* %P1
%sel = select i1 %B, i32 %load0, i32 %load1
ret i32 %sel
; CHECK: load i32, i32* %P1
; CHECK: load i32, i32* %P1
}
define void @dse1(i32 *%P) {
; CHECK-LABEL: @dse1
; CHECK-NOT: store
%v = load i32, i32* %P
store i32 %v, i32* %P
ret void
}
define void @dse2(i32 *%P) {
; CHECK-LABEL: @dse2
; CHECK-NOT: store
%v = load atomic i32, i32* %P seq_cst, align 4
store i32 %v, i32* %P
ret void
}
define void @dse3(i32 *%P) {
; CHECK-LABEL: @dse3
; CHECK-NOT: store
%v = load atomic i32, i32* %P seq_cst, align 4
store atomic i32 %v, i32* %P unordered, align 4
ret void
}
define i32 @dse4(i32 *%P, i32 *%Q) {
; CHECK-LABEL: @dse4
; CHECK-NOT: store
; CHECK: ret i32 0
%a = load i32, i32* %Q
%v = load atomic i32, i32* %P unordered, align 4
store atomic i32 %v, i32* %P unordered, align 4
%b = load i32, i32* %Q
%res = sub i32 %a, %b
ret i32 %res
}
; Note that in this example, %P and %Q could in fact be the same
; pointer. %v could be different than the value observed for %a
; and that's okay because we're using relaxed memory ordering.
; The only guarantee we have to provide is that each of the loads
; has to observe some value written to that location. We do
; not have to respect the order in which those writes were done.
define i32 @dse5(i32 *%P, i32 *%Q) {
; CHECK-LABEL: @dse5
; CHECK-NOT: store
; CHECK: ret i32 0
%v = load atomic i32, i32* %P unordered, align 4
%a = load atomic i32, i32* %Q unordered, align 4
store atomic i32 %v, i32* %P unordered, align 4
%b = load atomic i32, i32* %Q unordered, align 4
%res = sub i32 %a, %b
ret i32 %res
}
define void @dse_neg1(i32 *%P) {
; CHECK-LABEL: @dse_neg1
; CHECK: store
%v = load i32, i32* %P
store i32 5, i32* %P
ret void
}
; Could remove the store, but only if ordering was somehow
; encoded.
define void @dse_neg2(i32 *%P) {
; CHECK-LABEL: @dse_neg2
; CHECK: store
%v = load i32, i32* %P
store atomic i32 %v, i32* %P seq_cst, align 4
ret void
}
@c = external global i32, align 4
declare i32 @reads_c(i32 returned)
define void @pr28763() {
entry:
; CHECK-LABEL: @pr28763(
; CHECK: store i32 0, i32* @c, align 4
; CHECK: call i32 @reads_c(i32 0)
; CHECK: store i32 2, i32* @c, align 4
%load = load i32, i32* @c, align 4
store i32 0, i32* @c, align 4
%call = call i32 @reads_c(i32 0)
store i32 2, i32* @c, align 4
ret void
}

View File

@ -1,254 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
define void @test1(float %A, float %B, float* %PA, float* %PB) {
; CHECK-LABEL: @test1(
; CHECK-NEXT: [[C:%.*]] = fadd float %A, %B
; CHECK-NEXT: store float [[C]], float* %PA
; CHECK-NEXT: store float [[C]], float* %PB
; CHECK-NEXT: ret void
;
%C = fadd float %A, %B
store float %C, float* %PA
%D = fadd float %B, %A
store float %D, float* %PB
ret void
}
define void @test2(float %A, float %B, i1* %PA, i1* %PB) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: [[C:%.*]] = fcmp oeq float %A, %B
; CHECK-NEXT: store i1 [[C]], i1* %PA
; CHECK-NEXT: store i1 [[C]], i1* %PB
; CHECK-NEXT: ret void
;
%C = fcmp oeq float %A, %B
store i1 %C, i1* %PA
%D = fcmp oeq float %B, %A
store i1 %D, i1* %PB
ret void
}
define void @test3(float %A, float %B, i1* %PA, i1* %PB) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: [[C:%.*]] = fcmp uge float %A, %B
; CHECK-NEXT: store i1 [[C]], i1* %PA
; CHECK-NEXT: store i1 [[C]], i1* %PB
; CHECK-NEXT: ret void
;
%C = fcmp uge float %A, %B
store i1 %C, i1* %PA
%D = fcmp ule float %B, %A
store i1 %D, i1* %PB
ret void
}
define void @test4(i32 %A, i32 %B, i1* %PA, i1* %PB) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 %A, %B
; CHECK-NEXT: store i1 [[C]], i1* %PA
; CHECK-NEXT: store i1 [[C]], i1* %PB
; CHECK-NEXT: ret void
;
%C = icmp eq i32 %A, %B
store i1 %C, i1* %PA
%D = icmp eq i32 %B, %A
store i1 %D, i1* %PB
ret void
}
define void @test5(i32 %A, i32 %B, i1* %PA, i1* %PB) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 %A, %B
; CHECK-NEXT: store i1 [[C]], i1* %PA
; CHECK-NEXT: store i1 [[C]], i1* %PB
; CHECK-NEXT: ret void
;
%C = icmp sgt i32 %A, %B
store i1 %C, i1* %PA
%D = icmp slt i32 %B, %A
store i1 %D, i1* %PB
ret void
}
; Min/max operands may be commuted in the compare and select.
define i8 @smin_commute(i8 %a, i8 %b) {
; CHECK-LABEL: @smin_commute(
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M1]]
; CHECK-NEXT: ret i8 [[R]]
;
%cmp1 = icmp slt i8 %a, %b
%cmp2 = icmp slt i8 %b, %a
%m1 = select i1 %cmp1, i8 %a, i8 %b
%m2 = select i1 %cmp2, i8 %b, i8 %a
%r = mul i8 %m1, %m2
ret i8 %r
}
; Min/max can also have a swapped predicate and select operands.
define i1 @smin_swapped(i8 %a, i8 %b) {
; CHECK-LABEL: @smin_swapped(
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: ret i1 true
;
%cmp1 = icmp sgt i8 %a, %b
%cmp2 = icmp slt i8 %a, %b
%m1 = select i1 %cmp1, i8 %b, i8 %a
%m2 = select i1 %cmp2, i8 %a, i8 %b
%r = icmp eq i8 %m2, %m1
ret i1 %r
}
define i8 @smax_commute(i8 %a, i8 %b) {
; CHECK-LABEL: @smax_commute(
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: ret i8 0
;
%cmp1 = icmp sgt i8 %a, %b
%cmp2 = icmp sgt i8 %b, %a
%m1 = select i1 %cmp1, i8 %a, i8 %b
%m2 = select i1 %cmp2, i8 %b, i8 %a
%r = urem i8 %m2, %m1
ret i8 %r
}
define i8 @smax_swapped(i8 %a, i8 %b) {
; CHECK-LABEL: @smax_swapped(
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: ret i8 1
;
%cmp1 = icmp slt i8 %a, %b
%cmp2 = icmp sgt i8 %a, %b
%m1 = select i1 %cmp1, i8 %b, i8 %a
%m2 = select i1 %cmp2, i8 %a, i8 %b
%r = sdiv i8 %m1, %m2
ret i8 %r
}
define i8 @umin_commute(i8 %a, i8 %b) {
; CHECK-LABEL: @umin_commute(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: ret i8 0
;
%cmp1 = icmp ult i8 %a, %b
%cmp2 = icmp ult i8 %b, %a
%m1 = select i1 %cmp1, i8 %a, i8 %b
%m2 = select i1 %cmp2, i8 %b, i8 %a
%r = sub i8 %m2, %m1
ret i8 %r
}
; Choose a vector type just to show that works.
define <2 x i8> @umin_swapped(<2 x i8> %a, <2 x i8> %b) {
; CHECK-LABEL: @umin_swapped(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt <2 x i8> %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult <2 x i8> %a, %b
; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> %b, <2 x i8> %a
; CHECK-NEXT: ret <2 x i8> zeroinitializer
;
%cmp1 = icmp ugt <2 x i8> %a, %b
%cmp2 = icmp ult <2 x i8> %a, %b
%m1 = select <2 x i1> %cmp1, <2 x i8> %b, <2 x i8> %a
%m2 = select <2 x i1> %cmp2, <2 x i8> %a, <2 x i8> %b
%r = sub <2 x i8> %m2, %m1
ret <2 x i8> %r
}
define i8 @umax_commute(i8 %a, i8 %b) {
; CHECK-LABEL: @umax_commute(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: ret i8 1
;
%cmp1 = icmp ugt i8 %a, %b
%cmp2 = icmp ugt i8 %b, %a
%m1 = select i1 %cmp1, i8 %a, i8 %b
%m2 = select i1 %cmp2, i8 %b, i8 %a
%r = udiv i8 %m1, %m2
ret i8 %r
}
define i8 @umax_swapped(i8 %a, i8 %b) {
; CHECK-LABEL: @umax_swapped(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: [[R:%.*]] = add i8 [[M1]], [[M1]]
; CHECK-NEXT: ret i8 [[R]]
;
%cmp1 = icmp ult i8 %a, %b
%cmp2 = icmp ugt i8 %a, %b
%m1 = select i1 %cmp1, i8 %b, i8 %a
%m2 = select i1 %cmp2, i8 %a, i8 %b
%r = add i8 %m2, %m1
ret i8 %r
}
; Min/max may exist with non-canonical operands. Value tracking can match those.
define i8 @smax_nsw(i8 %a, i8 %b) {
; CHECK-LABEL: @smax_nsw(
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 %a, %b
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[SUB]], 0
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 0, i8 [[SUB]]
; CHECK-NEXT: ret i8 0
;
%sub = sub nsw i8 %a, %b
%cmp1 = icmp slt i8 %a, %b
%cmp2 = icmp sgt i8 %sub, 0
%m1 = select i1 %cmp1, i8 0, i8 %sub
%m2 = select i1 %cmp2, i8 %sub, i8 0
%r = sub i8 %m2, %m1
ret i8 %r
}
define i8 @abs_swapped(i8 %a) {
; CHECK-LABEL: @abs_swapped(
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, %a
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, 0
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %a, 0
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 [[NEG]]
; CHECK-NEXT: ret i8 [[M1]]
;
%neg = sub i8 0, %a
%cmp1 = icmp sgt i8 %a, 0
%cmp2 = icmp slt i8 %a, 0
%m1 = select i1 %cmp1, i8 %a, i8 %neg
%m2 = select i1 %cmp2, i8 %neg, i8 %a
%r = or i8 %m2, %m1
ret i8 %r
}
define i8 @nabs_swapped(i8 %a) {
; CHECK-LABEL: @nabs_swapped(
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, %a
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, 0
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %a, 0
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 [[NEG]]
; CHECK-NEXT: ret i8 0
;
%neg = sub i8 0, %a
%cmp1 = icmp slt i8 %a, 0
%cmp2 = icmp sgt i8 %a, 0
%m1 = select i1 %cmp1, i8 %a, i8 %neg
%m2 = select i1 %cmp2, i8 %neg, i8 %a
%r = xor i8 %m2, %m1
ret i8 %r
}

View File

@ -1,108 +0,0 @@
; RUN: opt -early-cse -S < %s | FileCheck %s
; RUN: opt -basicaa -early-cse-memssa -S < %s | FileCheck %s
; Can we CSE a known condition to a constant?
define i1 @test(i8* %p) {
; CHECK-LABEL: @test
entry:
%cnd1 = icmp eq i8* %p, null
br i1 %cnd1, label %taken, label %untaken
taken:
; CHECK-LABEL: taken:
; CHECK-NEXT: ret i1 true
%cnd2 = icmp eq i8* %p, null
ret i1 %cnd2
untaken:
; CHECK-LABEL: untaken:
; CHECK-NEXT: ret i1 false
%cnd3 = icmp eq i8* %p, null
ret i1 %cnd3
}
; We can CSE the condition, but we *don't* know it's value after the merge
define i1 @test_neg1(i8* %p) {
; CHECK-LABEL: @test_neg1
entry:
%cnd1 = icmp eq i8* %p, null
br i1 %cnd1, label %taken, label %untaken
taken:
br label %merge
untaken:
br label %merge
merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: ret i1 %cnd1
%cnd3 = icmp eq i8* %p, null
ret i1 %cnd3
}
; Check specifically for a case where we have a unique predecessor, but
; not a single predecessor. We can not know the value of the condition here.
define i1 @test_neg2(i8* %p) {
; CHECK-LABEL: @test_neg2
entry:
%cnd1 = icmp eq i8* %p, null
br i1 %cnd1, label %merge, label %merge
merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: ret i1 %cnd1
%cnd3 = icmp eq i8* %p, null
ret i1 %cnd3
}
; Replace a use rather than CSE
define i1 @test2(i8* %p) {
; CHECK-LABEL: @test2
entry:
%cnd = icmp eq i8* %p, null
br i1 %cnd, label %taken, label %untaken
taken:
; CHECK-LABEL: taken:
; CHECK-NEXT: ret i1 true
ret i1 %cnd
untaken:
; CHECK-LABEL: untaken:
; CHECK-NEXT: ret i1 false
ret i1 %cnd
}
; Not legal to replace use given it's not dominated by edge
define i1 @test2_neg1(i8* %p) {
; CHECK-LABEL: @test2_neg1
entry:
%cnd1 = icmp eq i8* %p, null
br i1 %cnd1, label %taken, label %untaken
taken:
br label %merge
untaken:
br label %merge
merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: ret i1 %cnd1
ret i1 %cnd1
}
; Another single predecessor test, but for dominated use
define i1 @test2_neg2(i8* %p) {
; CHECK-LABEL: @test2_neg2
entry:
%cnd1 = icmp eq i8* %p, null
br i1 %cnd1, label %merge, label %merge
merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: ret i1 %cnd1
ret i1 %cnd1
}

View File

@ -1,39 +0,0 @@
; RUN: opt -early-cse -S %s | FileCheck %s
%mystruct = type { i32 }
; @var is global so that *every* GEP argument is Constant.
@var = external global %mystruct
; Control flow is to make the dominance tree consider the final icmp before it
; gets to simplify the purely constant one (%tst). Since that icmp uses the
; select that gets considered next. Finally the select simplification looks at
; the %tst icmp and we don't want it to speculate about what happens if "i32 0"
; is actually "i32 1", broken universes are automatic UB.
;
; In this case doing the speculation would create an invalid GEP(@var, 0, 1) and
; crash.
define i1 @test_constant_speculation() {
; CHECK-LABEL: define i1 @test_constant_speculation
entry:
br i1 undef, label %end, label %select
select:
; CHECK: select:
; CHECK-NOT: icmp
; CHECK-NOT: getelementptr
; CHECK-NOT: select
%tst = icmp eq i32 1, 0
%elt = getelementptr %mystruct, %mystruct* @var, i64 0, i32 0
%sel = select i1 %tst, i32* null, i32* %elt
br label %end
end:
; CHECK: end:
; CHECK: %tmp = phi i32* [ null, %entry ], [ getelementptr inbounds (%mystruct, %mystruct* @var, i64 0, i32 0), %select ]
%tmp = phi i32* [null, %entry], [%sel, %select]
%res = icmp eq i32* %tmp, null
ret i1 %res
}

View File

@ -1,174 +0,0 @@
; RUN: opt -early-cse -S < %s | FileCheck %s
; RUN: opt -basicaa -early-cse-memssa -S < %s | FileCheck %s
; Same as GVN/edge.ll, but updated to reflect EarlyCSE's less powerful
; implementation. EarlyCSE currently doesn't exploit equality comparisons
; against constants.
define i32 @f1(i32 %x) {
; CHECK-LABEL: define i32 @f1(
bb0:
%cmp = icmp eq i32 %x, 0
br i1 %cmp, label %bb2, label %bb1
bb1:
br label %bb2
bb2:
%cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
%foo = add i32 %cond, %x
ret i32 %foo
; CHECK: bb2:
; CHECK: %cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
}
define i32 @f2(i32 %x) {
; CHECK-LABEL: define i32 @f2(
bb0:
%cmp = icmp ne i32 %x, 0
br i1 %cmp, label %bb1, label %bb2
bb1:
br label %bb2
bb2:
%cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
%foo = add i32 %cond, %x
ret i32 %foo
; CHECK: bb2:
; CHECK: %cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
}
define i32 @f3(i32 %x) {
; CHECK-LABEL: define i32 @f3(
bb0:
switch i32 %x, label %bb1 [ i32 0, label %bb2]
bb1:
br label %bb2
bb2:
%cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
%foo = add i32 %cond, %x
ret i32 %foo
; CHECK: bb2:
; CHECK: %cond = phi i32 [ %x, %bb0 ], [ 0, %bb1 ]
}
declare void @g(i1)
define void @f4(i8 * %x) {
; CHECK-LABEL: define void @f4(
bb0:
%y = icmp eq i8* null, %x
br i1 %y, label %bb2, label %bb1
bb1:
br label %bb2
bb2:
%zed = icmp eq i8* null, %x
call void @g(i1 %zed)
; CHECK: call void @g(i1 %y)
ret void
}
define double @fcmp_oeq_not_zero(double %x, double %y) {
entry:
%cmp = fcmp oeq double %y, 2.0
br i1 %cmp, label %if, label %return
if:
%div = fdiv double %x, %y
br label %return
return:
%retval = phi double [ %div, %if ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_oeq_not_zero(
; CHECK: %div = fdiv double %x, %y
}
define double @fcmp_une_not_zero(double %x, double %y) {
entry:
%cmp = fcmp une double %y, 2.0
br i1 %cmp, label %return, label %else
else:
%div = fdiv double %x, %y
br label %return
return:
%retval = phi double [ %div, %else ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_une_not_zero(
; CHECK: %div = fdiv double %x, %y
}
; PR22376 - We can't propagate zero constants because -0.0
; compares equal to 0.0. If %y is -0.0 in this test case,
; we would produce the wrong sign on the infinity return value.
define double @fcmp_oeq_zero(double %x, double %y) {
entry:
%cmp = fcmp oeq double %y, 0.0
br i1 %cmp, label %if, label %return
if:
%div = fdiv double %x, %y
br label %return
return:
%retval = phi double [ %div, %if ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_oeq_zero(
; CHECK: %div = fdiv double %x, %y
}
define double @fcmp_une_zero(double %x, double %y) {
entry:
%cmp = fcmp une double %y, -0.0
br i1 %cmp, label %return, label %else
else:
%div = fdiv double %x, %y
br label %return
return:
%retval = phi double [ %div, %else ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_une_zero(
; CHECK: %div = fdiv double %x, %y
}
; We also cannot propagate a value if it's not a constant.
; This is because the value could be 0.0 or -0.0.
define double @fcmp_oeq_maybe_zero(double %x, double %y, double %z1, double %z2) {
entry:
%z = fadd double %z1, %z2
%cmp = fcmp oeq double %y, %z
br i1 %cmp, label %if, label %return
if:
%div = fdiv double %x, %z
br label %return
return:
%retval = phi double [ %div, %if ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_oeq_maybe_zero(
; CHECK: %div = fdiv double %x, %z
}
define double @fcmp_une_maybe_zero(double %x, double %y, double %z1, double %z2) {
entry:
%z = fadd double %z1, %z2
%cmp = fcmp une double %y, %z
br i1 %cmp, label %return, label %else
else:
%div = fdiv double %x, %z
br label %return
return:
%retval = phi double [ %div, %else ], [ %x, %entry ]
ret double %retval
; CHECK-LABEL: define double @fcmp_une_maybe_zero(
; CHECK: %div = fdiv double %x, %z
}

View File

@ -1,87 +0,0 @@
; RUN: opt -S -early-cse < %s | FileCheck %s
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
; NOTE: This file is testing the current implementation. Some of
; the transforms used as negative tests below would be legal, but
; only if reached through a chain of logic which EarlyCSE is incapable
; of performing. To say it differently, this file tests a conservative
; version of the memory model. If we want to extend EarlyCSE to be more
; aggressive in the future, we may need to relax some of the negative tests.
; We can value forward across the fence since we can (semantically)
; reorder the following load before the fence.
define i32 @test(i32* %addr.i) {
; CHECK-LABEL: @test
; CHECK: store
; CHECK: fence
; CHECK-NOT: load
; CHECK: ret
store i32 5, i32* %addr.i, align 4
fence release
%a = load i32, i32* %addr.i, align 4
ret i32 %a
}
; Same as above
define i32 @test2(i32* noalias %addr.i, i32* noalias %otheraddr) {
; CHECK-LABEL: @test2
; CHECK: load
; CHECK: fence
; CHECK-NOT: load
; CHECK: ret
%a = load i32, i32* %addr.i, align 4
fence release
%a2 = load i32, i32* %addr.i, align 4
%res = sub i32 %a, %a2
ret i32 %a
}
; We can not value forward across an acquire barrier since we might
; be syncronizing with another thread storing to the same variable
; followed by a release fence. If this thread observed the release
; had happened, we must present a consistent view of memory at the
; fence. Note that it would be legal to reorder '%a' after the fence
; and then remove '%a2'. The current implementation doesn't know how
; to do this, but if it learned, this test will need revised.
define i32 @test3(i32* noalias %addr.i, i32* noalias %otheraddr) {
; CHECK-LABEL: @test3
; CHECK: load
; CHECK: fence
; CHECK: load
; CHECK: sub
; CHECK: ret
%a = load i32, i32* %addr.i, align 4
fence acquire
%a2 = load i32, i32* %addr.i, align 4
%res = sub i32 %a, %a2
ret i32 %res
}
; We can not dead store eliminate accross the fence. We could in
; principal reorder the second store above the fence and then DSE either
; store, but this is beyond the simple last-store DSE which EarlyCSE
; implements.
define void @test4(i32* %addr.i) {
; CHECK-LABEL: @test4
; CHECK: store
; CHECK: fence
; CHECK: store
; CHECK: ret
store i32 5, i32* %addr.i, align 4
fence release
store i32 5, i32* %addr.i, align 4
ret void
}
; We *could* DSE across this fence, but don't. No other thread can
; observe the order of the acquire fence and the store.
define void @test5(i32* %addr.i) {
; CHECK-LABEL: @test5
; CHECK: store
; CHECK: fence
; CHECK: store
; CHECK: ret
store i32 5, i32* %addr.i, align 4
fence acquire
store i32 5, i32* %addr.i, align 4
ret void
}

View File

@ -1,19 +0,0 @@
; RUN: opt -early-cse -S < %s | FileCheck %s
; RUN: opt -basicaa -early-cse-memssa -S < %s | FileCheck %s
declare void @use(i1)
define void @test1(float %x, float %y) {
entry:
%cmp1 = fcmp nnan oeq float %y, %x
%cmp2 = fcmp oeq float %x, %y
call void @use(i1 %cmp1)
call void @use(i1 %cmp2)
ret void
}
; CHECK-LABEL: define void @test1(
; CHECK: %[[cmp:.*]] = fcmp oeq float %y, %x
; CHECK-NEXT: call void @use(i1 %[[cmp]])
; CHECK-NEXT: call void @use(i1 %[[cmp]])
; CHECK-NEXT: ret void

View File

@ -1,15 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
; Ensure we don't simplify away additions vectors of +0.0's (same as scalars).
define <4 x float> @fV( <4 x float> %a) {
; CHECK: %b = fadd <4 x float> %a, zeroinitializer
%b = fadd <4 x float> %a, <float 0.0,float 0.0,float 0.0,float 0.0>
ret <4 x float> %b
}
define <4 x float> @fW( <4 x float> %a) {
; CHECK: ret <4 x float> %a
%b = fadd <4 x float> %a, <float -0.0,float -0.0,float -0.0,float -0.0>
ret <4 x float> %b
}

View File

@ -1,25 +0,0 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -S -globals-aa -early-cse-memssa | FileCheck %s
define i16 @f1() readonly {
ret i16 0
}
declare void @f2()
; Check that EarlyCSE correctly handles function calls that don't have
; a MemoryAccess. In this case the calls to @f1 have no
; MemoryAccesses since globals-aa determines that @f1 doesn't
; read/write memory at all.
define void @f3() {
; CHECK-LABEL: @f3(
; CHECK-NEXT: [[CALL1:%.*]] = call i16 @f1()
; CHECK-NEXT: call void @f2()
; CHECK-NEXT: ret void
;
%call1 = call i16 @f1()
call void @f2()
%call2 = call i16 @f1()
ret void
}

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
; RUN: opt -early-cse -S < %s | FileCheck %s
; RUN: opt -basicaa -early-cse-memssa -S < %s | FileCheck %s
; PR12231
declare i32 @f()
define i32 @fn() {
entry:
br label %lbl_1215
lbl_1215:
%ins34 = phi i32 [ %ins35, %xxx ], [ undef, %entry ]
ret i32 %ins34
xxx:
%ins35 = call i32 @f()
br label %lbl_1215
}
; CHECK-LABEL: define i32 @fn(

View File

@ -1,27 +0,0 @@
; RUN: opt -S < %s -early-cse | FileCheck %s
declare void @llvm.sideeffect()
; Store-to-load forwarding across a @llvm.sideeffect.
; CHECK-LABEL: s2l
; CHECK-NOT: load
define float @s2l(float* %p) {
store float 0.0, float* %p
call void @llvm.sideeffect()
%t = load float, float* %p
ret float %t
}
; Redundant load elimination across a @llvm.sideeffect.
; CHECK-LABEL: rle
; CHECK: load
; CHECK-NOT: load
define float @rle(float* %p) {
%r = load float, float* %p
call void @llvm.sideeffect()
%s = load float, float* %p
%t = fadd float %r, %s
ret float %t
}

View File

@ -1,96 +0,0 @@
; RUN: opt -S -early-cse < %s | FileCheck %s
; RUN: opt -S -basicaa -early-cse-memssa < %s | FileCheck %s
declare void @clobber_and_use(i32)
define void @f_0(i32* %ptr) {
; CHECK-LABEL: @f_0(
; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0
; CHECK: call void @clobber_and_use(i32 %val0)
; CHECK: call void @clobber_and_use(i32 %val0)
; CHECK: call void @clobber_and_use(i32 %val0)
; CHECK: ret void
%val0 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val0)
%val1 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val1)
%val2 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val2)
ret void
}
define void @f_1(i32* %ptr) {
; We can forward invariant loads to non-invariant loads.
; CHECK-LABEL: @f_1(
; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0
; CHECK: call void @clobber_and_use(i32 %val0)
; CHECK: call void @clobber_and_use(i32 %val0)
%val0 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val0)
%val1 = load i32, i32* %ptr
call void @clobber_and_use(i32 %val1)
ret void
}
define void @f_2(i32* %ptr) {
; We can forward a non-invariant load into an invariant load.
; CHECK-LABEL: @f_2(
; CHECK: %val0 = load i32, i32* %ptr
; CHECK: call void @clobber_and_use(i32 %val0)
; CHECK: call void @clobber_and_use(i32 %val0)
%val0 = load i32, i32* %ptr
call void @clobber_and_use(i32 %val0)
%val1 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val1)
ret void
}
define void @f_3(i1 %cond, i32* %ptr) {
; CHECK-LABEL: @f_3(
%val0 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val0)
br i1 %cond, label %left, label %right
; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0
; CHECK: left:
; CHECK-NEXT: call void @clobber_and_use(i32 %val0)
left:
%val1 = load i32, i32* %ptr
call void @clobber_and_use(i32 %val1)
ret void
right:
ret void
}
define void @f_4(i1 %cond, i32* %ptr) {
; Negative test -- can't forward %val0 to %va1 because that'll break
; def-dominates-use.
; CHECK-LABEL: @f_4(
br i1 %cond, label %left, label %merge
left:
; CHECK: left:
; CHECK-NEXT: %val0 = load i32, i32* %ptr, !invariant.load !
; CHECK-NEXT: call void @clobber_and_use(i32 %val0)
%val0 = load i32, i32* %ptr, !invariant.load !{}
call void @clobber_and_use(i32 %val0)
br label %merge
merge:
; CHECK: merge:
; CHECK-NEXT: %val1 = load i32, i32* %ptr
; CHECK-NEXT: call void @clobber_and_use(i32 %val1)
%val1 = load i32, i32* %ptr
call void @clobber_and_use(i32 %val1)
ret void
}

View File

@ -1,71 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s
; RUN: opt < %s -S -passes=early-cse | FileCheck %s
declare {}* @llvm.invariant.start.p0i8(i64, i8* nocapture) nounwind readonly
declare void @llvm.invariant.end.p0i8({}*, i64, i8* nocapture) nounwind
; Check that we do load-load forwarding over invariant.start, since it does not
; clobber memory
define i8 @test1(i8 *%P) {
; CHECK-LABEL: @test1(
; CHECK-NEXT: %V1 = load i8, i8* %P
; CHECK-NEXT: %i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
; CHECK-NEXT: ret i8 0
%V1 = load i8, i8* %P
%i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
%V2 = load i8, i8* %P
%Diff = sub i8 %V1, %V2
ret i8 %Diff
}
; Trivial Store->load forwarding over invariant.start
define i8 @test2(i8 *%P) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: store i8 42, i8* %P
; CHECK-NEXT: %i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
; CHECK-NEXT: ret i8 42
store i8 42, i8* %P
%i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
%V1 = load i8, i8* %P
ret i8 %V1
}
; We can DSE over invariant.start calls, since the first store to
; %P is valid, and the second store is actually unreachable based on semantics
; of invariant.start.
define void @test3(i8* %P) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: %i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
; CHECK-NEXT: store i8 60, i8* %P
store i8 50, i8* %P
%i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
store i8 60, i8* %P
ret void
}
; FIXME: Now the first store can actually be eliminated, since there is no read within
; the invariant region, between start and end.
define void @test4(i8* %P) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: store i8 50, i8* %P
; CHECK-NEXT: %i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
; CHECK-NEXT: call void @llvm.invariant.end.p0i8({}* %i, i64 1, i8* %P)
; CHECK-NEXT: store i8 60, i8* %P
store i8 50, i8* %P
%i = call {}* @llvm.invariant.start.p0i8(i64 1, i8* %P)
call void @llvm.invariant.end.p0i8({}* %i, i64 1, i8* %P)
store i8 60, i8* %P
ret void
}

View File

@ -1,108 +0,0 @@
; RUN: opt < %s -S -early-cse | FileCheck %s --check-prefix=CHECK-NOMEMSSA
; RUN: opt < %s -S -basicaa -early-cse-memssa | FileCheck %s
; RUN: opt < %s -S -passes='early-cse' | FileCheck %s --check-prefix=CHECK-NOMEMSSA
; RUN: opt < %s -S -aa-pipeline=basic-aa -passes='early-cse-memssa' | FileCheck %s
@G1 = global i32 zeroinitializer
@G2 = global i32 zeroinitializer
@G3 = global i32 zeroinitializer
;; Simple load value numbering across non-clobbering store.
; CHECK-LABEL: @test1(
; CHECK-NOMEMSSA-LABEL: @test1(
define i32 @test1() {
%V1 = load i32, i32* @G1
store i32 0, i32* @G2
%V2 = load i32, i32* @G1
; CHECK-NOMEMSSA: sub i32 %V1, %V2
%Diff = sub i32 %V1, %V2
ret i32 %Diff
; CHECK: ret i32 0
}
;; Simple dead store elimination across non-clobbering store.
; CHECK-LABEL: @test2(
; CHECK-NOMEMSSA-LABEL: @test2(
define void @test2() {
entry:
%V1 = load i32, i32* @G1
; CHECK: store i32 0, i32* @G2
store i32 0, i32* @G2
; CHECK-NOT: store
; CHECK-NOMEMSSA: store i32 %V1, i32* @G1
store i32 %V1, i32* @G1
ret void
}
;; Check that memoryphi optimization happens during EarlyCSE, enabling
;; more load CSE opportunities.
; CHECK-LABEL: @test_memphiopt(
; CHECK-NOMEMSSA-LABEL: @test_memphiopt(
define void @test_memphiopt(i1 %c, i32* %p) {
; CHECK-LABEL: entry:
; CHECK-NOMEMSSA-LABEL: entry:
entry:
; CHECK: load
; CHECK-NOMEMSSA: load
%v1 = load i32, i32* @G1
br i1 %c, label %then, label %end
; CHECK-LABEL: then:
; CHECK-NOMEMSSA-LABEL: then:
then:
; CHECK: load
; CHECK-NOMEMSSA: load
%pv = load i32, i32* %p
; CHECK-NOT: store
; CHECK-NOMEMSSA-NOT: store
store i32 %pv, i32* %p
br label %end
; CHECK-LABEL: end:
; CHECK-NOMEMSSA-LABEL: end:
end:
; CHECK-NOT: load
; CHECK-NOMEMSSA: load
%v2 = load i32, i32* @G1
%sum = add i32 %v1, %v2
store i32 %sum, i32* @G2
ret void
}
;; Check that MemoryPhi optimization and MemoryUse re-optimization
;; happens during EarlyCSE, enabling more load CSE opportunities.
; CHECK-LABEL: @test_memphiopt2(
; CHECK-NOMEMSSA-LABEL: @test_memphiopt2(
define void @test_memphiopt2(i1 %c, i32* %p) {
; CHECK-LABEL: entry:
; CHECK-NOMEMSSA-LABEL: entry:
entry:
; CHECK: load
; CHECK-NOMEMSSA: load
%v1 = load i32, i32* @G1
; CHECK: store
; CHECK-NOMEMSSA: store
store i32 %v1, i32* @G2
br i1 %c, label %then, label %end
; CHECK-LABEL: then:
; CHECK-NOMEMSSA-LABEL: then:
then:
; CHECK: load
; CHECK-NOMEMSSA: load
%pv = load i32, i32* %p
; CHECK-NOT: store
; CHECK-NOMEMSSA-NOT: store
store i32 %pv, i32* %p
br label %end
; CHECK-LABEL: end:
; CHECK-NOMEMSSA-LABEL: end:
end:
; CHECK-NOT: load
; CHECK-NOMEMSSA: load
%v2 = load i32, i32* @G1
store i32 %v2, i32* @G3
ret void
}

View File

@ -1,26 +0,0 @@
; RUN: opt -early-cse-memssa -S %s | FileCheck %s
; CHECK: define void @patatino() {
; CHECK: for.cond:
; CHECK-NEXT: br i1 true, label %if.end, label %for.inc
; CHECK: if.end:
; CHECK-NEXT: %tinkywinky = load i32, i32* @b
; CHECK-NEXT: br i1 true, label %for.inc, label %for.inc
; CHECK: for.inc:
; CHECK-NEXT: ret void
@b = external global i32
define void @patatino() {
for.cond:
br i1 true, label %if.end, label %for.inc
if.end:
%tinkywinky = load i32, i32* @b
store i32 %tinkywinky, i32* @b
br i1 true, label %for.inc, label %for.inc
for.inc:
ret void
}

Some files were not shown because too many files have changed in this diff Show More