This patch adds two IR intrinsics for vsetvli instruction. One to set the vector length to a user specified value and one to set it to vlmax. The vlmax uses the X0 source register encoding.
Clang builtins will follow in a separate patch
Differential Revision: https://reviews.llvm.org/D92973
}
let TargetPrefix = "riscv" in {
+ // We use anyint here but we only support XLen.
+ def int_riscv_vsetvli : Intrinsic<[llvm_anyint_ty],
+ /* AVL */ [LLVMMatchType<0>,
+ /* VSEW */ LLVMMatchType<0>,
+ /* VLMUL */ LLVMMatchType<0>],
+ [IntrNoMem, IntrHasSideEffects,
+ ImmArg<ArgIndex<1>>,
+ ImmArg<ArgIndex<2>>]>;
+ def int_riscv_vsetvlimax : Intrinsic<[llvm_anyint_ty],
+ /* VSEW */ [LLVMMatchType<0>,
+ /* VLMUL */ LLVMMatchType<0>],
+ [IntrNoMem, IntrHasSideEffects,
+ ImmArg<ArgIndex<0>>,
+ ImmArg<ArgIndex<1>>]>;
+
// For unit stride load
// Input: (pointer, vl)
class RISCVUSLoad
#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "Utils/RISCVMatInt.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
}
break;
}
+ case ISD::INTRINSIC_W_CHAIN: {
+ unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue();
+ switch (IntNo) {
+ // By default we do not custom select any intrinsic.
+ default:
+ break;
+
+ case Intrinsic::riscv_vsetvli: {
+ if (!Subtarget->hasStdExtV())
+ break;
+
+ assert(Node->getNumOperands() == 5);
+
+ RISCVVSEW VSEW =
+ static_cast<RISCVVSEW>(Node->getConstantOperandVal(3) & 0x7);
+ RISCVVLMUL VLMul =
+ static_cast<RISCVVLMUL>(Node->getConstantOperandVal(4) & 0x7);
+
+ unsigned VTypeI = RISCVVType::encodeVTYPE(
+ VLMul, VSEW, /*TailAgnostic*/ true, /*MaskAgnostic*/ false);
+ SDValue VTypeIOp = CurDAG->getTargetConstant(VTypeI, DL, XLenVT);
+
+ SDValue VLOperand = Node->getOperand(2);
+ if (auto *C = dyn_cast<ConstantSDNode>(VLOperand)) {
+ if (C->isNullValue()) {
+ VLOperand = SDValue(
+ CurDAG->getMachineNode(RISCV::ADDI, DL, XLenVT,
+ CurDAG->getRegister(RISCV::X0, XLenVT),
+ CurDAG->getTargetConstant(0, DL, XLenVT)),
+ 0);
+ }
+ }
+
+ ReplaceNode(Node,
+ CurDAG->getMachineNode(RISCV::PseudoVSETVLI, DL, XLenVT,
+ MVT::Other, VLOperand, VTypeIOp,
+ /* Chain */ Node->getOperand(0)));
+ return;
+ }
+ case Intrinsic::riscv_vsetvlimax: {
+ if (!Subtarget->hasStdExtV())
+ break;
+
+ assert(Node->getNumOperands() == 4);
+
+ RISCVVSEW VSEW =
+ static_cast<RISCVVSEW>(Node->getConstantOperandVal(2) & 0x7);
+ RISCVVLMUL VLMul =
+ static_cast<RISCVVLMUL>(Node->getConstantOperandVal(3) & 0x7);
+
+ unsigned VTypeI = RISCVVType::encodeVTYPE(
+ VLMul, VSEW, /*TailAgnostic*/ true, /*MaskAgnostic*/ false);
+ SDValue VTypeIOp = CurDAG->getTargetConstant(VTypeI, DL, XLenVT);
+
+ SDValue VLOperand = CurDAG->getRegister(RISCV::X0, XLenVT);
+ ReplaceNode(Node,
+ CurDAG->getMachineNode(RISCV::PseudoVSETVLI, DL, XLenVT,
+ MVT::Other, VLOperand, VTypeIOp,
+ /* Chain */ Node->getOperand(0)));
+ return;
+ }
+ }
+ break;
+ }
}
// Select the default instruction.
OS << "e" << Sew;
switch (VLMUL) {
+ case RISCVVLMUL::LMUL_RESERVED:
+ llvm_unreachable("Unexpected LMUL value!");
case RISCVVLMUL::LMUL_1:
case RISCVVLMUL::LMUL_2:
case RISCVVLMUL::LMUL_4:
LMUL_2,
LMUL_4,
LMUL_8,
- LMUL_F8 = 5,
+ LMUL_RESERVED,
+ LMUL_F8,
LMUL_F4,
LMUL_F2
};
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-v -verify-machineinstrs < %s | FileCheck %s
+
+declare i32 @llvm.riscv.vsetvli.i32(i32, i32, i32)
+declare i32 @llvm.riscv.vsetvlimax.i32(i32, i32)
+
+define void @test_vsetvli_e64mf8(i32 %avl) nounwind {
+; CHECK-LABEL: test_vsetvli_e64mf8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, a0, e64,mf8,ta,mu
+; CHECK-NEXT: ret
+ call i32 @llvm.riscv.vsetvli.i32(i32 %avl, i32 3, i32 5)
+ ret void
+}
+
+define void @test_vsetvli_e8mf2_zero_avl() nounwind {
+; CHECK-LABEL: test_vsetvli_e8mf2_zero_avl:
+; CHECK: # %bb.0:
+; CHECK-NEXT: mv a0, zero
+; CHECK-NEXT: vsetvli a0, a0, e8,mf2,ta,mu
+; CHECK-NEXT: ret
+ call i32 @llvm.riscv.vsetvli.i32(i32 0, i32 0, i32 7)
+ ret void
+}
+
+define void @test_vsetvlimax_e64m8() nounwind {
+; CHECK-LABEL: test_vsetvlimax_e64m8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e64,m8,ta,mu
+; CHECK-NEXT: ret
+ call i32 @llvm.riscv.vsetvlimax.i32(i32 3, i32 3)
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-v -verify-machineinstrs < %s | FileCheck %s
+
+declare i64 @llvm.riscv.vsetvli.i64(i64, i64, i64)
+declare i64 @llvm.riscv.vsetvlimax.i64(i64, i64)
+
+define void @test_vsetvli_e8m1(i64 %avl) nounwind {
+; CHECK-LABEL: test_vsetvli_e8m1:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, a0, e8,m1,ta,mu
+; CHECK-NEXT: ret
+ call i64 @llvm.riscv.vsetvli.i64(i64 %avl, i64 0, i64 0)
+ ret void
+}
+
+define void @test_vsetvli_e16mf4(i64 %avl) nounwind {
+; CHECK-LABEL: test_vsetvli_e16mf4:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, a0, e16,mf4,ta,mu
+; CHECK-NEXT: ret
+ call i64 @llvm.riscv.vsetvli.i64(i64 %avl, i64 1, i64 6)
+ ret void
+}
+
+define void @test_vsetvli_e32mf8_zero_avl() nounwind {
+; CHECK-LABEL: test_vsetvli_e32mf8_zero_avl:
+; CHECK: # %bb.0:
+; CHECK-NEXT: mv a0, zero
+; CHECK-NEXT: vsetvli a0, a0, e16,mf4,ta,mu
+; CHECK-NEXT: ret
+ call i64 @llvm.riscv.vsetvli.i64(i64 0, i64 1, i64 6)
+ ret void
+}
+
+define void @test_vsetvlimax_e32m2() nounwind {
+; CHECK-LABEL: test_vsetvlimax_e32m2:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e32,m2,ta,mu
+; CHECK-NEXT: ret
+ call i64 @llvm.riscv.vsetvlimax.i64(i64 2, i64 1)
+ ret void
+}
+
+define void @test_vsetvlimax_e64m4() nounwind {
+; CHECK-LABEL: test_vsetvlimax_e64m4:
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetvli a0, zero, e64,m4,ta,mu
+; CHECK-NEXT: ret
+ call i64 @llvm.riscv.vsetvlimax.i64(i64 3, i64 2)
+ ret void
+}