LLVM学习笔记:TableGen及内建记录数据结构
TableGen语法
td文件由一系列的class、def、multiclass、defm组成,class为抽象描述,def为class的具体实现,multiclass和defm可以定义一组class和def。例如:
class XXXInst<dag outs, dag ins, string asmstr, list<dag> pattern, Format f>
: Instruction
{
field bits<32> Inst;
let Size = 4;
Format Form = f;
bits<3> FormBits = Form.Value;
let Namespace = "XXX";
let DecoderNamespace = "XXX";
bits<6> Opcode = 0;
let Inst{31-26} = Opcode;
let OutOperandList = outs;
let InOperandList = ins;
let AsmString = asmstr;
let Pattern = pattern;
}
def XXX : Target {
let InstructionSet = XXXInstrInfo;
}
内建纪录
Target
Target包含了全局的目标信息:
class Target {
// 目标机器指令集
InstrInfo InstructionSet;
// 可用于此目标的AsmParser实例
list<AsmParser> AssemblyParsers = [DefaultAsmParser];
// 可用于此目标的AsmParserVariant实例
list<AsmParserVariant> AssemblyParserVariants = [DefaultAsmParserVariant];
// 可用于此目标的AsmWriter实例
list<AsmWriter> AssemblyWriters = [DefaultAsmWriter];
// 控制此目标是否允许寄存器的寄存器分配后重命名
int AllowRegisterRenaming = 0;
}
其中InstrInfo只能被实例化一次,以提供对目标计算机全局的参数。
class InstrInfo {
// 大小端
bit isLittleEndianEncoding = 0;
// TableGen会自动推算这个值,选项是临时迁移帮助。它会消失的
bit guessInstructionProperties = 1;
// TableGen的指令编码器生成器支持按名称和位置将操作数与位字段变量匹配。
//虽然首选按名称匹配,但这对于复杂操作数来说目前是不可能的,而且一些目标仍然会回复位置编码规则。
//当为这些目标生成解码器时,解码器生成器也必须使用位置编码规则。
//此选项是临时的;一旦TableGen解码器生成器更好地支持复杂操作数,
//并且目标不再使用位置编码操作数,它就会消失.
bit decodePositionallyEncodedOperands = 0;
//此选项是临时的;
//一旦TableGen解码器生成器对复杂操作数有了更好的支持,
//并且目标不再使用位置编码操作数
bit noNamedPositionallyEncodedOperands = 0;
}
SubtargetFeature
SubtargetFeature定义子目标处理器的特性
class SubtargetFeature<string n, string a, string v, string d,
list<SubtargetFeature> i = []> {
// 特性名称。由命令行(-matter=)确定适当的目标芯片
string Name = n;
// 要按特性设置的属性
string Attribute = a;
// 值要按特性设置的属性
string Value = v;
// 特性描述。由命令行(-matter=)显示帮助信息
string Desc = d;
// Implies - Features that this feature implies are present. If one of those
// features isn't set, then this one shouldn't be set either.
list<SubtargetFeature> Implies = i;
}
ProcessorModel
ProcessorModel:
//如果是ProcessorItineary,
//则ProcessorModel允许子目标指定更通用的ScheduleMachineModel。
//子目标将逐渐转向这种新的形式。
//尽管该类始终将NoItineries传递给Processor类,
//但ScheduleMachineModel仍然可以定义有效的行程
class ProcessorModel<string n, SchedMachineModel m, list<SubtargetFeature> f>
: Processor<n, NoItineraries, f> {
//NoItineraries是处理器可以在没有计划信息的情况下使用的标记。
//使用NoItineries的子目标可以绕过调度器昂贵的HazardRecognizer,因为不需要预订表
let SchedModel = m;
}
Processor:处理器芯片集,这些值表示调度器支持的每个芯片集。每个处理器定义都需要相应的指令行程
class Processor<string n, ProcessorItineraries pi, list<SubtargetFeature> f> {
//芯片集名称。由命令行(-mcpu=)确定适当的目标芯片
string Name = n;
// 调度和指令成本的机器模型
SchedMachineModel SchedModel = NoSchedModel;
// 目标处理器的调度信息
ProcessorItineraries ProcItin = pi;
// Features - list of
list<SubtargetFeature> Features = f;
}
SchedMachineModel
定义SchedMachineModel并为粗粒度指令成本模型提供基本属性。属性的默认值在MCSchedModel中定义。目标描述的SchedMachineModel中的“-1”值表示该属性不被目标覆盖
class SchedMachineModel {
int IssueWidth = -1; // 每个周期可安排的最大微操作
int MicroOpBufferSize = -1; // 可以缓冲的最大微操作.
int LoopMicroOpBufferSize = -1; // 最大的微操作,可以缓冲优化循环调度/执行
int LoadLatency = -1; // 加载访问缓存的周期
int HighLatency = -1; // “高延迟”操作周期的近似值.
int MispredictPenalty = -1; // 错误预测分支的额外循环
// 每周期资源表
ProcessorItineraries Itineraries = NoItineraries;
bit PostRAScheduler = 0; // Enable Post RegAlloc Scheduler pass.
// 如果子目标仅为具有调度类(行程类或SchedRW列表)的指令子集定义模型,
//并且可能实际为该子目标生成,则必须清除此位。
//否则,调度器将未建模的操作码视为错误。
//这应该只在初始启动时设置,
//否则将无法捕获由于修改指令定义而导致的模型中的简单错误
bit CompleteModel = 1;
// 指示我们应该对在同一个SchedMachineModel中定义相同指令的
//多个instrw进行完全重叠检查。
//修正:移除时,所有在树的目标是干净的全检查启用
bit FullInstRWOverlapCheck = 1;
// 由于新的ISA扩展(例如Pentium 4没有AVX)
//或实现(ARM/MIPS/PowerPC/SPARC软浮核),
//处理器可能只实现已发布的ISA的一部分。
//
//对于不支持某些特性的处理器,调度模型
//可以使用:
//
// let<Predicate> UnsupportedFeatures = [HaveA,..,HaveY];
//
//在构建LLVM时,如果指令的predicates字段中有任何列出的谓词,则跳过对调度信息的检查
list<Predicate> UnsupportedFeatures = [];
bit NoModel = 0; // 特殊标签,显示丢失的机器型号
}
Register
应该为目标机器中的每个寄存器定义一个Register实例
class Register<string n, list<string> altNames = []> {
string Namespace = "";
string AsmName = n;
list<string> AltNames = altNames;
// 与此寄存器重叠的寄存器列表。读取或修改此寄存器可能会读取或修改别名寄存器
list<Register> Aliases = [];
// 是该寄存器组成部分的寄存器列表。
//注意,这些是“即时”子寄存器,列表中的寄存器本身并不重叠。
//例:对于X86, EAX的SubRegs列表只包含[AX],而不是[AX, AH, AL]
list<Register> SubRegs = [];
// 对于子寄存器中的每个寄存器,指定用于寻址的SubRegIndex。
//子子寄存器索引自动继承自SubRegs
list<SubRegIndex> SubRegIndices = [];
// 对该寄存器有效的备用名称索引
list<RegAltNameIndex> RegAltNameIndices = [];
// gcc/gdb内部用来标识寄存器的数字。
//这些值可以通过在llvmgcc/gcc/config/<target>/目录下
//查找<target>.h文件并查找REGISTER_NAMES来确定。
//这些名称的顺序对应于gcc使用的枚举。
//值为-1表示gcc数未定义,而-2表示该模式/特点的寄存器数无效
list<int> DwarfNumbers = [];
// 与同类其他寄存器相比,使用此寄存器的指令的额外开销。
//寄存器分配器将尝试使用带有CostPerUse的寄存器来最小化指令数。
//这被x86-64和ARM Thumb目标所使用,其中一些寄存器需要更大的指令编码
int CostPerUse = 0;
// CoveredBySubRegs - When this bit is set, the value of this register is
// completely determined by the value of its sub-registers. For example, the
// x86 register AX is covered by its sub-registers AL and AH, but EAX is not
// covered by its sub-register AX.
bit CoveredBySubRegs = 0;
// HWEncoding - The target specific hardware encoding for this register.
bits<16> HWEncoding = 0;
bit isArtificial = 0;
}
RegisterClass
现在已经定义了所有寄存器,并且定义了寄存器之间的别名,要指定哪些寄存器属于哪些寄存器类RegisterClass。还根据寄存器分配器定义了寄存器的默认分配顺序
class RegisterClass<string namespace, list<ValueType> regTypes, int alignment,
dag regList, RegAltNameIndex idx = NoRegAltName>
: DAGOperand {
string Namespace = namespace;
// 寄存器大小/对齐信息,由HW模式参数化
RegInfoByHwMode RegInfos;
// 指定此寄存器类中寄存器的列表ValueType。
//注意,寄存器类中的所有寄存器必须具有相同的ValueTypes。
//这是一个列表,因为一些目标允许在同一个寄存器中存储不同的类型,
//例如总大小为128位的向量值,但项目的计数/大小不同,例如x86上的SSE
list<ValueType> RegTypes = regTypes;
// 以寄存器的位为单位指定溢出大小。默认值为0可以让tablgen选择合适的大小
int Size = 0;
// 指定寄存器在存储或加载到内存时所需的对齐方式
int Alignment = alignment;
// 此值用于指定在此寄存器类中的两个寄存器之间复制值的代价。
//默认值是1,这意味着它需要一条指令来执行复制。
//负值意味着复制非常昂贵或不可能
int CopyCost = 1;
// 指定哪些寄存器在这个类中。
//如果没有指定allocation_order_*方法,这也定义了寄存器分配器使用的分配顺序
dag MemberList = regList;
// 打印此寄存器类的操作数时使用的备用寄存器名。
//寄存器类中的每个寄存器对于给定的索引必须有一个有效的替代名称
RegAltNameIndex altNameIndex = idx;
// 指定寄存器类可用于虚拟寄存器和寄存器分配。
//一些寄存器类只用于模拟指令操作数约束,并且应该有isAllocatable = 0
bit isAllocatable = 1;
// 可供选择的分配顺序列表。
//默认顺序是MemberList本身,这对于大多数目标来说已经足够好了,
//因为寄存器分配器会自动删除保留的寄存器,并将被调用者保存的寄存器移到末尾
list<dag> AltOrders = [];
// 在给定的机器函数中选择要使用的分配顺序的函数体。代码将像这样插入到函数中:
//
// static inline unsigned f(const MachineFunction &MF) { ... }
//
// 该函数应该返回0以选择MemberList定义的默认顺序,
//返回1以选择第一个AltOrders条目,等等.
code AltOrderSelect = [{}];
// 使用贪婪启发式方法为寄存器分配器指定分配优先级。
//优先级高的类首先被分配。
//这很有用,因为有时先将寄存器分配给高度受限的类是有益的。
//该值必须在[0,63]范围内.
int AllocationPriority = 0;
// 在匹配失败错误消息中引用此操作数时要显示的诊断类型。
//如果为空,将使用默认的Match_InvalidOperand诊断类型。
//如果这是"<name>",将生成一个Match_<name> enum值,并用于该操作数类型。
//目标程序集解析器负责将其转换为面向用户的诊断消息.
string DiagnosticType = "";
// 在为此寄存器类提供无效值时发出的诊断消息,当此寄存器类被程序集操作数使用时发出。
//如果该参数为非空,则将生成匿名诊断类型enum值,程序集匹配器将提供从诊断类型映射到消息字符串的函数.
string DiagnosticString = "";
}
Instruction
对应于Target/TargetInstrInfo.h文件中的c++类
class Instruction : InstructionEncoding {
string Namespace = "";
dag OutOperandList; // 包含MI def操作数列表的dag
dag InOperandList; // 包含MI use操作数列表的dag
string AsmString = ""; // 用于打印指令的.s格式
// 允许通过HwMode指定一个规范的InstructionEncoding。如果非空,则忽略该指令的Inst成员
EncodingByHwMode EncodingInfos;
// 如果知道DAG模式,则设置为此指令的DAG模式,否则为未初始化
list<dag> Pattern;
// follow状态最终会自动从指令模式中推断出来
list<Register> Uses = []; // 默认不使用非操作数寄存器
list<Register> Defs = []; // 默认为不修改非操作数寄存器
// 将转换为isel匹配代码的谓词列表
list<Predicate> Predicates = [];
// 编码指令的大小,如果不能从操作码确定大小,则为零
int Size = 0;
// 代码大小,用于指令选择
// FIXME: What does this actually mean?
int CodeSize = 0;
// 增加了匹配模式的复杂性
int AddedComplexity = 0;
// 指示这是否是一个应被legalized/regbankselected/selected的pre-isel操作码
bit isPreISelOpcode = 0;
// 下面这些位捕获有关指令高级语义的信息
bit isReturn = 0; // 这个指令是返回指令吗?
bit isBranch = 0; // 这条指令是分支指令吗?
bit isEHScopeReturn = 0; // 这个指令结束EH作用域了吗?
bit isIndirectBranch = 0; // 这个指令是间接分支吗?
bit isCompare = 0; // 这个指令是比较指令吗?
bit isMoveImm = 0; // 这个指令是移动立即数指令吗?
bit isMoveReg = 0; // 这个指令是移动寄存器指令吗?
bit isBitcast = 0; // 这条指令是Bitcast指令吗?
bit isSelect = 0; // 这条指令是选择指令吗?
bit isBarrier = 0; // 控制流可以通过这条指令吗?
bit isCall = 0; // 这个指令是调用指令吗?
bit isAdd = 0; // 这个指令是加法指令吗?
bit isTrap = 0; // 这个指令是陷阱指令吗?
bit canFoldAsLoad = 0; // 这个可以折叠成一个简单的内存操作数吗?
bit mayLoad = ?; // 可能读取内存吗?
bit mayStore = ?; // 可能写入内存吗?
bit mayRaiseFPException = 0; // 这会引发浮点异常吗?
bit isConvertibleToThreeAddress = 0; // 这个2地址指令能提升吗?
bit isCommutable = 0; // 这个3操作数指令是可替代的吗?
bit isTerminator = 0; // 这是基本块的终止符的一部分吗?
bit isReMaterializable = 0; // 这个指令是可重新具体化的吗?
bit isPredicable = 0; // 1表示该指令是可预测的,即使它没有任何tablegen可以识别为谓词的操作数
bit isUnpredicable = 0; // 1表示该指令是不可预测的,即使它没有一个谓词操作数
bit hasDelaySlot = 0; // 这条指令有延迟槽吗?
bit usesCustomInserter = 0; // 伪指令需要特殊帮助。
bit hasPostISelHook = 0; // isel后被目标钩*调整*
bit hasCtrlDep = 0; // 这个指令r/w控制流链吗?
bit isNotDuplicable = 0; // 复制此指令是否不安全?
bit isConvergent = 0; // 这个指令收敛吗?
bit isAsCheapAsAMove = 0; // 与move指令一样便宜(或更便宜)。
bit hasExtraSrcRegAllocReq = 0; // 来源有特殊的分配要求?
bit hasExtraDefRegAllocReq = 0; // Defs有特殊的regalloc要求?
bit isRegSequence = 0; // 这个指令是一种reg序列吗?如果是这样,请确保重写TargetInstrInfo::getRegSequenceLikeInputs
bit isPseudo = 0; // 这个指令是伪指令吗?如果是这样,将没有[MC]CodeEmitter的编码信息
bit isExtractSubreg = 0; // 这个指令是一种提取subreg吗?如果是这样,请确保覆盖TargetInstrInfo::getExtractSubregLikeInputs.
bit isInsertSubreg = 0; // 这个指令是一种插入subreg吗?如果是这样,请确保覆盖TargetInstrInfo::getInsertSubregLikeInputs.
bit variadicOpsAreDefs = 0; // 可变操作数定义吗?
// 该指令是否具有未被该指令的任何操作数或其他标志捕获的副作用?
bit hasSideEffects = ?;
// 这条指令是一条“真实的”指令(具有不同的机器编码),还是一条用于码原建模目的的伪指令。
// FIXME:目前,这与上面的isPseudo不同,因为只有代码生成的指令仍然可以(并且经常这样做)与它们相关的编码信息。
//一旦我们将它们全部迁移到真正的伪指令(在打印机/发射器之前降级为真正的指令),
//我们就可以删除这个属性,只使用isPseudo。
//
//预期用途是:
// isPseudo:没有编码信息,最迟在降低到MCInst时应该展开.
//
// isCodeGenOnly:具有编码信息,可以不加修改地传递给CodeEmitter,
//但复制规范指令定义的编码,在构造汇编匹配表时应忽略
bit isCodeGenOnly = 0;
// 这个指令是供汇编分析器使用的伪指令吗
bit isAsmParserOnly = 0;
// 这条指令不需要查询调度延迟,因此即使对于一个完整的调度模型也不需要调度信息。
bit hasNoSchedulingInfo = 0;
InstrItinClass Itinerary = NoItinerary;// 用于调度的执行步骤。
// 来自TargetSchedule.td的调度信息。
list<SchedReadWrite> SchedRW;
string Constraints = ""; // 操作约束,例如$src = $dst。
///操作数名称的列表(例如:"$op1,$op2")不应该被编码到输出machineinstr中。
string DisableEncoding = "";
string PostEncoderMethod = "";
/// 有针对性的Flag。这成为TargetInstrDesc中的TSFlags字段.
bits<64> TSFlags = 0;
///@name Assembler Parser Support
///@{
string AsmMatchConverter = "";
/// 启用TableGen为三个操作数指令自动生成两个操作数匹配器insta -alias。例如,ARM指令“add r3, r3, r5”可以写成“add r3, r5”。该约束与绑定操作数约束的形式相同。例如"$Rn = $Rd".
string TwoOperandAliasConstraint = "";
/// 用于此指令的汇编程序变体名称。
//如果指定了,那么指令将只在匹配表中为这个变体。
//如果没有指定,则将根据AsmString确定汇编变量
string AsmVariantName = "";
///@}
/// 如果设置了,该指令的操作数索引可以通过由TableGen生成的getNamedOperandIdx()函数查询.
bit UseNamedOperandTable = 0;
/// FastISel应该忽略这个指令。
//对于某些isa,它们具有映射到相同ISD操作码、值类型操作数和指令选择谓词的指令。
//FastISel不能处理这种情况,但SelectionDAG可以
bit FastISelShouldIgnore = 0;
}
Operand
Operand Types:它们提供了目标可以使用的内置操作数类型。目标可以根据需要提供自己的操作数类型,但RISC目标不需要这样做
class Operand<ValueType ty> : DAGOperand {
ValueType Type = ty;
string PrintMethod = "printOperand";
string EncoderMethod = "";
bit hasCompleteDecoder = 1;
string OperandType = "OPERAND_UNKNOWN";
dag MIOperandInfo = (ops);
//可选的,操作const MCOperand &MCOp的代码片段,并返回bool值,
//以指示MCOp的值对于操作数的特定子类是否有效
code MCOperandPredicate;
// ParserMatchClass - 这种类型的操作数适合的“match类”。
//Match类用于定义指令被匹配的顺序,以确保哪些指令被匹配是确定的.
//
// 特定于目标的解析器必须能够将已解析的操作数分类为惟一的类,该类不能与任何其他类部分重叠。
//它可以匹配其他类的子集,在这种情况下,AsmOperandClass应该将另一个操作数声明为它的超类之一.
AsmOperandClass ParserMatchClass = ImmAsmOperand;
}
SDNode
class SDNode<string opcode, SDTypeProfile typeprof,
list<SDNodeProperty> props = [], string sdclass = "SDNode">
: SDPatternOperator {
string Opcode = opcode;
string SDClass = sdclass;
let Properties = props;
SDTypeProfile TypeProfile = typeprof;
}
Pattern
Patterns是目标指令选择DAG实际匹配的内容。目标定义的指令在大多数情况下隐式地定义了模式,但是当一个操作由指令序列定义时,模式也可以显式地添加(例如,在不支持像GPRs一样大的立即值的RISC目标上加载一个大的立即值)
class Pattern<dag patternToMatch, list<dag> resultInstrs> {
dag PatternToMatch = patternToMatch;
list<dag> ResultInstrs = resultInstrs;
list<Predicate> Predicates = []; // See class Instruction in Target.td.
int AddedComplexity = 0; // See class Instruction in Target.td.
}
PatFrags
模式片段是可重用的dag块,用于匹配特定的内容。它们可以接受参数,并具有控制它们是否匹配的c++谓词。它们的目的是使通用指令的模式更加紧凑和可读。
PatFrags——表示一组模式片段。每个片段都可以匹配DAG上的某些内容,从单个节点到多个嵌套的其他片段。如果单个片段中的任何一个匹配,则整个片段集都匹配。这允许匹配和“溢出添加”和具有相同片段集的常规“添加”。
class PatFrags<dag ops, list<dag> frags, code pred = [{}],
SDNodeXForm xform = NOOP_SDNodeXForm> : SDPatternOperator {
dag Operands = ops;
list<dag> Fragments = frags;
code PredicateCode = pred;
code GISelPredicateCode = [{}];
code ImmediateCode = [{}];
SDNodeXForm OperandTransform = xform;
// 当设置此值时,PredicateCode可以引用一个常量操作数向量,
//其中包含DAG捕获的节点,顺序由上面的操作数字段列出.
//
// 当fragment涉及关联/交换操作符时,这很有用:
//即使匹配了片段的重新关联/交换变量,一段代码也可以很容易地引用所有操作数.
bit PredicateCodeUsesOperands = 0;
// 定义几个预先打包的谓词。这有助于GlobalISel在许多常见情况下从SelectionDAG导入现有规则。
//它们将在pred中的代码之前进行测试,并且不能在ImmLeaf及其子类中使用
// Load所需的预打包谓词吗?
bit IsLoad = ?;
// Store所需的预打包谓词吗?
bit IsStore = ?;
// 是原子操作所需的预打包谓词吗?
bit IsAtomic = ?;
// cast<LoadSDNode>(N)->getAddressingMode() == ISD::UNINDEXED;
// cast<StoreSDNode>(N)->getAddressingMode() == ISD::UNINDEXED;
bit IsUnindexed = ?;
// cast<LoadSDNode>(N)->getExtensionType() != ISD::NON_EXTLOAD
bit IsNonExtLoad = ?;
// cast<LoadSDNode>(N)->getExtensionType() == ISD::EXTLOAD;
bit IsAnyExtLoad = ?;
// cast<LoadSDNode>(N)->getExtensionType() == ISD::SEXTLOAD;
bit IsSignExtLoad = ?;
// cast<LoadSDNode>(N)->getExtensionType() == ISD::ZEXTLOAD;
bit IsZeroExtLoad = ?;
// !cast<StoreSDNode>(N)->isTruncatingStore();
// cast<StoreSDNode>(N)->isTruncatingStore();
bit IsTruncStore = ?;
// cast<MemSDNode>(N)->getAddressSpace() ==
// If this empty, accept any address space.
list<int> AddressSpaces = ?;
// cast<MemSDNode>(N)->getAlignment() >=
// If this is empty, accept any alignment.
int MinAlignment = ?;
// cast<AtomicSDNode>(N)->getOrdering() == AtomicOrdering::Monotonic
bit IsAtomicOrderingMonotonic = ?;
// cast<AtomicSDNode>(N)->getOrdering() == AtomicOrdering::Acquire
bit IsAtomicOrderingAcquire = ?;
// cast<AtomicSDNode>(N)->getOrdering() == AtomicOrdering::Release
bit IsAtomicOrderingRelease = ?;
// cast<AtomicSDNode>(N)->getOrdering() == AtomicOrdering::AcquireRelease
bit IsAtomicOrderingAcquireRelease = ?;
// cast<AtomicSDNode>(N)->getOrdering() == AtomicOrdering::SequentiallyConsistent
bit IsAtomicOrderingSequentiallyConsistent = ?;
// isAcquireOrStronger(cast<AtomicSDNode>(N)->getOrdering())
// !isAcquireOrStronger(cast<AtomicSDNode>(N)->getOrdering())
bit IsAtomicOrderingAcquireOrStronger = ?;
// isReleaseOrStronger(cast<AtomicSDNode>(N)->getOrdering())
// !isReleaseOrStronger(cast<AtomicSDNode>(N)->getOrdering())
bit IsAtomicOrderingReleaseOrStronger = ?;
// cast<LoadSDNode>(N)->getMemoryVT() == MVT::<VT>;
// cast<StoreSDNode>(N)->getMemoryVT() == MVT::<VT>;
ValueType MemoryVT = ?;
// cast<LoadSDNode>(N)->getMemoryVT().getScalarType() == MVT::<VT>;
// cast<StoreSDNode>(N)->getMemoryVT().getScalarType() == MVT::<VT>;
ValueType ScalarMemoryVT = ?;
}
PatFrag:一个版本的PatFrags只匹配一个片段
class PatFrag<dag ops, dag frag, code pred = [{}],
SDNodeXForm xform = NOOP_SDNodeXForm>
: PatFrags<ops, [frag], pred, xform>;
ComplexPattern
ComplexPattern,例如X86寻址模式,需要c++中的模式匹配代码。NumOperands是select函数返回的操作数的数量;SelectFunc是用于模式匹配最大值的函数名。pattern;RootNodes是要匹配的sub-dags的可能根节点的列表。例如,X86寻址模式- def addr: ComplexPattern<4, “SelectAddr”, [add]>;
class ComplexPattern<ValueType ty, int numops, string fn,
list<SDNode> roots = [], list<SDNodeProperty> props = [],
int complexity = -1> {
ValueType Ty = ty;
int NumOperands = numops;
string SelectFunc = fn;
list<SDNode> RootNodes = roots;
list<SDNodeProperty> Properties = props;
int Complexity = complexity;
}
CallingConv
CallingConv——它的一个实例用于定义目标支持的每个调用约定
class CallingConv<list<CCAction> actions> {
list<CCAction> Actions = actions;
/// 如果为真,此调用约定将在llvm名称空间中作为外部可见的形式发出,而不是作为静态函数
bit Entry = 0;
bit Custom = 0;
}
CalleeSavedRegs
CalleeSavedRegs -为给定调用约定保存的被调用者寄存器列表。当为保存的寄存器分配堆栈插槽时,prologepiloginsert使用寄存器的顺序。
对于每个CalleeSavedRegs def, TableGen将发出一个用于从getCalleeSavedRegs()返回的FOO_SaveList数组,以及一个适合从getCallPreservedMask()返回的FOO_RegMask位掩码。
class CalleeSavedRegs<dag saves> {
dag SaveList = saves;
// 在函数调用中也保留的寄存器,但不应该包含在生成的FOO_SaveList数组中。
//这些寄存器将包含在FOO_RegMask位掩码中。这可以用于自动保存的寄存器,如SPARC寄存器窗口.
dag OtherPreserved;
}