国外 网站有做验证码吗,网站幕布拍照什么样子的,哪个网站教做西餐,佛山企业一般在哪网站发布消息3. LVM有趣的代码
3.1. dyn_cast()与cast()
C支持类型间的自动转换#xff08;如operator 声明的转换#xff09;#xff0c;但在转换的调用链里自动转换只能调用一次#xff0c;这固然是避免给编译器带来过分的复杂性#xff0c;但更重要的是允许自动转换接力调用几乎很…3. LVM有趣的代码
3.1. dyn_cast()与cast()
C支持类型间的自动转换如operator 声明的转换但在转换的调用链里自动转换只能调用一次这固然是避免给编译器带来过分的复杂性但更重要的是允许自动转换接力调用几乎很难避免出现递归调用而且调用链过长会很快失去控制给人带来意想不到的结果。但是C原生的类型转换系统对于LLVM/MLIR来说局限性太大因此LLVM打造了自己的类型转换系统它仿造C的惯例给出了自己的类型转换函数cast、dyn_cast。显然它们分别对应其他cast与dynamic_cast。它们与C版本的最大不同在于cast、dyn_cast都支持无限次自动转换这在用户眼皮底下发生但用户需要提供一些指定的方法。在这个体系中dyn_cast最有代表性llvm里一共定义了3个版本它们的区别在于函数参数类型
331 template class X, class Y
332 LLVM_NODISCARD inline std::enable_if_t
333 !is_simple_typeY::value, typename cast_rettyX, const Y::ret_type
334 dyn_cast(const Y Val) {
335 return isaX(Val) ? castX(Val) : nullptr;
336 }
337
338 template class X, class Y
339 LLVM_NODISCARD inline typename cast_rettyX, Y::ret_type dyn_cast(Y Val) {
340 return isaX(Val) ? castX(Val) : nullptr;
341 }
342
343 template class X, class Y
344 LLVM_NODISCARD inline typename cast_rettyX, Y *::ret_type dyn_cast(Y *Val) {
345 return isaX(Val) ? castX(Val) : nullptr;
346 }
在这些函数里用到isa()与cast()isa()是这样的它检查参数的类型是否与模板参数里给出的类型一致。
141 template class X, class Y LLVM_NODISCARD inline bool isa(const Y Val) { // 最终调用的函数
142 return isa_impl_wrapX, const Y,
143 typename simplify_typeconst Y::SimpleType::doit(Val);
144 }
145
146 template typename First, typename Second, typename... Rest, typename Y
147 LLVM_NODISCARD inline bool isa(const Y Val) {
148 return isaFirst(Val) || isaSecond, Rest...(Val);
149 }
实际上isa()支持对某个类型列表检查指定类型是否与其中一个类型一致。而两个特定类型比较的实现是由isa_impl_wrap这个类提供的
116 templatetypename To, typename From, typename SimpleFrom
117 struct isa_impl_wrap {
118 // When From ! SimplifiedType, we can simplify the type some more by using
119 // the simplify_type template.
120 static bool doit(const From Val) {
121 return isa_impl_wrapTo, SimpleFrom,
122 typename simplify_typeSimpleFrom::SimpleType::doit(
123 simplify_typeconst From::getSimplifiedValue(Val));
124 }
125 };
126
127 templatetypename To, typename FromTy
128 struct isa_impl_wrapTo, FromTy, FromTy {
129 // When From SimpleType, we are as simple as we are going to get.
130 static bool doit(const FromTy Val) {
131 return isa_impl_clTo,FromTy::doit(Val);
132 }
133 };
为了支持无限次自动转换这里指出了3个类型To、From、SimpleFrom。其中被转换值从From类型转换到SimpleFrom类型这个SimpleFrom类型还可能进一步转换为另一个SimpleFrom类型直到From与SimpleFrom类型一致为止这意味着From不能再“简化了”。这时我们使用128行的isa_impl_wrap特化定义。
首先我们先看一下完成从From到SimpleFrom的simplify_type类。注意如果存在llvm所不知道的From到SimpleFrom的转换必须提供这个目的的simplify_type特化版本。如果不提供将使用simplify_type的非特化版本llvm对自己的数据结构定义了若干simplify_type特化版本
33 templatetypename From struct simplify_type {
34 using SimpleType From; // The real type this represents...
35
36 // An accessor to get the real value...
37 static SimpleType getSimplifiedValue(From Val) { return Val; }
38 };
39
40 templatetypename From struct simplify_typeconst From {
41 using NonConstSimpleType typename simplify_typeFrom::SimpleType;
42 using SimpleType
43 typename add_const_past_pointerNonConstSimpleType::type;
44 using RetType
45 typename add_lvalue_reference_if_not_pointerSimpleType::type;
46
47 static RetType getSimplifiedValue(const From Val) {
48 return simplify_typeFrom::getSimplifiedValue(const_castFrom(Val));
49 }
50 };
可以看到非特化版本的simplify_type通过getSimplifiedValue()给出的SimpleFrom就是值自己。
在simplify_type再也给不出“简化”时isa_impl_wrap提供最后的判断同样它有许多特化版本我们只看llvm的非定制版本好了
55 template typename To, typename From, typename Enabler void
56 struct isa_impl {
57 static inline bool doit(const From Val) {
58 return To::classof(Val); // -- 最终的裁决者
59 }
60 };
61
62 /// Always allow upcasts, and perform no dynamic check for them.
63 template typename To, typename From
64 struct isa_implTo, From, std::enable_if_tstd::is_base_ofTo, From::value {
65 static inline bool doit(const From ) { return true; }
66 };
67
68 template typename To, typename From struct isa_impl_cl {
69 static inline bool doit(const From Val) {
70 return isa_implTo, From::doit(Val);
71 }
72 };
这里的调用关系始于68行的isa_impl_cl()它所调用的isa_impl()也需要各个类提供自己的特化版本来实现特定的类型转换。在不提供特化版本时如果两个类有继承关系那么就会使用64行llvm的特化版本无条件放行否则由上面58行To的classof()方法来判定它们是否一致。
在dyn_cast()中当isa()认定两个类型是一致时cast()就可以执行具体的转换了。类似的cast()有4个重载版本
251 template class X, class Y
252 inline std::enable_if_t!is_simple_typeY::value,
253 typename cast_rettyX, const Y::ret_type
254 cast(const Y Val) {
255 assert(isaX(Val) castTy() argument of incompatible type!);
256 return cast_convert_val
257 X, const Y, typename simplify_typeconst Y::SimpleType::doit(Val);
258 }
259
260 template class X, class Y
261 inline typename cast_rettyX, Y::ret_type cast(Y Val) {
262 assert(isaX(Val) castTy() argument of incompatible type!);
263 return cast_convert_valX, Y,
264 typename simplify_typeY::SimpleType::doit(Val);
265 }
266
267 template class X, class Y
268 inline typename cast_rettyX, Y *::ret_type cast(Y *Val) {
269 assert(isaX(Val) castTy() argument of incompatible type!);
270 return cast_convert_valX, Y*,
271 typename simplify_typeY*::SimpleType::doit(Val);
272 }
273
274 template class X, class Y
275 inline typename cast_rettyX, std::unique_ptrY::ret_type
276 cast(std::unique_ptrY Val) {
277 assert(isaX(Val.get()) castTy() argument of incompatible type!);
278 using ret_type typename cast_rettyX, std::unique_ptrY::ret_type;
279 return ret_type(
280 cast_convert_valX, Y *, typename simplify_typeY *::SimpleType::doit(
281 Val.release()));
282 }
这里首先由cast_retty::ret_type决定cast()的返回值类型
212 templateclass To, class From
213 struct cast_retty {
214 using ret_type typename cast_retty_wrap
215 To, From, typename simplify_typeFrom::SimpleType::ret_type;
216 };
其中cast_retty_wrap是这样的定义
198 templateclass To, class From, class SimpleFrom
199 struct cast_retty_wrap {
200 // When the simplified type and the from type are not the same, use the type
201· // simplifier to reduce the type, then reuse cast_retty_impl to get the
202 // resultant type.
203 using ret_type typename cast_rettyTo, SimpleFrom::ret_type;
204 };
205
206 templateclass To, class FromTy
207 struct cast_retty_wrapTo, FromTy, FromTy {
208 // When the simplified type is equal to the from type, use it directly.
209 using ret_type typename cast_retty_implTo,FromTy::ret_type;
210 };
同样第二个版本的cast_retty_wrap是最终调用的版本它使用的cast_retty_impl同样有多个特化版本用户还可以自己定制为了处理常量和指针等情况我们看一下非特化版本
169 templateclass To, class From struct cast_retty_impl {
170 using ret_type To ; // Normal case, return Ty
171 };
不出意料cast()的返回值类型就是To类型指针情况下使用引用类型。确定了返回值类型后cast()使用cast_convert_val::doit()执行转换
221 templateclass To, class From, class SimpleFrom struct cast_convert_val {
222 // This is not a simple type, use the template to simplify it...
223 static typename cast_rettyTo, From::ret_type doit(From Val) {
224 return cast_convert_valTo, SimpleFrom,
225 typename simplify_typeSimpleFrom::SimpleType::doit(
226 simplify_typeFrom::getSimplifiedValue(Val));
227 }
228 };
229
230 templateclass To, class FromTy struct cast_convert_valTo,FromTy,FromTy {
231 // This _is_ a simple type, just cast it.
232 static typename cast_rettyTo, FromTy::ret_type doit(const FromTy Val) {
233 typename cast_rettyTo, FromTy::ret_type Res2
234 (typename cast_rettyTo, FromTy::ret_type)const_castFromTy(Val);
235 return Res2;
236 }
237 };
对于我们最终调用的第二个版本来说所谓的转换就是一个简单的C形式的强制转换不过由于前面的一系列检查这个转换是安全的。
3.2. TrailingObjects
不同于C语言里比较原始的变长类型即union在LLVM里对变长类型有更好的支持。在需要使用变长类型时只需要将llvm::TrailingObjects作为基类。这个类型定义的开头几行是这样的
228 template typename BaseTy, typename... TrailingTys
229 class TrailingObjects : private trailing_objects_internal::TrailingObjectsImpl
230 trailing_objects_internal::AlignmentCalcHelper
231 TrailingTys...::Alignment,
232 BaseTy, TrailingObjectsBaseTy, TrailingTys...,
233 BaseTy, TrailingTys... {
234
235 template int A, typename B, typename T, typename P, typename... M
236 friend class trailing_objects_internal::TrailingObjectsImpl;
237
238 template typename... Tys class Foo {};
239
240 typedef trailing_objects_internal::TrailingObjectsImpl
241 trailing_objects_internal::AlignmentCalcHelperTrailingTys...::Alignment,
242 BaseTy, TrailingObjectsBaseTy, TrailingTys..., BaseTy, TrailingTys...
243 ParentType;
244 using TrailingObjectsBase trailing_objects_internal::TrailingObjectsBase;
245
246 using ParentType::getTrailingObjectsImpl;
这里需要注意232行的TrailingObjectsBaseTy, TrailingTys...这其实是当前TrailingObjects的类型它用途我们会在下面看到。
变长类型对象的大小由totalSizeToAlloc()方法给出在创建对象时它用于确定分配资源的数量。
340 template typename... Tys
341 static constexpr std::enable_if_t
342 std::is_sameFooTrailingTys..., FooTys...::value, size_t
343 totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType
344 TrailingTys, size_t::type... Counts) {
345 return sizeof(BaseTy) ParentType::additionalSizeToAllocImpl(0, Counts...);
346 }
在上面243行定义的ParentType类型不出意料有两个特化版本注意ParentType也是当前TrailingObjects的父类。第一个是全特化它是特化产生的派生树的叶子类型模板展开到此结束开始回溯
206 template int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy
207 class TrailingObjectsImplAlign, BaseTy, TopTrailingObj, PrevTy
208 : public TrailingObjectsAlignerAlign {
209 protected:
210 // This is a dummy method, only here so the using doesnt fail --
211 // it will never be called, because this function recurses backwards
212 // up the inheritance chain to subclasses.
213 static void getTrailingObjectsImpl();
214
215 static constexpr size_t additionalSizeToAllocImpl(size_t SizeSoFar) {
216 return SizeSoFar;
217 }
218
219 template bool CheckAlignment static void verifyTrailingObjectsAlignment() {}
220 };
另一个则是偏特化是整个模板展开过程的核心它开头几行是这样的
130 template int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy,
131 typename NextTy, typename... MoreTys
132 class TrailingObjectsImplAlign, BaseTy, TopTrailingObj, PrevTy, NextTy,
133 MoreTys...
134 : public TrailingObjectsImplAlign, BaseTy, TopTrailingObj, NextTy,
135 MoreTys... {
136
137 typedef TrailingObjectsImplAlign, BaseTy, TopTrailingObj, NextTy, MoreTys...
138 ParentType;
139
140 struct RequiresRealignment {
141 static const bool value alignof(PrevTy) alignof(NextTy);
142 };
143
144 static constexpr bool requiresRealignment() {
145 return RequiresRealignment::value;
146 }
这里需要注意对这两个版本来说与它们相关的类型是模板参数PrevTy。另外BaseTy都是变长类型里的第一个类型。我们关注的additionalSizeToAllocImpl()方法是
193 static constexpr size_t additionalSizeToAllocImpl(
194 size_t SizeSoFar, size_t Count1,
195 typename ExtractSecondTypeMoreTys, size_t::type... MoreCounts) {
196 return ParentType::additionalSizeToAllocImpl( // -- 调用父类的additionalSizeToAllocImpl()
197 (requiresRealignment() ? llvm::alignToalignof(NextTy)(SizeSoFar)
198 : SizeSoFar)
199 sizeof(NextTy) * Count1, // -- 加上我们管的这部分
200 MoreCounts...); // -- 剩下的交给父类处理
201 }
除了确定对象大小访问指定对象的能力也是不可缺的这由getTrailingObjects()提供它有两个版本一个返回const指针另一个返回非const指针除此之外实现上没有其他区别
297 template typename T T *getTrailingObjects() {
298 verifyTrailingObjectsAssertions();
299 // Forwards to an impl function with overloads, since member
300 // function templates cant be specialized.
301 return this-getTrailingObjectsImpl(
302 static_castBaseTy *(this), TrailingObjectsBase::OverloadTokenT());
303 }
上面的方法没有特化版本模板参数T在派生体系里选择最恰当的重载版本。我们以Operation为例
29 class alignas(8) Operation final
29 : public llvm::ilist_node_with_parentOperation, Block,
30 private llvm::TrailingObjectsOperation, BlockOperand, Region,
31 detail::OperandStorage {
30 ~ 31行将展开为这样的继承树最底下是叶子节点自底向上继承。与特定TrailingObjectsImpl相关的类型绿色高亮
TrailingObjectsImpl…, Operation , TrailingObjects, Operation, BlockOperand…
TrailingObjectsImpl…, Operation , TrailingObjects, BlockOperand, Region…
TrailingObjectsImpl…, Operation , TrailingObjects, Region, detail::OperandStorage
TrailingObjectsImpl…, Operation , TrailingObjects, detail::OperandStorage
下面的getTrailingObjectsImpl()是由TrailingObjectsImpl定义的。注意返回类型NextTy是模板参数是与当前TrailingObjectsImpl相关类型PrevTy的下一个类型。也就是说下一个对象的位置由前一个对象返回这也是合理的因为只有找到前一个对象才能知道它后面的对象在哪里。
159 static NextTy *
160 getTrailingObjectsImpl(BaseTy *Obj,
161 TrailingObjectsBase::OverloadTokenNextTy) {
162 auto *Ptr TopTrailingObj::getTrailingObjectsImpl(
163 Obj, TrailingObjectsBase::OverloadTokenPrevTy())
164 TopTrailingObj::callNumTrailingObjects(
165 Obj, TrailingObjectsBase::OverloadTokenPrevTy());
166
167 if (requiresRealignment())
168 return reinterpret_castNextTy *(alignAddr(Ptr, Align::OfNextTy()));
169 else
170 return reinterpret_castNextTy *(Ptr);
171 }
因此这个方法的实现仍然是一个递归的过程在162行的getTrailingObjectsImpl()找出PrevTy类型对象的地址164行的callNumTrailingObjects()返回对象的个数这个方法有两个版本
259 static size_t
260 callNumTrailingObjects(const BaseTy *Obj,
261 TrailingObjectsBase::OverloadTokenBaseTy) {
262 return 1;
263 }
264
265 template typename T
266 static size_t callNumTrailingObjects(const BaseTy *Obj,
267 TrailingObjectsBase::OverloadTokenT) {
268 return Obj-numTrailingObjects(TrailingObjectsBase::OverloadTokenT());
269 }
第一个版本是缺省实现有多个对象的类型才需要实现第二个版本所需的numTrailingObjects()例如Operation里的BlockOperand与Region。
getTrailingObjectsImpl() 162行的递归也有尽头这个尽头定义在TrailingObjects里
246 static BaseTy *
247 getTrailingObjectsImpl(BaseTy *Obj,
248 TrailingObjectsBase::OverloadTokenBaseTy) {
249 return Obj;
250 }
这个重载返回了对象的基址即第一个对象的地址。自此回溯回去不断加上每一级的偏移最终得到指定对象的地址。