Introduce a new SROA implementation.

This is essentially a ground up re-think of the SROA pass in LLVM. It
was initially inspired by a few problems with the existing pass:
- It is subject to the bane of my existence in optimizations: arbitrary
  thresholds.
- It is overly conservative about which constructs can be split and
  promoted.
- The vector value replacement aspect is separated from the splitting
  logic, missing many opportunities where splitting and vector value
  formation can work together.
- The splitting is entirely based around the underlying type of the
  alloca, despite this type often having little to do with the reality
  of how that memory is used. This is especially prevelant with unions
  and base classes where we tail-pack derived members.
- When splitting fails (often due to the thresholds), the vector value
  replacement (again because it is separate) can kick in for
  preposterous cases where we simply should have split the value. This
  results in forming i1024 and i2048 integer "bit vectors" that
  tremendously slow down subsequnet IR optimizations (due to large
  APInts) and impede the backend's lowering.

The new design takes an approach that fundamentally is not susceptible
to many of these problems. It is the result of a discusison between
myself and Duncan Sands over IRC about how to premptively avoid these
types of problems and how to do SROA in a more principled way. Since
then, it has evolved and grown, but this remains an important aspect: it
fixes real world problems with the SROA process today.

First, the transform of SROA actually has little to do with replacement.
It has more to do with splitting. The goal is to take an aggregate
alloca and form a composition of scalar allocas which can replace it and
will be most suitable to the eventual replacement by scalar SSA values.
The actual replacement is performed by mem2reg (and in the future
SSAUpdater).

The splitting is divided into four phases. The first phase is an
analysis of the uses of the alloca. This phase recursively walks uses,
building up a dense datastructure representing the ranges of the
alloca's memory actually used and checking for uses which inhibit any
aspects of the transform such as the escape of a pointer.

Once we have a mapping of the ranges of the alloca used by individual
operations, we compute a partitioning of the used ranges. Some uses are
inherently splittable (such as memcpy and memset), while scalar uses are
not splittable. The goal is to build a partitioning that has the minimum
number of splits while placing each unsplittable use in its own
partition. Overlapping unsplittable uses belong to the same partition.
This is the target split of the aggregate alloca, and it maximizes the
number of scalar accesses which become accesses to their own alloca and
candidates for promotion.

Third, we re-walk the uses of the alloca and assign each specific memory
access to all the partitions touched so that we have dense use-lists for
each partition.

Finally, we build a new, smaller alloca for each partition and rewrite
each use of that partition to use the new alloca. During this phase the
pass will also work very hard to transform uses of an alloca into a form
suitable for promotion, including forming vector operations, speculating
loads throguh PHI nodes and selects, etc.

After splitting is complete, each newly refined alloca that is
a candidate for promotion to a scalar SSA value is run through mem2reg.

There are lots of reasonably detailed comments in the source code about
the design and algorithms, and I'm going to be trying to improve them in
subsequent commits to ensure this is well documented, as the new pass is
in many ways more complex than the old one.

Some of this is still a WIP, but the current state is reasonbly stable.
It has passed bootstrap, the nightly test suite, and Duncan has run it
successfully through the ACATS and DragonEgg test suites. That said, it
remains behind a default-off flag until the last few pieces are in
place, and full testing can be done.

Specific areas I'm looking at next:
- Improved comments and some code cleanup from reviews.
- SSAUpdater and enabling this pass inside the CGSCC pass manager.
- Some datastructure tuning and compile-time measurements.
- More aggressive FCA splitting and vector formation.

Many thanks to Duncan Sands for the thorough final review, as well as
Benjamin Kramer for lots of review during the process of writing this
pass, and Daniel Berlin for reviewing the data structures and algorithms
and general theory of the pass. Also, several other people on IRC, over
lunch tables, etc for lots of feedback and advice.

llvm-svn: 163883
This commit is contained in:
Chandler Carruth 2012-09-14 09:22:59 +00:00
parent 291d47efdf
commit 1b398ae0ae
10 changed files with 3907 additions and 2 deletions

View File

@ -221,6 +221,7 @@ void initializeRegionOnlyViewerPass(PassRegistry&);
void initializeRegionPrinterPass(PassRegistry&);
void initializeRegionViewerPass(PassRegistry&);
void initializeSCCPPass(PassRegistry&);
void initializeSROAPass(PassRegistry&);
void initializeSROA_DTPass(PassRegistry&);
void initializeSROA_SSAUpPass(PassRegistry&);
void initializeScalarEvolutionAliasAnalysisPass(PassRegistry&);

View File

@ -68,6 +68,12 @@ FunctionPass *createDeadStoreEliminationPass();
//
FunctionPass *createAggressiveDCEPass();
//===----------------------------------------------------------------------===//
//
// SROA - Replace aggregates or pieces of aggregates with scalar SSA values.
//
FunctionPass *createSROAPass();
//===----------------------------------------------------------------------===//
//
// ScalarReplAggregates - Break up alloca's of aggregates into multiple allocas

View File

