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 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: 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 48define 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 61define 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 74define 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 87define 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 104define 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 117define 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 129define 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 141define 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 155define 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 167define 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 183define 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 196define 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 208define 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 220define 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 232define 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 244define 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 257define 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 274define 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 288define 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 302define 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 316define 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 330define 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 344define 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' 358define 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 372define 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 386define 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