1; RUN: llc -mtriple=thumbv6m-eabi %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-T1
2; RUN: llc -mtriple=thumbv7m-eabi %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-T2
3
4
5; Register offset
6
7; CHECK-LABEL: ldrsb_rr
8; CHECK:    ldrsb   r0, [r0, r1]
9define i32 @ldrsb_rr(i8* %p, i32 %n) {
10entry:
11  %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
12  %0 = load i8, i8* %arrayidx, align 1
13  %conv = sext i8 %0 to i32
14  ret i32 %conv
15}
16
17; CHECK-LABEL: ldrsh_rr
18; CHECK-T1: lsls    r1, r1, #1
19; CHECK-T1: ldrsh   r0, [r0, r1]
20; CHECK-T2: ldrsh.w r0, [r0, r1, lsl #1]
21define i32 @ldrsh_rr(i16* %p, i32 %n) {
22entry:
23  %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
24  %0 = load i16, i16* %arrayidx, align 2
25  %conv = sext i16 %0 to i32
26  ret i32 %conv
27}
28
29; CHECK-LABEL: ldrb_rr
30; CHECK:    ldrb r0, [r0, r1]
31define i32 @ldrb_rr(i8* %p, i32 %n) {
32entry:
33  %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
34  %0 = load i8, i8* %arrayidx, align 1
35  %conv = zext i8 %0 to i32
36  ret i32 %conv
37}
38
39; CHECK-LABEL: ldrh_rr
40; CHECK-T1: lsls    r1, r1, #1
41; CHECK-T1: ldrh    r0, [r0, r1]
42; CHECK-T2: ldrh.w  r0, [r0, r1, lsl #1]
43define i32 @ldrh_rr(i16* %p, i32 %n) {
44entry:
45  %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
46  %0 = load i16, i16* %arrayidx, align 2
47  %conv = zext i16 %0 to i32
48  ret i32 %conv
49}
50
51; CHECK-LABEL: ldr_rr
52; CHECK-T1: lsls    r1, r1, #2
53; CHECK-T1: ldr     r0, [r0, r1]
54; CHECK-T2: ldr.w   r0, [r0, r1, lsl #2]
55define i32 @ldr_rr(i32* %p, i32 %n) {
56entry:
57  %arrayidx = getelementptr inbounds i32, i32* %p, i32 %n
58  %0 = load i32, i32* %arrayidx, align 4
59  ret i32 %0
60}
61
62; CHECK-LABEL: strb_rr
63; CHECK:    strb    r2, [r0, r1]
64define void @strb_rr(i8* %p, i32 %n, i32 %x) {
65entry:
66  %conv = trunc i32 %x to i8
67  %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
68  store i8 %conv, i8* %arrayidx, align 1
69  ret void
70}
71
72; CHECK-LABEL: strh_rr
73; CHECK-T1: lsls    r1, r1, #1
74; CHECK-T1: strh    r2, [r0, r1]
75; CHECK-T2: strh.w  r2, [r0, r1, lsl #1]
76define void @strh_rr(i16* %p, i32 %n, i32 %x) {
77entry:
78  %conv = trunc i32 %x to i16
79  %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
80  store i16 %conv, i16* %arrayidx, align 2
81  ret void
82}
83
84; CHECK-LABEL: str_rr
85; CHECK-T1: lsls    r1, r1, #2
86; CHECK-T1: str     r2, [r0, r1]
87; CHECK-T2: str.w   r2, [r0, r1, lsl #2]
88define void @str_rr(i32* %p, i32 %n, i32 %x) {
89entry:
90  %arrayidx = getelementptr inbounds i32, i32* %p, i32 %n
91  store i32 %x, i32* %arrayidx, align 4
92  ret void
93}
94
95
96; Immediate offset of zero
97
98; CHECK-LABEL: ldrsb_ri_zero
99; CHECK-T1: ldrb    r0, [r0]
100; CHECK-T1: sxtb    r0, r0
101; CHECK-T2: ldrsb.w r0, [r0]
102define i32 @ldrsb_ri_zero(i8* %p) {
103entry:
104  %0 = load i8, i8* %p, align 1
105  %conv = sext i8 %0 to i32
106  ret i32 %conv
107}
108
109; CHECK-LABEL: ldrsh_ri_zero
110; CHECK-T1: ldrh    r0, [r0]
111; CHECK-T1: sxth    r0, r0
112; CHECK-T2: ldrsh.w r0, [r0]
113define i32 @ldrsh_ri_zero(i16* %p) {
114entry:
115  %0 = load i16, i16* %p, align 2
116  %conv = sext i16 %0 to i32
117  ret i32 %conv
118}
119
120; CHECK-LABEL: ldrb_ri_zero
121; CHECK:    ldrb    r0, [r0]
122define i32 @ldrb_ri_zero(i8* %p) {
123entry:
124  %0 = load i8, i8* %p, align 1
125  %conv = zext i8 %0 to i32
126  ret i32 %conv
127}
128
129; CHECK-LABEL: ldrh_ri_zero
130; CHECK:    ldrh    r0, [r0]
131define i32 @ldrh_ri_zero(i16* %p) {
132entry:
133  %0 = load i16, i16* %p, align 2
134  %conv = zext i16 %0 to i32
135  ret i32 %conv
136}
137
138; CHECK-LABEL: ldr_ri_zero
139; CHECK:    ldr     r0, [r0]
140define i32 @ldr_ri_zero(i32* %p) {
141entry:
142  %0 = load i32, i32* %p, align 4
143  ret i32 %0
144}
145
146; CHECK-LABEL: strb_ri_zero
147; CHECK:    strb    r1, [r0]
148define void @strb_ri_zero(i8* %p, i32 %x) {
149entry:
150  %conv = trunc i32 %x to i8
151  store i8 %conv, i8* %p, align 1
152  ret void
153}
154
155; CHECK-LABEL: strh_ri_zero
156; CHECK:    strh    r1, [r0]
157define void @strh_ri_zero(i16* %p, i32 %x) {
158entry:
159  %conv = trunc i32 %x to i16
160  store i16 %conv, i16* %p, align 2
161  ret void
162}
163
164; CHECK-LABEL: str_ri_zero
165; CHECK:    str     r1, [r0]
166define void @str_ri_zero(i32* %p, i32 %x) {
167entry:
168  store i32 %x, i32* %p, align 4
169  ret void
170}
171
172
173; Maximum Thumb-1 immediate offset
174
175; CHECK-LABEL: ldrsb_ri_t1_max
176; CHECK-T1: movs    r1, #31
177; CHECK-T1: ldrsb   r0, [r0, r1]
178; CHECK-T2: ldrsb.w r0, [r0, #31]
179define i32 @ldrsb_ri_t1_max(i8* %p) {
180entry:
181  %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
182  %0 = load i8, i8* %arrayidx, align 1
183  %conv = sext i8 %0 to i32
184  ret i32 %conv
185}
186
187; CHECK-LABEL: ldrsh_ri_t1_max
188; CHECK-T1: movs    r1, #62
189; CHECK-T1: ldrsh   r0, [r0, r1]
190; CHECK-T2: ldrsh.w r0, [r0, #62]
191define i32 @ldrsh_ri_t1_max(i16* %p) {
192entry:
193  %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
194  %0 = load i16, i16* %arrayidx, align 2
195  %conv = sext i16 %0 to i32
196  ret i32 %conv
197}
198
199; CHECK-LABEL: ldrb_ri_t1_max
200; CHECK:    ldrb    r0, [r0, #31]
201define i32 @ldrb_ri_t1_max(i8* %p) {
202entry:
203  %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
204  %0 = load i8, i8* %arrayidx, align 1
205  %conv = zext i8 %0 to i32
206  ret i32 %conv
207}
208
209; CHECK-LABEL: ldrh_ri_t1_max
210; CHECK:    ldrh    r0, [r0, #62]
211define i32 @ldrh_ri_t1_max(i16* %p) {
212entry:
213  %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
214  %0 = load i16, i16* %arrayidx, align 2
215  %conv = zext i16 %0 to i32
216  ret i32 %conv
217}
218
219; CHECK-LABEL: ldr_ri_t1_max
220; CHECK:    ldr     r0, [r0, #124]
221define i32 @ldr_ri_t1_max(i32* %p) {
222entry:
223  %arrayidx = getelementptr inbounds i32, i32* %p, i32 31
224  %0 = load i32, i32* %arrayidx, align 4
225  ret i32 %0
226}
227
228; CHECK-LABEL: strb_ri_t1_max
229; CHECK:    strb    r1, [r0, #31]
230define void @strb_ri_t1_max(i8* %p, i32 %x) {
231entry:
232  %conv = trunc i32 %x to i8
233  %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
234  store i8 %conv, i8* %arrayidx, align 1
235  ret void
236}
237
238; CHECK-LABEL: strh_ri_t1_max
239; CHECK:    strh    r1, [r0, #62]
240define void @strh_ri_t1_max(i16* %p, i32 %x) {
241entry:
242  %conv = trunc i32 %x to i16
243  %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
244  store i16 %conv, i16* %arrayidx, align 2
245  ret void
246}
247
248; CHECK-LABEL: str_ri_t1_max
249; CHECK:    str     r1, [r0, #124]
250define void @str_ri_t1_max(i32* %p, i32 %x) {
251entry:
252  %arrayidx = getelementptr inbounds i32, i32* %p, i32 31
253  store i32 %x, i32* %arrayidx, align 4
254  ret void
255}
256
257
258; One past maximum Thumb-1 immediate offset
259
260; CHECK-LABEL: ldrsb_ri_t1_too_big
261; CHECK-T1: movs    r1, #32
262; CHECK-T1: ldrsb   r0, [r0, r1]
263; CHECK-T2: ldrsb.w r0, [r0, #32]
264define i32 @ldrsb_ri_t1_too_big(i8* %p) {
265entry:
266  %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
267  %0 = load i8, i8* %arrayidx, align 1
268  %conv = sext i8 %0 to i32
269  ret i32 %conv
270}
271
272; CHECK-LABEL: ldrsh_ri_t1_too_big
273; CHECK-T1: movs    r1, #64
274; CHECK-T1: ldrsh   r0, [r0, r1]
275; CHECK-T2: ldrsh.w r0, [r0, #64]
276define i32 @ldrsh_ri_t1_too_big(i16* %p) {
277entry:
278  %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
279  %0 = load i16, i16* %arrayidx, align 2
280  %conv = sext i16 %0 to i32
281  ret i32 %conv
282}
283
284; CHECK-LABEL: ldrb_ri_t1_too_big
285; CHECK-T1: movs    r1, #32
286; CHECK-T1: ldrb    r0, [r0, r1]
287; CHECK-T2: ldrb.w  r0, [r0, #32]
288define i32 @ldrb_ri_t1_too_big(i8* %p) {
289entry:
290  %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
291  %0 = load i8, i8* %arrayidx, align 1
292  %conv = zext i8 %0 to i32
293  ret i32 %conv
294}
295
296; CHECK-LABEL: ldrh_ri_t1_too_big
297; CHECK-T1: movs    r1, #64
298; CHECK-T1: ldrh    r0, [r0, r1]
299; CHECK-T2: ldrh.w  r0, [r0, #64]
300define i32 @ldrh_ri_t1_too_big(i16* %p) {
301entry:
302  %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
303  %0 = load i16, i16* %arrayidx, align 2
304  %conv = zext i16 %0 to i32
305  ret i32 %conv
306}
307
308; CHECK-LABEL: ldr_ri_t1_too_big
309; CHECK-T1: movs    r1, #128
310; CHECK-T1: ldr     r0, [r0, r1]
311; CHECK-T2: ldr.w   r0, [r0, #128]
312define i32 @ldr_ri_t1_too_big(i32* %p) {
313entry:
314  %arrayidx = getelementptr inbounds i32, i32* %p, i32 32
315  %0 = load i32, i32* %arrayidx, align 4
316  ret i32 %0
317}
318
319; CHECK-LABEL: strb_ri_t1_too_big
320; CHECK-T1: movs    r2, #32
321; CHECK-T1: strb    r1, [r0, r2]
322; CHECK-T2: strb.w  r1, [r0, #32]
323define void @strb_ri_t1_too_big(i8* %p, i32 %x) {
324entry:
325  %conv = trunc i32 %x to i8
326  %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
327  store i8 %conv, i8* %arrayidx, align 1
328  ret void
329}
330
331; CHECK-LABEL: strh_ri_t1_too_big
332; CHECK-T1: movs    r2, #64
333; CHECK-T1: strh    r1, [r0, r2]
334; CHECK-T2: strh.w  r1, [r0, #64]
335define void @strh_ri_t1_too_big(i16* %p, i32 %x) {
336entry:
337  %conv = trunc i32 %x to i16
338  %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
339  store i16 %conv, i16* %arrayidx, align 2
340  ret void
341}
342
343; CHECK-LABEL: str_ri_t1_too_big
344; CHECK-T1: movs    r2, #128
345; CHECK-T1: str     r1, [r0, r2]
346; CHECK-T2: str.w   r1, [r0, #128]
347define void @str_ri_t1_too_big(i32* %p, i32 %x) {
348entry:
349  %arrayidx = getelementptr inbounds i32, i32* %p, i32 32
350  store i32 %x, i32* %arrayidx, align 4
351  ret void
352}
353
354
355; Maximum Thumb-2 immediate offset
356
357; CHECK-LABEL: ldrsb_ri_t2_max
358; CHECK-T1: ldr     r1, .LCP
359; CHECK-T1: ldrsb   r0, [r0, r1]
360; CHECK-T2: ldrsb.w r0, [r0, #4095]
361define i32 @ldrsb_ri_t2_max(i8* %p) {
362entry:
363  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
364  %0 = load i8, i8* %add.ptr, align 1
365  %conv = sext i8 %0 to i32
366  ret i32 %conv
367}
368
369; CHECK-LABEL: ldrsh_ri_t2_max
370; CHECK-T1: ldr     r1, .LCP
371; CHECK-T1: ldrsh   r0, [r0, r1]
372; CHECK-T2: ldrsh.w r0, [r0, #4095]
373define i32 @ldrsh_ri_t2_max(i8* %p) {
374entry:
375  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
376  %0 = bitcast i8* %add.ptr to i16*
377  %1 = load i16, i16* %0, align 2
378  %conv = sext i16 %1 to i32
379  ret i32 %conv
380}
381
382; CHECK-LABEL: ldrb_ri_t2_max
383; CHECK-T1: ldr     r1, .LCP
384; CHECK-T1: ldrb    r0, [r0, r1]
385; CHECK-T2: ldrb.w  r0, [r0, #4095]
386define i32 @ldrb_ri_t2_max(i8* %p) {
387entry:
388  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
389  %0 = load i8, i8* %add.ptr, align 1
390  %conv = zext i8 %0 to i32
391  ret i32 %conv
392}
393
394; CHECK-LABEL: ldrh_ri_t2_max
395; CHECK-T1: ldr     r1, .LCP
396; CHECK-T1: ldrh    r0, [r0, r1]
397; CHECK-T2: ldrh.w  r0, [r0, #4095]
398define i32 @ldrh_ri_t2_max(i8* %p) {
399entry:
400  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
401  %0 = bitcast i8* %add.ptr to i16*
402  %1 = load i16, i16* %0, align 2
403  %conv = zext i16 %1 to i32
404  ret i32 %conv
405}
406
407; CHECK-LABEL: ldr_ri_t2_max
408; CHECK-T1: ldr     r1, .LCP
409; CHECK-T1: ldr     r0, [r0, r1]
410; CHECK-T2: ldr.w   r0, [r0, #4095]
411define i32 @ldr_ri_t2_max(i8* %p) {
412entry:
413  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
414  %0 = bitcast i8* %add.ptr to i32*
415  %1 = load i32, i32* %0, align 4
416  ret i32 %1
417}
418
419; CHECK-LABEL: strb_ri_t2_max
420; CHECK-T1: ldr     r2, .LCP
421; CHECK-T1: strb    r1, [r0, r2]
422; CHECK-T2: strb.w  r1, [r0, #4095]
423define void @strb_ri_t2_max(i8* %p, i32 %x) {
424entry:
425  %conv = trunc i32 %x to i8
426  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
427  store i8 %conv, i8* %add.ptr, align 1
428  ret void
429}
430
431; CHECK-LABEL: strh_ri_t2_max
432; CHECK-T1: ldr     r2, .LCP
433; CHECK-T1: strh    r1, [r0, r2]
434; CHECK-T2: strh.w  r1, [r0, #4095]
435define void @strh_ri_t2_max(i8* %p, i32 %x) {
436entry:
437  %conv = trunc i32 %x to i16
438  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
439  %0 = bitcast i8* %add.ptr to i16*
440  store i16 %conv, i16* %0, align 2
441  ret void
442}
443
444; CHECK-LABEL: str_ri_t2_max
445; CHECK-T1: ldr     r2, .LCP
446; CHECK-T1: str     r1, [r0, r2]
447; CHECK-T2: str.w   r1, [r0, #4095]
448define void @str_ri_t2_max(i8* %p, i32 %x) {
449entry:
450  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
451  %0 = bitcast i8* %add.ptr to i32*
452  store i32 %x, i32* %0, align 4
453  ret void
454}
455
456
457; One past maximum Thumb-2 immediate offset
458
459; CHECK-LABEL: ldrsb_ri_t2_too_big
460; CHECK-T1: movs    r1, #1
461; CHECK-T1: lsls    r1, r1, #12
462; CHECK-T2: mov.w   r1, #4096
463; CHECK:    ldrsb   r0, [r0, r1]
464define i32 @ldrsb_ri_t2_too_big(i8* %p) {
465entry:
466  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
467  %0 = load i8, i8* %add.ptr, align 1
468  %conv = sext i8 %0 to i32
469  ret i32 %conv
470}
471
472; CHECK-LABEL: ldrsh_ri_t2_too_big
473; CHECK-T1: movs    r1, #1
474; CHECK-T1: lsls    r1, r1, #12
475; CHECK-T2: mov.w   r1, #4096
476; CHECK:    ldrsh   r0, [r0, r1]
477define i32 @ldrsh_ri_t2_too_big(i8* %p) {
478entry:
479  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
480  %0 = bitcast i8* %add.ptr to i16*
481  %1 = load i16, i16* %0, align 2
482  %conv = sext i16 %1 to i32
483  ret i32 %conv
484}
485
486; CHECK-LABEL: ldrb_ri_t2_too_big
487; CHECK-T1: movs    r1, #1
488; CHECK-T1: lsls    r1, r1, #12
489; CHECK-T2: mov.w   r1, #4096
490; CHECK:    ldrb    r0, [r0, r1]
491define i32 @ldrb_ri_t2_too_big(i8* %p) {
492entry:
493  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
494  %0 = load i8, i8* %add.ptr, align 1
495  %conv = zext i8 %0 to i32
496  ret i32 %conv
497}
498
499; CHECK-LABEL: ldrh_ri_t2_too_big
500; CHECK-T1: movs    r1, #1
501; CHECK-T1: lsls    r1, r1, #12
502; CHECK-T2: mov.w   r1, #4096
503; CHECK:    ldrh    r0, [r0, r1]
504define i32 @ldrh_ri_t2_too_big(i8* %p) {
505entry:
506  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
507  %0 = bitcast i8* %add.ptr to i16*
508  %1 = load i16, i16* %0, align 2
509  %conv = zext i16 %1 to i32
510  ret i32 %conv
511}
512
513; CHECK-LABEL: ldr_ri_t2_too_big
514; CHECK-T1: movs    r1, #1
515; CHECK-T1: lsls    r1, r1, #12
516; CHECK-T2: mov.w   r1, #4096
517; CHECK:    ldr     r0, [r0, r1]
518define i32 @ldr_ri_t2_too_big(i8* %p) {
519entry:
520  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
521  %0 = bitcast i8* %add.ptr to i32*
522  %1 = load i32, i32* %0, align 4
523  ret i32 %1
524}
525
526; CHECK-LABEL: strb_ri_t2_too_big
527; CHECK-T1: movs    r2, #1
528; CHECK-T1: lsls    r2, r2, #12
529; CHECK-T2: mov.w   r2, #4096
530; CHECK:    strb    r1, [r0, r2]
531define void @strb_ri_t2_too_big(i8* %p, i32 %x) {
532entry:
533  %conv = trunc i32 %x to i8
534  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
535  store i8 %conv, i8* %add.ptr, align 1
536  ret void
537}
538
539; CHECK-LABEL: strh_ri_t2_too_big
540; CHECK-T1: movs    r2, #1
541; CHECK-T1: lsls    r2, r2, #12
542; CHECK-T2: mov.w   r2, #4096
543; CHECK:    strh    r1, [r0, r2]
544define void @strh_ri_t2_too_big(i8* %p, i32 %x) {
545entry:
546  %conv = trunc i32 %x to i16
547  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
548  %0 = bitcast i8* %add.ptr to i16*
549  store i16 %conv, i16* %0, align 2
550  ret void
551}
552
553; CHECK-LABEL: str_ri_t2_too_big
554; CHECK-T1: movs    r2, #1
555; CHECK-T1: lsls    r2, r2, #12
556; CHECK-T2: mov.w   r2, #4096
557; CHECK:    str     r1, [r0, r2]
558define void @str_ri_t2_too_big(i8* %p, i32 %x) {
559entry:
560  %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
561  %0 = bitcast i8* %add.ptr to i32*
562  store i32 %x, i32* %0, align 4
563  ret void
564}
565