@ -40,6 +40,10 @@ UseGVNAfterVectorization("use-gvn-after-vectorization",
cl::init(false), cl::Hidden,
cl::desc("Run GVN instead of Early CSE after vectorization passes"));
static cl::opt<bool> UseNewSROA("use-new-sroa",
cl::init(true), cl::Hidden,
cl::desc("Enable the new, experimental SROA pass"));
PassManagerBuilder::PassManagerBuilder() {
OptLevel = 2;
SizeLevel = 0;
@ -100,7 +104,10 @@ void PassManagerBuilder::populateFunctionPassManager(FunctionPassManager &FPM) {
addInitialAliasAnalysisPasses(FPM);
FPM.add(createCFGSimplificationPass());
FPM.add(createScalarReplAggregatesPass());
if (UseNewSROA)
FPM.add(createSROAPass());
else
FPM.add(createScalarReplAggregatesPass());
FPM.add(createEarlyCSEPass());
FPM.add(createLowerExpectIntrinsicPass());
}
@ -265,7 +272,10 @@ void PassManagerBuilder::populateLTOPassManager(PassManagerBase &PM,
PM.add(createInstructionCombiningPass());
PM.add(createJumpThreadingPass());
// Break up allocas
PM.add(createScalarReplAggregatesPass());
if (UseNewSROA)
PM.add(createSROAPass());
else
PM.add(createScalarReplAggregatesPass());
// Run a few AA driven optimizations here and now, to cleanup the code.
PM.add(createFunctionAttrsPass()); // Add nocapture.

View File

@ -25,6 +25,7 @@ add_llvm_library(LLVMScalarOpts
Reassociate.cpp
Reg2Mem.cpp
SCCP.cpp
SROA.cpp
Scalar.cpp
ScalarReplAggregates.cpp
SimplifyCFGPass.cpp

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) {
initializeRegToMemPass(Registry);
initializeSCCPPass(Registry);
initializeIPSCCPPass(Registry);
initializeSROAPass(Registry);
initializeSROA_DTPass(Registry);
initializeSROA_SSAUpPass(Registry);
initializeCFGSimplifyPassPass(Registry);

View File

@ -0,0 +1,741 @@
; RUN: opt < %s -sroa -S | FileCheck %s
target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define i32 @test0() {
; CHECK: @test0
; CHECK-NOT: alloca
; CHECK: ret i32
entry:
%a1 = alloca i32
%a2 = alloca float
store i32 0, i32* %a1
%v1 = load i32* %a1
store float 0.0, float* %a2
%v2 = load float * %a2
%v2.int = bitcast float %v2 to i32
%sum1 = add i32 %v1, %v2.int
ret i32 %sum1
}
define i32 @test1() {
; CHECK: @test1
; CHECK-NOT: alloca
; CHECK: ret i32 0
entry:
%X = alloca { i32, float }
%Y = getelementptr { i32, float }* %X, i64 0, i32 0
store i32 0, i32* %Y
%Z = load i32* %Y
ret i32 %Z
}
define i64 @test2(i64 %X) {
; CHECK: @test2
; CHECK-NOT: alloca
; CHECK: ret i64 %X
entry:
%A = alloca [8 x i8]
%B = bitcast [8 x i8]* %A to i64*
store i64 %X, i64* %B
br label %L2
L2:
%Z = load i64* %B
ret i64 %Z
}
define void @test3(i8* %dst, i8* %src) {
; CHECK: @test3
entry:
%a = alloca [300 x i8]
; CHECK-NOT: alloca
; CHECK: %[[test3_a1:.*]] = alloca [42 x i8]
; CHECK-NEXT: %[[test3_a2:.*]] = alloca [99 x i8]
; CHECK-NEXT: %[[test3_a3:.*]] = alloca [16 x i8]
; CHECK-NEXT: %[[test3_a4:.*]] = alloca [42 x i8]
; CHECK-NEXT: %[[test3_a5:.*]] = alloca [7 x i8]
; CHECK-NEXT: %[[test3_a6:.*]] = alloca [7 x i8]
; CHECK-NEXT: %[[test3_a7:.*]] = alloca [85 x i8]
%b = getelementptr [300 x i8]* %a, i64 0, i64 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %b, i8* %src, i32 300, i32 1, i1 false)
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [42 x i8]* %[[test3_a1]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %src, i32 42
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 42
; CHECK-NEXT: %[[test3_r1:.*]] = load i8* %[[gep]]
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 43
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [99 x i8]* %[[test3_a2]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 99
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 142
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 16
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 158
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [42 x i8]* %[[test3_a4]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 42
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 200
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 207
; CHECK-NEXT: %[[test3_r2:.*]] = load i8* %[[gep]]
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 208
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 215
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [85 x i8]* %[[test3_a7]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 85
; Clobber a single element of the array, this should be promotable.
%c = getelementptr [300 x i8]* %a, i64 0, i64 42
store i8 0, i8* %c
; Make a sequence of overlapping stores to the array. These overlap both in
; forward strides and in shrinking accesses.
%overlap.1.i8 = getelementptr [300 x i8]* %a, i64 0, i64 142
%overlap.2.i8 = getelementptr [300 x i8]* %a, i64 0, i64 143
%overlap.3.i8 = getelementptr [300 x i8]* %a, i64 0, i64 144
%overlap.4.i8 = getelementptr [300 x i8]* %a, i64 0, i64 145
%overlap.5.i8 = getelementptr [300 x i8]* %a, i64 0, i64 146
%overlap.6.i8 = getelementptr [300 x i8]* %a, i64 0, i64 147
%overlap.7.i8 = getelementptr [300 x i8]* %a, i64 0, i64 148
%overlap.8.i8 = getelementptr [300 x i8]* %a, i64 0, i64 149
%overlap.9.i8 = getelementptr [300 x i8]* %a, i64 0, i64 150
%overlap.1.i16 = bitcast i8* %overlap.1.i8 to i16*
%overlap.1.i32 = bitcast i8* %overlap.1.i8 to i32*
%overlap.1.i64 = bitcast i8* %overlap.1.i8 to i64*
%overlap.2.i64 = bitcast i8* %overlap.2.i8 to i64*
%overlap.3.i64 = bitcast i8* %overlap.3.i8 to i64*
%overlap.4.i64 = bitcast i8* %overlap.4.i8 to i64*
%overlap.5.i64 = bitcast i8* %overlap.5.i8 to i64*
%overlap.6.i64 = bitcast i8* %overlap.6.i8 to i64*
%overlap.7.i64 = bitcast i8* %overlap.7.i8 to i64*
%overlap.8.i64 = bitcast i8* %overlap.8.i8 to i64*
%overlap.9.i64 = bitcast i8* %overlap.9.i8 to i64*
store i8 1, i8* %overlap.1.i8
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 0
; CHECK-NEXT: store i8 1, i8* %[[gep]]
store i16 1, i16* %overlap.1.i16
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [16 x i8]* %[[test3_a3]] to i16*
; CHECK-NEXT: store i16 1, i16* %[[bitcast]]
store i32 1, i32* %overlap.1.i32
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [16 x i8]* %[[test3_a3]] to i32*
; CHECK-NEXT: store i32 1, i32* %[[bitcast]]
store i64 1, i64* %overlap.1.i64
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [16 x i8]* %[[test3_a3]] to i64*
; CHECK-NEXT: store i64 1, i64* %[[bitcast]]
store i64 2, i64* %overlap.2.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 1
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 2, i64* %[[bitcast]]
store i64 3, i64* %overlap.3.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 2
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 3, i64* %[[bitcast]]
store i64 4, i64* %overlap.4.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 3
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 4, i64* %[[bitcast]]
store i64 5, i64* %overlap.5.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 4
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 5, i64* %[[bitcast]]
store i64 6, i64* %overlap.6.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 5
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 6, i64* %[[bitcast]]
store i64 7, i64* %overlap.7.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 6
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 7, i64* %[[bitcast]]
store i64 8, i64* %overlap.8.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 7
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 8, i64* %[[bitcast]]
store i64 9, i64* %overlap.9.i64
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 8
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i64*
; CHECK-NEXT: store i64 9, i64* %[[bitcast]]
; Make two sequences of overlapping stores with more gaps and irregularities.
%overlap2.1.0.i8 = getelementptr [300 x i8]* %a, i64 0, i64 200
%overlap2.1.1.i8 = getelementptr [300 x i8]* %a, i64 0, i64 201
%overlap2.1.2.i8 = getelementptr [300 x i8]* %a, i64 0, i64 202
%overlap2.1.3.i8 = getelementptr [300 x i8]* %a, i64 0, i64 203
%overlap2.2.0.i8 = getelementptr [300 x i8]* %a, i64 0, i64 208
%overlap2.2.1.i8 = getelementptr [300 x i8]* %a, i64 0, i64 209
%overlap2.2.2.i8 = getelementptr [300 x i8]* %a, i64 0, i64 210
%overlap2.2.3.i8 = getelementptr [300 x i8]* %a, i64 0, i64 211
%overlap2.1.0.i16 = bitcast i8* %overlap2.1.0.i8 to i16*
%overlap2.1.0.i32 = bitcast i8* %overlap2.1.0.i8 to i32*
%overlap2.1.1.i32 = bitcast i8* %overlap2.1.1.i8 to i32*
%overlap2.1.2.i32 = bitcast i8* %overlap2.1.2.i8 to i32*
%overlap2.1.3.i32 = bitcast i8* %overlap2.1.3.i8 to i32*
store i8 1, i8* %overlap2.1.0.i8
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 0
; CHECK-NEXT: store i8 1, i8* %[[gep]]
store i16 1, i16* %overlap2.1.0.i16
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [7 x i8]* %[[test3_a5]] to i16*
; CHECK-NEXT: store i16 1, i16* %[[bitcast]]
store i32 1, i32* %overlap2.1.0.i32
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [7 x i8]* %[[test3_a5]] to i32*
; CHECK-NEXT: store i32 1, i32* %[[bitcast]]
store i32 2, i32* %overlap2.1.1.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 1
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 2, i32* %[[bitcast]]
store i32 3, i32* %overlap2.1.2.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 2
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 3, i32* %[[bitcast]]
store i32 4, i32* %overlap2.1.3.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 3
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 4, i32* %[[bitcast]]
%overlap2.2.0.i32 = bitcast i8* %overlap2.2.0.i8 to i32*
%overlap2.2.1.i16 = bitcast i8* %overlap2.2.1.i8 to i16*
%overlap2.2.1.i32 = bitcast i8* %overlap2.2.1.i8 to i32*
%overlap2.2.2.i32 = bitcast i8* %overlap2.2.2.i8 to i32*
%overlap2.2.3.i32 = bitcast i8* %overlap2.2.3.i8 to i32*
store i32 1, i32* %overlap2.2.0.i32
; CHECK-NEXT: %[[bitcast:.*]] = bitcast [7 x i8]* %[[test3_a6]] to i32*
; CHECK-NEXT: store i32 1, i32* %[[bitcast]]
store i8 1, i8* %overlap2.2.1.i8
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 1
; CHECK-NEXT: store i8 1, i8* %[[gep]]
store i16 1, i16* %overlap2.2.1.i16
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 1
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: store i16 1, i16* %[[bitcast]]
store i32 1, i32* %overlap2.2.1.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 1
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 1, i32* %[[bitcast]]
store i32 3, i32* %overlap2.2.2.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 2
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 3, i32* %[[bitcast]]
store i32 4, i32* %overlap2.2.3.i32
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 3
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i32*
; CHECK-NEXT: store i32 4, i32* %[[bitcast]]
%overlap2.prefix = getelementptr i8* %overlap2.1.1.i8, i64 -4
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %overlap2.prefix, i8* %src, i32 8, i32 1, i1 false)
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [42 x i8]* %[[test3_a4]], i64 0, i64 39
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %src, i32 3
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 3
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 5
; Bridge between the overlapping areas
call void @llvm.memset.p0i8.i32(i8* %overlap2.1.2.i8, i8 42, i32 8, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 2
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %[[gep]], i8 42, i32 5
; ...promoted i8 store...
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %[[gep]], i8 42, i32 2
; Entirely within the second overlap.
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %overlap2.2.1.i8, i8* %src, i32 5, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 1
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep]], i8* %src, i32 5
; Trailing past the second overlap.
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %overlap2.2.2.i8, i8* %src, i32 8, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 2
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep]], i8* %src, i32 5
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 5
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [85 x i8]* %[[test3_a7]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 3
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %b, i32 300, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [42 x i8]* %[[test3_a1]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %[[gep]], i32 42
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 42
; CHECK-NEXT: store i8 0, i8* %[[gep]]
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 43
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [99 x i8]* %[[test3_a2]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 99
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 142
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [16 x i8]* %[[test3_a3]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 16
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 158
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [42 x i8]* %[[test3_a4]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 42
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 200
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 207
; CHECK-NEXT: store i8 42, i8* %[[gep]]
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 208
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test3_a6]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 215
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [85 x i8]* %[[test3_a7]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 85
ret void
}
define void @test4(i8* %dst, i8* %src) {
; CHECK: @test4
entry:
%a = alloca [100 x i8]
; CHECK-NOT: alloca
; CHECK: %[[test4_a1:.*]] = alloca [20 x i8]
; CHECK-NEXT: %[[test4_a2:.*]] = alloca [7 x i8]
; CHECK-NEXT: %[[test4_a3:.*]] = alloca [10 x i8]
; CHECK-NEXT: %[[test4_a4:.*]] = alloca [7 x i8]
; CHECK-NEXT: %[[test4_a5:.*]] = alloca [7 x i8]
; CHECK-NEXT: %[[test4_a6:.*]] = alloca [40 x i8]
%b = getelementptr [100 x i8]* %a, i64 0, i64 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %b, i8* %src, i32 100, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [20 x i8]* %[[test4_a1]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep]], i8* %src, i32 20
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 20
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: %[[test4_r1:.*]] = load i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 22
; CHECK-NEXT: %[[test4_r2:.*]] = load i8* %[[gep]]
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 23
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a2]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 30
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [10 x i8]* %[[test4_a3]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 10
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 40
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: %[[test4_r3:.*]] = load i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 42
; CHECK-NEXT: %[[test4_r4:.*]] = load i8* %[[gep]]
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 43
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a4]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 50
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: %[[test4_r5:.*]] = load i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %src, i64 52
; CHECK-NEXT: %[[test4_r6:.*]] = load i8* %[[gep]]
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 53
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds i8* %src, i64 60
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [40 x i8]* %[[test4_a6]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 40
%a.src.1 = getelementptr [100 x i8]* %a, i64 0, i64 20
%a.dst.1 = getelementptr [100 x i8]* %a, i64 0, i64 40
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a.dst.1, i8* %a.src.1, i32 10, i32 1, i1 false)
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a4]], i64 0, i64 0
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a2]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; Clobber a single element of the array, this should be promotable, and be deleted.
%c = getelementptr [100 x i8]* %a, i64 0, i64 42
store i8 0, i8* %c
%a.src.2 = getelementptr [100 x i8]* %a, i64 0, i64 50
call void @llvm.memmove.p0i8.p0i8.i32(i8* %a.dst.1, i8* %a.src.2, i32 10, i32 1, i1 false)
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a4]], i64 0, i64 0
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %b, i32 100, i32 1, i1 false)
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds [20 x i8]* %[[test4_a1]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %[[gep]], i32 20
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 20
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: store i16 %[[test4_r1]], i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 22
; CHECK-NEXT: store i8 %[[test4_r2]], i8* %[[gep]]
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 23
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a2]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 30
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [10 x i8]* %[[test4_a3]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 10
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 40
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: store i16 %[[test4_r5]], i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 42
; CHECK-NEXT: store i8 %[[test4_r6]], i8* %[[gep]]
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 43
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a4]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 50
; CHECK-NEXT: %[[bitcast:.*]] = bitcast i8* %[[gep]] to i16*
; CHECK-NEXT: store i16 %[[test4_r5]], i16* %[[bitcast]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds i8* %dst, i64 52
; CHECK-NEXT: store i8 %[[test4_r6]], i8* %[[gep]]
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 53
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [7 x i8]* %[[test4_a5]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 7
; CHECK-NEXT: %[[gep_dst:.*]] = getelementptr inbounds i8* %dst, i64 60
; CHECK-NEXT: %[[gep_src:.*]] = getelementptr inbounds [40 x i8]* %[[test4_a6]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[gep_dst]], i8* %[[gep_src]], i32 40
ret void
}
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind
declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind
declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) nounwind
define i16 @test5() {
; CHECK: @test5
; CHECK: alloca float
; CHECK: ret i16 %
entry:
%a = alloca [4 x i8]
%fptr = bitcast [4 x i8]* %a to float*
store float 0.0, float* %fptr
%ptr = getelementptr [4 x i8]* %a, i32 0, i32 2
%iptr = bitcast i8* %ptr to i16*
%val = load i16* %iptr
ret i16 %val
}
define i32 @test6() {
; CHECK: @test6
; CHECK: alloca i32
; CHECK-NEXT: store volatile i32
; CHECK-NEXT: load i32*
; CHECK-NEXT: ret i32
entry:
%a = alloca [4 x i8]
%ptr = getelementptr [4 x i8]* %a, i32 0, i32 0
call void @llvm.memset.p0i8.i32(i8* %ptr, i8 42, i32 4, i32 1, i1 true)
%iptr = bitcast i8* %ptr to i32*
%val = load i32* %iptr
ret i32 %val
}
define void @test7(i8* %src, i8* %dst) {
; CHECK: @test7
; CHECK: alloca i32
; CHECK-NEXT: bitcast i8* %src to i32*
; CHECK-NEXT: load volatile i32*
; CHECK-NEXT: store volatile i32
; CHECK-NEXT: bitcast i8* %dst to i32*
; CHECK-NEXT: load volatile i32*
; CHECK-NEXT: store volatile i32
; CHECK-NEXT: ret
entry:
%a = alloca [4 x i8]
%ptr = getelementptr [4 x i8]* %a, i32 0, i32 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr, i8* %src, i32 4, i32 1, i1 true)
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %ptr, i32 4, i32 1, i1 true)
ret void
}
%S1 = type { i32, i32, [16 x i8] }
%S2 = type { %S1*, %S2* }
define %S2 @test8(%S2* %s2) {
; CHECK: @test8
entry:
%new = alloca %S2
; CHECK-NOT: alloca
%s2.next.ptr = getelementptr %S2* %s2, i64 0, i32 1
%s2.next = load %S2** %s2.next.ptr
; CHECK: %[[gep:.*]] = getelementptr %S2* %s2, i64 0, i32 1
; CHECK-NEXT: %[[next:.*]] = load %S2** %[[gep]]
%s2.next.s1.ptr = getelementptr %S2* %s2.next, i64 0, i32 0
%s2.next.s1 = load %S1** %s2.next.s1.ptr
%new.s1.ptr = getelementptr %S2* %new, i64 0, i32 0
store %S1* %s2.next.s1, %S1** %new.s1.ptr
%s2.next.next.ptr = getelementptr %S2* %s2.next, i64 0, i32 1
%s2.next.next = load %S2** %s2.next.next.ptr
%new.next.ptr = getelementptr %S2* %new, i64 0, i32 1
store %S2* %s2.next.next, %S2** %new.next.ptr
; CHECK-NEXT: %[[gep:.*]] = getelementptr %S2* %[[next]], i64 0, i32 0
; CHECK-NEXT: %[[next_s1:.*]] = load %S1** %[[gep]]
; CHECK-NEXT: %[[gep:.*]] = getelementptr %S2* %[[next]], i64 0, i32 1
; CHECK-NEXT: %[[next_next:.*]] = load %S2** %[[gep]]
%new.s1 = load %S1** %new.s1.ptr
%result1 = insertvalue %S2 undef, %S1* %new.s1, 0
; CHECK-NEXT: %[[result1:.*]] = insertvalue %S2 undef, %S1* %[[next_s1]], 0
%new.next = load %S2** %new.next.ptr
%result2 = insertvalue %S2 %result1, %S2* %new.next, 1
; CHECK-NEXT: %[[result2:.*]] = insertvalue %S2 %[[result1]], %S2* %[[next_next]], 1
ret %S2 %result2
; CHECK-NEXT: ret %S2 %[[result2]]
}
define i64 @test9() {
; Ensure we can handle loads off the end of an alloca even when wrapped in
; weird bit casts and types. The result is undef, but this shouldn't crash
; anything.
; CHECK: @test9
; CHECK-NOT: alloca
; CHECK: ret i64 undef
entry:
%a = alloca { [3 x i8] }
%gep1 = getelementptr inbounds { [3 x i8] }* %a, i32 0, i32 0, i32 0
store i8 0, i8* %gep1, align 1
%gep2 = getelementptr inbounds { [3 x i8] }* %a, i32 0, i32 0, i32 1
store i8 0, i8* %gep2, align 1
%gep3 = getelementptr inbounds { [3 x i8] }* %a, i32 0, i32 0, i32 2
store i8 26, i8* %gep3, align 1
%cast = bitcast { [3 x i8] }* %a to { i64 }*
%elt = getelementptr inbounds { i64 }* %cast, i32 0, i32 0
%result = load i64* %elt
ret i64 %result
}
define %S2* @test10() {
; CHECK: @test10
; CHECK-NOT: alloca %S2*
; CHECK: ret %S2* null
entry:
%a = alloca [8 x i8]
%ptr = getelementptr [8 x i8]* %a, i32 0, i32 0
call void @llvm.memset.p0i8.i32(i8* %ptr, i8 0, i32 8, i32 1, i1 false)
%s2ptrptr = bitcast i8* %ptr to %S2**
%s2ptr = load %S2** %s2ptrptr
ret %S2* %s2ptr
}
define i32 @test11() {
; CHECK: @test11
; CHECK-NOT: alloca
; CHECK: ret i32 0
entry:
%X = alloca i32
br i1 undef, label %good, label %bad
good:
%Y = getelementptr i32* %X, i64 0
store i32 0, i32* %Y
%Z = load i32* %Y
ret i32 %Z
bad:
%Y2 = getelementptr i32* %X, i64 1
store i32 0, i32* %Y2
%Z2 = load i32* %Y2
ret i32 %Z2
}
define i32 @test12() {
; CHECK: @test12
; CHECK: alloca i24
;
; FIXME: SROA should promote accesses to this into whole i24 operations instead
; of i8 operations.
; CHECK: store i8 0
; CHECK: store i8 0
; CHECK: store i8 0
;
; CHECK: load i24*
entry:
%a = alloca [3 x i8]
%b0ptr = getelementptr [3 x i8]* %a, i64 0, i32 0
store i8 0, i8* %b0ptr
%b1ptr = getelementptr [3 x i8]* %a, i64 0, i32 1
store i8 0, i8* %b1ptr
%b2ptr = getelementptr [3 x i8]* %a, i64 0, i32 2
store i8 0, i8* %b2ptr
%iptr = bitcast [3 x i8]* %a to i24*
%i = load i24* %iptr
%ret = zext i24 %i to i32
ret i32 %ret
}
define i32 @test13() {
; Ensure we don't crash and handle undefined loads that straddle the end of the
; allocation.
; CHECK: @test13
; CHECK: %[[ret:.*]] = zext i16 undef to i32
; CHECK: ret i32 %[[ret]]
entry:
%a = alloca [3 x i8]
%b0ptr = getelementptr [3 x i8]* %a, i64 0, i32 0
store i8 0, i8* %b0ptr
%b1ptr = getelementptr [3 x i8]* %a, i64 0, i32 1
store i8 0, i8* %b1ptr
%b2ptr = getelementptr [3 x i8]* %a, i64 0, i32 2
store i8 0, i8* %b2ptr
%iptrcast = bitcast [3 x i8]* %a to i16*
%iptrgep = getelementptr i16* %iptrcast, i64 1
%i = load i16* %iptrgep
%ret = zext i16 %i to i32
ret i32 %ret
}
%test14.struct = type { [3 x i32] }
define void @test14(...) nounwind uwtable {
; This is a strange case where we split allocas into promotable partitions, but
; also gain enough data to prove they must be dead allocas due to GEPs that walk
; across two adjacent allocas. Test that we don't try to promote or otherwise
; do bad things to these dead allocas, they should just be removed.
; CHECK: @test14
; CHECK-NEXT: entry:
; CHECK-NEXT: ret void
entry:
%a = alloca %test14.struct
%p = alloca %test14.struct*
%0 = bitcast %test14.struct* %a to i8*
%1 = getelementptr i8* %0, i64 12
%2 = bitcast i8* %1 to %test14.struct*
%3 = getelementptr inbounds %test14.struct* %2, i32 0, i32 0
%4 = getelementptr inbounds %test14.struct* %a, i32 0, i32 0
%5 = bitcast [3 x i32]* %3 to i32*
%6 = bitcast [3 x i32]* %4 to i32*
%7 = load i32* %6, align 4
store i32 %7, i32* %5, align 4
%8 = getelementptr inbounds i32* %5, i32 1
%9 = getelementptr inbounds i32* %6, i32 1
%10 = load i32* %9, align 4
store i32 %10, i32* %8, align 4
%11 = getelementptr inbounds i32* %5, i32 2
%12 = getelementptr inbounds i32* %6, i32 2
%13 = load i32* %12, align 4
store i32 %13, i32* %11, align 4
ret void
}
define i32 @test15(i1 %flag) nounwind uwtable {
; Ensure that when there are dead instructions using an alloca that are not
; loads or stores we still delete them during partitioning and rewriting.
; Otherwise we'll go to promote them while thy still have unpromotable uses.
; CHECK: @test15
; CHECK-NEXT: entry:
; CHECK-NEXT: br label %loop
; CHECK: loop:
; CHECK-NEXT: br label %loop
entry:
%l0 = alloca i64
%l1 = alloca i64
%l2 = alloca i64
%l3 = alloca i64
br label %loop
loop:
%dead3 = phi i8* [ %gep3, %loop ], [ null, %entry ]
store i64 1879048192, i64* %l0, align 8
%bc0 = bitcast i64* %l0 to i8*
%gep0 = getelementptr i8* %bc0, i64 3
%dead0 = bitcast i8* %gep0 to i64*
store i64 1879048192, i64* %l1, align 8
%bc1 = bitcast i64* %l1 to i8*
%gep1 = getelementptr i8* %bc1, i64 3
%dead1 = getelementptr i8* %gep1, i64 1
store i64 1879048192, i64* %l2, align 8
%bc2 = bitcast i64* %l2 to i8*
%gep2.1 = getelementptr i8* %bc2, i64 1
%gep2.2 = getelementptr i8* %bc2, i64 3
; Note that this select should get visited multiple times due to using two
; different GEPs off the same alloca. We should only delete it once.
%dead2 = select i1 %flag, i8* %gep2.1, i8* %gep2.2
store i64 1879048192, i64* %l3, align 8
%bc3 = bitcast i64* %l3 to i8*
%gep3 = getelementptr i8* %bc3, i64 3
br label %loop
}
define void @test16(i8* %src, i8* %dst) {
; Ensure that we can promote an alloca of [3 x i8] to an i24 SSA value.
; CHECK: @test16
; CHECK-NOT: alloca
; CHECK: %[[srccast:.*]] = bitcast i8* %src to i24*
; CHECK-NEXT: load i24* %[[srccast]]
; CHECK-NEXT: %[[dstcast:.*]] = bitcast i8* %dst to i24*
; CHECK-NEXT: store i24 0, i24* %[[dstcast]]
; CHECK-NEXT: ret void
entry:
%a = alloca [3 x i8]
%ptr = getelementptr [3 x i8]* %a, i32 0, i32 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr, i8* %src, i32 4, i32 1, i1 false)
%cast = bitcast i8* %ptr to i24*
store i24 0, i24* %cast
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %ptr, i32 4, i32 1, i1 false)
ret void
}
define void @test17(i8* %src, i8* %dst) {
; Ensure that we can rewrite unpromotable memcpys which extend past the end of
; the alloca.
; CHECK: @test17
; CHECK: %[[a:.*]] = alloca [3 x i8]
; CHECK-NEXT: %[[ptr:.*]] = getelementptr [3 x i8]* %[[a]], i32 0, i32 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[ptr]], i8* %src,
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %[[ptr]],
; CHECK-NEXT: ret void
entry:
%a = alloca [3 x i8]
%ptr = getelementptr [3 x i8]* %a, i32 0, i32 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr, i8* %src, i32 4, i32 1, i1 true)
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %ptr, i32 4, i32 1, i1 true)
ret void
}
define void @test18(i8* %src, i8* %dst, i32 %size) {
; Preserve transfer instrinsics with a variable size, even if they overlap with
; fixed size operations. Further, continue to split and promote allocas preceding
; the variable sized intrinsic.
; CHECK: @test18
; CHECK: %[[a:.*]] = alloca [34 x i8]
; CHECK: %[[srcgep1:.*]] = getelementptr inbounds i8* %src, i64 4
; CHECK-NEXT: %[[srccast1:.*]] = bitcast i8* %[[srcgep1]] to i32*
; CHECK-NEXT: %[[srcload:.*]] = load i32* %[[srccast1]]
; CHECK-NEXT: %[[agep1:.*]] = getelementptr inbounds [34 x i8]* %[[a]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[agep1]], i8* %src, i32 %size,
; CHECK-NEXT: %[[agep2:.*]] = getelementptr inbounds [34 x i8]* %[[a]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %[[agep2]], i8 42, i32 %size,
; CHECK-NEXT: %[[dstcast1:.*]] = bitcast i8* %dst to i32*
; CHECK-NEXT: store i32 42, i32* %[[dstcast1]]
; CHECK-NEXT: %[[dstgep1:.*]] = getelementptr inbounds i8* %dst, i64 4
; CHECK-NEXT: %[[dstcast2:.*]] = bitcast i8* %[[dstgep1]] to i32*
; CHECK-NEXT: store i32 %[[srcload]], i32* %[[dstcast2]]
; CHECK-NEXT: %[[agep3:.*]] = getelementptr inbounds [34 x i8]* %[[a]], i64 0, i64 0
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %[[agep3]], i32 %size,
; CHECK-NEXT: ret void
entry:
%a = alloca [42 x i8]
%ptr = getelementptr [42 x i8]* %a, i32 0, i32 0
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr, i8* %src, i32 8, i32 1, i1 false)
%ptr2 = getelementptr [42 x i8]* %a, i32 0, i32 8
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %ptr2, i8* %src, i32 %size, i32 1, i1 false)
call void @llvm.memset.p0i8.i32(i8* %ptr2, i8 42, i32 %size, i32 1, i1 false)
%cast = bitcast i8* %ptr to i32*
store i32 42, i32* %cast
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %ptr, i32 8, i32 1, i1 false)
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %ptr2, i32 %size, i32 1, i1 false)
ret void
}

