LLVM指令选择

后端部分:
作用:负责将 LLVM IR 转换成目标代码,主要过程有指令选择,寄存器分配,指令调度。
前置知识:
SelectionDAG 节点:在编译优化阶段生成的一种抽象的数据结构,用以表示程序的计算过程,帮助优化器进行高效的指令选择和调度。
Machinelnstr:机器相关的指令格式,用于描述特定目标架构下的指令集和操作码。
MCInst:机器指令,是具体的目标代码表示,包含了特定架构下的二进制编码指令。
定义一个指令
已知

1
2
3
4
let Predicates = [HasExtZpn095b] in {
def SRL8 : RVPBinary<0b0101101, 0b000, "srl8">,
Sched<[WritePShift8, ReadPShift8, ReadPShift8]>;
}

此处Sched是指令模型调度相关,可以尝试对P指令和非P指令调整Latency,需要考虑指令依赖和寄存器压力。在这里RVPBinary为一个Instruction class templates

1
2
3
4
5
6
let hasSideEffects = 0, mayLoad = 0, mayStore = 0,
DecoderNamespace = "RISCVP095_" in
class RVPBinary<bits<7> funct7, bits<3> funct3, string opcodestr>
: RVInstR<funct7, funct3, OPC_OP_VE,
(outs GPRP:$rd), (ins GPRP:$rs1, GPRP:$rs2),
opcodestr, "$rd, $rs1, $rs2">;

我们只需要填入func7,func3,和opcodestr即可定义一个具体的Instruction。
定义intrinsic
以kabs16为例,首先可以先添加一个指令class模板,用来批量添加结构相同指令

1
2
3
4
5
6
7
let hasSideEffects = 0, mayLoad = 0, mayStore = 0,
DecoderNamespace = "RISCVP095_" in
class RVPUnary<bits<7> funct7, bits<5> funct5, bits<3> funct3, string opcodestr>
: RVInstR<funct7, funct3, OPC_OP_VE, (outs GPRP:$rd), (ins GPRP:$rs1),
opcodestr, "$rd, $rs1"> {
let Inst{24-20} = funct5;
}

hasSideEffects标记计算的表达式是否有副作用,详细可查看clang/include/clang/AST/Expr.h:635. mayLoad标记是否会读取内存,mayStore标记是否会修改内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// Return true if this instruction could possibly read memory.
/// Instructions with this flag set are not necessarily simple load
/// instructions, they may load a value and modify it, for example.
bool mayLoad(QueryType Type = AnyInBundle) const {
if (isInlineAsm()) {
unsigned ExtraInfo = getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
if (ExtraInfo & InlineAsm::Extra_MayLoad)
return true;
}
return hasProperty(MCID::MayLoad, Type);
}

/// Return true if this instruction could possibly modify memory.
/// Instructions with this flag set are not necessarily simple store
/// instructions, they may store a modified value based on their operands, or
/// may not actually modify anything, for example.
bool mayStore(QueryType Type = AnyInBundle) const {
if (isInlineAsm()) {
unsigned ExtraInfo = getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
if (ExtraInfo & InlineAsm::Extra_MayStore)
return true;
}
return hasProperty(MCID::MayStore, Type);
}

RVPUnary继承于RVInstR,RVInstR继承于RVInstRBase

1
2
3
4
5
class RVInstR<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, dag outs,
dag ins, string opcodestr, string argstr>
: RVInstRBase<funct3, opcode, outs, ins, opcodestr, argstr> {
let Inst{31-25} = funct7;
}

需要注意的是这里RVInstRBase给出了Bits {31-25} should be set by the subclasses.而其他encoding范围已经规定好。

1
2
3
4
5
6
7
8
9
10
11
12
13
class RVInstRBase<bits<3> funct3, RISCVOpcode opcode, dag outs,
dag ins, string opcodestr, string argstr>
: RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> {
bits<5> rs2;
bits<5> rs1;
bits<5> rd;

let Inst{24-20} = rs2;
let Inst{19-15} = rs1;
let Inst{14-12} = funct3;
let Inst{11-7} = rd;
let Inst{6-0} = opcode.Value;
}

