; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=instcombine -S < %s | FileCheck %s ; Function Attrs: noinline uwtable define i32 @foo(i32* nocapture writeonly %arg) { ; CHECK-LABEL: @foo( ; CHECK-NEXT: bb: ; CHECK-NEXT: [[VAR:%.*]] = call i32 @baz() ; CHECK-NEXT: store i32 [[VAR]], i32* [[ARG:%.*]], align 4 ; CHECK-NEXT: [[VAR1:%.*]] = call i32 @baz() ; CHECK-NEXT: ret i32 [[VAR1]] ; bb: %var = call i32 @baz() store i32 %var, i32* %arg, align 4 %var1 = call i32 @baz() ret i32 %var1 } declare i32 @baz() ; Function Attrs: uwtable ; This is an equivalent IR for a c-style example with a large function (foo) ; with out-params which are unused in the caller(test8). Note that foo is ; marked noinline to prevent IPO transforms. ; int foo(); ; ; extern int foo(int *out) __attribute__((noinline)); ; int foo(int *out) { ; *out = baz(); ; return baz(); ; } ; ; int test() { ; ; int notdead; ; if (foo(¬dead)) ; return 0; ; ; int dead; ; int tmp = foo(&dead); ; if (notdead) ; return tmp; ; return bar(); ; } ; TODO: We should be able to sink the second call @foo at bb5 down to bb_crit_edge define i32 @test() { ; CHECK-LABEL: @test( ; CHECK-NEXT: bb: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[VAR1:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[VAR2:%.*]] = bitcast i32* [[VAR]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[VAR2]]) ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @foo(i32* nonnull writeonly [[VAR]]) ; CHECK-NEXT: [[VAR4:%.*]] = icmp eq i32 [[VAR3]], 0 ; CHECK-NEXT: br i1 [[VAR4]], label [[BB5:%.*]], label [[BB14:%.*]] ; CHECK: bb5: ; CHECK-NEXT: [[VAR6:%.*]] = bitcast i32* [[VAR1]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[VAR6]]) ; CHECK-NEXT: [[VAR8:%.*]] = load i32, i32* [[VAR]], align 4 ; CHECK-NEXT: [[VAR9:%.*]] = icmp eq i32 [[VAR8]], 0 ; CHECK-NEXT: [[VAR7:%.*]] = call i32 @foo(i32* nonnull writeonly [[VAR1]]) ; CHECK-NEXT: br i1 [[VAR9]], label [[BB10:%.*]], label [[BB_CRIT_EDGE:%.*]] ; CHECK: bb10: ; CHECK-NEXT: [[VAR11:%.*]] = call i32 @bar() ; CHECK-NEXT: br label [[BB12:%.*]] ; CHECK: bb_crit_edge: ; CHECK-NEXT: br label [[BB12]] ; CHECK: bb12: ; CHECK-NEXT: [[VAR13:%.*]] = phi i32 [ [[VAR11]], [[BB10]] ], [ [[VAR7]], [[BB_CRIT_EDGE]] ] ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[VAR6]]) ; CHECK-NEXT: br label [[BB14]] ; CHECK: bb14: ; CHECK-NEXT: [[VAR15:%.*]] = phi i32 [ [[VAR13]], [[BB12]] ], [ 0, [[BB:%.*]] ] ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[VAR2]]) ; CHECK-NEXT: ret i32 [[VAR15]] ; bb: %var = alloca i32, align 4 %var1 = alloca i32, align 4 %var2 = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %var2) #4 %var3 = call i32 @foo(i32* nonnull writeonly %var) %var4 = icmp eq i32 %var3, 0 br i1 %var4, label %bb5, label %bb14 bb5: ; preds = %bb %var6 = bitcast i32* %var1 to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %var6) #4 %var8 = load i32, i32* %var, align 4 %var9 = icmp eq i32 %var8, 0 %var7 = call i32 @foo(i32* nonnull writeonly %var1) br i1 %var9, label %bb10, label %bb_crit_edge bb10: ; preds = %bb5 %var11 = call i32 @bar() br label %bb12 bb_crit_edge: br label %bb12 bb12: ; preds = %bb10, %bb5 %var13 = phi i32 [ %var11, %bb10 ], [ %var7, %bb_crit_edge ] call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %var6) #4 br label %bb14 bb14: ; preds = %bb12, %bb %var15 = phi i32 [ %var13, %bb12 ], [ 0, %bb ] call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %var2) ret i32 %var15 } declare i32 @unknown(i32* %dest) declare i32 @unknown.as2(i32 addrspace(2)* %dest) define i32 @sink_write_to_use(i1 %c) { ; CHECK-LABEL: @sink_write_to_use( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull writeonly [[VAR]]) #[[ATTR1:[0-9]+]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %var3 = call i32 @unknown(i32* writeonly %var) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_readwrite_to_use(i1 %c) { ; CHECK-LABEL: @sink_readwrite_to_use( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %var3 = call i32 @unknown(i32* %var) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_bitcast(i1 %c) { ; CHECK-LABEL: @sink_bitcast( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i8, align 8 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i8* [[VAR]] to i32* ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[BITCAST]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i8, align 8 %bitcast = bitcast i8* %var to i32* %var3 = call i32 @unknown(i32* %bitcast) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_gep1(i1 %c) { ; CHECK-LABEL: @sink_gep1( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR1:%.*]] = alloca [2 x i32], align 8 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[VAR1]], i64 0, i64 1 ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[GEP]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i64, align 8 %bitcast = bitcast i64* %var to i32* %gep = getelementptr i32, i32* %bitcast, i32 1 %var3 = call i32 @unknown(i32* %gep) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_gep2(i1 %c) { ; CHECK-LABEL: @sink_gep2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR1:%.*]] = alloca [2 x i32], align 8 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[VAR1_SUB:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[VAR1]], i64 0, i64 0 ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR1_SUB]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i64, align 8 %bitcast = bitcast i64* %var to i32* %var3 = call i32 @unknown(i32* %bitcast) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_addrspacecast(i1 %c) { ; CHECK-LABEL: @sink_addrspacecast( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 8 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[CAST:%.*]] = addrspacecast i32* [[VAR]] to i32 addrspace(2)* ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown.as2(i32 addrspace(2)* [[CAST]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 8 %cast = addrspacecast i32* %var to i32 addrspace(2)* %var3 = call i32 @unknown.as2(i32 addrspace(2)* %cast) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @neg_infinite_loop(i1 %c) { ; CHECK-LABEL: @neg_infinite_loop( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR2:[0-9]+]] ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %var3 = call i32 @unknown(i32* %var) argmemonly nounwind br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @neg_throw(i1 %c) { ; CHECK-LABEL: @neg_throw( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR3:[0-9]+]] ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %var3 = call i32 @unknown(i32* %var) argmemonly willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @neg_unknown_write(i1 %c) { ; CHECK-LABEL: @neg_unknown_write( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR4:[0-9]+]] ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %var3 = call i32 @unknown(i32* %var) nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_lifetime1(i1 %c) { ; CHECK-LABEL: @sink_lifetime1( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[VAR]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %bitcast = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast) %var3 = call i32 @unknown(i32* %var) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast) ret i32 %var3 } define i32 @sink_lifetime2(i1 %c) { ; CHECK-LABEL: @sink_lifetime2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[VAR]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: br i1 [[C:%.*]], label [[MERGE:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: merge: ; CHECK-NEXT: [[RET:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[VAR3]], [[USE_BLOCK]] ] ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: ret i32 [[RET]] ; CHECK: use_block: ; CHECK-NEXT: br label [[MERGE]] ; entry: %var = alloca i32, align 4 %bitcast = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast) %var3 = call i32 @unknown(i32* %var) argmemonly nounwind willreturn br i1 %c, label %merge, label %use_block merge: %ret = phi i32 [0, %entry], [%var3, %use_block] call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast) ret i32 %ret use_block: br label %merge } define i32 @sink_lifetime3(i1 %c) { ; CHECK-LABEL: @sink_lifetime3( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %bitcast = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast) call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast) ; If unknown accesses %var, that's UB %var3 = call i32 @unknown(i32* %var) argmemonly nounwind willreturn br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } define i32 @sink_lifetime4a(i1 %c) { ; CHECK-LABEL: @sink_lifetime4a( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[VAR]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %bitcast = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast) %var3 = call i32 @unknown(i32* %var) argmemonly nounwind willreturn call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast) br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } ; Version which only writes to var, and thus can't rely on may-read scan for ; clobbers to prevent the transform define i32 @sink_lifetime4b(i1 %c) { ; CHECK-LABEL: @sink_lifetime4b( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[VAR]] to i8* ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: [[VAR3:%.*]] = call i32 @unknown(i32* nonnull writeonly [[VAR]]) #[[ATTR1]] ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[BITCAST]]) ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 %bitcast = bitcast i32* %var to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast) %var3 = call i32 @unknown(i32* writeonly %var) argmemonly nounwind willreturn call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast) br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } ; Mostly checking that trying to sink a non-call doesn't crash (i.e. prior bug) define i32 @sink_atomicrmw_to_use(i1 %c) { ; CHECK-LABEL: @sink_atomicrmw_to_use( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[VAR:%.*]] = alloca i32, align 4 ; CHECK-NEXT: store i32 0, i32* [[VAR]], align 4 ; CHECK-NEXT: [[VAR3:%.*]] = atomicrmw add i32* [[VAR]], i32 1 seq_cst, align 4 ; CHECK-NEXT: br i1 [[C:%.*]], label [[EARLY_RETURN:%.*]], label [[USE_BLOCK:%.*]] ; CHECK: early_return: ; CHECK-NEXT: ret i32 0 ; CHECK: use_block: ; CHECK-NEXT: ret i32 [[VAR3]] ; entry: %var = alloca i32, align 4 store i32 0, i32* %var %var3 = atomicrmw add i32* %var, i32 1 seq_cst, align 4 br i1 %c, label %early_return, label %use_block early_return: ret i32 0 use_block: ret i32 %var3 } declare i32 @bar() declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)