View File

@ -0,0 +1 @@
config.suffixes = ['.ll']

View File

@ -0,0 +1,325 @@
; RUN: opt < %s -sroa -S | FileCheck %s
target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define i32 @test1() {
; CHECK: @test1
entry:
%a = alloca [2 x i32]
; CHECK-NOT: alloca
%a0 = getelementptr [2 x i32]* %a, i64 0, i32 0
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 0, i32* %a0
store i32 1, i32* %a1
%v0 = load i32* %a0
%v1 = load i32* %a1
; CHECK-NOT: store
; CHECK-NOT: load
%cond = icmp sle i32 %v0, %v1
br i1 %cond, label %then, label %exit
then:
br label %exit
exit:
%phi = phi i32* [ %a1, %then ], [ %a0, %entry ]
; CHECK: phi i32 [ 1, %{{.*}} ], [ 0, %{{.*}} ]
%result = load i32* %phi
ret i32 %result
}
define i32 @test2() {
; CHECK: @test2
entry:
%a = alloca [2 x i32]
; CHECK-NOT: alloca
%a0 = getelementptr [2 x i32]* %a, i64 0, i32 0
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 0, i32* %a0
store i32 1, i32* %a1
%v0 = load i32* %a0
%v1 = load i32* %a1
; CHECK-NOT: store
; CHECK-NOT: load
%cond = icmp sle i32 %v0, %v1
%select = select i1 %cond, i32* %a1, i32* %a0
; CHECK: select i1 %{{.*}}, i32 1, i32 0
%result = load i32* %select
ret i32 %result
}
define i32 @test3(i32 %x) {
; CHECK: @test3
entry:
%a = alloca [2 x i32]
; CHECK-NOT: alloca
%a0 = getelementptr [2 x i32]* %a, i64 0, i32 0
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 0, i32* %a0
store i32 1, i32* %a1
; CHECK-NOT: store
switch i32 %x, label %bb0 [ i32 1, label %bb1
i32 2, label %bb2
i32 3, label %bb3 ]
bb0:
br label %exit
bb1:
br label %exit
bb2:
br label %exit
bb3:
br label %exit
exit:
%phi = phi i32* [ %a1, %bb0 ], [ %a0, %bb1 ], [ %a0, %bb2 ], [ %a1, %bb3 ]
; CHECK: phi i32 [ 1, %{{.*}} ], [ 0, %{{.*}} ], [ 0, %{{.*}} ], [ 1, %{{.*}} ]
%result = load i32* %phi
ret i32 %result
}
define i32 @test4() {
; CHECK: @test4
entry:
%a = alloca [2 x i32]
; CHECK-NOT: alloca
%a0 = getelementptr [2 x i32]* %a, i64 0, i32 0
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 0, i32* %a0
store i32 1, i32* %a1
%v0 = load i32* %a0
%v1 = load i32* %a1
; CHECK-NOT: store
; CHECK-NOT: load
%cond = icmp sle i32 %v0, %v1
%select = select i1 %cond, i32* %a0, i32* %a0
; CHECK-NOT: select
%result = load i32* %select
ret i32 %result
; CHECK: ret i32 0
}
define i32 @test5(i32* %b) {
; CHECK: @test5
entry:
%a = alloca [2 x i32]
; CHECK-NOT: alloca
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 1, i32* %a1
; CHECK-NOT: store
%select = select i1 true, i32* %a1, i32* %b
; CHECK-NOT: select
%result = load i32* %select
; CHECK-NOT: load
ret i32 %result
; CHECK: ret i32 1
}
declare void @f(i32*)
define i32 @test6(i32* %b) {
; CHECK: @test6
entry:
%a = alloca [2 x i32]
; The alloca remains because it is used in a dead select.
; CHECK: alloca
%a1 = getelementptr [2 x i32]* %a, i64 0, i32 1
store i32 1, i32* %a1
%select = select i1 true, i32* %a1, i32* %b
%select2 = select i1 false, i32* %a1, i32* %b
; CHECK-NOT: select i1 true
; We don't aggressively DCE this select.
; CHECK: select i1 false
; Note, this would potentially escape the alloca pointer except for the
; constant folding of the select.
call void @f(i32* %select2)
%result = load i32* %select
; CHECK-NOT: load
ret i32 %result
; CHECK: ret i32 1
}
define i32 @test7() {
; CHECK: @test7
; CHECK-NOT: alloca
entry:
%X = alloca i32
br i1 undef, label %good, label %bad
good:
%Y1 = getelementptr i32* %X, i64 0
store i32 0, i32* %Y1
br label %exit
bad:
%Y2 = getelementptr i32* %X, i64 1
store i32 0, i32* %Y2
br label %exit
exit:
%P = phi i32* [ %Y1, %good ], [ %Y2, %bad ]
; CHECK: %[[phi:.*]] = phi i32 [ 0, %good ],
%Z2 = load i32* %P
ret i32 %Z2
; CHECK: ret i32 %[[phi]]
}
define i32 @test8(i32 %b, i32* %ptr) {
; Ensure that we rewrite allocas to the used type when that use is hidden by
; a PHI that can be speculated.
; CHECK: @test8
; CHECK-NOT: alloca
; CHECK-NOT: load
; CHECK: %[[value:.*]] = load i32* %ptr
; CHECK-NOT: load
; CHECK: %[[result:.*]] = phi i32 [ undef, %else ], [ %[[value]], %then ]
; CHECK-NEXT: ret i32 %[[result]]
entry:
%f = alloca float
%test = icmp ne i32 %b, 0
br i1 %test, label %then, label %else
then:
br label %exit
else:
%bitcast = bitcast float* %f to i32*
br label %exit
exit:
%phi = phi i32* [ %bitcast, %else ], [ %ptr, %then ]
%loaded = load i32* %phi, align 4
ret i32 %loaded
}
define i32 @test9(i32 %b, i32* %ptr) {
; Same as @test8 but for a select rather than a PHI node.
; CHECK: @test9
; CHECK-NOT: alloca
; CHECK-NOT: load
; CHECK: %[[value:.*]] = load i32* %ptr
; CHECK-NOT: load
; CHECK: %[[result:.*]] = select i1 %{{.*}}, i32 undef, i32 %[[value]]
; CHECK-NEXT: ret i32 %[[result]]
entry:
%f = alloca float
store i32 0, i32* %ptr
%test = icmp ne i32 %b, 0
%bitcast = bitcast float* %f to i32*
%select = select i1 %test, i32* %bitcast, i32* %ptr
%loaded = load i32* %select, align 4
ret i32 %loaded
}
define i32 @test10(i32 %b, i32* %ptr) {
; Don't try to promote allocas which are not elligible for it even after
; rewriting due to the necessity of inserting bitcasts when speculating a PHI
; node.
; CHECK: @test10
; CHECK: %[[alloca:.*]] = alloca
; CHECK: %[[argvalue:.*]] = load i32* %ptr
; CHECK: %[[cast:.*]] = bitcast double* %[[alloca]] to i32*
; CHECK: %[[allocavalue:.*]] = load i32* %[[cast]]
; CHECK: %[[result:.*]] = phi i32 [ %[[allocavalue]], %else ], [ %[[argvalue]], %then ]
; CHECK-NEXT: ret i32 %[[result]]
entry:
%f = alloca double
store double 0.0, double* %f
%test = icmp ne i32 %b, 0
br i1 %test, label %then, label %else
then:
br label %exit
else:
%bitcast = bitcast double* %f to i32*
br label %exit
exit:
%phi = phi i32* [ %bitcast, %else ], [ %ptr, %then ]
%loaded = load i32* %phi, align 4
ret i32 %loaded
}
define i32 @test11(i32 %b, i32* %ptr) {
; Same as @test10 but for a select rather than a PHI node.
; CHECK: @test11
; CHECK: %[[alloca:.*]] = alloca
; CHECK: %[[cast:.*]] = bitcast double* %[[alloca]] to i32*
; CHECK: %[[allocavalue:.*]] = load i32* %[[cast]]
; CHECK: %[[argvalue:.*]] = load i32* %ptr
; CHECK: %[[result:.*]] = select i1 %{{.*}}, i32 %[[allocavalue]], i32 %[[argvalue]]
; CHECK-NEXT: ret i32 %[[result]]
entry:
%f = alloca double
store double 0.0, double* %f
store i32 0, i32* %ptr
%test = icmp ne i32 %b, 0
%bitcast = bitcast double* %f to i32*
%select = select i1 %test, i32* %bitcast, i32* %ptr
%loaded = load i32* %select, align 4
ret i32 %loaded
}
define i32 @test12(i32 %x, i32* %p) {
; Ensure we don't crash or fail to nuke dead selects of allocas if no load is
; never found.
; CHECK: @test12
; CHECK-NOT: alloca
; CHECK-NOT: select
; CHECK: ret i32 %x
entry:
%a = alloca i32
store i32 %x, i32* %a
%dead = select i1 undef, i32* %a, i32* %p
%load = load i32* %a
ret i32 %load
}
define i32 @test13(i32 %x, i32* %p) {
; Ensure we don't crash or fail to nuke dead phis of allocas if no load is ever
; found.
; CHECK: @test13
; CHECK-NOT: alloca
; CHECK-NOT: phi
; CHECK: ret i32 %x
entry:
%a = alloca i32
store i32 %x, i32* %a
br label %loop
loop:
%phi = phi i32* [ %p, %entry ], [ %a, %loop ]
br i1 undef, label %loop, label %exit
exit:
%load = load i32* %a
ret i32 %load
}

