3.7. 描述调用惯例的数据结构
选项“-gen-callingconv”用于生成处理函数调用惯例的代码。调用惯例是函数调用者与被调用者之间关于参数及返回值传递方式的一个共识。存在多个调用惯例,以适合各种机器架构。LLVM目前已经基本能完全通过TableGen生成处理调用惯例的代码。
3.7.1. TD的基本类型与描述
在文件TargetCallingConv.td里,首先出现的是CCAction。这是一个空类,作为表示调用惯例操作的基类。从它出发,有这些派生类:
19 class CCCustom <string fn> : CCAction {
20 string FuncName = fn;
21 }
CCCustom记录处理参数的定制方法。
25 class CCPredicateAction <CCAction A> : CCAction {
26 CCAction SubAction = A;
27 }
CCPredicateAction作为基类,用在检测特定谓词,在谓词成立时执行CCAction A的定义中。
31 class CCIfType <list<ValueType> vts, CCAction A> : CCPredicateAction<A> {
32 list<ValueType> VTs = vts;
33 }
CCPredicateAction派生类CCIfType用作判定指定类型,在指定类型之一出现时执行CCAction A。
36 class CCIf <string predicate, CCAction A> : CCPredicateAction<A> {
37 string Predicate = predicate;
38 }
而派生类CCIf作为基类,用在执行指定谓词代码片段,在成立时执行CCAction A的定义中。
42 class CCIfByVal <CCAction A> : CCIf<"ArgFlags.isByVal()", A> {
43 }
CCIf派生类CCIfByVal则固化了谓词代码片段——参数必须按值传递。
47 class CCIfConsecutiveRegs <CCAction A> : CCIf<"ArgFlags.isInConsecutiveRegs()", A> {
48 }
派生类CCIfConsecutiveRegs固化了另一种谓词代码片段——参数必须在连续的寄存器里。
51 class CCIfCC <string CC, CCAction A>
52 : CCIf<!strconcat("State.getCallingConv() == ", CC), A> {}
56 class CCIfInReg <CCAction A> : CCIf<"ArgFlags.isInReg()", A> {}
60 class CCIfNest <CCAction A> : CCIf<"ArgFlags.isNest()", A> {}
64 class CCIfSplit <CCAction A> : CCIf<"ArgFlags.isSplit()", A> {}
68 class CCIfSRet <CCAction A> : CCIf<"ArgFlags.isSRet()", A> {}
71 class CCIfVarArg <CCAction A> : CCIf<"State.isVarArg()", A> {}
74 class CCIfNotVarArg <CCAction A> : CCIf<"!State.isVarArg()", A> {}
上面出现的ArgFlags是源文件CallingConvLower.h及CallingConvLower.cpp中类型为ISD::ArgFlagsTy 的函数参数,TableGen使用这个类型来描述被编译函数的参数。
79 class CCAssignToReg <list<Register> regList> : CCAction {
80 list<Register> RegList = regList;
81 }
CCAssignToReg的语义由其名字隐含表述(即TableGen会解释如下):如果regList中仍有一个寄存器可用,就将值(定义中没有出现)赋给该寄存器并返回成功。
85 class CCAssignToRegWithShadow <list<Register> regList,
86 list<Register> shadowList> : CCAction {
87 list<Register> RegList = regList;
88 list<Register> ShadowRegList = shadowList;
89 }
CCAssignToRegWithShadow类似于CCAssignToReg,不过还包括了一组成功时将被屏蔽的寄存器。
95 class CCAssignToStack <int size, int align> : CCAction {
96 int Size = size;
97 int Align = align;
98 }
CCAssignToStack则总是使得TableGen产生将值赋给指定大小与对齐边界的栈区间。如果大小或对齐是0,使用ABI的定义值。
103 class CCAssignToStackWithShadow <int size,
104 int align,
105 list<Register> shadowList> : CCAction {
106 int Size = size;
107 int Align = align;
108 list<Register> ShadowRegList = shadowList;
109 }
同样CCAssignToStackWithShadow类似CCAssignToStack,但还包括一组成功时将被屏蔽的寄存器。
114 class CCPassByVal <int size, int align> : CCAction {
115 int Size = size;
116 int Align = align;
117 }
CCPassByVal则会使TableGen产生将在栈上按值传递聚集参数的代码。
121 class CCPromoteToType <ValueType destTy> : CCAction {
122 ValueType DestTy = destTy;
123 }
CCPromoteToType告诉TableGen产生将当前值(定义中没有出现)提升到destTy类型。
127 class CCPromoteToUpperBitsInType <ValueType destTy> : CCAction {
128 ValueType DestTy = destTy;
129 }
CCPromoteToUpperBitsInType类似CCPromoteToType,但还要将值移到高位。
133 class CCBitConvertToType <ValueType destTy> : CCAction {
134 ValueType DestTy = destTy;
135 }
CCBitConvertToType则是将当前值按位转换(bitconvert)到destTy类型。
139 class CCPassIndirect <ValueType destTy> : CCAction {
140 ValueType DestTy = destTy;
141 }
CCPassIndirect告诉TableGen产生将值(定义中没有出现)存入栈,将对应指针作为值传递的代码。
145 class CCDelegateTo <CallingConv cc> : CCAction {
146 CallingConv CC = cc;
147 }
CCDelegateTo指定所要委派执行的调用惯例。
接下来是描述调用惯例的基本类型。首先是CallingConv,它通过一组CCAction来刻画调用惯例的具体行为。
151 class CallingConv <list<CCAction> actions> {
152 list<CCAction> Actions = actions;
153 bit Custom = 0;
154 }
然后是CustomCallingConv。实际使用上从CustomCallingConv派生的定义,表示使用LLVM中同名的函数来处理该调用惯例(这是少数不能由机器描述生成处理调用惯例代码的例子)。
158 class CustomCallingConv : CallingConv<[]> {
159 let Custom = 1;
160 }
最后就是CalleeSavedRegs的定义,描述被调用者保存的寄存器,我们在一节中已经看过它的定义,以及X86对应的派生定义。
3.7.2. X86调用惯例的TD描述
X86调用惯例的描述在文件X86CallingConv.td中。首先是CCIfSubtarget定义,它用于判定目标机器是否具有指定的特征(feature)。
16 class CCIfSubtarget <string F, CCAction A>
17 : CCIf<!strconcat("static_cast<const X86Subtarget&>"
18 "(State.getMachineFunction().getSubtarget()).", F),
这里State是CCState类型的对象,我们后面会详细了解这个类。
3.7.2.1. 返回值惯例
接着是定义如何传递返回值的返回值惯例。首先是RetCC_X86Common。
26 def RetCC_X86Common : CallingConv<[
27 // Scalar values are returned in AX first, then DX. For i8, the ABI
28 // requires the values to be in AL and AH, however this code uses AL and DL
29 // instead. This is because using AH for the second register conflicts with
30 // the way LLVM does multiple return values -- a return of {i16,i8} would end
31 // up in AX and AH, which overlap. Front-ends wishing to conform to the ABI
32 // for functions that return two i8 values are currently expected to pack the
33 // values into an i16 (which uses AX, and thus AL:AH).
34 //
35 // For code that doesn't care about the ABI, we allow returning more than two
36 // integer values in registers.
37 <[i1], <i8>>,
38 CCIfType<[i8] ,<[AL, DL, CL]>>,
39 CCIfType<[i16], CCAssignToReg<[AX, DX, CX]>>,
40 CCIfType<[i32], CCAssignToReg<[EAX, EDX, ECX]>>,
41 CCIfType<[i64], CCAssignToReg<[RAX, RDX, RCX]>>,
42
43 // Boolean vectors of AVX-512 are returned in SIMD registers.
44 // The call from AVX to AVX-512 function should work,
45 // since the boolean types in AVX/AVX2 are promoted by default.
46 CCIfType<[v2i1], CCPromoteToType<v2i64>>,
47 CCIfType<[v4i1], CCPromoteToType<v4i32>>,
48 CCIfType<[v8i1], CCPromoteToType<v8i16>>,
49 CCIfType<[v16i1], CCPromoteToType<v16i8>>,
50 CCIfType<[v32i1], CCPromoteToType<v32i8>>,
51 CCIfType<[v64i1], CCPromoteToType<v64i8>>,
52
53 // Vector types are returned in XMM0 and XMM1, when they fit. XMM2 and XMM3
54 // can only be used by ABI non-compliant code. If the target doesn't have XMM
55 // registers, it won't have vector types.
56 CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
57 CCAssignToReg<[XMM0,XMM1,XMM2,XMM3]>>,
58
59 // 256-bit vectors are returned in YMM0 and XMM1, when they fit. YMM2 and YMM3
60 // can only be used by ABI non-compliant code. This vector type is only
61 // supported while using the AVX target feature.
62 CCIfType<[v32i8, v16i16, v8i32, v4i64, v8f32, v4f64],
63 CCAssignToReg<[YMM0,YMM1,YMM2,YMM3]>>,
64
65 // 512-bit vectors are returned in ZMM0 and ZMM1, when they fit. ZMM2 and ZMM3
66 // can only be used by ABI non-compliant code. This vector type is only
67 // supported while using the AVX-512 target feature.
68 CCIfType<[v64i8, v32i16, v16i32, v8i64, v16f32, v8f64],
69 CCAssignToReg<[ZMM0,ZMM1,ZMM2,ZMM3]>>,
70
71 // MMX vector types are always returned in MM0. If the target doesn't have
72 // MM0, it doesn't support these vector types.
73 CCIfType<[x86mmx], CCAssignToReg<[MM0]>>,
74
75 // Long double types are always returned in FP0 (even with SSE).
76 CCIfType<[f80], CCAssignToReg<[FP0, FP1]>>
77 ]>;
这个调用惯例由一系列CCIfType定义组成,这使得其定义十分清晰。比如37行的CCIfType定义说明,如果当前参数类型是i1,把它提升至i8(即字节类型)。这些CCIfType在TableGen生成代码时将被展开为一系列的if语句块。
接着是32位的C返回值惯例RetCC_X86_32_C。除了类型为f32或f64的参数,其他处理它交由RetCC_X86Common来代劳。
80 def RetCC_X86_32_C : CallingConv<[
81 // The X86-32 calling convention returns FP values in FP0, unless marked
82 // with "inreg" (used here to distinguish one kind of reg from another,
83 // weirdly; this is really the sse-regparm calling convention) in which
84 // case they use XMM0, otherwise it is the same as the common X86 calling
85 // conv.
86 <<"hasSSE2()",
87 CCIfType<[f32, f64], CCAssignToReg<[XMM0,XMM1,XMM2]>>>>,
88 CCIfType<[f32,f64], CCAssignToReg<[FP0, FP1]>>,
89 <RetCC_X86Common>
90 ]>;
然后是32位的FastCC返回值惯例RetCC_X86_32_Fast。
93 def RetCC_X86_32_Fast : CallingConv<[
94 // The X86-32 fastcc returns 1, 2, or 3 FP values in XMM0-2 if the target has
95 // SSE2.
96 // This can happen when a float, 2 x float, or 3 x float vector is split by
97 // target lowering, and is returned in 1-3 sse regs.
98 CCIfType<[f32], CCIfSubtarget<"hasSSE2()", CCAssignToReg<[XMM0,XMM1,XMM2]>>>,
99 CCIfType<[f64], CCIfSubtarget<"hasSSE2()", CCAssignToReg<[XMM0,XMM1,XMM2]>>>,
100
101 // For integers, ECX can be used as an extra return register
102 CCIfType<[i8], CCAssignToReg<[AL, DL, CL]>>,
103 CCIfType<[i16], CCAssignToReg<[AX, DX, CX]>>,
104 CCIfType<[i32], CCAssignToReg<[EAX, EDX, ECX]>>,
105
106 // Otherwise, it is the same as the common X86 calling convention.
107 CCDelegateTo<RetCC_X86Common>
108 ]>;
以及Intel_OCL_BI的返回值惯例RetCC_Intel_OCL_BI(Intel OpenCL内置函数的调用惯例)。
111 def RetCC_Intel_OCL_BI : CallingConv<[
112 // Vector types are returned in XMM0,XMM1,XMMM2 and XMM3.
113 CCIfType<[f32, f64, v4i32, v2i64, v4f32, v2f64],
114 CCAssignToReg<[XMM0,XMM1,XMM2,XMM3]>>,
115
116 // 256-bit FP vectors
117 // No more than 4 registers
118 CCIfType<[v8f32, v4f64, v8i32, v4i64],
119 CCAssignToReg<[YMM0,YMM1,YMM2,YMM3]>>,
120
121 // 512-bit FP vectors
122 CCIfType<[v16f32, v8f64, v16i32, v8i64],
123 CCAssignToReg<[ZMM0,ZMM1,ZMM2,ZMM3]>>,
124
125 // i32, i64 in the standard way
126 CCDelegateTo<RetCC_X86Common>
127 ]>;
32位HiPE返回值惯例RetCC_X86_32_HiPE(高性能Erlang编译器的调用惯例)。
130 def RetCC_X86_32_HiPE : CallingConv<[
131 // Promote all types to i32
132 CCIfType<[i8, i16], CCPromoteToType<i32>>,
133
134 // Return: HP, P, VAL1, VAL2
135 CCIfType<[i32], CCAssignToReg<[ESI, EBP, EAX, EDX]>>
136 ]>;
32位MSVC返回值惯例RetCC_X86_32_VectorCall,通过SSE寄存器传递向量。
139 def RetCC_X86_32_VectorCall : CallingConv<[
140 // Vector types are returned in XMM0,XMM1,XMMM2 and XMM3.
141 CCIfType<[f32, f64, v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
142 CCAssignToReg<[XMM0,XMM1,XMM2,XMM3]>>,
143
144 // 256-bit FP vectors
145 CCIfType<[v32i8, v16i16, v8i32, v4i64, v8f32, v4f64],
146 CCAssignToReg<[YMM0,YMM1,YMM2,YMM3]>>,
147
148 // 512-bit FP vectors
149 CCIfType<[v64i8, v32i16, v16i32, v8i64, v16f32, v8f64],
150 CCAssignToReg<[ZMM0,ZMM1,ZMM2,ZMM3]>>,
151
152 // Return integers in the standard way.
153 CCDelegateTo<RetCC_X86Common>
154 ]>;
64位的C返回值惯例RetCC_X86_64_C。
157 def RetCC_X86_64_C : CallingConv<[
158 // The X86-64 calling convention always returns FP values in XMM0.
159 CCIfType<[f32], CCAssignToReg<[XMM0, XMM1]>>,
160 CCIfType<[f64], CCAssignToReg<[XMM0, XMM1]>>,
161
162 // MMX vector types are always returned in XMM0.
163 CCIfType<[x86mmx], CCAssignToReg<[XMM0, XMM1]>>,
164 CCDelegateTo<RetCC_X86Common>
165 ]>;
64位Windows返回值惯例RetCC_X86_Win64_C。
168 def RetCC_X86_Win64_C : CallingConv<[
169 // The X86-Win64 calling convention always returns __m64 values in RAX.
170 CCIfType<[x86mmx], CCBitConvertToType<i64>>,
171
172 // Otherwise, everything is the same as 'normal' X86-64 C CC.
173 CCDelegateTo<RetCC_X86_64_C>
174 ]>;
64位HiPE返回值惯例RetCC_X86_64_HiPE。
177 def RetCC_X86_64_HiPE : CallingConv<[
178 // Promote all types to i64
179 CCIfType<[i8, i16, i32], CCPromoteToType<i64>>,
180
181 // Return: HP, P, VAL1, VAL2
182 CCIfType<[i64], CCAssignToReg<[R15, RBP, RAX, RDX]>>
183 ]>;
64位JScript返回值惯例RetCC_X86_64_WebKit_JS。
186 def RetCC_X86_64_WebKit_JS : CallingConv<[
187 // Promote all types to i64
188 CCIfType<[i8, i16, i32], CCPromoteToType<i64>>,
189
190 // Return: RAX
191 CCIfType<[i64], CCAssignToReg<[RAX]>>
192 ]>;
AnyReg返回值惯例RetCC_X86_64_AnyReg。它允许寄存器分配器选择任何空闲的寄存器。中Debug build时,RetCC_X86_64_AnyReg将产生一个assert,在Release build时,会落入C惯例。
201 def RetCC_X86_64_AnyReg : CallingConv<[
202 CCCustom<"CC_X86_AnyReg_Error">
203 ]>;
最后三个定义将生成最终的返回值处理分派函数。
206 def RetCC_X86_32 : CallingConv<[
207 // If FastCC, use RetCC_X86_32_Fast.
208 CCIfCC<"CallingConv::Fast", CCDelegateTo<RetCC_X86_32_Fast>>,
209 // If HiPE, use RetCC_X86_32_HiPE.
210 CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_32_HiPE>>,
211 CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_32_VectorCall>>,
212
213 // Otherwise, use RetCC_X86_32_C.
214 CCDelegateTo<RetCC_X86_32_C>
215 ]>;
218 def RetCC_X86_64 : CallingConv<[
219 // HiPE uses RetCC_X86_64_HiPE
220 CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_64_HiPE>>,
221
222 // Handle JavaScript calls.
223 CCIfCC<"CallingConv::WebKit_JS", CCDelegateTo<RetCC_X86_64_WebKit_JS>>,
224 CCIfCC<"CallingConv::AnyReg", CCDelegateTo<RetCC_X86_64_AnyReg>>,
225
226 // Handle explicit CC selection
227 CCIfCC<"CallingConv::X86_64_Win64", CCDelegateTo<RetCC_X86_Win64_C>>,
228 CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<RetCC_X86_64_C>>,
229
230 // Mingw64 and native Win64 use Win64 CC
231 CCIfSubtarget<"isTargetWin64()", CCDelegateTo<RetCC_X86_Win64_C>>,
232
233 // Otherwise, drop to normal X86-64 CC
234 CCDelegateTo<RetCC_X86_64_C>
235 ]>;
238 def RetCC_X86 : CallingConv<[
239
240 // Check if this is the Intel OpenCL built-ins calling convention
241 CCIfCC<"CallingConv::Intel_OCL_BI", CCDelegateTo<RetCC_Intel_OCL_BI>>,
242
243 CCIfSubtarget<"is64Bit()", CCDelegateTo<RetCC_X86_64>>,
244 CCDelegateTo<RetCC_X86_32>
245 ]>;
以上所述就是小编给大家介绍的《LLVM学习笔记(45)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【每日笔记】【Go学习笔记】2019-01-04 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-02 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-07 Codis笔记
- Golang学习笔记-调度器学习
- Vue学习笔记(二)------axios学习
- 算法/NLP/深度学习/机器学习面试笔记
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
两周自制脚本语言
[日]千叶 滋 / 陈筱烟 / 人民邮电出版社 / 2014-6 / 59.00元
《两周自制脚本语言》是一本优秀的编译原理入门读物。全书穿插了大量轻松风趣的对话,读者可以随书中的人物一起从最简单的语言解释器开始,逐步添加新功能,最终完成一个支持函数、数组、对象等高级功能的语言编译器。本书与众不同的实现方式不仅大幅简化了语言处理器的复杂度,还有助于拓展读者的视野。 《两周自制脚本语言》适合对编译原理及语言处理器设计有兴趣的读者以及正在学习相关课程的大中专院校学生。同时,已经......一起来看看 《两周自制脚本语言》 这本书的介绍吧!