Index: test/Transforms/LoadElim/local-vars.ll =================================================================== --- /dev/null +++ test/Transforms/LoadElim/local-vars.ll @@ -0,0 +1,433 @@ + +;; NOTE: The CHECKLOAD-* prefixes indicate occurences of redundant loads in the output. +;; The CHECK-* prefixes indicate removal of redundant loads in the output. (ALL == 4-5A1-5A2-5B) + +;; * When the available load scan limit is 6, -instcombine does not +;; eliminate some redundant loads that either it would eliminate +;; with a load scan limit of 8, or -gvn would eliminate. +; RUN: opt < %s -available-load-scan-limit=6 -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECKLOAD-ALL +; RUN: opt < %s -available-load-scan-limit=6 -instcombine -gvn -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECK-ALL +; RUN: opt < %s -available-load-scan-limit=8 -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECK-5A2-5B --check-prefix=CHECKLOAD-4-5A1 +; RUN: opt < %s -available-load-scan-limit=8 -instcombine -gvn -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECK-ALL + +;; * Adding '-inline -early-cse' enables a few more load eliminations. +; RUN: opt < %s -available-load-scan-limit=6 -instcombine -inline -early-cse -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKINL --check-prefix=CHECKINV --check-prefix=CHECK-5B1 --check-prefix=CHECKLOAD-4-5A-5B2 +; RUN: opt < %s -available-load-scan-limit=6 -instcombine -inline -early-cse -gvn -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKINL --check-prefix=CHECKINV3 --check-prefix=CHECK-ALL +; RUN: opt < %s -available-load-scan-limit=8 -instcombine -inline -early-cse -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKINL --check-prefix=CHECKINV --check-prefix=CHECK-5A2-5B --check-prefix=CHECKLOAD-4-5A1 +; RUN: opt < %s -available-load-scan-limit=8 -instcombine -inline -early-cse -gvn -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKINL --check-prefix=CHECKINV3 --check-prefix=CHECK-ALL + +;; * When the load scan limit is 8, +;; '-functionattrs -tailcallelim -instcombine' may be as good as '-gvn'. +;; But the same can't be said when the limit is 6. +; RUN: opt < %s -available-load-scan-limit=8 -instcombine -functionattrs -tailcallelim -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECK-ALL +; RUN: opt < %s -available-load-scan-limit=6 -instcombine -functionattrs -tailcallelim -instcombine -S | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKNOINL --check-prefix=CHECK-5A-5B1 --check-prefix=CHECKLOAD-4-5B2 + + +%struct.A = type { i32 } + +;; Example 1: Duplicate loads. +;; void ex1() { +;; const Type i(one()); +;; bar(i); // First load. +;; foo(&i); // Does not change i. +;; bar(i); // No load. +;; } +define void @_Z3ex1v() { +entry: + %i = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %agg.tmp to i8* + %4 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %4, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %5 = load i32, i32* %coerce.dive + ; CHECKNOINL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %5) + call void @_Z3fooPK1A(%struct.A* %i) + %6 = bitcast %struct.A* %agg.tmp1 to i8* + %7 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %7, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %8 = load i32, i32* %coerce.dive2 + ; CHECK-NOT: load i32, i32* + call void @_Z3bar1A(i32 %8) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %9 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %9) + ret void +} + +;; Example 2: Unnecessary stores and loads. +;; void ex2() { +;; const Type i(one()); +;; const Type j = i; // Note: i == j, &i != &j ==> No store. +;; bar(i); // First load. +;; foo(&i); // Does not change i, nor j. +;; bar(j); // No load; Reuse i location. +;; } +define void @_Z3ex2v() { +entry: + %i = alloca %struct.A + %j = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %j to i8* + call void @llvm.lifetime.start(i64 4, i8* %3) + %4 = bitcast %struct.A* %j to i8* + %5 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %5, i64 4, i32 4, i1 false) + %6 = bitcast %struct.A* %j to i8* + %7 = call {}* @llvm.invariant.start(i64 4, i8* %6) + ; CHECK-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %8 = bitcast %struct.A* %agg.tmp to i8* + %9 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %8, i8* %9, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %10 = load i32, i32* %coerce.dive + ; CHECKNOINL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %10) + call void @_Z3fooPK1A(%struct.A* %i) + %11 = bitcast %struct.A* %agg.tmp1 to i8* + %12 = bitcast %struct.A* %j to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %11, i8* %12, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %13 = load i32, i32* %coerce.dive2 + ; CHECK-NOT: load i32, i32* + call void @_Z3bar1A(i32 %13) + call void @llvm.invariant.end({}* %7, i64 4, i8* %6) + ; CHECK-NOT: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %14 = bitcast %struct.A* %j to i8* + call void @llvm.lifetime.end(i64 4, i8* %14) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %15 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %15) + ret void +} + +;; Example 3: Necessary stores and loads. +;; void ex3() { +;; const Type i(1); +;; Type k = i; // Note: i == k, &i != &k ==> Keep store. +;; bar(i); // First load. +;; foo(&k); // Does not change i; May change k. +;; bar(k); // Keep load. +;; } +define void @_Z3ex3v() { +entry: + %i = alloca %struct.A + %k = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECKNOINL: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + ; CHECKINV3: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + ; CHECKINV-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.start(i64 4, i8* %3) + %4 = bitcast %struct.A* %k to i8* + %5 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %5, i64 4, i32 4, i1 false) + %6 = bitcast %struct.A* %agg.tmp to i8* + %7 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %7, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %8 = load i32, i32* %coerce.dive + ; CHECKNOINL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %8) + call void @_Z3fooPK1A(%struct.A* %k) + %9 = bitcast %struct.A* %agg.tmp1 to i8* + %10 = bitcast %struct.A* %k to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %9, i8* %10, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %11 = load i32, i32* %coerce.dive2 + ; CHECK: load i32, i32* + call void @_Z3bar1A(i32 %11) + %12 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.end(i64 4, i8* %12) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHEC K-ALL: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* ; FIXME: + ; CHECKNOINL: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + ; CHECKINV3: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + ; CHECKINV-NOT: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %13 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %13) + ret void +} + +;; Example 4: Smart stores and loads. +;; void ex4() { +;; const Type i(one()); +;; Type k = i; // Note: i == k, &i != &k ==> May keep store. +;; bar(i); // First load. +;; foo(&i); // Does not change i, nor k. +;; bar(k); // No load; Reuse i location. +;; foo(&k); // Does not change i; May change k. +;; bar(k); // Keep load. +;; } +define void @_Z3ex4v() { +entry: + %i = alloca %struct.A + %k = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %agg.tmp3 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.start(i64 4, i8* %3) + %4 = bitcast %struct.A* %k to i8* + %5 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %5, i64 4, i32 4, i1 false) + %6 = bitcast %struct.A* %agg.tmp to i8* + %7 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %7, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %8 = load i32, i32* %coerce.dive + ; CHECKNOINL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %8) + call void @_Z3fooPK1A(%struct.A* %i) + %9 = bitcast %struct.A* %agg.tmp1 to i8* + %10 = bitcast %struct.A* %k to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %9, i8* %10, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %11 = load i32, i32* %coerce.dive2 + ; CHECKLOAD-4-5A1: load i32, i32* + ; CHECKLOAD-4-5B2: load i32, i32* + ; CHECKLOAD-4-5A-5B2: load i32, i32* + ; CHECKLOAD-ALL: load i32, i32* + ; CHECK-ALL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %11) + call void @_Z3fooPK1A(%struct.A* %k) + %12 = bitcast %struct.A* %agg.tmp3 to i8* + %13 = bitcast %struct.A* %k to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %12, i8* %13, i64 4, i32 4, i1 false) + %coerce.dive4 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp3, i32 0, i32 0 + %14 = load i32, i32* %coerce.dive4 + ; CHECK: load i32, i32* + call void @_Z3bar1A(i32 %14) + %15 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.end(i64 4, i8* %15) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %16 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %16) + ret void +} + +;; Example 5: Duplicate and smart loads (and stores). +;; void ex5a() { +;; const Type i(one()); +;; Type k = i; // Note: i == k, &i != &k ==> May keep store. +;; bar(i); // First load. +;; bar(k); // No load; Reuse i location. +;; foo2(&k, &i); // Does not change i; May change k. +;; bar(i); // No load. +;; bar(k); // Keep load. +;; } +define void @_Z4ex5av() { +entry: + %i = alloca %struct.A + %k = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %agg.tmp3 = alloca %struct.A + %agg.tmp5 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.start(i64 4, i8* %3) + %4 = bitcast %struct.A* %k to i8* + %5 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %5, i64 4, i32 4, i1 false) + %6 = bitcast %struct.A* %agg.tmp to i8* + %7 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %7, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %8 = load i32, i32* %coerce.dive + ; CHECKNOINL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %8) + %9 = bitcast %struct.A* %agg.tmp1 to i8* + %10 = bitcast %struct.A* %k to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %9, i8* %10, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %11 = load i32, i32* %coerce.dive2 + ; CHECKLOAD-4-5A1: load i32, i32* + ; CHECKLOAD-4-5A-5B2: load i32, i32* + ; CHECK-5A-5B1-NOT: load i32, i32* + ; CHECK-ALL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %11) + call void @_Z4foo2PK1AS1_(%struct.A* %k, %struct.A* %i) + %12 = bitcast %struct.A* %agg.tmp3 to i8* + %13 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %12, i8* %13, i64 4, i32 4, i1 false) + %coerce.dive4 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp3, i32 0, i32 0 + %14 = load i32, i32* %coerce.dive4 + ; CHECKLOAD-4-5A-5B2: load i32, i32* + ; CHECKLOAD-ALL: load i32, i32* + ; CHECK-5A-5B1-NOT: load i32, i32* + ; CHECK-5A2-NOT: load i32, i32* + ; CHECK-5A2-5B-NOT: load i32, i32* + ; CHECK-ALL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %14) + %15 = bitcast %struct.A* %agg.tmp5 to i8* + %16 = bitcast %struct.A* %k to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %15, i8* %16, i64 4, i32 4, i1 false) + %coerce.dive6 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp5, i32 0, i32 0 + %17 = load i32, i32* %coerce.dive6 + ; CHECK: load i32, i32* + call void @_Z3bar1A(i32 %17) + %18 = bitcast %struct.A* %k to i8* + call void @llvm.lifetime.end(i64 4, i8* %18) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %19 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %19) + ret void +} + +;; Example 5: Duplicate and smart loads (and stores). +;; void ex5b() { +;; const Type i(one()); +;; const Type j = i; // Note: i == j, &i != &j ==> No store. +;; bar(i); // First load. +;; bar(j); // No load; Reuse i location. +;; foo2(&j, &i); // Does not change i, nor j. +;; bar(i); // No load. +;; bar(j); // No load; Reuse i location. +;; } +define void @_Z4ex5bv() { +entry: + %i = alloca %struct.A + %j = alloca %struct.A + %agg.tmp = alloca %struct.A + %agg.tmp1 = alloca %struct.A + %agg.tmp3 = alloca %struct.A + %agg.tmp5 = alloca %struct.A + %0 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %0) + %call = call i32 @_Z3onev() + call void @_ZN1AC2Ei(%struct.A* %i, i32 %call) + ; CHECKINL: store i32 {{.*}}, i32* + %1 = bitcast %struct.A* %i to i8* + %2 = call {}* @llvm.invariant.start(i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %3 = bitcast %struct.A* %j to i8* + call void @llvm.lifetime.start(i64 4, i8* %3) + %4 = bitcast %struct.A* %j to i8* + %5 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %5, i64 4, i32 4, i1 false) + %6 = bitcast %struct.A* %j to i8* + %7 = call {}* @llvm.invariant.start(i64 4, i8* %6) + ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + %8 = bitcast %struct.A* %agg.tmp to i8* + %9 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %8, i8* %9, i64 4, i32 4, i1 false) + %coerce.dive = getelementptr inbounds %struct.A, %struct.A* %agg.tmp, i32 0, i32 0 + %10 = load i32, i32* %coerce.dive + ; CHECKNOINL-ALL: load i32, i32* + ; CHECKINL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %10) + %11 = bitcast %struct.A* %agg.tmp1 to i8* + %12 = bitcast %struct.A* %j to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %11, i8* %12, i64 4, i32 4, i1 false) + %coerce.dive2 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp1, i32 0, i32 0 + %13 = load i32, i32* %coerce.dive2 + ; CHECK-NOT: load i32, i32* + call void @_Z3bar1A(i32 %13) + call void @_Z4foo2PK1AS1_(%struct.A* %j, %struct.A* %i) + %14 = bitcast %struct.A* %agg.tmp3 to i8* + %15 = bitcast %struct.A* %i to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %14, i8* %15, i64 4, i32 4, i1 false) + %coerce.dive4 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp3, i32 0, i32 0 + %16 = load i32, i32* %coerce.dive4 + ; CHECKLOAD-ALL: load i32, i32* + ; CHECK-5A2-5B-NOT: load i32, i32* + ; CHECK-5A-5B1-NOT: load i32, i32* + ; CHECK-ALL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %16) + %17 = bitcast %struct.A* %agg.tmp5 to i8* + %18 = bitcast %struct.A* %j to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %17, i8* %18, i64 4, i32 4, i1 false) + %coerce.dive6 = getelementptr inbounds %struct.A, %struct.A* %agg.tmp5, i32 0, i32 0 + %19 = load i32, i32* %coerce.dive6 + ; CHECKLOAD-4-5B2: load i32, i32* + ; CHECKLOAD-4-5A-5B2: load i32, i32* + ; CHECKLOAD-ALL: load i32, i32* + ; CHECK-5A2-5B-NOT: load i32, i32* + ; CHECK-ALL-NOT: load i32, i32* + call void @_Z3bar1A(i32 %19) + call void @llvm.invariant.end({}* %7, i64 4, i8* %6) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %20 = bitcast %struct.A* %j to i8* + call void @llvm.lifetime.end(i64 4, i8* %20) + call void @llvm.invariant.end({}* %2, i64 4, i8* %1) + ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + %21 = bitcast %struct.A* %i to i8* + call void @llvm.lifetime.end(i64 4, i8* %21) + ret void +} + +declare i32 @_Z3onev() +declare void @_Z3bar1A(i32) +declare void @_Z3fooPK1A(%struct.A*) +declare void @_Z4foo2PK1AS1_(%struct.A*, %struct.A*) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) +declare {}* @llvm.invariant.start(i64, i8* nocapture) +declare void @llvm.invariant.end({}*, i64, i8* nocapture) +declare void @llvm.lifetime.start(i64, i8* nocapture) +declare void @llvm.lifetime.end(i64, i8* nocapture) + +define linkonce_odr void @_ZN1AC2Ei(%struct.A* %this, i32 %a) unnamed_addr { +entry: + %this.addr = alloca %struct.A* + %a.addr = alloca i32 + store %struct.A* %this, %struct.A** %this.addr + store i32 %a, i32* %a.addr + %this1 = load %struct.A*, %struct.A** %this.addr + %a2 = getelementptr inbounds %struct.A, %struct.A* %this1, i32 0, i32 0 + %0 = load i32, i32* %a.addr + store i32 %0, i32* %a2 + ret void +}