View File

@ -0,0 +1,191 @@
; RUN: opt < %s -sroa -S | FileCheck %s
target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
%S1 = type { i64, [42 x float] }
define i32 @test1(<4 x i32> %x, <4 x i32> %y) {
; CHECK: @test1
entry:
%a = alloca [2 x <4 x i32>]
; CHECK-NOT: alloca
%a.x = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0
store <4 x i32> %x, <4 x i32>* %a.x
%a.y = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1
store <4 x i32> %y, <4 x i32>* %a.y
; CHECK-NOT: store
%a.tmp1 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0, i64 2
%tmp1 = load i32* %a.tmp1
%a.tmp2 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 3
%tmp2 = load i32* %a.tmp2
%a.tmp3 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 0
%tmp3 = load i32* %a.tmp3
; CHECK-NOT: load
; CHECK: extractelement <4 x i32> %x, i32 2
; CHECK-NEXT: extractelement <4 x i32> %y, i32 3
; CHECK-NEXT: extractelement <4 x i32> %y, i32 0
%tmp4 = add i32 %tmp1, %tmp2
%tmp5 = add i32 %tmp3, %tmp4
ret i32 %tmp5
; CHECK-NEXT: add
; CHECK-NEXT: add
; CHECK-NEXT: ret
}
define i32 @test2(<4 x i32> %x, <4 x i32> %y) {
; CHECK: @test2
; FIXME: This should be handled!
entry:
%a = alloca [2 x <4 x i32>]
; CHECK: alloca <4 x i32>
%a.x = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0
store <4 x i32> %x, <4 x i32>* %a.x
%a.y = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1
store <4 x i32> %y, <4 x i32>* %a.y
%a.tmp1 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0, i64 2
%tmp1 = load i32* %a.tmp1
%a.tmp2 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 3
%tmp2 = load i32* %a.tmp2
%a.tmp3 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 0
%a.tmp3.cast = bitcast i32* %a.tmp3 to <2 x i32>*
%tmp3.vec = load <2 x i32>* %a.tmp3.cast
%tmp3 = extractelement <2 x i32> %tmp3.vec, i32 0
%tmp4 = add i32 %tmp1, %tmp2
%tmp5 = add i32 %tmp3, %tmp4
ret i32 %tmp5
}
define i32 @test3(<4 x i32> %x, <4 x i32> %y) {
; CHECK: @test3
entry:
%a = alloca [2 x <4 x i32>]
; CHECK-NOT: alloca
%a.x = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0
store <4 x i32> %x, <4 x i32>* %a.x
%a.y = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1
store <4 x i32> %y, <4 x i32>* %a.y
; CHECK-NOT: store
%a.y.cast = bitcast <4 x i32>* %a.y to i8*
call void @llvm.memset.p0i8.i32(i8* %a.y.cast, i8 0, i32 16, i32 1, i1 false)
; CHECK-NOT: memset
%a.tmp1 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0, i64 2
%a.tmp1.cast = bitcast i32* %a.tmp1 to i8*
call void @llvm.memset.p0i8.i32(i8* %a.tmp1.cast, i8 -1, i32 4, i32 1, i1 false)
%tmp1 = load i32* %a.tmp1
%a.tmp2 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 3
%tmp2 = load i32* %a.tmp2
%a.tmp3 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 0
%tmp3 = load i32* %a.tmp3
; CHECK-NOT: load
; CHECK: %[[insert:.*]] = insertelement <4 x i32> %x, i32 -1, i32 2
; CHECK-NEXT: extractelement <4 x i32> %[[insert]], i32 2
; CHECK-NEXT: extractelement <4 x i32> zeroinitializer, i32 3
; CHECK-NEXT: extractelement <4 x i32> zeroinitializer, i32 0
%tmp4 = add i32 %tmp1, %tmp2
%tmp5 = add i32 %tmp3, %tmp4
ret i32 %tmp5
; CHECK-NEXT: add
; CHECK-NEXT: add
; CHECK-NEXT: ret
}
define i32 @test4(<4 x i32> %x, <4 x i32> %y, <4 x i32>* %z) {
; CHECK: @test4
entry:
%a = alloca [2 x <4 x i32>]
; CHECK-NOT: alloca
%a.x = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0
store <4 x i32> %x, <4 x i32>* %a.x
%a.y = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1
store <4 x i32> %y, <4 x i32>* %a.y
; CHECK-NOT: store
%a.y.cast = bitcast <4 x i32>* %a.y to i8*
%z.cast = bitcast <4 x i32>* %z to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a.y.cast, i8* %z.cast, i32 16, i32 1, i1 false)
; CHECK-NOT: memcpy
%a.tmp1 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0, i64 2
%a.tmp1.cast = bitcast i32* %a.tmp1 to i8*
%z.tmp1 = getelementptr inbounds <4 x i32>* %z, i64 0, i64 2
%z.tmp1.cast = bitcast i32* %z.tmp1 to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a.tmp1.cast, i8* %z.tmp1.cast, i32 4, i32 1, i1 false)
%tmp1 = load i32* %a.tmp1
%a.tmp2 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 3
%tmp2 = load i32* %a.tmp2
%a.tmp3 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 0
%tmp3 = load i32* %a.tmp3
; CHECK-NOT: memcpy
; CHECK: %[[load:.*]] = load <4 x i32>* %z
; CHECK-NEXT: %[[gep:.*]] = getelementptr inbounds <4 x i32>* %z, i64 0, i64 2
; CHECK-NEXT: %[[element_load:.*]] = load i32* %[[gep]]
; CHECK-NEXT: %[[insert:.*]] = insertelement <4 x i32> %x, i32 %[[element_load]], i32 2
; CHECK-NEXT: extractelement <4 x i32> %[[insert]], i32 2
; CHECK-NEXT: extractelement <4 x i32> %[[load]], i32 3
; CHECK-NEXT: extractelement <4 x i32> %[[load]], i32 0
%tmp4 = add i32 %tmp1, %tmp2
%tmp5 = add i32 %tmp3, %tmp4
ret i32 %tmp5
; CHECK-NEXT: add
; CHECK-NEXT: add
; CHECK-NEXT: ret
}
define i32 @test5(<4 x i32> %x, <4 x i32> %y, <4 x i32>* %z) {
; CHECK: @test5
; The same as the above, but with reversed source and destination for the
; element memcpy, and a self copy.
entry:
%a = alloca [2 x <4 x i32>]
; CHECK-NOT: alloca
%a.x = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0
store <4 x i32> %x, <4 x i32>* %a.x
%a.y = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1
store <4 x i32> %y, <4 x i32>* %a.y
; CHECK-NOT: store
%a.y.cast = bitcast <4 x i32>* %a.y to i8*
%a.x.cast = bitcast <4 x i32>* %a.x to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a.x.cast, i8* %a.y.cast, i32 16, i32 1, i1 false)
; CHECK-NOT: memcpy
%a.tmp1 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 0, i64 2
%a.tmp1.cast = bitcast i32* %a.tmp1 to i8*
%z.tmp1 = getelementptr inbounds <4 x i32>* %z, i64 0, i64 2
%z.tmp1.cast = bitcast i32* %z.tmp1 to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %z.tmp1.cast, i8* %a.tmp1.cast, i32 4, i32 1, i1 false)
%tmp1 = load i32* %a.tmp1
%a.tmp2 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 3
%tmp2 = load i32* %a.tmp2
%a.tmp3 = getelementptr inbounds [2 x <4 x i32>]* %a, i64 0, i64 1, i64 0
%tmp3 = load i32* %a.tmp3
; CHECK-NOT: memcpy
; CHECK: %[[gep:.*]] = getelementptr inbounds <4 x i32>* %z, i64 0, i64 2
; CHECK-NEXT: %[[extract:.*]] = extractelement <4 x i32> %y, i32 2
; CHECK-NEXT: store i32 %[[extract]], i32* %[[gep]]
; CHECK-NEXT: extractelement <4 x i32> %y, i32 2
; CHECK-NEXT: extractelement <4 x i32> %y, i32 3
; CHECK-NEXT: extractelement <4 x i32> %y, i32 0
%tmp4 = add i32 %tmp1, %tmp2
%tmp5 = add i32 %tmp3, %tmp4
ret i32 %tmp5
; CHECK-NEXT: add
; CHECK-NEXT: add
; CHECK-NEXT: ret
}
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) nounwind
declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) nounwind