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 equality-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 eq <- not canonical 12 ; shl + ashr + icmp eq 13 ; add + icmp uge 14 ; add + icmp ult/ule 15 ; However only the simplest form (with two shifts) gets lowered best. 16 17 ; ---------------------------------------------------------------------------- ; 18 ; shl + ashr + icmp eq 19 ; ---------------------------------------------------------------------------- ; 20 21 define i1 @shifts_eqcmp_i16_i8(i16 %x) nounwind { 22 ; CHECK-LABEL: shifts_eqcmp_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, eq 28 ; CHECK-NEXT: ret 29 %tmp0 = shl i16 %x, 8 ; 16-8 30 %tmp1 = ashr exact i16 %tmp0, 8 ; 16-8 31 %tmp2 = icmp eq i16 %tmp1, %x 32 ret i1 %tmp2 33 } 34 35 define i1 @shifts_eqcmp_i32_i16(i32 %x) nounwind { 36 ; CHECK-LABEL: shifts_eqcmp_i32_i16: 37 ; CHECK: // %bb.0: 38 ; CHECK-NEXT: sxth w8, w0 39 ; CHECK-NEXT: cmp w8, w0 40 ; CHECK-NEXT: cset w0, eq 41 ; CHECK-NEXT: ret 42 %tmp0 = shl i32 %x, 16 ; 32-16 43 %tmp1 = ashr exact i32 %tmp0, 16 ; 32-16 44 %tmp2 = icmp eq i32 %tmp1, %x 45 ret i1 %tmp2 46 } 47 48 define i1 @shifts_eqcmp_i32_i8(i32 %x) nounwind { 49 ; CHECK-LABEL: shifts_eqcmp_i32_i8: 50 ; CHECK: // %bb.0: 51 ; CHECK-NEXT: sxtb w8, w0 52 ; CHECK-NEXT: cmp w8, w0 53 ; CHECK-NEXT: cset w0, eq 54 ; CHECK-NEXT: ret 55 %tmp0 = shl i32 %x, 24 ; 32-8 56 %tmp1 = ashr exact i32 %tmp0, 24 ; 32-8 57 %tmp2 = icmp eq i32 %tmp1, %x 58 ret i1 %tmp2 59 } 60 61 define i1 @shifts_eqcmp_i64_i32(i64 %x) nounwind { 62 ; CHECK-LABEL: shifts_eqcmp_i64_i32: 63 ; CHECK: // %bb.0: 64 ; CHECK-NEXT: sxtw x8, w0 65 ; CHECK-NEXT: cmp x8, x0 66 ; CHECK-NEXT: cset w0, eq 67 ; CHECK-NEXT: ret 68 %tmp0 = shl i64 %x, 32 ; 64-32 69 %tmp1 = ashr exact i64 %tmp0, 32 ; 64-32 70 %tmp2 = icmp eq i64 %tmp1, %x 71 ret i1 %tmp2 72 } 73 74 define i1 @shifts_eqcmp_i64_i16(i64 %x) nounwind { 75 ; CHECK-LABEL: shifts_eqcmp_i64_i16: 76 ; CHECK: // %bb.0: 77 ; CHECK-NEXT: sxth x8, w0 78 ; CHECK-NEXT: cmp x8, x0 79 ; CHECK-NEXT: cset w0, eq 80 ; CHECK-NEXT: ret 81 %tmp0 = shl i64 %x, 48 ; 64-16 82 %tmp1 = ashr exact i64 %tmp0, 48 ; 64-16 83 %tmp2 = icmp eq i64 %tmp1, %x 84 ret i1 %tmp2 85 } 86 87 define i1 @shifts_eqcmp_i64_i8(i64 %x) nounwind { 88 ; CHECK-LABEL: shifts_eqcmp_i64_i8: 89 ; CHECK: // %bb.0: 90 ; CHECK-NEXT: sxtb x8, w0 91 ; CHECK-NEXT: cmp x8, x0 92 ; CHECK-NEXT: cset w0, eq 93 ; CHECK-NEXT: ret 94 %tmp0 = shl i64 %x, 56 ; 64-8 95 %tmp1 = ashr exact i64 %tmp0, 56 ; 64-8 96 %tmp2 = icmp eq i64 %tmp1, %x 97 ret i1 %tmp2 98 } 99 100 ; ---------------------------------------------------------------------------- ; 101 ; add + icmp uge 102 ; ---------------------------------------------------------------------------- ; 103 104 define i1 @add_ugecmp_i16_i8(i16 %x) nounwind { 105 ; CHECK-LABEL: add_ugecmp_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, #254 // =254 110 ; CHECK-NEXT: cset w0, hi 111 ; CHECK-NEXT: ret 112 %tmp0 = add i16 %x, -128 ; ~0U << (8-1) 113 %tmp1 = icmp uge i16 %tmp0, -256 ; ~0U << 8 114 ret i1 %tmp1 115 } 116 117 define i1 @add_ugecmp_i32_i16(i32 %x) nounwind { 118 ; CHECK-LABEL: add_ugecmp_i32_i16: 119 ; CHECK: // %bb.0: 120 ; CHECK-NEXT: sub w8, w0, #8, lsl #12 // =32768 121 ; CHECK-NEXT: orr w9, wzr, #0xfffeffff 122 ; CHECK-NEXT: cmp w8, w9 123 ; CHECK-NEXT: cset w0, hi 124 ; CHECK-NEXT: ret 125 %tmp0 = add i32 %x, -32768 ; ~0U << (16-1) 126 %tmp1 = icmp uge i32 %tmp0, -65536 ; ~0U << 16 127 ret i1 %tmp1 128 } 129 130 define i1 @add_ugecmp_i32_i8(i32 %x) nounwind { 131 ; CHECK-LABEL: add_ugecmp_i32_i8: 132 ; CHECK: // %bb.0: 133 ; CHECK-NEXT: sub w8, w0, #128 // =128 134 ; CHECK-NEXT: cmn w8, #257 // =257 135 ; CHECK-NEXT: cset w0, hi 136 ; CHECK-NEXT: ret 137 %tmp0 = add i32 %x, -128 ; ~0U << (8-1) 138 %tmp1 = icmp uge i32 %tmp0, -256 ; ~0U << 8 139 ret i1 %tmp1 140 } 141 142 define i1 @add_ugecmp_i64_i32(i64 %x) nounwind { 143 ; CHECK-LABEL: add_ugecmp_i64_i32: 144 ; CHECK: // %bb.0: 145 ; CHECK-NEXT: mov x8, #-2147483648 146 ; CHECK-NEXT: add x8, x0, x8 147 ; CHECK-NEXT: orr x9, xzr, #0xfffffffeffffffff 148 ; CHECK-NEXT: cmp x8, x9 149 ; CHECK-NEXT: cset w0, hi 150 ; CHECK-NEXT: ret 151 %tmp0 = add i64 %x, -2147483648 ; ~0U << (32-1) 152 %tmp1 = icmp uge i64 %tmp0, -4294967296 ; ~0U << 32 153 ret i1 %tmp1 154 } 155 156 define i1 @add_ugecmp_i64_i16(i64 %x) nounwind { 157 ; CHECK-LABEL: add_ugecmp_i64_i16: 158 ; CHECK: // %bb.0: 159 ; CHECK-NEXT: sub x8, x0, #8, lsl #12 // =32768 160 ; CHECK-NEXT: orr x9, xzr, #0xfffffffffffeffff 161 ; CHECK-NEXT: cmp x8, x9 162 ; CHECK-NEXT: cset w0, hi 163 ; CHECK-NEXT: ret 164 %tmp0 = add i64 %x, -32768 ; ~0U << (16-1) 165 %tmp1 = icmp uge i64 %tmp0, -65536 ; ~0U << 16 166 ret i1 %tmp1 167 } 168 169 define i1 @add_ugecmp_i64_i8(i64 %x) nounwind { 170 ; CHECK-LABEL: add_ugecmp_i64_i8: 171 ; CHECK: // %bb.0: 172 ; CHECK-NEXT: sub x8, x0, #128 // =128 173 ; CHECK-NEXT: cmn x8, #257 // =257 174 ; CHECK-NEXT: cset w0, hi 175 ; CHECK-NEXT: ret 176 %tmp0 = add i64 %x, -128 ; ~0U << (8-1) 177 %tmp1 = icmp uge i64 %tmp0, -256 ; ~0U << 8 178 ret i1 %tmp1 179 } 180 181 ; ---------------------------------------------------------------------------- ; 182 ; add + icmp ult 183 ; ---------------------------------------------------------------------------- ; 184 185 define i1 @add_ultcmp_i16_i8(i16 %x) nounwind { 186 ; CHECK-LABEL: add_ultcmp_i16_i8: 187 ; CHECK: // %bb.0: 188 ; CHECK-NEXT: sxtb w8, w0 189 ; CHECK-NEXT: and w8, w8, #0xffff 190 ; CHECK-NEXT: cmp w8, w0, uxth 191 ; CHECK-NEXT: cset w0, eq 192 ; CHECK-NEXT: ret 193 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 194 %tmp1 = icmp ult i16 %tmp0, 256 ; 1U << 8 195 ret i1 %tmp1 196 } 197 198 define i1 @add_ultcmp_i32_i16(i32 %x) nounwind { 199 ; CHECK-LABEL: add_ultcmp_i32_i16: 200 ; CHECK: // %bb.0: 201 ; CHECK-NEXT: sxth w8, w0 202 ; CHECK-NEXT: cmp w8, w0 203 ; CHECK-NEXT: cset w0, eq 204 ; CHECK-NEXT: ret 205 %tmp0 = add i32 %x, 32768 ; 1U << (16-1) 206 %tmp1 = icmp ult i32 %tmp0, 65536 ; 1U << 16 207 ret i1 %tmp1 208 } 209 210 define i1 @add_ultcmp_i32_i8(i32 %x) nounwind { 211 ; CHECK-LABEL: add_ultcmp_i32_i8: 212 ; CHECK: // %bb.0: 213 ; CHECK-NEXT: sxtb w8, w0 214 ; CHECK-NEXT: cmp w8, w0 215 ; CHECK-NEXT: cset w0, eq 216 ; CHECK-NEXT: ret 217 %tmp0 = add i32 %x, 128 ; 1U << (8-1) 218 %tmp1 = icmp ult i32 %tmp0, 256 ; 1U << 8 219 ret i1 %tmp1 220 } 221 222 define i1 @add_ultcmp_i64_i32(i64 %x) nounwind { 223 ; CHECK-LABEL: add_ultcmp_i64_i32: 224 ; CHECK: // %bb.0: 225 ; CHECK-NEXT: sxtw x8, w0 226 ; CHECK-NEXT: cmp x8, x0 227 ; CHECK-NEXT: cset w0, eq 228 ; CHECK-NEXT: ret 229 %tmp0 = add i64 %x, 2147483648 ; 1U << (32-1) 230 %tmp1 = icmp ult i64 %tmp0, 4294967296 ; 1U << 32 231 ret i1 %tmp1 232 } 233 234 define i1 @add_ultcmp_i64_i16(i64 %x) nounwind { 235 ; CHECK-LABEL: add_ultcmp_i64_i16: 236 ; CHECK: // %bb.0: 237 ; CHECK-NEXT: sxth x8, w0 238 ; CHECK-NEXT: cmp x8, x0 239 ; CHECK-NEXT: cset w0, eq 240 ; CHECK-NEXT: ret 241 %tmp0 = add i64 %x, 32768 ; 1U << (16-1) 242 %tmp1 = icmp ult i64 %tmp0, 65536 ; 1U << 16 243 ret i1 %tmp1 244 } 245 246 define i1 @add_ultcmp_i64_i8(i64 %x) nounwind { 247 ; CHECK-LABEL: add_ultcmp_i64_i8: 248 ; CHECK: // %bb.0: 249 ; CHECK-NEXT: sxtb x8, w0 250 ; CHECK-NEXT: cmp x8, x0 251 ; CHECK-NEXT: cset w0, eq 252 ; CHECK-NEXT: ret 253 %tmp0 = add i64 %x, 128 ; 1U << (8-1) 254 %tmp1 = icmp ult i64 %tmp0, 256 ; 1U << 8 255 ret i1 %tmp1 256 } 257 258 ; Slightly more canonical variant 259 define i1 @add_ulecmp_i16_i8(i16 %x) nounwind { 260 ; CHECK-LABEL: add_ulecmp_i16_i8: 261 ; CHECK: // %bb.0: 262 ; CHECK-NEXT: sxtb w8, w0 263 ; CHECK-NEXT: and w8, w8, #0xffff 264 ; CHECK-NEXT: cmp w8, w0, uxth 265 ; CHECK-NEXT: cset w0, eq 266 ; CHECK-NEXT: ret 267 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 268 %tmp1 = icmp ule i16 %tmp0, 255 ; (1U << 8) - 1 269 ret i1 %tmp1 270 } 271 272 ; Negative tests 273 ; ---------------------------------------------------------------------------- ; 274 275 ; Adding not a constant 276 define i1 @add_ultcmp_bad_i16_i8_add(i16 %x, i16 %y) nounwind { 277 ; CHECK-LABEL: add_ultcmp_bad_i16_i8_add: 278 ; CHECK: // %bb.0: 279 ; CHECK-NEXT: add w8, w0, w1 280 ; CHECK-NEXT: and w8, w8, #0xffff 281 ; CHECK-NEXT: cmp w8, #256 // =256 282 ; CHECK-NEXT: cset w0, lo 283 ; CHECK-NEXT: ret 284 %tmp0 = add i16 %x, %y 285 %tmp1 = icmp ult i16 %tmp0, 256 ; 1U << 8 286 ret i1 %tmp1 287 } 288 289 ; Comparing not with a constant 290 define i1 @add_ultcmp_bad_i16_i8_cmp(i16 %x, i16 %y) nounwind { 291 ; CHECK-LABEL: add_ultcmp_bad_i16_i8_cmp: 292 ; CHECK: // %bb.0: 293 ; CHECK-NEXT: add w8, w0, #128 // =128 294 ; CHECK-NEXT: and w8, w8, #0xffff 295 ; CHECK-NEXT: cmp w8, w1, uxth 296 ; CHECK-NEXT: cset w0, lo 297 ; CHECK-NEXT: ret 298 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 299 %tmp1 = icmp ult i16 %tmp0, %y 300 ret i1 %tmp1 301 } 302 303 ; Second constant is not larger than the first one 304 define i1 @add_ultcmp_bad_i8_i16(i16 %x) nounwind { 305 ; CHECK-LABEL: add_ultcmp_bad_i8_i16: 306 ; CHECK: // %bb.0: 307 ; CHECK-NEXT: and w8, w0, #0xffff 308 ; CHECK-NEXT: add w8, w8, #128 // =128 309 ; CHECK-NEXT: lsr w0, w8, #16 310 ; CHECK-NEXT: ret 311 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 312 %tmp1 = icmp ult i16 %tmp0, 128 ; 1U << (8-1) 313 ret i1 %tmp1 314 } 315 316 ; First constant is not power of two 317 define i1 @add_ultcmp_bad_i16_i8_c0notpoweroftwo(i16 %x) nounwind { 318 ; CHECK-LABEL: add_ultcmp_bad_i16_i8_c0notpoweroftwo: 319 ; CHECK: // %bb.0: 320 ; CHECK-NEXT: add w8, w0, #192 // =192 321 ; CHECK-NEXT: and w8, w8, #0xffff 322 ; CHECK-NEXT: cmp w8, #256 // =256 323 ; CHECK-NEXT: cset w0, lo 324 ; CHECK-NEXT: ret 325 %tmp0 = add i16 %x, 192 ; (1U << (8-1)) + (1U << (8-1-1)) 326 %tmp1 = icmp ult i16 %tmp0, 256 ; 1U << 8 327 ret i1 %tmp1 328 } 329 330 ; Second constant is not power of two 331 define i1 @add_ultcmp_bad_i16_i8_c1notpoweroftwo(i16 %x) nounwind { 332 ; CHECK-LABEL: add_ultcmp_bad_i16_i8_c1notpoweroftwo: 333 ; CHECK: // %bb.0: 334 ; CHECK-NEXT: add w8, w0, #128 // =128 335 ; CHECK-NEXT: and w8, w8, #0xffff 336 ; CHECK-NEXT: cmp w8, #768 // =768 337 ; CHECK-NEXT: cset w0, lo 338 ; CHECK-NEXT: ret 339 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 340 %tmp1 = icmp ult i16 %tmp0, 768 ; (1U << 8)) + (1U << (8+1)) 341 ret i1 %tmp1 342 } 343 344 ; Magic check fails, 64 << 1 != 256 345 define i1 @add_ultcmp_bad_i16_i8_magic(i16 %x) nounwind { 346 ; CHECK-LABEL: add_ultcmp_bad_i16_i8_magic: 347 ; CHECK: // %bb.0: 348 ; CHECK-NEXT: add w8, w0, #64 // =64 349 ; CHECK-NEXT: and w8, w8, #0xffff 350 ; CHECK-NEXT: cmp w8, #256 // =256 351 ; CHECK-NEXT: cset w0, lo 352 ; CHECK-NEXT: ret 353 %tmp0 = add i16 %x, 64 ; 1U << (8-1-1) 354 %tmp1 = icmp ult i16 %tmp0, 256 ; 1U << 8 355 ret i1 %tmp1 356 } 357 358 ; Bad 'destination type' 359 define i1 @add_ultcmp_bad_i16_i4(i16 %x) nounwind { 360 ; CHECK-LABEL: add_ultcmp_bad_i16_i4: 361 ; CHECK: // %bb.0: 362 ; CHECK-NEXT: add w8, w0, #8 // =8 363 ; CHECK-NEXT: and w8, w8, #0xffff 364 ; CHECK-NEXT: cmp w8, #16 // =16 365 ; CHECK-NEXT: cset w0, lo 366 ; CHECK-NEXT: ret 367 %tmp0 = add i16 %x, 8 ; 1U << (4-1) 368 %tmp1 = icmp ult i16 %tmp0, 16 ; 1U << 4 369 ret i1 %tmp1 370 } 371 372 ; Bad storage type 373 define i1 @add_ultcmp_bad_i24_i8(i24 %x) nounwind { 374 ; CHECK-LABEL: add_ultcmp_bad_i24_i8: 375 ; CHECK: // %bb.0: 376 ; CHECK-NEXT: add w8, w0, #128 // =128 377 ; CHECK-NEXT: and w8, w8, #0xffffff 378 ; CHECK-NEXT: cmp w8, #256 // =256 379 ; CHECK-NEXT: cset w0, lo 380 ; CHECK-NEXT: ret 381 %tmp0 = add i24 %x, 128 ; 1U << (8-1) 382 %tmp1 = icmp ult i24 %tmp0, 256 ; 1U << 8 383 ret i1 %tmp1 384 } 385 386 define i1 @add_ulecmp_bad_i16_i8(i16 %x) nounwind { 387 ; CHECK-LABEL: add_ulecmp_bad_i16_i8: 388 ; CHECK: // %bb.0: 389 ; CHECK-NEXT: orr w0, wzr, #0x1 390 ; CHECK-NEXT: ret 391 %tmp0 = add i16 %x, 128 ; 1U << (8-1) 392 %tmp1 = icmp ule i16 %tmp0, -1 ; when we +1 it, it will wrap to 0 393 ret i1 %tmp1 394 } 395