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] 9 define i32 @ldrsb_rr(i8* %p, i32 %n) { 10 entry: 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] 21 define i32 @ldrsh_rr(i16* %p, i32 %n) { 22 entry: 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] 31 define i32 @ldrb_rr(i8* %p, i32 %n) { 32 entry: 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] 43 define i32 @ldrh_rr(i16* %p, i32 %n) { 44 entry: 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] 55 define i32 @ldr_rr(i32* %p, i32 %n) { 56 entry: 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] 64 define void @strb_rr(i8* %p, i32 %n, i32 %x) { 65 entry: 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] 76 define void @strh_rr(i16* %p, i32 %n, i32 %x) { 77 entry: 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] 88 define void @str_rr(i32* %p, i32 %n, i32 %x) { 89 entry: 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] 102 define i32 @ldrsb_ri_zero(i8* %p) { 103 entry: 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] 113 define i32 @ldrsh_ri_zero(i16* %p) { 114 entry: 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] 122 define i32 @ldrb_ri_zero(i8* %p) { 123 entry: 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] 131 define i32 @ldrh_ri_zero(i16* %p) { 132 entry: 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] 140 define i32 @ldr_ri_zero(i32* %p) { 141 entry: 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] 148 define void @strb_ri_zero(i8* %p, i32 %x) { 149 entry: 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] 157 define void @strh_ri_zero(i16* %p, i32 %x) { 158 entry: 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] 166 define void @str_ri_zero(i32* %p, i32 %x) { 167 entry: 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] 179 define i32 @ldrsb_ri_t1_max(i8* %p) { 180 entry: 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] 191 define i32 @ldrsh_ri_t1_max(i16* %p) { 192 entry: 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] 201 define i32 @ldrb_ri_t1_max(i8* %p) { 202 entry: 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] 211 define i32 @ldrh_ri_t1_max(i16* %p) { 212 entry: 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] 221 define i32 @ldr_ri_t1_max(i32* %p) { 222 entry: 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] 230 define void @strb_ri_t1_max(i8* %p, i32 %x) { 231 entry: 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] 240 define void @strh_ri_t1_max(i16* %p, i32 %x) { 241 entry: 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] 250 define void @str_ri_t1_max(i32* %p, i32 %x) { 251 entry: 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] 264 define i32 @ldrsb_ri_t1_too_big(i8* %p) { 265 entry: 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] 276 define i32 @ldrsh_ri_t1_too_big(i16* %p) { 277 entry: 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] 288 define i32 @ldrb_ri_t1_too_big(i8* %p) { 289 entry: 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] 300 define i32 @ldrh_ri_t1_too_big(i16* %p) { 301 entry: 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] 312 define i32 @ldr_ri_t1_too_big(i32* %p) { 313 entry: 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] 323 define void @strb_ri_t1_too_big(i8* %p, i32 %x) { 324 entry: 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] 335 define void @strh_ri_t1_too_big(i16* %p, i32 %x) { 336 entry: 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] 347 define void @str_ri_t1_too_big(i32* %p, i32 %x) { 348 entry: 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] 361 define i32 @ldrsb_ri_t2_max(i8* %p) { 362 entry: 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] 373 define i32 @ldrsh_ri_t2_max(i8* %p) { 374 entry: 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] 386 define i32 @ldrb_ri_t2_max(i8* %p) { 387 entry: 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] 398 define i32 @ldrh_ri_t2_max(i8* %p) { 399 entry: 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] 411 define i32 @ldr_ri_t2_max(i8* %p) { 412 entry: 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] 423 define void @strb_ri_t2_max(i8* %p, i32 %x) { 424 entry: 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] 435 define void @strh_ri_t2_max(i8* %p, i32 %x) { 436 entry: 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] 448 define void @str_ri_t2_max(i8* %p, i32 %x) { 449 entry: 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] 464 define i32 @ldrsb_ri_t2_too_big(i8* %p) { 465 entry: 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] 477 define i32 @ldrsh_ri_t2_too_big(i8* %p) { 478 entry: 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] 491 define i32 @ldrb_ri_t2_too_big(i8* %p) { 492 entry: 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] 504 define i32 @ldrh_ri_t2_too_big(i8* %p) { 505 entry: 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] 518 define i32 @ldr_ri_t2_too_big(i8* %p) { 519 entry: 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] 531 define void @strb_ri_t2_too_big(i8* %p, i32 %x) { 532 entry: 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] 544 define void @strh_ri_t2_too_big(i8* %p, i32 %x) { 545 entry: 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] 558 define void @str_ri_t2_too_big(i8* %p, i32 %x) { 559 entry: 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