1 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2 ; RUN: llc -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s 3 4 ; https://bugs.llvm.org/show_bug.cgi?id=38149 5 6 ; We are truncating from wider width, and then sign-extending 7 ; back to the original width. Then we inequality-comparing orig and src. 8 ; If they don't match, then we had signed truncation during truncation. 9 10 ; This can be expressed in a several ways in IR: 11 ; trunc + sext + icmp ne <- not canonical 12 ; shl + ashr + icmp ne 13 ; add + icmp ult 14 ; add + icmp uge/ugt 15 ; However only the simplest form (with two shifts) gets lowered best. 16 17 ; ---------------------------------------------------------------------------- ; 18 ; shl + ashr + icmp ne 19 ; ---------------------------------------------------------------------------- ; 20 21 define i1 @shifts_necmp_i16_i8(i16 %x) nounwind { 22 ; CHECK-LABEL: shifts_necmp_i16_i8: 23 ; CHECK: // %bb.0: 24 ; CHECK-NEXT: sxtb w8, w0 25 ; CHECK-NEXT: and w8, w8, #0xffff 26 ; CHECK-NEXT: cmp w8, w0, uxth 27 ; CHECK-NEXT: cset w0, ne 28 ; CHECK-NEXT: ret 29 %tmp0 = shl i16 %x, 8 ; 16-8 30 %tmp1 = ashr exact i16 %tmp0, 8 ; 16-8 31 %tmp2 = icmp ne i16 %tmp1, %x 32 ret i1 %tmp2 33 } 34 35 define i1 @shifts_necmp_i32_i16(i32 %x) nounwind { 36 ; CHECK-LABEL: shifts_necmp_i32_i16: 37 ; CHECK: // %bb.0: 38 ; CHECK-NEXT: sxth w8, w0 39 ; CHECK-NEXT: cmp w8, w0 40 ; CHECK-NEXT: cset w0, ne 41 ; CHECK-NEXT: ret 42 %tmp0 = shl i32 %x, 16 ; 32-16 43 %tmp1 = ashr exact i32 %tmp0, 16 ; 32-16 44 %tmp2 = icmp ne i32 %tmp1, %x 45 ret i1 %tmp2 46 } 47 48 define i1 @shifts_necmp_i32_i8(i32 %x) nounwind { 49 ; CHECK-LABEL: shifts_necmp_i32_i8: 50 ; CHECK: // %bb.0: 51 ; CHECK-NEXT: sxtb w8, w0 52 ; CHECK-NEXT: cmp w8, w0 53 ; CHECK-NEXT: cset w0, ne 54 ; CHECK-NEXT: ret 55 %tmp0 = shl i32 %x, 24 ; 32-8 56 %tmp1 = ashr exact i32 %tmp0, 24 ; 32-8 57 %tmp2 = icmp ne i32 %tmp1, %x 58 ret i1 %tmp2 59 } 60 61 define i1 @shifts_necmp_i64_i32(i64 %x) nounwind { 62 ; CHECK-LABEL: shifts_necmp_i64_i32: 63 ; CHECK: // %bb.0: 64 ; CHECK-NEXT: sxtw x8, w0 65 ; CHECK-NEXT: cmp x8, x0 66 ; CHECK-NEXT: cset w0, ne 67 ; CHECK-NEXT: ret 68 %tmp0 = shl i64 %x, 32 ; 64-32 69 %tmp1 = ashr exact i64 %tmp0, 32 ; 64-32 70 %tmp2 = icmp ne i64 %tmp1, %x 71 ret i1 %tmp2 72 } 73 74 define i1 @shifts_necmp_i64_i16(i64 %x) nounwind { 75 ; CHECK-LABEL: shifts_necmp_i64_i16: 76 ; CHECK: // %bb.0: 77 ; CHECK-NEXT: sxth x8, w0 78 ; CHECK-NEXT: cmp x8, x0 79 ; CHECK-NEXT: cset w0, ne 80 ; CHECK-NEXT: ret 81 %tmp0 = shl i64 %x, 48 ; 64-16 82 %tmp1 = ashr exact i64 %tmp0, 48 ; 64-16 83 %tmp2 = icmp ne i64 %tmp1, %x 84 ret i1 %tmp2 85 } 86 87 define i1 @shifts_necmp_i64_i8(i64 %x) nounwind { 88 ; CHECK-LABEL: shifts_necmp_i64_i8: 89 ; CHECK: // %bb.0: 90 ; CHECK-NEXT: sxtb x8, w0 91 ; CHECK-NEXT: cmp x8, x0 92 ; CHECK-NEXT: cset w0, ne 93 ; CHECK-NEXT: ret 94 %tmp0 = shl i64 %x, 56 ; 64-8 95 %tmp1 = ashr exact i64 %tmp0, 56 ; 64-8 96 %tmp2 = icmp ne i64 %tmp1, %x 97 ret i1 %tmp2 98 } 99 100 ; ---------------------------------------------------------------------------- ; 101 ; add + icmp ult 102 ; ---------------------------------------------------------------------------- ; 103 104 define i1 @add_ultcmp_i16_i8(i16 %x) nounwind { 105 ; CHECK-LABEL: add_ultcmp_i16_i8: 106 ; CHECK: // %bb.0: 107 ; CHECK-NEXT: sub w8, w0, #128 // =128 108 ; CHECK-NEXT: ubfx w8, w8, #8, #8 109 ; CHECK-NEXT: cmp w8, #255 // =255 110 ; CHECK-NEXT: cset w0, lo 111 ; CHECK-NEXT: ret 112 %tmp0 = add i16 %x, -128 ; ~0U << (8-1) 113 %tmp1 = icmp ult i16 %tmp0, -256 ; ~0U << 8 114 ret i1 %tmp1 115 } 116 117 define i1 @add_ultcmp_i32_i16(i32 %x) nounwind { 118 ; CHECK-LABEL: add_ultcmp_i32_i16: 119 ; CHECK: // %bb.0: 120 ; CHECK-NEXT: sub w8, w0, #8, lsl #12 // =32768 121 ; CHECK-NEXT: cmn w8, #16, lsl #12 // =65536 122 ; CHECK-NEXT: cset w0, lo 123 ; CHECK-NEXT: ret 124 %tmp0 = add i32 %x, -32768 ; ~0U << (16-1) 125 %tmp1 = icmp ult i32 %tmp0, -65536 ; ~0U << 16 126 ret i1 %tmp1 127 } 128 129 define i1 @add_ultcmp_i32_i8(i32 %x) nounwind { 130 ; CHECK-LABEL: add_ultcmp_i32_i8: 131 ; CHECK: // %bb.0: 132 ; CHECK-NEXT: sub w8, w0, #128 // =128 133 ; CHECK-NEXT: cmn w8, #256 // =256 134 ; CHECK-NEXT: cset w0, lo 135 ; CHECK-NEXT: ret 136 %tmp0 = add i32 %x, -128 ; ~0U << (8-1) 137 %tmp1 = icmp ult i32 %tmp0, -256 ; ~0U << 8 138 ret i1 %tmp1 139 } 140 141 define i1 @add_ultcmp_i64_i32(i64 %x) nounwind { 142 ; CHECK-LABEL: add_ultcmp_i64_i32: 143 ; CHECK: // %bb.0: 144 ; CHECK-NEXT: mov x8, #-2147483648 145 ; CHECK-NEXT: add x8, x0, x8 146 ; CHECK-NEXT: mov x9, #-4294967296 147 ; CHECK-NEXT: cmp x8, x9 148 ; CHECK-NEXT: cset w0, lo 149 ; CHECK-NEXT: ret 150 %tmp0 = add i64 %x, -2147483648 ; ~0U << (32-1) 151 %tmp1 = icmp ult i64 %tmp0, -4294967296 ; ~0U << 32 152 ret i1 %tmp1 153 } 154 155 define i1 @add_ultcmp_i64_i16(i64 %x) nounwind { 156 ; CHECK-LABEL: add_ultcmp_i64_i16: 157 ; CHECK: // %bb.0: 158 ; CHECK-NEXT: sub x8, x0, #8, lsl #12 // =32768 159 ; CHECK-NEXT: cmn x8, #16, lsl #12 // =65536 160 ; CHECK-NEXT: cset w0, lo 161 ; CHECK-NEXT: ret 162 %tmp0 = add i64 %x, -32768 ; ~0U << (16-1) 163 %tmp1 = icmp ult i64 %tmp0, -65536 ; ~0U << 16 164 ret i1 %tmp1 165 } 166 167 define i1 @add_ultcmp_i64_i8(i64 %x) nounwind { 168 ; CHECK-LABEL: add_ultcmp_i64_i8: 169 ; CHECK: // %bb.0: 170 ; CHECK-NEXT: sub x8, x0, #128 // =128 171 ; CHECK-NEXT: cmn x8, #256 // =256 172 ; CHECK-NEXT: cset w0, lo 173 ; CHECK-NEXT: ret 174 %tmp0 = add i64 %x, -128 ; ~0U << (8-1) 175 %tmp1 = icmp ult i64 %tmp0, -256 ; ~0U << 8 176 ret i1 %tmp1 177 } 178 179 ; ---------------------------------------------------------------------------- ; 180 ; add + icmp uge 181 ; ---------------------------------------------------------------------------- ; 182 183 define i1 @add_ugecmp_i16_i8(i16 %x) nounwind { 184 ; CHECK-LABEL: add_ugecmp_i16_i8: 185 ; CHECK: // %bb.0: 186 ; CHECK-NEXT: sxtb w8, w0 187 ; CHECK-NEXT: and w8, w8, #0xffff 188 ; CHECK-NEXT: cmp w8, w0, uxth 189 ; CHECK-NEXT: cset w0, ne 190 ; CHECK-NEXT: ret 191 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 192 %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8 193 ret i1 %tmp1 194 } 195 196 define i1 @add_ugecmp_i32_i16(i32 %x) nounwind { 197 ; CHECK-LABEL: add_ugecmp_i32_i16: 198 ; CHECK: // %bb.0: 199 ; CHECK-NEXT: sxth w8, w0 200 ; CHECK-NEXT: cmp w8, w0 201 ; CHECK-NEXT: cset w0, ne 202 ; CHECK-NEXT: ret 203 %tmp0 = add i32 %x, 32768 ; 1U << (16-1) 204 %tmp1 = icmp uge i32 %tmp0, 65536 ; 1U << 16 205 ret i1 %tmp1 206 } 207 208 define i1 @add_ugecmp_i32_i8(i32 %x) nounwind { 209 ; CHECK-LABEL: add_ugecmp_i32_i8: 210 ; CHECK: // %bb.0: 211 ; CHECK-NEXT: sxtb w8, w0 212 ; CHECK-NEXT: cmp w8, w0 213 ; CHECK-NEXT: cset w0, ne 214 ; CHECK-NEXT: ret 215 %tmp0 = add i32 %x, 128 ; 1U << (8-1) 216 %tmp1 = icmp uge i32 %tmp0, 256 ; 1U << 8 217 ret i1 %tmp1 218 } 219 220 define i1 @add_ugecmp_i64_i32(i64 %x) nounwind { 221 ; CHECK-LABEL: add_ugecmp_i64_i32: 222 ; CHECK: // %bb.0: 223 ; CHECK-NEXT: sxtw x8, w0 224 ; CHECK-NEXT: cmp x8, x0 225 ; CHECK-NEXT: cset w0, ne 226 ; CHECK-NEXT: ret 227 %tmp0 = add i64 %x, 2147483648 ; 1U << (32-1) 228 %tmp1 = icmp uge i64 %tmp0, 4294967296 ; 1U << 32 229 ret i1 %tmp1 230 } 231 232 define i1 @add_ugecmp_i64_i16(i64 %x) nounwind { 233 ; CHECK-LABEL: add_ugecmp_i64_i16: 234 ; CHECK: // %bb.0: 235 ; CHECK-NEXT: sxth x8, w0 236 ; CHECK-NEXT: cmp x8, x0 237 ; CHECK-NEXT: cset w0, ne 238 ; CHECK-NEXT: ret 239 %tmp0 = add i64 %x, 32768 ; 1U << (16-1) 240 %tmp1 = icmp uge i64 %tmp0, 65536 ; 1U << 16 241 ret i1 %tmp1 242 } 243 244 define i1 @add_ugecmp_i64_i8(i64 %x) nounwind { 245 ; CHECK-LABEL: add_ugecmp_i64_i8: 246 ; CHECK: // %bb.0: 247 ; CHECK-NEXT: sxtb x8, w0 248 ; CHECK-NEXT: cmp x8, x0 249 ; CHECK-NEXT: cset w0, ne 250 ; CHECK-NEXT: ret 251 %tmp0 = add i64 %x, 128 ; 1U << (8-1) 252 %tmp1 = icmp uge i64 %tmp0, 256 ; 1U << 8 253 ret i1 %tmp1 254 } 255 256 ; Slightly more canonical variant 257 define i1 @add_ugtcmp_i16_i8(i16 %x) nounwind { 258 ; CHECK-LABEL: add_ugtcmp_i16_i8: 259 ; CHECK: // %bb.0: 260 ; CHECK-NEXT: sxtb w8, w0 261 ; CHECK-NEXT: and w8, w8, #0xffff 262 ; CHECK-NEXT: cmp w8, w0, uxth 263 ; CHECK-NEXT: cset w0, ne 264 ; CHECK-NEXT: ret 265 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 266 %tmp1 = icmp ugt i16 %tmp0, 255 ; (1U << 8) - 1 267 ret i1 %tmp1 268 } 269 270 ; Negative tests 271 ; ---------------------------------------------------------------------------- ; 272 273 ; Adding not a constant 274 define i1 @add_ugecmp_bad_i16_i8_add(i16 %x, i16 %y) nounwind { 275 ; CHECK-LABEL: add_ugecmp_bad_i16_i8_add: 276 ; CHECK: // %bb.0: 277 ; CHECK-NEXT: add w8, w0, w1 278 ; CHECK-NEXT: and w8, w8, #0xffff 279 ; CHECK-NEXT: cmp w8, #255 // =255 280 ; CHECK-NEXT: cset w0, hi 281 ; CHECK-NEXT: ret 282 %tmp0 = add i16 %x, %y 283 %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8 284 ret i1 %tmp1 285 } 286 287 ; Comparing not with a constant 288 define i1 @add_ugecmp_bad_i16_i8_cmp(i16 %x, i16 %y) nounwind { 289 ; CHECK-LABEL: add_ugecmp_bad_i16_i8_cmp: 290 ; CHECK: // %bb.0: 291 ; CHECK-NEXT: add w8, w0, #128 // =128 292 ; CHECK-NEXT: and w8, w8, #0xffff 293 ; CHECK-NEXT: cmp w8, w1, uxth 294 ; CHECK-NEXT: cset w0, hs 295 ; CHECK-NEXT: ret 296 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 297 %tmp1 = icmp uge i16 %tmp0, %y 298 ret i1 %tmp1 299 } 300 301 ; Second constant is not larger than the first one 302 define i1 @add_ugecmp_bad_i8_i16(i16 %x) nounwind { 303 ; CHECK-LABEL: add_ugecmp_bad_i8_i16: 304 ; CHECK: // %bb.0: 305 ; CHECK-NEXT: add w8, w0, #128 // =128 306 ; CHECK-NEXT: and w8, w8, #0xffff 307 ; CHECK-NEXT: cmp w8, #127 // =127 308 ; CHECK-NEXT: cset w0, hi 309 ; CHECK-NEXT: ret 310 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 311 %tmp1 = icmp uge i16 %tmp0, 128 ; 1U << (8-1) 312 ret i1 %tmp1 313 } 314 315 ; First constant is not power of two 316 define i1 @add_ugecmp_bad_i16_i8_c0notpoweroftwo(i16 %x) nounwind { 317 ; CHECK-LABEL: add_ugecmp_bad_i16_i8_c0notpoweroftwo: 318 ; CHECK: // %bb.0: 319 ; CHECK-NEXT: add w8, w0, #192 // =192 320 ; CHECK-NEXT: and w8, w8, #0xffff 321 ; CHECK-NEXT: cmp w8, #255 // =255 322 ; CHECK-NEXT: cset w0, hi 323 ; CHECK-NEXT: ret 324 %tmp0 = add i16 %x, 192 ; (1U << (8-1)) + (1U << (8-1-1)) 325 %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8 326 ret i1 %tmp1 327 } 328 329 ; Second constant is not power of two 330 define i1 @add_ugecmp_bad_i16_i8_c1notpoweroftwo(i16 %x) nounwind { 331 ; CHECK-LABEL: add_ugecmp_bad_i16_i8_c1notpoweroftwo: 332 ; CHECK: // %bb.0: 333 ; CHECK-NEXT: add w8, w0, #128 // =128 334 ; CHECK-NEXT: and w8, w8, #0xffff 335 ; CHECK-NEXT: cmp w8, #767 // =767 336 ; CHECK-NEXT: cset w0, hi 337 ; CHECK-NEXT: ret 338 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 339 %tmp1 = icmp uge i16 %tmp0, 768 ; (1U << 8)) + (1U << (8+1)) 340 ret i1 %tmp1 341 } 342 343 ; Magic check fails, 64 << 1 != 256 344 define i1 @add_ugecmp_bad_i16_i8_magic(i16 %x) nounwind { 345 ; CHECK-LABEL: add_ugecmp_bad_i16_i8_magic: 346 ; CHECK: // %bb.0: 347 ; CHECK-NEXT: add w8, w0, #64 // =64 348 ; CHECK-NEXT: and w8, w8, #0xffff 349 ; CHECK-NEXT: cmp w8, #255 // =255 350 ; CHECK-NEXT: cset w0, hi 351 ; CHECK-NEXT: ret 352 %tmp0 = add i16 %x, 64 ; 1U << (8-1-1) 353 %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8 354 ret i1 %tmp1 355 } 356 357 ; Bad 'destination type' 358 define i1 @add_ugecmp_bad_i16_i4(i16 %x) nounwind { 359 ; CHECK-LABEL: add_ugecmp_bad_i16_i4: 360 ; CHECK: // %bb.0: 361 ; CHECK-NEXT: add w8, w0, #8 // =8 362 ; CHECK-NEXT: and w8, w8, #0xffff 363 ; CHECK-NEXT: cmp w8, #15 // =15 364 ; CHECK-NEXT: cset w0, hi 365 ; CHECK-NEXT: ret 366 %tmp0 = add i16 %x, 8 ; 1U << (4-1) 367 %tmp1 = icmp uge i16 %tmp0, 16 ; 1U << 4 368 ret i1 %tmp1 369 } 370 371 ; Bad storage type 372 define i1 @add_ugecmp_bad_i24_i8(i24 %x) nounwind { 373 ; CHECK-LABEL: add_ugecmp_bad_i24_i8: 374 ; CHECK: // %bb.0: 375 ; CHECK-NEXT: add w8, w0, #128 // =128 376 ; CHECK-NEXT: and w8, w8, #0xffffff 377 ; CHECK-NEXT: cmp w8, #255 // =255 378 ; CHECK-NEXT: cset w0, hi 379 ; CHECK-NEXT: ret 380 %tmp0 = add i24 %x, 128 ; 1U << (8-1) 381 %tmp1 = icmp uge i24 %tmp0, 256 ; 1U << 8 382 ret i1 %tmp1 383 } 384 385 ; Slightly more canonical variant 386 define i1 @add_ugtcmp_bad_i16_i8(i16 %x) nounwind { 387 ; CHECK-LABEL: add_ugtcmp_bad_i16_i8: 388 ; CHECK: // %bb.0: 389 ; CHECK-NEXT: mov w0, wzr 390 ; CHECK-NEXT: ret 391 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 392 %tmp1 = icmp ugt i16 %tmp0, -1 ; when we +1 it, it will wrap to 0 393 ret i1 %tmp1 394 } 395