; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=aarch64-unknown-linux-gnu -mattr=+sve2 < %s | FileCheck %s --check-prefixes=CHECK define @bsl( %a, %b) { ; CHECK-LABEL: bsl: ; CHECK: // %bb.0: ; CHECK-NEXT: mov z2.s, #0x7fffffff ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %1 = and %a, splat(i32 2147483647) %2 = and %b, splat(i32 -2147483648) %c = or %1, %2 ret %c } define @bsl_add_sub( %pre_cond, %left, %right) #0 { ; CHECK-LABEL: bsl_add_sub: ; CHECK: // %bb.0: ; CHECK-NEXT: movprfx z3, z0 ; CHECK-NEXT: subr z3.s, z3.s, #0 // =0x0 ; CHECK-NEXT: mov z0.d, z1.d ; CHECK-NEXT: bsl z0.d, z0.d, z2.d, z3.d ; CHECK-NEXT: ret %neg_cond = sub zeroinitializer, %pre_cond %min_cond = add %pre_cond, splat(i32 -1) %left_bits_0 = and %neg_cond, %left %right_bits_0 = and %min_cond, %right %bsl0000 = or %right_bits_0, %left_bits_0 ret %bsl0000 } ; we are not expecting bsl instruction here. the constants do not match to fold to bsl. define @no_bsl_fold( %a, %b) { ; CHECK-LABEL: no_bsl_fold: ; CHECK: // %bb.0: ; CHECK-NEXT: and z0.s, z0.s, #0x7fffffff ; CHECK-NEXT: and z1.s, z1.s, #0x7ffffffe ; CHECK-NEXT: orr z0.d, z0.d, z1.d ; CHECK-NEXT: ret %1 = and %a, splat(i32 2147483647) %2 = and %b, splat(i32 2147483646) %c = or %1, %2 ret %c } define @nbsl_i8( %a, %b) { ; CHECK-LABEL: nbsl_i8: ; CHECK: // %bb.0: ; CHECK-NEXT: mov z2.b, #127 // =0x7f ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %1 = and %a, splat(i8 127) %2 = and %b, splat(i8 -128) %3 = or %1, %2 %4 = xor %3, splat(i8 -1) ret %4 } define @nbsl_i16( %a, %b) { ; CHECK-LABEL: nbsl_i16: ; CHECK: // %bb.0: ; CHECK-NEXT: mov z2.h, #32767 // =0x7fff ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %1 = and %a, splat(i16 32767) %2 = and %b, splat(i16 -32768) %3 = or %1, %2 %4 = xor %3, splat(i16 -1) ret %4 } define @nbsl_i32( %a, %b) { ; CHECK-LABEL: nbsl_i32: ; CHECK: // %bb.0: ; CHECK-NEXT: mov z2.s, #0x7fffffff ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %1 = and %a, splat(i32 2147483647) %2 = and %b, splat(i32 -2147483648) %3 = or %1, %2 %4 = xor %3, splat(i32 -1) ret %4 } define @nbsl_i64( %a, %b) { ; CHECK-LABEL: nbsl_i64: ; CHECK: // %bb.0: ; CHECK-NEXT: mov z2.d, #0x7fffffffffffffff ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %1 = and %a, splat(i64 9223372036854775807) %2 = and %b, splat(i64 -9223372036854775808) %3 = or %1, %2 %4 = xor %3, splat(i64 -1) ret %4 } ; Test BSL/NBSL/BSL1N/BSL2N code generation for: ; #define BSL(x,y,z) ( ((x) & (z)) | ( (y) & ~(z))) ; #define NBSL(x,y,z) (~(((x) & (z)) | ( (y) & ~(z)))) ; #define BSL1N(x,y,z) ( (~(x) & (z)) | ( (y) & ~(z))) ; #define BSL2N(x,y,z) ( ((x) & (z)) | (~(y) & ~(z))) define @codegen_bsl_i8( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl_i8: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i8 -1) %6 = and %1, %5 %7 = or %4, %6 ret %7 } define @codegen_nbsl_i8( %0, %1, %2) { ; CHECK-LABEL: codegen_nbsl_i8: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i8 -1) %6 = and %1, %5 %7 = or %4, %6 %8 = xor %7, splat (i8 -1) ret %8 } define @codegen_bsl1n_i8( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl1n_i8: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl1n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = xor %0, splat (i8 -1) %5 = and %2, %4 %6 = xor %2, splat (i8 -1) %7 = and %1, %6 %8 = or %5, %7 ret %8 } define @codegen_bsl2n_i8( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl2n_i8: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = or %2, %1 %6 = xor %5, splat (i8 -1) %7 = or %4, %6 ret %7 } define @codegen_bsl_i16( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl_i16: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i16 -1) %6 = and %1, %5 %7 = or %4, %6 ret %7 } define @codegen_nbsl_i16( %0, %1, %2) { ; CHECK-LABEL: codegen_nbsl_i16: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i16 -1) %6 = and %1, %5 %7 = or %4, %6 %8 = xor %7, splat (i16 -1) ret %8 } define @codegen_bsl1n_i16( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl1n_i16: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl1n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = xor %0, splat (i16 -1) %5 = and %2, %4 %6 = xor %2, splat (i16 -1) %7 = and %1, %6 %8 = or %5, %7 ret %8 } define @codegen_bsl2n_i16( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl2n_i16: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = or %2, %1 %6 = xor %5, splat (i16 -1) %7 = or %4, %6 ret %7 } define @codegen_bsl_i32( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl_i32: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i32 -1) %6 = and %1, %5 %7 = or %4, %6 ret %7 } define @codegen_nbsl_i32( %0, %1, %2) { ; CHECK-LABEL: codegen_nbsl_i32: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i32 -1) %6 = and %1, %5 %7 = or %4, %6 %8 = xor %7, splat (i32 -1) ret %8 } define @codegen_bsl1n_i32( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl1n_i32: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl1n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = xor %0, splat (i32 -1) %5 = and %2, %4 %6 = xor %2, splat (i32 -1) %7 = and %1, %6 %8 = or %5, %7 ret %8 } define @codegen_bsl2n_i32( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl2n_i32: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = or %2, %1 %6 = xor %5, splat (i32 -1) %7 = or %4, %6 ret %7 } define @codegen_bsl_i64( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl_i64: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i64 -1) %6 = and %1, %5 %7 = or %4, %6 ret %7 } define @codegen_nbsl_i64( %0, %1, %2) { ; CHECK-LABEL: codegen_nbsl_i64: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = xor %2, splat (i64 -1) %6 = and %1, %5 %7 = or %4, %6 %8 = xor %7, splat (i64 -1) ret %8 } define @codegen_bsl1n_i64( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl1n_i64: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl1n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = xor %0, splat (i64 -1) %5 = and %2, %4 %6 = xor %2, splat (i64 -1) %7 = and %1, %6 %8 = or %5, %7 ret %8 } define @codegen_bsl2n_i64( %0, %1, %2) { ; CHECK-LABEL: codegen_bsl2n_i64: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret %4 = and %2, %0 %5 = or %2, %1 %6 = xor %5, splat (i64 -1) %7 = or %4, %6 ret %7 } ; (A ^ B) & C) ^ B -> (A & C) | (B & !C) when BIC instructions are available. define @bsl_combine_when_bic_available( %a, %b, %c) { ; CHECK-LABEL: bsl_combine_when_bic_available: ; CHECK: // %bb.0: // %entry ; CHECK-NEXT: bsl z0.d, z0.d, z1.d, z2.d ; CHECK-NEXT: ret entry: %t1 = xor %a, %b %t2 = and %t1, %c %t3 = xor %t2, %b ret %t3 } ; NOT (a) = NBSL (a, a, a) = SUBR (a, -1). define @not( %0) #0 { ; CHECK-LABEL: not: ; CHECK: // %bb.0: ; CHECK-NEXT: subr z0.b, z0.b, #255 // =0xff ; CHECK-NEXT: ret %2 = xor %0, splat (i64 -1) ret %2 } ; NAND (a, b) = NBSL (a, b, b) = NBSL (b, a, a). define @nand( %0, %1) #0 { ; CHECK-LABEL: nand: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z1.d ; CHECK-NEXT: ret %3 = and %1, %0 %4 = xor %3, splat (i64 -1) ret %4 } ; NOR (a, b) = NBSL (a, b, a) = NBSL (b, a, b). define @nor( %0, %1) #0 { ; CHECK-LABEL: nor: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z0.d ; CHECK-NEXT: ret %3 = or %1, %0 %4 = xor %3, splat (i64 -1) ret %4 } ; EON (a, b) = BSL2N (a, a, b) = BSL2N (b, b, a). define @eon( %0, %1) #0 { ; CHECK-LABEL: eon: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z0.d, z1.d ; CHECK-NEXT: ret %3 = xor %0, %1 %4 = xor %3, splat (i64 -1) ret %4 } ; ORN (a, b) = BSL2N (a, b, a). define @orn( %0, %1) #0 { ; CHECK-LABEL: orn: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z0.d, z0.d, z1.d, z0.d ; CHECK-NEXT: ret %3 = xor %1, splat (i64 -1) %4 = or %0, %3 ret %4 } ; Commute operands of EON idiom to avoid the copy of z0. define @eon_comm( %m, %a, %b) { ; CHECK-LABEL: eon_comm: ; CHECK: // %bb.0: ; CHECK-NEXT: bsl2n z1.d, z1.d, z1.d, z0.d ; CHECK-NEXT: sel z0.d, p0, z0.d, z1.d ; CHECK-NEXT: ret %xor = xor %a, %b %eon = xor %xor, splat (i64 -1) %sel = select %m, %a, %eon ret %sel } ; As above for NAND idiom. define @nand_comm( %m, %a, %b) { ; CHECK-LABEL: nand_comm: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z1.d ; CHECK-NEXT: mov z0.d, p0/m, z1.d ; CHECK-NEXT: ret %and = and %a, %b %nand = xor %and, splat (i64 -1) %sel = select %m, %b, %nand ret %sel } ; As above for NOR idiom. define @nor_comm( %m, %a, %b) { ; CHECK-LABEL: nor_comm: ; CHECK: // %bb.0: ; CHECK-NEXT: nbsl z0.d, z0.d, z1.d, z0.d ; CHECK-NEXT: mov z0.d, p0/m, z1.d ; CHECK-NEXT: ret %or = or %a, %b %nor = xor %or, splat (i64 -1) %sel = select %m, %b, %nor ret %sel } ; Neither z0 nor z1 can be clobbered by the EON idiom, so one of them needs copying. define @eon_needs_mov( %m1, %m2, %a, %b) { ; CHECK-LABEL: eon_needs_mov: ; CHECK: // %bb.0: ; CHECK-NEXT: movprfx z2, z0 ; CHECK-NEXT: bsl2n z2.d, z2.d, z0.d, z1.d ; CHECK-NEXT: sel z0.d, p0, z0.d, z2.d ; CHECK-NEXT: mov z0.d, p1/m, z1.d ; CHECK-NEXT: ret %xor = xor %a, %b %eon = xor %xor, splat (i64 -1) %sel1 = select %m1, %a, %eon %sel2 = select %m2, %b, %sel1 ret %sel2 }