LLVM学习笔记(45)

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。它表示使用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),

19                  A>;

这里State是CCState类型的对象,我们后面会详细了解这个类。

V7.0由上面的定义,引申出CCIfSubtargetNot

22       class CCIfNotSubtarget<string F, CCAction A>

23           : CCIf<!strconcat("!static_cast<const X86Subtarget&>"

24                              "(State.getMachineFunction().getSubtarget()).", F),

​​​​​​​3.7.2.1. 返回值惯例

接着是定义如何传递返回值的返回值惯例。首先是RetCC_X86Common,它是所有X86都适用的通用惯例。下面的注释很详细地说明了规则的细节。

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         CCIfType<[i1],  CCPromoteToType<i8>>,

38         CCIfType<[i8] , CCAssignToReg<[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).                             <- v7.0删除

76         CCIfType<[f80], CCAssignToReg<[FP0, FP1]>>

  // Long double types are always returned in FP0 (even with SSE),                             <- v7.0增加

  // except on Win64.

  CCIfNotSubtarget<"isTargetWin64()", 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         CCIfInReg<CCIfSubtarget<"hasSSE2()",

87           CCIfType<[f32, f64], CCAssignToReg<[XMM0,XMM1,XMM2]>>>>,

88         CCIfType<[f32,f64], CCAssignToReg<[FP0, FP1]>>,

89         CCDelegateTo<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],                                                     <- v7.0删除

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     ]>;

X86-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]>>,

  CCIfType<[f128], CCAssignToReg<[XMM0, XMM1]>>,                                                   <- v7.0增加

161    

162       // MMX vector types are always returned in XMM0.

163       CCIfType<[x86mmx], CCAssignToReg<[XMM0, XMM1]>>,

  CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>,                                             <- v7.0增加

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     ]>;

V7.0支持x86-64的向量调用返回值惯例:

354     def RetCC_X86_64_Vectorcall : CallingConv<[

355       // Vectorcall calling convention always returns FP values in XMMs.

356       CCIfType<[f32, f64, f128],

357         CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>,

358    

359       // Otherwise, everything is the same as Windows X86-64 C CC.

360       CCDelegateTo<RetCC_X86_Win64_C>

361     ]>;

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     ]>;

V7.0增加了支持64SwiftRetCC_X86_64_Swift

381     def RetCC_X86_64_Swift : CallingConv<[

382    

383       CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>,

384    

385       // For integers, ECX, R8D can be used as extra return registers.

386       CCIfType<[v1i1],  CCPromoteToType<i8>>,

387       CCIfType<[i1],  CCPromoteToType<i8>>,

388       CCIfType<[i8] , CCAssignToReg<[AL, DL, CL, R8B]>>,

389       CCIfType<[i16], CCAssignToReg<[AX, DX, CX, R8W]>>,

390       CCIfType<[i32], CCAssignToReg<[EAX, EDX, ECX, R8D]>>,

391       CCIfType<[i64], CCAssignToReg<[RAX, RDX, RCX, R8]>>,

392    

393       // XMM0, XMM1, XMM2 and XMM3 can be used to return FP values.

394       CCIfType<[f32], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>,

395       CCIfType<[f64], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>,

396       CCIfType<[f128], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>,

397    

398       // MMX vector types are returned in XMM0, XMM1, XMM2 and XMM3.

399       CCIfType<[x86mmx], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>,

400       CCDelegateTo<RetCC_X86Common>

401     ]>;

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     ]>;

V7.0增加了支持HHVMRetCC_X86_64_HHVM

415     def RetCC_X86_64_HHVM: CallingConv<[

416       // Promote all types to i64

417       CCIfType<[i8, i16, i32], CCPromoteToType<i64>>,

418    

419       // Return: could return in any GP register save RSP and R12.

420       CCIfType<[i64], CCAssignToReg<[RBX, RBP, RDI, RSI, RDX, RCX, R8, R9,

421                                      RAX, R10, R11, R13, R14, R15]>>

422     ]>;

下面的定义将生成最终的返回值处理分派函数。

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>>,

 

  // Handle Swift calls.                                                                                                               <- v7.0增加

  CCIfCC<"CallingConv::Swift", CCDelegateTo<RetCC_X86_64_Swift>>,

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    

  // Handle Vectorcall CC                                                                                                          <- v7.0增加

  CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_64_Vectorcall>>,

 

  // Handle HHVM calls.

  CCIfCC<"CallingConv::HHVM", CCDelegateTo<RetCC_X86_64_HHVM>>,

 

  CCIfCC<"CallingConv::X86_RegCall",

          CCIfSubtarget<"isTargetWin64()",

                        CCDelegateTo<RetCC_X86_Win64_RegCall>>>,

  CCIfCC<"CallingConv::X86_RegCall", CCDelegateTo<RetCC_X86_SysV64_RegCall>>,

 

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     ]>;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值