1. 程式人生 > >LLVM學習筆記(45)

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