forked from OSchip/llvm-project
[NFC][InstCombine] Add tests for PHI merging/aggregate reconstruction (PR47060)
We should be able to see that the new aggregate we have produced is identical to the source aggregate from which we've extracted the elements that we used to form a new aggregate. This happens (a lot) in clang C++ exception code on unwind branch.
This commit is contained in:
parent
9b211a5076
commit
e74e8b2b69
|
@ -0,0 +1,328 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -S -instcombine < %s | FileCheck %s
|
||||
|
||||
declare void @foo()
|
||||
declare void @bar()
|
||||
declare void @baz()
|
||||
|
||||
declare void @usei32(i32)
|
||||
declare void @usei32i32agg({ i32, i32 })
|
||||
|
||||
; Most basic test - we explode the original aggregate into it's elements,
|
||||
; and then merge them back together exactly the way they were.
|
||||
; We should just return the source aggregate.
|
||||
define { i32, i32 } @test0({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @test0(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
%i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Arrays are still aggregates
|
||||
define [2 x i32] @test1([2 x i32] %srcagg) {
|
||||
; CHECK-LABEL: @test1(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue [2 x i32] [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue [2 x i32] [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue [2 x i32] undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue [2 x i32] [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret [2 x i32] [[I3]]
|
||||
;
|
||||
%i0 = extractvalue [2 x i32] %srcagg, 0
|
||||
%i1 = extractvalue [2 x i32] %srcagg, 1
|
||||
%i2 = insertvalue [2 x i32] undef, i32 %i0, 0
|
||||
%i3 = insertvalue [2 x i32] %i2, i32 %i1, 1
|
||||
ret [2 x i32] %i3
|
||||
}
|
||||
|
||||
; Right now we don't deal with case where there are more than 2 elements.
|
||||
; FIXME: should we?
|
||||
define [3 x i32] @test2([3 x i32] %srcagg) {
|
||||
; CHECK-LABEL: @test2(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue [3 x i32] [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue [3 x i32] [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue [3 x i32] [[SRCAGG]], 2
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue [3 x i32] undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I4:%.*]] = insertvalue [3 x i32] [[I3]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: [[I5:%.*]] = insertvalue [3 x i32] [[I4]], i32 [[I2]], 2
|
||||
; CHECK-NEXT: ret [3 x i32] [[I5]]
|
||||
;
|
||||
%i0 = extractvalue [3 x i32] %srcagg, 0
|
||||
%i1 = extractvalue [3 x i32] %srcagg, 1
|
||||
%i2 = extractvalue [3 x i32] %srcagg, 2
|
||||
%i3 = insertvalue [3 x i32] undef, i32 %i0, 0
|
||||
%i4 = insertvalue [3 x i32] %i3, i32 %i1, 1
|
||||
%i5 = insertvalue [3 x i32] %i4, i32 %i2, 2
|
||||
ret [3 x i32] %i5
|
||||
}
|
||||
|
||||
; Likewise, we only deal with a single-level aggregates.
|
||||
; FIXME: should we?
|
||||
define {{ i32, i32 }} @test3({{ i32, i32 }} %srcagg) {
|
||||
; CHECK-LABEL: @test3(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { { i32, i32 } } [[SRCAGG:%.*]], 0, 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { { i32, i32 } } [[SRCAGG]], 0, 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { { i32, i32 } } undef, i32 [[I0]], 0, 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { { i32, i32 } } [[I2]], i32 [[I1]], 0, 1
|
||||
; CHECK-NEXT: ret { { i32, i32 } } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue {{ i32, i32 }} %srcagg, 0, 0
|
||||
%i1 = extractvalue {{ i32, i32 }} %srcagg, 0, 1
|
||||
%i2 = insertvalue {{ i32, i32 }} undef, i32 %i0, 0, 0
|
||||
%i3 = insertvalue {{ i32, i32 }} %i2, i32 %i1, 0, 1
|
||||
ret {{ i32, i32 }} %i3
|
||||
}
|
||||
|
||||
; This is fine, however, all elements are on the same level
|
||||
define { i32, { i32 } } @test4({ i32, { i32 } } %srcagg) {
|
||||
; CHECK-LABEL: @test4(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, { i32 } } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, { i32 } } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, { i32 } } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, { i32 } } [[I2]], { i32 } [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, { i32 } } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, { i32 } } %srcagg, 0
|
||||
%i1 = extractvalue { i32, { i32 } } %srcagg, 1
|
||||
%i2 = insertvalue { i32, { i32 } } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, { i32 } } %i2, { i32 } %i1, 1
|
||||
ret { i32, { i32 } } %i3
|
||||
}
|
||||
|
||||
; All element of the newly-created aggregate must come from the same base
|
||||
; aggregate. Here the second element comes from some other origin.
|
||||
define { i32, i32 } @negative_test5({ i32, i32 } %srcagg, i32 %replacement) {
|
||||
; CHECK-LABEL: @negative_test5(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[REPLACEMENT:%.*]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %replacement, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Here we don't know the value of second element of %otheragg,
|
||||
define { i32, i32 } @negative_test6({ i32, i32 } %srcagg, { i32, i32 } %otheragg) {
|
||||
; CHECK-LABEL: @negative_test6(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } [[OTHERAGG:%.*]], i32 [[I0]], 0
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I2]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } %otheragg, i32 %i0, 0
|
||||
ret { i32, i32 } %i2
|
||||
}
|
||||
|
||||
; All element of the newly-created aggregate must come from the same base
|
||||
; aggregate. Here different elements come from different base aggregates.
|
||||
define { i32, i32 } @negative_test7({ i32, i32 } %srcagg0, { i32, i32 } %srcagg1) {
|
||||
; CHECK-LABEL: @negative_test7(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG0:%.*]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[SRCAGG1:%.*]], 1
|
||||
; CHECK-NEXT: [[I4:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I5:%.*]] = insertvalue { i32, i32 } [[I4]], i32 [[I3]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I5]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg0, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg0, 1
|
||||
|
||||
; %i2 = extractvalue { i32, i32 } %srcagg1, 0
|
||||
%i3 = extractvalue { i32, i32 } %srcagg1, 1
|
||||
|
||||
%i4 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i5 = insertvalue { i32, i32 } %i4, i32 %i3, 1
|
||||
ret { i32, i32 } %i5
|
||||
}
|
||||
|
||||
; Here the element order is swapped as compared to the base aggregate.
|
||||
define { i32, i32 } @negative_test8({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test8(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 1
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 0
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
%i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 1
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 0
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Here both elements of the new aggregate come from the same element of the old aggregate.
|
||||
define { i32, i32 } @negative_test9({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test9(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I0]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i0, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Here the second element of the new aggregate is undef, , so we must keep this as-is, because in %srcagg it might be poison.
|
||||
; FIXME: defer to noundef attribute on %srcagg
|
||||
define { i32, i32 } @negative_test10({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test10(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I2]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
ret { i32, i32 } %i2
|
||||
}
|
||||
|
||||
; Here the second element of the new aggregate is undef, so we must keep this as-is, because in %srcagg it might be poison.
|
||||
; FIXME: defer to noundef attribute on %srcagg
|
||||
define { i32, i32 } @negative_test11({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test11(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 undef, 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
; %i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 undef, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; This fold does not care whether or not intermediate instructions have extra uses.
|
||||
define { i32, i32 } @test12({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @test12(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: call void @usei32(i32 [[I0]])
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: call void @usei32(i32 [[I1]])
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: call void @usei32i32agg({ i32, i32 } [[I2]])
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
call void @usei32(i32 %i0)
|
||||
%i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
call void @usei32(i32 %i1)
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
call void @usei32i32agg({ i32, i32 } %i2)
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Even though we originally store %i1 into first element, it is later
|
||||
; overwritten with %i0, so all is fine.
|
||||
define { i32, i32 } @test13({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @test13(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I4:%.*]] = insertvalue { i32, i32 } [[I3]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I4]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
%i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i1, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i0, 0
|
||||
%i4 = insertvalue { i32, i32 } %i3, i32 %i1, 1
|
||||
ret { i32, i32 } %i4
|
||||
}
|
||||
|
||||
; The aggregate type must match exactly between the original and recreation.
|
||||
define { i32, i32 } @negative_test14({ i32, i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test14(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, i32, i32 } %srcagg, 0
|
||||
%i1 = extractvalue { i32, i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
define { i32, i32 } @negative_test15({ i32, {i32} } %srcagg) {
|
||||
; CHECK-LABEL: @negative_test15(
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, { i32 } } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, { i32 } } [[SRCAGG]], 1, 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
%i0 = extractvalue { i32, {i32} } %srcagg, 0
|
||||
%i1 = extractvalue { i32, {i32} } %srcagg, 1, 0
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Just because there are predecessors doesn't mean we should look into them.
|
||||
define { i32, i32 } @test16({ i32, i32 } %srcagg) {
|
||||
; CHECK-LABEL: @test16(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
entry:
|
||||
br label %end
|
||||
end:
|
||||
%i0 = extractvalue { i32, i32 } %srcagg, 0
|
||||
%i1 = extractvalue { i32, i32 } %srcagg, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
||||
|
||||
; Again, we should first try to perform local reasoning, without looking to predecessors.
|
||||
define { i32, i32 } @test17({ i32, i32 } %srcagg0, { i32, i32 } %srcagg1, i1 %c) {
|
||||
; CHECK-LABEL: @test17(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C:%.*]], label [[INTERMEDIATE:%.*]], label [[END:%.*]]
|
||||
; CHECK: intermediate:
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[SRCAGG_PHI:%.*]] = phi { i32, i32 } [ [[SRCAGG0:%.*]], [[ENTRY:%.*]] ], [ [[SRCAGG1:%.*]], [[INTERMEDIATE]] ]
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[SRCAGG_PHI]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[SRCAGG_PHI]], 1
|
||||
; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32 } undef, i32 [[I0]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = insertvalue { i32, i32 } [[I2]], i32 [[I1]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I3]]
|
||||
;
|
||||
entry:
|
||||
br i1 %c, label %intermediate, label %end
|
||||
intermediate:
|
||||
br label %end
|
||||
end:
|
||||
%srcagg.phi = phi { i32, i32 } [ %srcagg0, %entry ], [ %srcagg1, %intermediate ]
|
||||
%i0 = extractvalue { i32, i32 } %srcagg.phi, 0
|
||||
%i1 = extractvalue { i32, i32 } %srcagg.phi, 1
|
||||
%i2 = insertvalue { i32, i32 } undef, i32 %i0, 0
|
||||
%i3 = insertvalue { i32, i32 } %i2, i32 %i1, 1
|
||||
ret { i32, i32 } %i3
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -S -instcombine < %s | FileCheck %s
|
||||
|
||||
declare void @foo()
|
||||
declare void @bar()
|
||||
declare void @baz()
|
||||
declare void @qux()
|
||||
|
||||
declare i1 @geni1()
|
||||
|
||||
declare void @usei32(i32)
|
||||
declare void @usei32i32agg({ i32, i32 })
|
||||
|
||||
; Most basic test - diamond structure
|
||||
define { i32, i32 } @test0({ i32, i32 } %agg_left, { i32, i32 } %agg_right, i1 %c) {
|
||||
; CHECK-LABEL: @test0(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
||||
; CHECK: left:
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT]], 1
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: right:
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT:%.*]], 0
|
||||
; CHECK-NEXT: [[I4:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT]], 1
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I0]], [[LEFT]] ], [ [[I3]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: [[I6:%.*]] = phi i32 [ [[I2]], [[LEFT]] ], [ [[I4]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[I7:%.*]] = insertvalue { i32, i32 } undef, i32 [[I5]], 0
|
||||
; CHECK-NEXT: [[I8:%.*]] = insertvalue { i32, i32 } [[I7]], i32 [[I6]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I8]]
|
||||
;
|
||||
entry:
|
||||
br i1 %c, label %left, label %right
|
||||
|
||||
left:
|
||||
%i0 = extractvalue { i32, i32 } %agg_left, 0
|
||||
%i2 = extractvalue { i32, i32 } %agg_left, 1
|
||||
call void @foo()
|
||||
br label %end
|
||||
|
||||
right:
|
||||
%i3 = extractvalue { i32, i32 } %agg_right, 0
|
||||
%i4 = extractvalue { i32, i32 } %agg_right, 1
|
||||
call void @bar()
|
||||
br label %end
|
||||
|
||||
end:
|
||||
%i5 = phi i32 [ %i0, %left ], [ %i3, %right ]
|
||||
%i6 = phi i32 [ %i2, %left ], [ %i4, %right ]
|
||||
call void @baz()
|
||||
%i7 = insertvalue { i32, i32 } undef, i32 %i5, 0
|
||||
%i8 = insertvalue { i32, i32 } %i7, i32 %i6, 1
|
||||
ret { i32, i32 } %i8
|
||||
}
|
||||
|
||||
; Second element is coming from wrong aggregate
|
||||
define { i32, i32 } @negative_test1({ i32, i32 } %agg_left, { i32, i32 } %agg_right, i1 %c) {
|
||||
; CHECK-LABEL: @negative_test1(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
||||
; CHECK: left:
|
||||
; CHECK-NEXT: [[I4:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT:%.*]], 1
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT:%.*]], 0
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: right:
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT]], 1
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I0]], [[LEFT]] ], [ [[I3]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: [[I6:%.*]] = phi i32 [ [[I4]], [[LEFT]] ], [ [[I2]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[I7:%.*]] = insertvalue { i32, i32 } undef, i32 [[I5]], 0
|
||||
; CHECK-NEXT: [[I8:%.*]] = insertvalue { i32, i32 } [[I7]], i32 [[I6]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I8]]
|
||||
;
|
||||
entry:
|
||||
%i0 = extractvalue { i32, i32 } %agg_left, 0
|
||||
%i2 = extractvalue { i32, i32 } %agg_left, 1
|
||||
%i3 = extractvalue { i32, i32 } %agg_right, 0
|
||||
%i4 = extractvalue { i32, i32 } %agg_right, 1
|
||||
br i1 %c, label %left, label %right
|
||||
|
||||
left:
|
||||
call void @foo()
|
||||
br label %end
|
||||
|
||||
right:
|
||||
call void @bar()
|
||||
br label %end
|
||||
|
||||
end:
|
||||
%i5 = phi i32 [ %i0, %left ], [ %i3, %right ]
|
||||
%i6 = phi i32 [ %i4, %left ], [ %i2, %right ]
|
||||
call void @baz()
|
||||
%i7 = insertvalue { i32, i32 } undef, i32 %i5, 0
|
||||
%i8 = insertvalue { i32, i32 } %i7, i32 %i6, 1
|
||||
ret { i32, i32 } %i8
|
||||
}
|
||||
|
||||
; When coming from %left, elements are swapped
|
||||
define { i32, i32 } @negative_test2({ i32, i32 } %agg_left, { i32, i32 } %agg_right, i1 %c) {
|
||||
; CHECK-LABEL: @negative_test2(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
||||
; CHECK: left:
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT:%.*]], 1
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT]], 0
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: right:
|
||||
; CHECK-NEXT: [[I4:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT:%.*]], 1
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT]], 0
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I2]], [[LEFT]] ], [ [[I3]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: [[I6:%.*]] = phi i32 [ [[I0]], [[LEFT]] ], [ [[I4]], [[RIGHT]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[I7:%.*]] = insertvalue { i32, i32 } undef, i32 [[I5]], 0
|
||||
; CHECK-NEXT: [[I8:%.*]] = insertvalue { i32, i32 } [[I7]], i32 [[I6]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I8]]
|
||||
;
|
||||
entry:
|
||||
%i0 = extractvalue { i32, i32 } %agg_left, 0
|
||||
%i2 = extractvalue { i32, i32 } %agg_left, 1
|
||||
%i3 = extractvalue { i32, i32 } %agg_right, 0
|
||||
%i4 = extractvalue { i32, i32 } %agg_right, 1
|
||||
br i1 %c, label %left, label %right
|
||||
|
||||
left:
|
||||
call void @foo()
|
||||
br label %end
|
||||
|
||||
right:
|
||||
call void @bar()
|
||||
br label %end
|
||||
|
||||
end:
|
||||
%i5 = phi i32 [ %i2, %left ], [ %i3, %right ]
|
||||
%i6 = phi i32 [ %i0, %left ], [ %i4, %right ]
|
||||
call void @baz()
|
||||
%i7 = insertvalue { i32, i32 } undef, i32 %i5, 0
|
||||
%i8 = insertvalue { i32, i32 } %i7, i32 %i6, 1
|
||||
ret { i32, i32 } %i8
|
||||
}
|
||||
|
||||
; FIXME: we should probably be able to handle multiple levels of PHI indirection
|
||||
define { i32, i32 } @test3({ i32, i32 } %agg_00, { i32, i32 } %agg_01, { i32, i32 } %agg_10, i1 %c0, i1 %c1) {
|
||||
; CHECK-LABEL: @test3(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB0_DISPATCH:%.*]], label [[BB10:%.*]]
|
||||
; CHECK: bb0.dispatch:
|
||||
; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB00:%.*]], label [[BB01:%.*]]
|
||||
; CHECK: bb00:
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_00:%.*]], 0
|
||||
; CHECK-NEXT: [[I1:%.*]] = extractvalue { i32, i32 } [[AGG_00]], 1
|
||||
; CHECK-NEXT: br label [[BB0_MERGE:%.*]]
|
||||
; CHECK: bb01:
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_01:%.*]], 0
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_01]], 1
|
||||
; CHECK-NEXT: br label [[BB0_MERGE]]
|
||||
; CHECK: bb0.merge:
|
||||
; CHECK-NEXT: [[I4:%.*]] = phi i32 [ [[I0]], [[BB00]] ], [ [[I2]], [[BB01]] ]
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I1]], [[BB00]] ], [ [[I3]], [[BB01]] ]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: bb10:
|
||||
; CHECK-NEXT: [[I6:%.*]] = extractvalue { i32, i32 } [[AGG_10:%.*]], 0
|
||||
; CHECK-NEXT: [[I7:%.*]] = extractvalue { i32, i32 } [[AGG_10]], 1
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I8:%.*]] = phi i32 [ [[I4]], [[BB0_MERGE]] ], [ [[I6]], [[BB10]] ]
|
||||
; CHECK-NEXT: [[I9:%.*]] = phi i32 [ [[I5]], [[BB0_MERGE]] ], [ [[I7]], [[BB10]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[I10:%.*]] = insertvalue { i32, i32 } undef, i32 [[I8]], 0
|
||||
; CHECK-NEXT: [[I11:%.*]] = insertvalue { i32, i32 } [[I10]], i32 [[I9]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I11]]
|
||||
;
|
||||
entry:
|
||||
br i1 %c0, label %bb0.dispatch, label %bb10
|
||||
|
||||
bb0.dispatch:
|
||||
br i1 %c1, label %bb00, label %bb01
|
||||
|
||||
bb00:
|
||||
%i0 = extractvalue { i32, i32 } %agg_00, 0
|
||||
%i1 = extractvalue { i32, i32 } %agg_00, 1
|
||||
br label %bb0.merge
|
||||
|
||||
bb01:
|
||||
%i2 = extractvalue { i32, i32 } %agg_01, 0
|
||||
%i3 = extractvalue { i32, i32 } %agg_01, 1
|
||||
br label %bb0.merge
|
||||
|
||||
bb0.merge:
|
||||
%i4 = phi i32 [ %i0, %bb00 ], [ %i2, %bb01 ]
|
||||
%i5 = phi i32 [ %i1, %bb00 ], [ %i3, %bb01 ]
|
||||
br label %end
|
||||
|
||||
bb10:
|
||||
%i6 = extractvalue { i32, i32 } %agg_10, 0
|
||||
%i7 = extractvalue { i32, i32 } %agg_10, 1
|
||||
br label %end
|
||||
|
||||
end:
|
||||
%i8 = phi i32 [ %i4, %bb0.merge ], [ %i6, %bb10 ]
|
||||
%i9 = phi i32 [ %i5, %bb0.merge ], [ %i7, %bb10 ]
|
||||
call void @baz()
|
||||
%i10 = insertvalue { i32, i32 } undef, i32 %i8, 0
|
||||
%i11 = insertvalue { i32, i32 } %i10, i32 %i9, 1
|
||||
ret { i32, i32 } %i11
|
||||
}
|
||||
|
||||
; Not sure what should happen for cycles.
|
||||
define { i32, i32 } @test4({ i32, i32 } %agg_left, { i32, i32 } %agg_right, i1 %c0) {
|
||||
; CHECK-LABEL: @test4(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C0:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
||||
; CHECK: left:
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT]], 1
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[MIDDLE:%.*]]
|
||||
; CHECK: right:
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT:%.*]], 0
|
||||
; CHECK-NEXT: [[I4:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT]], 1
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[MIDDLE]]
|
||||
; CHECK: middle:
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I0]], [[LEFT]] ], [ [[I3]], [[RIGHT]] ], [ [[I5]], [[MIDDLE]] ]
|
||||
; CHECK-NEXT: [[I6:%.*]] = phi i32 [ [[I2]], [[LEFT]] ], [ [[I4]], [[RIGHT]] ], [ [[I6]], [[MIDDLE]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[C1:%.*]] = call i1 @geni1()
|
||||
; CHECK-NEXT: br i1 [[C1]], label [[END:%.*]], label [[MIDDLE]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I7:%.*]] = insertvalue { i32, i32 } undef, i32 [[I5]], 0
|
||||
; CHECK-NEXT: [[I8:%.*]] = insertvalue { i32, i32 } [[I7]], i32 [[I6]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I8]]
|
||||
;
|
||||
entry:
|
||||
br i1 %c0, label %left, label %right
|
||||
|
||||
left:
|
||||
%i0 = extractvalue { i32, i32 } %agg_left, 0
|
||||
%i2 = extractvalue { i32, i32 } %agg_left, 1
|
||||
call void @foo()
|
||||
br label %middle
|
||||
|
||||
right:
|
||||
%i3 = extractvalue { i32, i32 } %agg_right, 0
|
||||
%i4 = extractvalue { i32, i32 } %agg_right, 1
|
||||
call void @bar()
|
||||
br label %middle
|
||||
|
||||
middle:
|
||||
%i5 = phi i32 [ %i0, %left ], [ %i3, %right ], [ %i5, %middle ]
|
||||
%i6 = phi i32 [ %i2, %left ], [ %i4, %right ], [ %i6, %middle ]
|
||||
call void @baz()
|
||||
%i7 = insertvalue { i32, i32 } undef, i32 %i5, 0
|
||||
%i8 = insertvalue { i32, i32 } %i7, i32 %i6, 1
|
||||
%c1 = call i1 @geni1()
|
||||
br i1 %c1, label %end, label %middle
|
||||
|
||||
end:
|
||||
ret { i32, i32 } %i8
|
||||
}
|
||||
|
||||
; But here since we start without an explicit self-cycle, we already manage to fold it.
|
||||
define { i32, i32 } @test5({ i32, i32 } %agg_left, { i32, i32 } %agg_right, i1 %c0) {
|
||||
; CHECK-LABEL: @test5(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br i1 [[C0:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
||||
; CHECK: left:
|
||||
; CHECK-NEXT: [[I0:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT:%.*]], 0
|
||||
; CHECK-NEXT: [[I2:%.*]] = extractvalue { i32, i32 } [[AGG_LEFT]], 1
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[MIDDLE:%.*]]
|
||||
; CHECK: right:
|
||||
; CHECK-NEXT: [[I3:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT:%.*]], 0
|
||||
; CHECK-NEXT: [[I4:%.*]] = extractvalue { i32, i32 } [[AGG_RIGHT]], 1
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[MIDDLE]]
|
||||
; CHECK: middle:
|
||||
; CHECK-NEXT: [[I5:%.*]] = phi i32 [ [[I0]], [[LEFT]] ], [ [[I3]], [[RIGHT]] ], [ [[I5]], [[MIDDLE]] ]
|
||||
; CHECK-NEXT: [[I6:%.*]] = phi i32 [ [[I2]], [[LEFT]] ], [ [[I4]], [[RIGHT]] ], [ [[I6]], [[MIDDLE]] ]
|
||||
; CHECK-NEXT: call void @baz()
|
||||
; CHECK-NEXT: [[C1:%.*]] = call i1 @geni1()
|
||||
; CHECK-NEXT: br i1 [[C1]], label [[END:%.*]], label [[MIDDLE]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[I7:%.*]] = insertvalue { i32, i32 } undef, i32 [[I5]], 0
|
||||
; CHECK-NEXT: [[I8:%.*]] = insertvalue { i32, i32 } [[I7]], i32 [[I6]], 1
|
||||
; CHECK-NEXT: ret { i32, i32 } [[I8]]
|
||||
;
|
||||
entry:
|
||||
br i1 %c0, label %left, label %right
|
||||
|
||||
left:
|
||||
%i0 = extractvalue { i32, i32 } %agg_left, 0
|
||||
%i2 = extractvalue { i32, i32 } %agg_left, 1
|
||||
call void @foo()
|
||||
br label %middle
|
||||
|
||||
right:
|
||||
%i3 = extractvalue { i32, i32 } %agg_right, 0
|
||||
%i4 = extractvalue { i32, i32 } %agg_right, 1
|
||||
call void @bar()
|
||||
br label %middle
|
||||
|
||||
middle:
|
||||
%i5 = phi i32 [ %i0, %left ], [ %i3, %right ], [ %i9, %middle ]
|
||||
%i6 = phi i32 [ %i2, %left ], [ %i4, %right ], [ %i10, %middle ]
|
||||
call void @baz()
|
||||
%i7 = insertvalue { i32, i32 } undef, i32 %i5, 0
|
||||
%i8 = insertvalue { i32, i32 } %i7, i32 %i6, 1
|
||||
%i9 = extractvalue { i32, i32 } %i8, 0
|
||||
%i10 = extractvalue { i32, i32 } %i8, 1
|
||||
%c1 = call i1 @geni1()
|
||||
br i1 %c1, label %end, label %middle
|
||||
|
||||
end:
|
||||
ret { i32, i32 } %i8
|
||||
}
|
Loading…
Reference in New Issue