0-6为RISCVOpcode,已经有llvm/lib/Target/RISCV/RISCVInstrFormats.td的133-160行def过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def OPC_LOAD      : RISCVOpcode<"LOAD",      0b0000011>;
def OPC_LOAD_FP : RISCVOpcode<"LOAD_FP", 0b0000111>;
def OPC_CUSTOM_0 : RISCVOpcode<"CUSTOM_0", 0b0001011>;
def OPC_MISC_MEM : RISCVOpcode<"MISC_MEM", 0b0001111>;
def OPC_OP_IMM : RISCVOpcode<"OP_IMM", 0b0010011>;
def OPC_AUIPC : RISCVOpcode<"AUIPC", 0b0010111>;
def OPC_OP_IMM_32 : RISCVOpcode<"OP_IMM_32", 0b0011011>;
def OPC_STORE : RISCVOpcode<"STORE", 0b0100011>;
def OPC_STORE_FP : RISCVOpcode<"STORE_FP", 0b0100111>;
def OPC_CUSTOM_1 : RISCVOpcode<"CUSTOM_1", 0b0101011>;
def OPC_AMO : RISCVOpcode<"AMO", 0b0101111>;
def OPC_OP : RISCVOpcode<"OP", 0b0110011>;
def OPC_LUI : RISCVOpcode<"LUI", 0b0110111>;
def OPC_OP_32 : RISCVOpcode<"OP_32", 0b0111011>;
def OPC_MADD : RISCVOpcode<"MADD", 0b1000011>;
def OPC_MSUB : RISCVOpcode<"MSUB", 0b1000111>;
def OPC_NMSUB : RISCVOpcode<"NMSUB", 0b1001011>;
def OPC_NMADD : RISCVOpcode<"NMADD", 0b1001111>;
def OPC_OP_FP : RISCVOpcode<"OP_FP", 0b1010011>;
def OPC_OP_V : RISCVOpcode<"OP_V", 0b1010111>;
def OPC_CUSTOM_2 : RISCVOpcode<"CUSTOM_2", 0b1011011>;
def OPC_BRANCH : RISCVOpcode<"BRANCH", 0b1100011>;
def OPC_JALR : RISCVOpcode<"JALR", 0b1100111>;
def OPC_JAL : RISCVOpcode<"JAL", 0b1101111>;
def OPC_SYSTEM : RISCVOpcode<"SYSTEM", 0b1110011>;
def OPC_OP_VE : RISCVOpcode<"OP_VE", 0b1110111>;
def OPC_CUSTOM_3 : RISCVOpcode<"CUSTOM_3", 0b1111011>;
def OPC_GE80B : RISCVOpcode<"GE80B", 0b1111111>;

每一个RISCVOpcode含有一个name和7bit的Value

1
2
3
4
class RISCVOpcode<string name, bits<7> val> {
string Name = name;
bits<7> Value = val;
}

对应白皮书
[图片]
7-11为rd,一般是返回值的寄存器,let Inst{14-12} = funct3;这个fun3一般是三个0, let Inst{19-15} = rs1;表示输入寄存器与空间,let Inst{24-20} = rs2;表示第二个输入
不过有时Encoding format并不固定,例如bitrevi:
[图片]
就需要利用llvm/lib/Target/RISCV/RISCVInstrInfo.td的uimmlog2xlen,在32Bit下是5bit的imm空间,在64Bit下是6位imm encoding空间

1
2
3
4
5
def uimmlog2xlen : RISCVOp, ImmLeaf<XLenVT, [{
if (Subtarget->is64Bit())
return isUInt<6>(Imm);
return isUInt<5>(Imm);
}]

写一个对应的模板类

1
2
3
4
5
6
7
8
9
10
11
let hasSideEffects = 0, mayLoad = 0, mayStore = 0,
DecoderNamespace = "RISCVP095_" in
class RVPShiftUImmLog2XLen<bits<6> funct6, bits<3> funct3, string opcodestr>
: RVInstI<funct3, OPC_OP_VE, (outs GPRP:$rd),
(ins GPRP:$rs1, uimmlog2xlen:$shamt),
opcodestr, "$rd, $rs1, $shamt"> {
bits<6> shamt;

let Inst{31-26} = funct6;
let Inst{25-20} = shamt;
}

