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