3.5.2. 代码生成
这部分代码输出到文件X86GenInstrInfo.inc中。
3.5.2.1. 枚举常量
从InstrInfoEmitter的构造函数返回到EmitInstrInfo,接下来调用的InstrInfoEmitter::run方法来输出相关的代码。
342 void InstrInfoEmitter::run (raw_ostream &OS) {
343 emitSourceFileHeader("Target Instruction Enum Values", OS);
344 (OS);
一如既往,首先需要输出枚举常量的定义。583行的getInstructionsByEnumValue方法以名字序返回指令的CodeGenInstruction对象集(除了部分有指定次序的指令,参考一节)。
567 void InstrInfoEmitter::emitEnums (raw_ostream &OS) {
568
569 OS << "\n#ifdef GET_INSTRINFO_ENUM\n";
570 OS << "#undef GET_INSTRINFO_ENUM\n";
571
572 OS << "namespace llvm {\n\n";
573
574 CodeGenTarget Target(Records);
575
576 // We must emit the PHI opcode first...
577 std::string Namespace = Target.getInstNamespace();
578
579 if (Namespace.empty())
580 PrintFatalError("No instructions defined!");
581
582 const std::vector< const CodeGenInstruction*> &NumberedInstructions =
583 Target. getInstructionsByEnumValue ();
584
585 OS << "namespace " << Namespace << " {\n";
586 OS << " enum {\n";
587 unsigned Num = 0;
588 for ( const CodeGenInstruction *Inst : NumberedInstructions)
589 OS << " " << Inst->TheDef->getName() << "\t= " << Num++ << ",\n";
590 OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << "\n";
591 OS << " };\n\n";
592 OS << "namespace Sched {\n";
593 OS << " enum {\n";
594 Num = 0;
595 for ( const auto &Class : SchedModels.explicit_classes())
596 OS << " " << Class.Name << "\t= " << Num++ << ",\n";
597 OS << " SCHED_LIST_END = " << SchedModels.numInstrSchedClasses() << "\n";
598 OS << " };\n";
599 OS << "} // End Sched namespace\n";
600 OS << "} // End " << Namespace << " namespace\n";
601 OS << "} // End llvm namespace \n";
602
603 OS << "#endif // GET_INSTRINFO_ENUM\n\n";
604 }
585~591行输出代表指令的枚举常量,其输出结果是:
namespace X86 {
enum {
PHI = 0,
INLINEASM = 1,
CFI_INSTRUCTION = 2,
EH_LABEL = 3,
GC_LABEL = 4,
…
XSHA256 = 12098,
XSTORE = 12099,
XTEST = 12100,
INSTRUCTION_LIST_END = 12101
};
X86家族的指令数真是令人印象深刻(这意味着有X86家族的各种处理器一共贡献了12100个Instruction定义,为此X86使用了20个指令描述文件)。相比之下,ARM家族的指令数是2822条。
CodeGenSchedModels的容器SchedClasses保存了已知的所有调度类型,CodeGenSchedClass的来源有两种,第一种来自指令定义,包括方法从InstRW定义直接得到的类型,它们优先保存在SchedClasses容器,其他推导自ItinRW,InstRW及指令定义中的SchedVariant定义。CodeGenSchedModels成员NumInstrSchedClasses记录了第一种CodeGenSchedClass对象的个数(即597行numInstrSchedClasses方法所返回的值)。因此,592~600行输出第一种调度类型的枚举值。
namespace Sched {
enum {
NoInstrModel = 0,
IIC_AAA_WriteMicrocoded = 1,
IIC_AAD_WriteMicrocoded = 2,
IIC_AAM_WriteMicrocoded = 3,
IIC_AAS_WriteMicrocoded = 4,
…
ANDNPDrm_ANDNPSrm_ANDPDrm_ANDPSrm_ORPDrm_ORPSrm_VANDNPDYrm_VANDNPDrm_VANDNPSYrm_VANDNPSrm_VANDPDYrm_VANDPDrm_VANDPSYrm_VANDPSrm_VORPDYrm_VORPDrm_VORPSYrm_VORPSrm_VXORPDYrm_VXORPDrm_VXORPSYrm_VXORPSrm_XORPDrm_XORPSrm = 945,
VZEROUPPER = 946,
VZEROALL = 947,
LDMXCSR_VLDMXCSR = 948,
STMXCSR_VSTMXCSR = 949,
SCHED_LIST_END = 950
};
} // End Sched namespace
} // End X86 namespace
在这些定义里我们能看到一个来自InstrRW定义的调度类型,它枚举值是945。这样调度类型的名字是由对应InstRW定义所援引的指令名合成而来。另外,枚举值为1的调度类型则是一个通过指令定义得到的调度类型,这些指令使用执行步骤IIC_AAA,资源使用情况由WriteMicrocoded描述。
3.5.2.2. 操作数描述数组
在InstrInfoEmitter::run接下来输出所谓的“目标机器指令描述符”。在355行获取目标机器的指令集描述,对X86目标机器就是定义X86InstrInfo(它与基类的内容是一致的)。
InstrInfoEmitter::run(续)
346 emitSourceFileHeader("Target Instruction Descriptors", OS);
347
348 OS << "\n#ifdef GET_INSTRINFO_MC_DESC\n";
349 OS << "#undef GET_INSTRINFO_MC_DESC\n";
350
351 OS << "namespace llvm {\n\n";
352
353 CodeGenTarget &Target = CDP.getTargetInfo();
354 const std::string &TargetName = Target.getName();
355 Record *InstrInfo = Target.getInstructionSet();
356
357 // Keep track of all of the def lists we have emitted already.
358 std::map<std::vector<Record*>, unsigned> EmittedLists;
359 unsigned ListNumber = 0;
360
361 // Emit all of the instruction's implicit uses and defs.
362 for ( const CodeGenInstruction *II : Target.instructions()) {
363 Record *Inst = II->TheDef;
364 std::vector<Record*> Uses = Inst->getValueAsListOfDefs("Uses");
365 if (!Uses.empty()) {
366 unsigned &IL = EmittedLists[Uses];
367 if (!IL)(Uses, IL = ++ListNumber, OS);
368 }
369 std::vector<Record*> Defs = Inst->getValueAsListOfDefs("Defs");
370 if (!Defs.empty()) {
371 unsigned &IL = EmittedLists[Defs];
372 if (!IL) PrintDefList(Defs, IL = ++ListNumber, OS);
373 }
374 }
375
376 OperandInfoMapTy OperandInfoIDs;
377
378 // Emit all of the operand info records.
379 (OS, OperandInfoIDs);
在指令定义中Uses与Defs分别表示该指令缺省使用及改写的非操作数寄存器。首先通过下面的PrintDefList方法输出一系列ImplicitList为前缀的数组。而容器EmittedLists则关联了这些寄存器与所输出的数组(367行)。
75 static void PrintDefList ( const std::vector<Record*> &Uses,
76 unsigned Num, raw_ostream &OS) {
77 OS << "static const uint16_t ImplicitList" << Num << "[] = { ";
78 for (unsigned i = 0, e = Uses.size(); i != e; ++i)
79 OS << getQualifiedName(Uses[i]) << ", ";
80 OS << "0 };\n";
81 }
对X86目标机器,当前版本一共会输出95个数组,我们只给出几个例子,不一一列举。它们将作为后面输出的X86Insts数组元素的成员。
static const uint16_t ImplicitList1[] = { X86::FPSW, 0 };
static const uint16_t ImplicitList2[] = { X86::AX, X86::EFLAGS, 0 };
static const uint16_t ImplicitList3[] = { X86::EFLAGS, 0 };
static const uint16_t ImplicitList4[] = { X86::EAX, X86::EFLAGS, 0 };
376行的OperandInfoMapTy是std::map<std::vector<std::string>, unsigned>的typedef,它的实例OperandInfoIDs作为379行EmitOperandInfo方法的一个参数。在下面的176行看到,这个容器的第一项是不使用的。
172 void InstrInfoEmitter::EmitOperandInfo (raw_ostream &OS,
173 OperandInfoMapTy &OperandInfoIDs) {
174 // ID #0 is for no operand info.
175 unsigned OperandListNum = 0;
176 OperandInfoIDs[std::vector<std::string>()] = ++OperandListNum;
177
178 OS << "\n";
179 const CodeGenTarget &Target = CDP.getTargetInfo();
180 for ( const CodeGenInstruction *Inst : Target.instructions()) {
181 std::vector<std::string> OperandInfo =(*Inst);
182 unsigned &N = OperandInfoIDs[OperandInfo];
183 if (N != 0) continue ;
184
185 N = ++OperandListNum;
186 OS << "static const MCOperandInfo OperandInfo" << N << "[] = { ";
187 for ( const std::string &Info : OperandInfo)
188 OS << "{ " << Info << " }, ";
189 OS << "};\n";
190 }
191 }
在180行遍历所有指令定义的CodeGenInstruction 实例,以该实例为参数,在181行调用下面的GetOperandInfo方法来获取描述类型MCOperandInfo的字符串。类型MCOperandInfo是这样定义的:
56 class MCOperandInfo {
57 public :
58 /// \brief This specifies the register class enumeration of the operand
59 /// if the operand is a register. If isLookupPtrRegClass is set, then this is
60 /// an index that is passed to TargetRegisterInfo::getPointerRegClass(x) to
61 /// get a dynamic register class.
62 int16_t RegClass;
63
64 /// \brief These are flags from the MCOI::OperandFlags enum.
65 uint8_t Flags;
66
67 /// \brief Information about the type of the operand.
68 uint8_t OperandType;
69 /// \brief The lower 16 bits are used to specify which constraints are set.
70 /// The higher 16 bits are used to specify the value of constraints (4 bits
71 /// each).
72 uint32_t Constraints;
73
74 /// \brief Set if this operand is a pointer value and it requires a callback
75 /// to look up its register class.
76 bool isLookupPtrRegClass() const {
77 return Flags & (1 << MCOI::LookupPtrRegClass);
78 }
79
80 /// \brief Set if this is one of the operands that made up of the predicate
81 /// operand that controls an isPredicable() instruction.
82 bool isPredicate() const { return Flags & (1 << MCOI::Predicate); }
83
84 /// \brief Set if this operand is a optional def.
85 bool isOptionalDef() const { return Flags & (1 << MCOI::OptionalDef); }
86 };
这是MC用来描述指令操作数的定义,也是我们需要为每个指令操作数所输出的类型。另外,在InstrInfoEmitter::GetOperandInfo用到的嵌套类CGIOperandList::OperandInfo定义如下:
65 struct OperandInfo {
66 /// Rec - The definition this operand is declared as.
67 ///
68 Record *Rec;
69
70 /// Name - If this operand was assigned a symbolic name, this is it,
71 /// otherwise, it's empty.
72 std::string Name;
73
74 /// PrinterMethodName - The method used to print operands of this type in
75 /// the asmprinter.
76 std::string PrinterMethodName;
77
78 /// EncoderMethodName - The method used to get the machine operand value
79 /// for binary encoding. "getMachineOpValue" by default.
80 std::string EncoderMethodName;
81
82 /// OperandType - A value from MCOI::OperandType representing the type of
83 /// the operand.
84 std::string OperandType;
85
86 /// MIOperandNo - Currently (this is meant to be phased out), some logical
87 /// operands correspond to multiple MachineInstr operands. In the X86
88 /// target for example, one address operand is represented as 4
89 /// MachineOperands. Because of this, the operand number in the
90 /// OperandList may not match the MachineInstr operand num. Until it
91 /// does, this contains the MI operand index of this operand.
92 unsigned MIOperandNo;
93 unsigned MINumOperands; // The number of operands.
94
95 /// DoNotEncode - Bools are set to true in this vector for each operand in
96 /// the DisableEncoding list. These should not be emitted by the code
97 /// emitter.
98 std::vector<bool> DoNotEncode;
99
100 /// MIOperandInfo - Default MI operand type. Note an operand may be made
101 /// up of multiple MI operands.
102 DagInit *MIOperandInfo;
103
104 /// Constraint info for this operand. This operand can have pieces, so we
105 /// track constraint info for each.
106 std::vector<ConstraintInfo> Constraints;
107
108 OperandInfo(Record *R, const std::string &N, const std::string &PMN,
109 const std::string &EMN, const std::string &OT, unsigned MION,
110 unsigned MINO, DagInit *MIOI)
111 : Rec(R), Name(N), PrinterMethodName(PMN), EncoderMethodName(EMN),
112 OperandType(OT), MIOperandNo(MION), MINumOperands(MINO),
113 MIOperandInfo(MIOI) {}
114
115
116 /// getTiedOperand - If this operand is tied to another one, return the
117 /// other operand number. Otherwise, return -1.
118 int getTiedRegister() const {
119 for (unsigned j = 0, e = Constraints.size(); j != e; ++j) {
120 const CGIOperandList::ConstraintInfo &CI = Constraints[j];
121 if (CI.isTied()) return CI.getTiedOperand();
122 }
123 return -1;
124 }
125 };
下面91行的循环变量Op的类型也是CGIOperandList::OperandInfo。我们已经知道有些复杂指令使用的操作数是包含子操作数的。这些子操作数在.td文件里构成了以ops为操作符的一个dag值。在解析.td文件时,这个dag值被记录在相应OperandInfo对象的MIOperandInfo成员(上面102行)。缺省地,MIOperandInfo在.td文件里是(ops),满足下面的102条件。而对于具有子操作数的情形,这些子操作数都被记录到容器OperandList里,这时OperandInfo定义里68行的Record被借用来记录子操作数的Record对象。
87 std::vector<std::string>
88 InstrInfoEmitter::GetOperandInfo ( const CodeGenInstruction &Inst) {
89 std::vector<std::string> Result;
90
91 for ( auto &Op : Inst.Operands) {
92 // Handle aggregate operands and normal operands the same way by expanding
93 // either case into a list of operands for this op.
94 std::vector<CGIOperandList::OperandInfo> OperandList;
95
96 // This might be a multiple operand thing. Targets like X86 have
97 // registers in their multi-operand operands. It may also be an anonymous
98 // operand, which has a single operand, but no declared class for the
99 // operand.
100 DagInit *MIOI = Op.MIOperandInfo;
101
102 if (!MIOI || MIOI->getNumArgs() == 0) {
103 // Single, anonymous, operand.
104 OperandList.push_back(Op);
105 } else {
106 for (unsigned j = 0, e = Op.MINumOperands; j != e; ++j) {
107 OperandList.push_back(Op);
108
109 Record *OpR = cast<DefInit>(MIOI->getArg(j))->getDef();
110 OperandList.back().Rec = OpR;
111 }
112 }
113
114 for (unsigned j = 0, e = OperandList.size(); j != e; ++j) {
115 Record *OpR = OperandList[j].Rec;
116 std::string Res;
117
118 if (OpR->isSubClassOf("RegisterOperand"))
119 OpR = OpR->getValueAsDef("RegClass");
120 if (OpR->isSubClassOf("RegisterClass"))
121 Res += getQualifiedName(OpR) + "RegClassID, ";
122 else if (OpR->isSubClassOf("PointerLikeRegClass"))
123 Res += utostr(OpR->getValueAsInt("RegClassKind")) + ", ";
124 else
125 // -1 means the operand does not have a fixed register class.
126 Res += "-1, ";
127
128 // Fill in applicable flags.
129 Res += "0";
130
131 // Ptr value whose register class is resolved via callback.
132 if (OpR->isSubClassOf("PointerLikeRegClass"))
133 Res += "|(1<<MCOI::LookupPtrRegClass)";
134
135 // Predicate operands. Check to see if the original unexpanded operand
136 // was of type PredicateOp.
137 if (Op.Rec->isSubClassOf("PredicateOp"))
138 Res += "|(1<<MCOI::Predicate)";
139
140 // Optional def operands. Check to see if the original unexpanded operand
141 // was of type OptionalDefOperand.
142 if (Op.Rec->isSubClassOf("OptionalDefOperand"))
143 Res += "|(1<<MCOI::OptionalDef)";
144
145 // Fill in operand type.
146 Res += ", ";
147 assert (!Op.OperandType.empty() && "Invalid operand type.");
148 Res += Op.OperandType;
149
150 // Fill in constraint info.
151 Res += ", ";
152
153 const CGIOperandList::ConstraintInfo &Constraint =
154 Op.Constraints[j];
155 if (Constraint.isNone())
156 Res += "0";
157 else if (Constraint.isEarlyClobber())
158 Res += "(1 << MCOI::EARLY_CLOBBER)";
159 else {
160 assert (Constraint.isTied());
161 Res += "((" + utostr(Constraint.getTiedOperand()) +
162 " << 16) | (1 << MCOI::TIED_TO))";
163 }
164
165 Result.push_back(Res);
166 }
167 }
168
169 return Result;
170 }
接下来遍历OperandList容器,根据下列枚举常量定义,输出对应的字符串。
31 namespace MCOI {
32 // Operand constraints
33 enum OperandConstraint {
34 TIED_TO = 0, // Must be allocated the same register as.
35 EARLY_CLOBBER // Operand is an early clobber register operand
36 };
37
38 /// \brief These are flags set on operands, but should be considered
39 /// private, all access should go through the MCOperandInfo accessors.
40 /// See the accessors for a description of what these are.
41 enum OperandFlags { LookupPtrRegClass = 0, Predicate, OptionalDef };
42
43 /// \brief Operands are tagged with one of the values of this enum.
44 enum OperandType {
45 OPERAND_UNKNOWN = 0,
46 OPERAND_IMMEDIATE = 1,
47 OPERAND_REGISTER = 2,
48 OPERAND_MEMORY = 3,
49 OPERAND_PCREL = 4,
50 OPERAND_FIRST_TARGET = 5
51 };
52 }
从GetOperandInfo回到EmitOperandInfo,返回值OperandInfo进而保存在OperandInfoIDs容器里作为键值,而与键值对应的则是输出MCOperandInfo的序号(0是不使用的)。因此,我们得到以下的输出(仅列举作为例子)。
static const MCOperandInfo OperandInfo2[] = { { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 }, };
static const MCOperandInfo OperandInfo3[] = { { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 }, };
static const MCOperandInfo OperandInfo4[] = { { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, ((0 << 16) | (1 << MCOI::TIED_TO)) }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 }, };
static const MCOperandInfo OperandInfo5[] = { { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, };
static const MCOperandInfo OperandInfo12[] = { { X86::RFP32RegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { X86::RFP32RegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, };
操作数由几个子操作数构成,这个数组就有多长。另外,注意EmitOperandInfo的180行,Target.instructions()实际上是返回由getInstructionsByEnumValue方法给出的迭代器范围,因此我们遍历指令的次序还是相同的。
对X86目标机器来说,这次输出的数组有821个,这也是.td文件里给出的Operand定义的个数。它们也将作为后面输出的X86Insts数组元素的成员。
以上所述就是小编给大家介绍的《LLVM学习笔记(38)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【每日笔记】【Go学习笔记】2019-01-04 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-02 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-07 Codis笔记
- Golang学习笔记-调度器学习
- Vue学习笔记(二)------axios学习
- 算法/NLP/深度学习/机器学习面试笔记
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Linux命令行大全
绍茨 (William E.Shotts) / 郭光伟、郝记生 / 人民邮电出版社 / 2013-3-1 / 69.00元
《Linux命令行大全》主要介绍Linux命令行的使用,循序渐进,深入浅出,引导读者全面掌握命令行的使用方法。 《Linux命令行大全》分为四部分。第一部分开始了对命令行基本语言的学习之旅,包括命令结构、文件系统的导引、命令行的编辑以及关于命令的帮助系统和使用手册。第二部分主要讲述配置文件的编辑,用于计算机操作的命令行控制。第三部分讲述了从命令行开始执行的常规任务。类UNIX操作系统,比如L......一起来看看 《Linux命令行大全》 这本书的介绍吧!