(待完成)
查看class Intrinsic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Intrinsic<list<LLVMType> ret_types,
list<LLVMType> param_types = [],
list<IntrinsicProperty> intr_properties = [],
string name = "",
list<SDNodeProperty> sd_properties = [],
bit disable_default_attributes = true> : SDPatternOperator {
string LLVMName = name;
string TargetPrefix = ""; // Set to a prefix for target-specific intrinsics.
list<LLVMType> RetTypes = ret_types;
list<LLVMType> ParamTypes = param_types;
list<IntrinsicProperty> IntrProperties = intr_properties;
let Properties = sd_properties;

// Disable applying IntrinsicProperties that are marked default with
// IntrinsicProperty<1>
bit DisableDefaultAttributes = disable_default_attributes;

bit isTarget = false;

TypeInfoGen TypeInfo = TypeInfoGen<RetTypes, ParamTypes>;
bit isOverloaded = TypeInfo.isOverloaded;
list<LLVMType> Types = TypeInfo.Types;
list<list<int>> TypeSig = TypeInfo.TypeSig;
}

riscv的intrinsics:

1
2
3
4
5
6
7
8
9
10
let TargetPrefix = "riscv" in {
class RVPUnaryIntrinsics
: Intrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>],
[IntrNoMem]>;

multiclass RVPUnaryIntrinsics {
def "int_riscv_" # NAME : RVPUnaryIntrinsics;
}
}

SDPatternOperator的用途为Selection DAG Pattern Operations

1
2
3
4
// Selection DAG Pattern Operations
class SDPatternOperator {
list<SDNodeProperty> Properties = [];
}

DAG是 LLVM CodeGen框架中的一个关键数据结构,主要用于指令选择阶段(待完成)
生成指令信息表会在编译llvm期间include进去
{ 15235,3, 1, 4, 4965, 0, 0, RISCVImpOpBase + 0, 8130, 0, 0x1ULL }, // Inst #15235 = SRL8
分别代表 指令信息表,(待完成)

对Intrinsics的类型检查:
class RVPBinaryAABIntrinsics
: Intrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, llvm_anyint_ty],
[IntrNoMem]>;

multiclass RVPBinaryAABIntrinsics {
def “int_riscv_” # NAME : RVPBinaryAABIntrinsics;
}

defm ksllw : RVPBinaryAABIntrinsics;
defm kslliw : RVPBinaryAABIntrinsics;
defm kslli8 : RVPBinaryAABIntrinsics;
defm slli16 : RVPBinaryAABIntrinsics;
DAG图的组成
LLVM使用SelectionDAG类(defined in include/llvm/CodeGen/SelectionDAG.h)表示一个架构无关的底层数据依赖图, 它将串联整个指令选择流程, 所以包含了许多成员与接口, 这里我们只截取与lowering相关的部分.

1
2
3
4
5
6
7
8
9
10
11
12
13
class SelectionDAG {
const TargetLowering *TLI = nullptr;
FunctionLoweringInfo * FLI = nullptr;

/// The starting token.
SDNode EntryNode;

/// The root of the entire DAG.
SDValue Root;

/// A linked list of nodes in the current DAG.
ilist<SDNode> AllNodes;
};

其中TargetLowering负责处理架构相关的lowering, FunctionLoweringInfo负责处理函数调用约定, 这两个成员后面会介绍. EntryNode与Root分别指向图的入口节点与根节点, 通过根节点即可遍历整个图
lowering的目的是将IR的数据结构转换为DAG, 方便之后指令选择做模式匹配. lowering的过程就是DAG建立的过程。

待讲解
clang/lib/CodeGen/CGBuiltin.cpp
llvm/include/llvm/IR/IntrinsicsRISCV.td
llvm/lib/Target/RISCV/RISCVInstrInfo/RISCVInstrInfoP/RISCVInstrInfoP.td
llvm/lib/Target/RISCV/RISCVISelLowering.cpp

