; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=riscv32 -global-isel -verify-machineinstrs < %s \ ; RUN: | FileCheck %s -check-prefixes=CHECK,RV32I ; RUN: llc -mtriple=riscv32 -global-isel -mattr=+zbb -verify-machineinstrs < %s \ ; RUN: | FileCheck %s -check-prefixes=CHECK,RV32ZBB-ZBKB,RV32ZBB ; RUN: llc -mtriple=riscv32 -global-isel -mattr=+zbkb -verify-machineinstrs < %s \ ; RUN: | FileCheck %s -check-prefixes=CHECK,RV32ZBB-ZBKB,RV32ZBKB define i32 @andn_i32(i32 %a, i32 %b) nounwind { ; RV32I-LABEL: andn_i32: ; RV32I: # %bb.0: ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: and a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: andn_i32: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: andn a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i32 %b, -1 %and = and i32 %neg, %a ret i32 %and } define i64 @andn_i64(i64 %a, i64 %b) nounwind { ; RV32I-LABEL: andn_i64: ; RV32I: # %bb.0: ; RV32I-NEXT: not a2, a2 ; RV32I-NEXT: not a3, a3 ; RV32I-NEXT: and a0, a2, a0 ; RV32I-NEXT: and a1, a3, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: andn_i64: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: andn a0, a0, a2 ; RV32ZBB-ZBKB-NEXT: andn a1, a1, a3 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i64 %b, -1 %and = and i64 %neg, %a ret i64 %and } define i32 @orn_i32(i32 %a, i32 %b) nounwind { ; RV32I-LABEL: orn_i32: ; RV32I: # %bb.0: ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: or a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: orn_i32: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: orn a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i32 %b, -1 %or = or i32 %neg, %a ret i32 %or } define i64 @orn_i64(i64 %a, i64 %b) nounwind { ; RV32I-LABEL: orn_i64: ; RV32I: # %bb.0: ; RV32I-NEXT: not a2, a2 ; RV32I-NEXT: not a3, a3 ; RV32I-NEXT: or a0, a2, a0 ; RV32I-NEXT: or a1, a3, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: orn_i64: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: orn a0, a0, a2 ; RV32ZBB-ZBKB-NEXT: orn a1, a1, a3 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i64 %b, -1 %or = or i64 %neg, %a ret i64 %or } define i32 @xnor_i32(i32 %a, i32 %b) nounwind { ; RV32I-LABEL: xnor_i32: ; RV32I: # %bb.0: ; RV32I-NEXT: not a0, a0 ; RV32I-NEXT: xor a0, a0, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: xnor_i32: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: xnor a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i32 %a, -1 %xor = xor i32 %neg, %b ret i32 %xor } define i64 @xnor_i64(i64 %a, i64 %b) nounwind { ; RV32I-LABEL: xnor_i64: ; RV32I: # %bb.0: ; RV32I-NEXT: not a0, a0 ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: xor a0, a0, a2 ; RV32I-NEXT: xor a1, a1, a3 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: xnor_i64: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: xnor a0, a0, a2 ; RV32ZBB-ZBKB-NEXT: xnor a1, a1, a3 ; RV32ZBB-ZBKB-NEXT: ret %neg = xor i64 %a, -1 %xor = xor i64 %neg, %b ret i64 %xor } define i32 @rol_i32(i32 %a, i32 %b) nounwind { ; RV32I-LABEL: rol_i32: ; RV32I: # %bb.0: ; RV32I-NEXT: neg a2, a1 ; RV32I-NEXT: sll a1, a0, a1 ; RV32I-NEXT: srl a0, a0, a2 ; RV32I-NEXT: or a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: rol_i32: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: rol a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %or = tail call i32 @llvm.fshl.i32(i32 %a, i32 %a, i32 %b) ret i32 %or } ; This test is presented here in case future expansions of the Bitmanip ; extensions introduce instructions suitable for this pattern. define i64 @rol_i64(i64 %a, i64 %b) nounwind { ; CHECK-LABEL: rol_i64: ; CHECK: # %bb.0: ; CHECK-NEXT: andi a6, a2, 63 ; CHECK-NEXT: li a4, 32 ; CHECK-NEXT: bltu a6, a4, .LBB7_2 ; CHECK-NEXT: # %bb.1: ; CHECK-NEXT: li a3, 0 ; CHECK-NEXT: sll a7, a0, a6 ; CHECK-NEXT: j .LBB7_3 ; CHECK-NEXT: .LBB7_2: ; CHECK-NEXT: sll a3, a0, a2 ; CHECK-NEXT: neg a5, a6 ; CHECK-NEXT: srl a5, a0, a5 ; CHECK-NEXT: sll a7, a1, a2 ; CHECK-NEXT: or a7, a5, a7 ; CHECK-NEXT: .LBB7_3: ; CHECK-NEXT: neg a5, a2 ; CHECK-NEXT: mv a2, a1 ; CHECK-NEXT: beqz a6, .LBB7_5 ; CHECK-NEXT: # %bb.4: ; CHECK-NEXT: mv a2, a7 ; CHECK-NEXT: .LBB7_5: ; CHECK-NEXT: andi a6, a5, 63 ; CHECK-NEXT: bltu a6, a4, .LBB7_7 ; CHECK-NEXT: # %bb.6: ; CHECK-NEXT: srl a7, a1, a6 ; CHECK-NEXT: bnez a6, .LBB7_8 ; CHECK-NEXT: j .LBB7_9 ; CHECK-NEXT: .LBB7_7: ; CHECK-NEXT: srl a7, a0, a5 ; CHECK-NEXT: neg t0, a6 ; CHECK-NEXT: sll t0, a1, t0 ; CHECK-NEXT: or a7, a7, t0 ; CHECK-NEXT: beqz a6, .LBB7_9 ; CHECK-NEXT: .LBB7_8: ; CHECK-NEXT: mv a0, a7 ; CHECK-NEXT: .LBB7_9: ; CHECK-NEXT: bltu a6, a4, .LBB7_11 ; CHECK-NEXT: # %bb.10: ; CHECK-NEXT: li a1, 0 ; CHECK-NEXT: j .LBB7_12 ; CHECK-NEXT: .LBB7_11: ; CHECK-NEXT: srl a1, a1, a5 ; CHECK-NEXT: .LBB7_12: ; CHECK-NEXT: or a0, a3, a0 ; CHECK-NEXT: or a1, a2, a1 ; CHECK-NEXT: ret %or = tail call i64 @llvm.fshl.i64(i64 %a, i64 %a, i64 %b) ret i64 %or } define i32 @ror_i32(i32 %a, i32 %b) nounwind { ; RV32I-LABEL: ror_i32: ; RV32I: # %bb.0: ; RV32I-NEXT: neg a2, a1 ; RV32I-NEXT: srl a1, a0, a1 ; RV32I-NEXT: sll a0, a0, a2 ; RV32I-NEXT: or a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: ror_i32: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: ror a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %or = tail call i32 @llvm.fshr.i32(i32 %a, i32 %a, i32 %b) ret i32 %or } ; This test is presented here in case future expansions of the Bitmanip ; extensions introduce instructions suitable for this pattern. define i64 @ror_i64(i64 %a, i64 %b) nounwind { ; CHECK-LABEL: ror_i64: ; CHECK: # %bb.0: ; CHECK-NEXT: andi a5, a2, 63 ; CHECK-NEXT: li a4, 32 ; CHECK-NEXT: bltu a5, a4, .LBB9_2 ; CHECK-NEXT: # %bb.1: ; CHECK-NEXT: srl a6, a1, a5 ; CHECK-NEXT: mv a3, a0 ; CHECK-NEXT: bnez a5, .LBB9_3 ; CHECK-NEXT: j .LBB9_4 ; CHECK-NEXT: .LBB9_2: ; CHECK-NEXT: srl a3, a0, a2 ; CHECK-NEXT: neg a6, a5 ; CHECK-NEXT: sll a6, a1, a6 ; CHECK-NEXT: or a6, a3, a6 ; CHECK-NEXT: mv a3, a0 ; CHECK-NEXT: beqz a5, .LBB9_4 ; CHECK-NEXT: .LBB9_3: ; CHECK-NEXT: mv a3, a6 ; CHECK-NEXT: .LBB9_4: ; CHECK-NEXT: neg a6, a2 ; CHECK-NEXT: bltu a5, a4, .LBB9_7 ; CHECK-NEXT: # %bb.5: ; CHECK-NEXT: li a2, 0 ; CHECK-NEXT: andi a5, a6, 63 ; CHECK-NEXT: bgeu a5, a4, .LBB9_8 ; CHECK-NEXT: .LBB9_6: ; CHECK-NEXT: sll a4, a0, a6 ; CHECK-NEXT: neg a7, a5 ; CHECK-NEXT: srl a0, a0, a7 ; CHECK-NEXT: sll a6, a1, a6 ; CHECK-NEXT: or a0, a0, a6 ; CHECK-NEXT: bnez a5, .LBB9_9 ; CHECK-NEXT: j .LBB9_10 ; CHECK-NEXT: .LBB9_7: ; CHECK-NEXT: srl a2, a1, a2 ; CHECK-NEXT: andi a5, a6, 63 ; CHECK-NEXT: bltu a5, a4, .LBB9_6 ; CHECK-NEXT: .LBB9_8: ; CHECK-NEXT: li a4, 0 ; CHECK-NEXT: sll a0, a0, a5 ; CHECK-NEXT: beqz a5, .LBB9_10 ; CHECK-NEXT: .LBB9_9: ; CHECK-NEXT: mv a1, a0 ; CHECK-NEXT: .LBB9_10: ; CHECK-NEXT: or a0, a3, a4 ; CHECK-NEXT: or a1, a2, a1 ; CHECK-NEXT: ret %or = tail call i64 @llvm.fshr.i64(i64 %a, i64 %a, i64 %b) ret i64 %or } define i32 @rori_i32_fshl(i32 %a) nounwind { ; RV32I-LABEL: rori_i32_fshl: ; RV32I: # %bb.0: ; RV32I-NEXT: slli a1, a0, 31 ; RV32I-NEXT: srli a0, a0, 1 ; RV32I-NEXT: or a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: rori_i32_fshl: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: rori a0, a0, 1 ; RV32ZBB-ZBKB-NEXT: ret %1 = tail call i32 @llvm.fshl.i32(i32 %a, i32 %a, i32 31) ret i32 %1 } define i32 @rori_i32_fshr(i32 %a) nounwind { ; RV32I-LABEL: rori_i32_fshr: ; RV32I: # %bb.0: ; RV32I-NEXT: srli a1, a0, 31 ; RV32I-NEXT: slli a0, a0, 1 ; RV32I-NEXT: or a0, a1, a0 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: rori_i32_fshr: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: rori a0, a0, 31 ; RV32ZBB-ZBKB-NEXT: ret %1 = tail call i32 @llvm.fshr.i32(i32 %a, i32 %a, i32 31) ret i32 %1 } define i64 @rori_i64(i64 %a) nounwind { ; CHECK-LABEL: rori_i64: ; CHECK: # %bb.0: ; CHECK-NEXT: slli a2, a0, 31 ; CHECK-NEXT: srli a0, a0, 1 ; CHECK-NEXT: slli a3, a1, 31 ; CHECK-NEXT: srli a1, a1, 1 ; CHECK-NEXT: or a0, a0, a3 ; CHECK-NEXT: or a1, a2, a1 ; CHECK-NEXT: ret %1 = tail call i64 @llvm.fshl.i64(i64 %a, i64 %a, i64 63) ret i64 %1 } define i64 @rori_i64_fshr(i64 %a) nounwind { ; CHECK-LABEL: rori_i64_fshr: ; CHECK: # %bb.0: ; CHECK-NEXT: srli a2, a1, 31 ; CHECK-NEXT: slli a3, a0, 1 ; CHECK-NEXT: slli a1, a1, 1 ; CHECK-NEXT: srli a0, a0, 31 ; CHECK-NEXT: or a1, a1, a0 ; CHECK-NEXT: or a0, a2, a3 ; CHECK-NEXT: ret %1 = tail call i64 @llvm.fshr.i64(i64 %a, i64 %a, i64 63) ret i64 %1 } define i8 @srli_i8(i8 %a) nounwind { ; CHECK-LABEL: srli_i8: ; CHECK: # %bb.0: ; CHECK-NEXT: zext.b a0, a0 ; CHECK-NEXT: srli a0, a0, 6 ; CHECK-NEXT: ret %1 = lshr i8 %a, 6 ret i8 %1 } ; FIXME: We should use slli+srai with Zbb for better compression. define i8 @srai_i8(i8 %a) nounwind { ; RV32I-LABEL: srai_i8: ; RV32I: # %bb.0: ; RV32I-NEXT: slli a0, a0, 24 ; RV32I-NEXT: srai a0, a0, 29 ; RV32I-NEXT: ret ; ; RV32ZBB-LABEL: srai_i8: ; RV32ZBB: # %bb.0: ; RV32ZBB-NEXT: sext.b a0, a0 ; RV32ZBB-NEXT: srai a0, a0, 5 ; RV32ZBB-NEXT: ret ; ; RV32ZBKB-LABEL: srai_i8: ; RV32ZBKB: # %bb.0: ; RV32ZBKB-NEXT: slli a0, a0, 24 ; RV32ZBKB-NEXT: srai a0, a0, 29 ; RV32ZBKB-NEXT: ret %1 = ashr i8 %a, 5 ret i8 %1 } ; FIXME: We should use slli+srli. define i16 @srli_i16(i16 %a) nounwind { ; RV32I-LABEL: srli_i16: ; RV32I: # %bb.0: ; RV32I-NEXT: slli a0, a0, 16 ; RV32I-NEXT: srli a0, a0, 16 ; RV32I-NEXT: srli a0, a0, 6 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: srli_i16: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: zext.h a0, a0 ; RV32ZBB-ZBKB-NEXT: srli a0, a0, 6 ; RV32ZBB-ZBKB-NEXT: ret %1 = lshr i16 %a, 6 ret i16 %1 } ; FIXME: We should use slli+srai with Zbb/Zbkb for better compression. define i16 @srai_i16(i16 %a) nounwind { ; RV32I-LABEL: srai_i16: ; RV32I: # %bb.0: ; RV32I-NEXT: slli a0, a0, 16 ; RV32I-NEXT: srai a0, a0, 25 ; RV32I-NEXT: ret ; ; RV32ZBB-LABEL: srai_i16: ; RV32ZBB: # %bb.0: ; RV32ZBB-NEXT: sext.h a0, a0 ; RV32ZBB-NEXT: srai a0, a0, 9 ; RV32ZBB-NEXT: ret ; ; RV32ZBKB-LABEL: srai_i16: ; RV32ZBKB: # %bb.0: ; RV32ZBKB-NEXT: slli a0, a0, 16 ; RV32ZBKB-NEXT: srai a0, a0, 25 ; RV32ZBKB-NEXT: ret %1 = ashr i16 %a, 9 ret i16 %1 } define i32 @binop_neg_and(i32 %a, i32 %b, i32 %c) { ; RV32I-LABEL: binop_neg_and: ; RV32I: # %bb.0: ; RV32I-NEXT: add a1, a1, a2 ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: and a0, a0, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: binop_neg_and: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: add a1, a1, a2 ; RV32ZBB-ZBKB-NEXT: andn a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %not_b = xor i32 %b, -1 %sub = sub i32 %not_b, %c %and = and i32 %a, %sub ret i32 %and } define i32 @binop_neg_or(i32 %a, i32 %b, i32 %c) { ; RV32I-LABEL: binop_neg_or: ; RV32I: # %bb.0: ; RV32I-NEXT: add a1, a1, a2 ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: or a0, a0, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: binop_neg_or: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: add a1, a1, a2 ; RV32ZBB-ZBKB-NEXT: orn a0, a0, a1 ; RV32ZBB-ZBKB-NEXT: ret %not_b = xor i32 %b, -1 %sub = sub i32 %not_b, %c %or = or i32 %a, %sub ret i32 %or } define i32 @binop_neg_xor(i32 %a, i32 %b, i32 %c) { ; RV32I-LABEL: binop_neg_xor: ; RV32I: # %bb.0: ; RV32I-NEXT: add a1, a1, a2 ; RV32I-NEXT: not a1, a1 ; RV32I-NEXT: xor a0, a0, a1 ; RV32I-NEXT: ret ; ; RV32ZBB-ZBKB-LABEL: binop_neg_xor: ; RV32ZBB-ZBKB: # %bb.0: ; RV32ZBB-ZBKB-NEXT: add a1, a1, a2 ; RV32ZBB-ZBKB-NEXT: xnor a0, a1, a0 ; RV32ZBB-ZBKB-NEXT: ret %not_b = xor i32 %b, -1 %sub = sub i32 %not_b, %c %xor = xor i32 %a, %sub ret i32 %xor }