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。实际使用上从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          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).

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

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

猜你喜欢

转载自blog.csdn.net/wuhui_gdnt/article/details/84136817