指令选择:
作用:LLVM IR 转换为目标特定的 SelectionDAG 节点,生成目标机器代码的指令序列。
有GlobalISel和SelectionDAG两种框架,目前都是SelectionDAG,SelectionDAGISel是核心类
主要动作:
指令选择,寄存器分配,指令调度,Machine Code Generation,Backend Optimizations

例如定义了一个__builtin_riscv_kabs32并加入feature限制

1
2
3
let Features = "64bit,(zp095b|zp054b|zp053b|zp052b)" in {
def kabs32 : RISCVBuiltin<"unsigned long int(unsigned long int)">;
}

注意,prototype是返回原型,features才是要匹配的输入

intrinsic再到ISDNode再到指令

1
2
3
4
5
class RVInstR<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, dag outs,
dag ins, string opcodestr, string argstr>
: RVInstRBase<funct3, opcode, outs, ins, opcodestr, argstr> {
let Inst{31-25} = funct7;
}

指令选择
第一阶段是以LLVM IR作为输入创建DAG(有向无环图)

1
2
3
4
5
6
7
let Predicates = [HasExtZpn095b] in {
def KABS16 : RVPUnary<0b1010110, 0b10001, 0b000, "kabs16">,
Sched<[WritePAbs16, ReadPAbs16]>;
} // Predicates = [HasExtZpn095b]

// Target-dependent nodes.
def riscv_srai : SDNode<"RISCVISD::SRAI", SDT_RISCVSIMDShiftOpImm>;

目前有问题的指令:
KSLLIW
思路:能正常生成ir,并且ksllw的32和64能正常生成,kslliw的64位不可以
kslliw SDNode之前出了问题,可能是intrinsic格式问题
猜测是llvm/lib/Target/RISCV/RISCVISelLowering.cpp的返回类型合法化问题
Don’t know how to custom type legalize this intrinsic!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(lldb) breakpoint list 
Current breakpoints:
3: name = 'llvm::RISCVTargetLowering::ReplaceNodeResults', locations = 1, resolved = 1, hit count = 1
3.1: where = clang1`llvm::RISCVTargetLowering::ReplaceNodeResults(llvm::SDNode*, llvm::SmallVectorImpl<llvm::SDValue>&, llvm::SelectionDAG&) const + 43 at RISCVISelLowering.cpp:13221:12, address = 0x00005555627e84bb, resolved, hit count = 1
(lldb) p N->getOperationName(&DAG)
(std::string) "llvm.riscv.kslliw"
(lldb) p N->getOpcode()
(unsigned int) 46
(lldb) p N->getOperationName(&DAG)
(std::string) "llvm.riscv.kslliw"
(lldb) p N->getValueType(0).getEVTString()
(std::string) "i32"
(lldb) p N->getNumOperands()
(unsigned int) 3
(lldb) p N->getOperand(0).getValueType().getEVTString()
N->getOperand(1).getValueType().getEVTString()
p N->getOperand(2).getValueType().getEVTString()
(std::string) "i64"
(lldb) p N->getOperand(1).getValueType().getEVTString()
(std::string) "i32"
(lldb) p N->getOperand(2).getValueType().getEVTString()
(std::string) "i32"
{14713, 3, 1, 4, 4964, 0, 0, RISCVImpOpBase + 0, 8130, 0, 0x1ULL }, // Inst #14713 = KSLLW

结论:白皮书发生变动,定义修改,按照054版本来
sub64:
报错为:

1
2
3
4
5
6
7
8
9
10
11
12
13
 $CLANG -march=rv32gc_xxldsp -mabi=ilp32d -O2 -c test.c  -S  -o -
.text
.attribute 4, 16
.attribute 5, "rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zp054b0p54_zp64054b0p54_xxldsp1p0"
.file "test.c"
fatal error: error in backend: Cannot select: intrinsic %llvm.riscv.sub64
PLEASE submit a bug report to xxx.com and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
1. Program arguments: /home/f/work/llvm/build-debug/llvm/bin/clang -march=rv32gc_xxxx -mabi=ilp32d -O2 -c test.c -S -o -
1. <eof> parser at end of file
2. Code generation
3. Running pass 'Function Pass Manager' on module 'test.c'.
4. Running pass 'RISC-V DAG->DAG Pattern Instruction Selection' on function '@test_sub64'

按照ksub64同类型解决
其他:
有isdnode的,检查lowering过程,检查模式匹配指令是否正确,打印ir查看

调试手段:
前端未定义
1.
查看对应.inc文件,对照白皮书
2.
3.
error in backend: Cannot select
4.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
打断点
b llvm::SelectionDAGISel::Select
b llvm::SelectionDAGISel::CannotYetSelect (SelectionDAGISel::CannotYetSelect(SDNode *N))

b RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN

b RISCVDAGToDAGISel::Select
b RISCVISelDAGToDAG::Select
(lldb) breakpoint list
Current breakpoints:
1: name = 'llvm::SelectionDAGISel::Select', locations = 0 (pending)

2: name = 'llvm::SelectionDAGISel::CannotYetSelect', locations = 1, resolved = 1, hit count = 1
2.1: where = clang1`llvm::SelectionDAGISel::CannotYetSelect(llvm::SDNode*) + 30 at SelectionDAGISel.cpp:4371:15, address = 0x000055556430b95e, resolved, hit count = 1

