1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
4 ; Test merging GEP of GEP with constant indices.
6 target datalayout = "i24:8:8"
8 %struct.A = type { [123 x i8], i32 }
9 %struct.B = type { i8, [3 x i16], %struct.A, float }
10 %struct.C = type { i8, i32, i32 }
12 ; result = (ptr) p + 3
13 define ptr @mergeBasic(ptr %p) {
14 ; CHECK-LABEL: @mergeBasic(
15 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 3
16 ; CHECK-NEXT: ret ptr [[TMP1]]
18 %1 = getelementptr inbounds i32, ptr %p, i64 1
19 %2 = getelementptr inbounds i32, ptr %1, i64 2
23 ; Converted to ptr and merged.
24 ; result = (ptr) p + 10
25 define ptr @mergeDifferentTypes(ptr %p) {
26 ; CHECK-LABEL: @mergeDifferentTypes(
27 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 10
28 ; CHECK-NEXT: ret ptr [[TMP1]]
30 %1 = getelementptr inbounds i8, ptr %p, i64 2
31 %2 = getelementptr inbounds i64, ptr %1, i64 1
35 ; Converted to ptr and merged.
36 ; result = (ptr) p + 10
37 define ptr @mergeReverse(ptr %p) {
38 ; CHECK-LABEL: @mergeReverse(
39 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 10
40 ; CHECK-NEXT: ret ptr [[TMP1]]
42 %1 = getelementptr inbounds i64, ptr %p, i64 1
43 %2 = getelementptr inbounds i8, ptr %1, i64 2
47 ; Offsets of first and last GEP cancel out.
49 define ptr @zeroSum(ptr %p) {
50 ; CHECK-LABEL: @zeroSum(
51 ; CHECK-NEXT: ret ptr [[P:%.*]]
53 %1 = getelementptr inbounds i32, ptr %p, i64 1
54 %2 = getelementptr inbounds i8, ptr %1, i64 -4
58 ; result = (ptr) ((ptr) p + 1) + 17
59 define ptr @array1(ptr %p) {
60 ; CHECK-LABEL: @array1(
61 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [20 x i8], ptr [[P:%.*]], i64 1, i64 17
62 ; CHECK-NEXT: ret ptr [[TMP1]]
64 %1 = getelementptr inbounds [20 x i8], ptr %p, i64 1, i64 1
65 %2 = getelementptr inbounds i64, ptr %1, i64 2
69 ; Converted to ptr and merged.
70 ; result = (ptr) p + 20
71 define ptr @array2(ptr %p) {
72 ; CHECK-LABEL: @array2(
73 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 20
74 ; CHECK-NEXT: ret ptr [[TMP1]]
76 %1 = getelementptr inbounds i64, ptr %p, i64 2
77 %2 = getelementptr inbounds [3 x i8], ptr %1, i64 1, i64 1
81 ; Converted to ptr and merged.
82 ; result = (ptr) p + 36
83 define ptr @struct1(ptr %p) {
84 ; CHECK-LABEL: @struct1(
85 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 36
86 ; CHECK-NEXT: ret ptr [[TMP1]]
88 %1 = getelementptr inbounds i64, ptr %p, i64 3
89 %2 = getelementptr inbounds %struct.C, ptr %1, i64 1
93 ; result = &((struct.A*) p - 1).member1
94 define ptr @struct2(ptr %p) {
95 ; CHECK-LABEL: @struct2(
96 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_A:%.*]], ptr [[P:%.*]], i64 -1, i32 1
97 ; CHECK-NEXT: ret ptr [[TMP1]]
99 %1 = getelementptr inbounds %struct.A, ptr %p, i64 0, i32 1
100 %2 = getelementptr inbounds i8, ptr %1, i64 -128
104 ; result = (ptr) &((struct.B) p)[0].member2.member0 + 7
105 define ptr @structStruct(ptr %p) {
106 ; CHECK-LABEL: @structStruct(
107 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 2, i32 0, i64 7
108 ; CHECK-NEXT: ret ptr [[TMP1]]
110 %1 = getelementptr inbounds %struct.B, ptr %p, i64 0, i32 2, i32 0, i64 3
111 %2 = getelementptr inbounds %struct.A, ptr %1, i64 0, i32 0, i64 4
115 ; First GEP offset is not divisible by last GEP's source element size, but first
116 ; GEP points to an array such that the last GEP offset is divisible by the
117 ; array's element size, so the first GEP can be rewritten with an extra index.
118 ; result = (ptr) &((struct.B*) p)[i].member1 + 2
119 define ptr @appendIndex(ptr %p, i64 %i) {
120 ; CHECK-LABEL: @appendIndex(
121 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 [[I:%.*]], i32 1, i64 2
122 ; CHECK-NEXT: ret ptr [[TMP1]]
124 %1 = getelementptr inbounds %struct.B, ptr %p, i64 %i, i32 1
125 %2 = getelementptr inbounds i32, ptr %1, i64 1
129 ; Offset of either GEP is not divisible by the other's size, converted to ptr
131 ; Here i24 is 8-bit aligned.
132 ; result = (ptr) p + 7
133 define ptr @notDivisible(ptr %p) {
134 ; CHECK-LABEL: @notDivisible(
135 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 7
136 ; CHECK-NEXT: ret ptr [[TMP1]]
138 %1 = getelementptr inbounds i24, ptr %p, i64 1
139 %2 = getelementptr inbounds i32, ptr %1, i64 1
143 ; Negative test. Two GEP should not be merged if not both offsets are constant
144 ; or divisible by the other's size.
145 define ptr @partialConstant2(ptr %p, i64 %a) {
146 ; CHECK-LABEL: @partialConstant2(
147 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
148 ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[A:%.*]], i64 2
149 ; CHECK-NEXT: ret ptr [[TMP2]]
151 %1 = getelementptr inbounds i32, ptr %p, i64 1
152 %2 = getelementptr inbounds [4 x i64], ptr %1, i64 %a, i64 2
156 ; Negative test. Two GEP should not be merged if there is another use of the
157 ; first GEP by the second GEP.
158 define ptr @partialConstant3(ptr %p) {
159 ; CHECK-LABEL: @partialConstant3(
160 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
161 ; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
162 ; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[TMP2]], i64 2
163 ; CHECK-NEXT: ret ptr [[TMP3]]
165 %1 = getelementptr inbounds i32, ptr %p, i64 1
166 %2 = ptrtoint ptr %1 to i64
167 %3 = getelementptr inbounds [4 x i64], ptr %1, i64 %2, i64 2
171 ; Two GEP instructions can be merged if one is constant-indexed and the other
172 ; is an aggregate type with a constant last index, and the resulting pointer
173 ; address by adding the constant offset aliases the address of another member.
174 ; result = &((struct.C*) p + a).member2
175 define ptr @partialConstantMemberAliasing1(ptr %p, i64 %a) {
176 ; CHECK-LABEL: @partialConstantMemberAliasing1(
177 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2
178 ; CHECK-NEXT: ret ptr [[TMP1]]
180 %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 1
181 %2 = getelementptr inbounds i32, ptr %1, i64 1
185 ; Negative test. Similar to above, but the new address does not alias the
186 ; address of another member.
187 define ptr @partialConstantMemberAliasing2(ptr %p, i64 %a) {
188 ; CHECK-LABEL: @partialConstantMemberAliasing2(
189 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 1
190 ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1
191 ; CHECK-NEXT: ret ptr [[TMP2]]
193 %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 1
194 %2 = getelementptr inbounds i8, ptr %1, i64 1
198 ; Negative test. Similar to above, but the new address falls outside the address
199 ; range of the object currently pointed by the non-constant GEP.
200 define ptr @partialConstantMemberAliasing3(ptr %p, i64 %a) {
201 ; CHECK-LABEL: @partialConstantMemberAliasing3(
202 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2
203 ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1
204 ; CHECK-NEXT: ret ptr [[TMP2]]
206 %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 2
207 %2 = getelementptr inbounds i32, ptr %1, i64 1