3: name = 'RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN', locations = 1, resolved = 1, hit count = 0
3.1: where = clang1`llvm::RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(llvm::SDValue, llvm::SelectionDAG&) const + 46 at RISCVISelLowering.cpp:9561:23, address = 0x00005555627a0dee, resolved, hit count = 0

4: name = 'RISCVDAGToDAGISel::Select', locations = 1, resolved = 1, hit count = 0
4.1: where = clang1 `llvm::RISCVDAGToDAGISel::Select(llvm::SDNode*) + 33 at RISCVISelDAGToDAG.cpp:911:7, address = 0x000055556292fcb1, resolved, hit count = 0

5: name = 'RISCVISelDAGToDAG::Select', locations = 0 (pending)

(lldb) p N->dump()
t6: i32 = llvm.riscv.sub64 TargetConstant:i32<10904>, t2, t4
(lldb) call N->dump()
t6: i32 = llvm.riscv.sub64 TargetConstant:i32<10904>, t2, t4
查看Opcode
p N->getOpcode()
(unsigned int) 46
b llvm::RISCVTargetLowering::ReplaceNodeResults
p N->getOperationName(&DAG)
p N->getOpcode()

Intrinsic: llvm.riscv.kslliw
Result VT: i32
Operands: (i32, i32)
Target: RV64
1.
参数
2.
–debug-only=isel
1.
打印出records结果
2.

1
2
3
4
5
llvm-tblgen   -I llvm/include \
-I llvm/lib/Target/RISCV \
-print-records \
llvm/lib/Target/RISCV/RISCV.td \
> riscv-print-records.txt

以 KSLL32为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def KSLL32 {        // InstructionEncoding Instruction RVInstCommon RVInst RVInstRBase RVInstR RVPBinary Sched
field bits<32> Inst = { 0, 1, 1, 0, 0, 1, 0, rs2{4}, rs2{3}, rs2{2}, rs2{1}, rs2{0}, rs1{4}, rs1{3}, rs1{2}, rs1{1}, rs1{0}, 0, 1, 0, rd{4}, rd{3}, rd{2}, rd{1}, rd{0}, 1, 1, 1, 0, 1, 1, 1 };
field bits<32> SoftFail = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int Size = 4;
string DecoderNamespace = "RISCVP095_";
list<Predicate> Predicates = [HasExtZprvsfextra095b, IsRV64];
string DecoderMethod = "";
bit hasCompleteDecoder = 1;
string Namespace = "RISCV";
dag OutOperandList = (outs GPRP:$rd);
dag InOperandList = (ins GPRP:$rs1, GPRP:$rs2);
string AsmString = "ksll32 $rd, $rs1, $rs2";
EncodingByHwMode EncodingInfos = ?;
list<dag> Pattern = [];
list<Register> Uses = [];
list<Register> Defs = [];
int CodeSize = 0;
int AddedComplexity = 0;
bit isPreISelOpcode = 0;
bit isReturn = 0;
bit isBranch = 0;
bit isEHScopeReturn = 0;
bit isIndirectBranch = 0;
bit isCompare = 0;
bit isMoveImm = 0;
bit isMoveReg = 0;
bit isBitcast = 0;
bit isSelect = 0;
bit isBarrier = 0;
bit isCall = 0;
bit isAdd = 0;
bit isTrap = 0;
bit canFoldAsLoad = 0;
bit mayLoad = 0;
bit mayStore = 0;
bit mayRaiseFPException = 0;
bit isConvertibleToThreeAddress = 0;
bit isCommutable = 0;
bit isTerminator = 0;
bit isReMaterializable = 0;
bit isPredicable = 0;
bit isUnpredicable = 0;
bit hasDelaySlot = 0;
bit usesCustomInserter = 0;
bit hasPostISelHook = 0;
bit hasCtrlDep = 0;
bit isNotDuplicable = 0;
bit isConvergent = 0;
bit isAuthenticated = 0;
bit isAsCheapAsAMove = 0;
bit hasExtraSrcRegAllocReq = 0;
bit hasExtraDefRegAllocReq = 0;
bit isRegSequence = 0;
bit isPseudo = 0;
bit isMeta = 0;
bit isExtractSubreg = 0;
bit isInsertSubreg = 0;
bit variadicOpsAreDefs = 0;
bit hasSideEffects = 0;
bit isCodeGenOnly = 0;
bit isAsmParserOnly = 0;
bit hasNoSchedulingInfo = 0;
InstrItinClass Itinerary = NoItinerary;
list<SchedReadWrite> SchedRW = [WritePShift32, ReadPShift32, ReadPShift32];
string Constraints = "";
string DisableEncoding = "";
string PostEncoderMethod = "";
bits<64> TSFlags = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
string AsmMatchConverter = "";
string TwoOperandAliasConstraint = "";
string AsmVariantName = "";
bit UseNamedOperandTable = 0;
bit UseLogicalOperandMappings = 0;
bit FastISelShouldIgnore = 0;
bit HasPositionOrder = 0;
RISCVVConstraint RVVConstraint = NoConstraint;
bits<3> VLMul = { 0, 0, 0 };
bit ForceTailAgnostic = 0;
bit IsTiedPseudo = 0;
bit HasSEWOp = 0;
bit HasVLOp = 0;
bit HasVecPolicyOp = 0;
bit IsRVVWideningReduction = 0;
bit UsesMaskPolicy = 0;
bit IsSignExtendingOpW = 0;
bit HasRoundModeOp = 0;
bit UsesVXRM = 0;
bits<2> TargetOverlapConstraintType = { 0, 0 };
bits<5> rs2 = { ?, ?, ?, ?, ? };
bits<5> rs1 = { ?, ?, ?, ?, ? };
bits<5> rd = { ?, ?, ?, ?, ? };
}
def anonymous_49608 { // Pattern Pat RVPBinaryPat
dag PatternToMatch = (XLenVT (int_riscv_ksll32 XLenVT:$rs1, XLenVT:$rs2));
list<dag> ResultInstrs = [(KSLL32 GPR:$rs1, GPR:$rs2)];
list<Predicate> Predicates = [HasExtZprvsfextra095b, IsRV64];
int AddedComplexity = 0;
bit GISelShouldIgnore = 0;
}

参考:
https://llvm.org/docs/TableGen/
https://llvm.gnu.ac.cn/docs/UserGuides.html
https://llvm.gnu.ac.cn/docs/RemoveDIsDebugInfo.html
https://blog.chiphub.top/2024/05/21/llvm-learning-define-intrinsics-1/
https://zhuanlan.zhihu.com/p/447318642
How to write a TableGen backend:
https://www.youtube.com/watch?v=UP-LBRbvI_U
lowering的过程
https://www.cnblogs.com/Five100Miles/p/12824942.html