LLVM是如何实现的?

24 c++ llvm

来自http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions

LLVM确实广泛使用了手动形式的RTTI,它使用了isa <>,cast <>和dyn_cast <>等模板.这种形式的RTTI是可选的,可以添加到任何类中.它也比dynamic_cast <>更有效.

isa和其他人是如何实施的?

Mat*_* M. 33

首先,LLVM系统非常具体,并不是RTTI系统的直接替代品.

房地

  • 对于大多数类,不必生成RTTI信息
  • 在需要时,信息仅在给定层次结构中有意义
  • 我们排除了这个系统的多重继承

识别对象类

采用简单的层次结构,例如:

struct Base {}; /* abstract */
struct DerivedLeft: Base {}; /* abstract */
struct DerivedRight:Base {};
struct MostDerivedL1: DerivedLeft {};
struct MostDerivedL2: DerivedLeft {};
struct MostDerivedR: DerivedRight {};
Run Code Online (Sandbox Code Playgroud)

我们将创建一个特定于此层次结构的枚举,其中每个层次结构成员的枚举成员都可以实例化(其他成员将无用).

enum BaseId {
  DerivedRightId,
  MostDerivedL1Id,
  MostDerivedL2Id,
  MostDerivedRId
};
Run Code Online (Sandbox Code Playgroud)

然后,Base将使用将返回此枚举的方法来扩充该类.

struct Base {
  static inline bool classof(Base const*) { return true; }

  Base(BaseId id): Id(id) {}

  BaseId getValueID() const { return Id; }

  BaseId Id;
};
Run Code Online (Sandbox Code Playgroud)

并且每个具体类都以这种方式增强:

struct DerivedRight: Base {
  static inline bool classof(DerivedRight const*) { return true; }

  static inline bool classof(Base const* B) {
    switch(B->getValueID()) {
    case DerivedRightId: case MostDerivedRId: return true;
    default: return false;
    }
  }

  DerivedRight(BaseId id = DerivedRightId): Base(id) {}
};
Run Code Online (Sandbox Code Playgroud)

现在,简单地说,可以查询确切的类型,以进行投射.

隐藏实施细节

让用户捣乱getValueID会很麻烦,所以在LLVM中,使用classof方法隐藏了这一点.

给定的类应该实现两个classof方法:一个用于其最深的基础(测试合适的值BaseId)和一个用于自身(纯粹的优化).例如:

struct MostDerivedL1: DerivedLeft {
  static inline bool classof(MostDerivedL1 const*) { return true; }

  static inline bool classof(Base const* B) {
    return B->getValueID() == MostDerivedL1Id;
  }

  MostDerivedL1(): DerivedLeft(MostDerivedL1Id) {}
};
Run Code Online (Sandbox Code Playgroud)

这样,我们可以通过模板检查是否可以进行强制转换:

template <typename To, typename From>
bool isa(From const& f) {
  return To::classof(&f);
}
Run Code Online (Sandbox Code Playgroud)

试想一下,如果ToMostDerivedL1:

  • 如果FromMostDerivedL1,那么我们调用第一个重载classof,它的工作原理
  • 如果From是其他任何东西,那么我们调用第二个重载classof,并且检查使用枚举来确定具体类型是否匹配.

希望它更清楚.

  • @ acidzombie24:Anton正在研究Clang和LLVM(并且有比我更多的经验),而我主要负责Clang.也许@Chris Lattner会注意到这个帖子并提供他的答案;)它根本不是自动的,这实际上是重点,因为只要你不使用它就不付钱.但是,我不建议将其概括为一般.LLVM和Clang是非常具体的项目,具有非常特殊的需求,对于大多数项目而言,普通的RTTI确实足够了. (2认同)

Ant*_*kov 6

只需在osgx的答案中添加内容:基本上每个类都应该实现classof()方法,该方法可以完成所有必要的操作.例如,Value的classof()例程如下所示:

  // Methods for support type inquiry through isa, cast, and dyn_cast:
  static inline bool classof(const Value *) {
    return true; // Values are always values.
  }
Run Code Online (Sandbox Code Playgroud)

要检查我们是否有适当类型的类,每个类都有唯一的ValueID.您可以在include/llvm/Value.h文件中查看ValueID的完整列表.此ValueID的用法如下(摘自Function.h):

  /// Methods for support type inquiry through isa, cast, and dyn_cast:
  static inline bool classof(const Function *) { return true; }
  static inline bool classof(const Value *V) {
    return V->getValueID() == Value::FunctionVal;
  }
Run Code Online (Sandbox Code Playgroud)

因此,简而言之:每个类都应该实现执行必要决策的classof()方法.有问题的实现包含一组唯一的ValueID.因此,为了实现classof(),应该只将参数的ValueID与自己的ValueID进行比较.

如果我没记错的话,isa <>和朋友的第一次实现是从10年前的boost中采用的.现在实现差异很大:)

  • 需要注意的是:该系统适用于LLVM,因为它是独立的.它不能扩展到任意代码,因为在DLL之间协调`ValueIDs`的属性会有问题. (3认同)
  • @ acidzombie24:好的,我会自己回答一下因为评论过于局限:p (2认同)

osg*_*sgx 5

我应该提一下http://llvm.org/docs/ProgrammersManual.html#isa-这个文档有一些额外的描述.

isa,cast和dyn_cast的源代码位于单个文件中,并且评论很多.

http://llvm.org/doxygen/Casting_8h_source.html

00047 // isa<X> - Return true if the parameter to the template is an instance of the
00048 // template type argument.  Used like this:
00049 //
00050 //  if (isa<Type*>(myVal)) { ... }
00051 //
00052 template <typename To, typename From>
00053 struct isa_impl {
00054   static inline bool doit(const From &Val) {
00055     return To::classof(&Val);
00056   }
00057 };

00193 // cast<X> - Return the argument parameter cast to the specified type.  This
00194 // casting operator asserts that the type is correct, so it does not return null
00195 // on failure.  It does not allow a null argument (use cast_or_null for that).
00196 // It is typically used like this:
00197 //
00198 //  cast<Instruction>(myVal)->getParent()
00199 //
00200 template <class X, class Y>
00201 inline typename cast_retty<X, Y>::ret_type cast(const Y &Val) {
00202   assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
00203   return cast_convert_val<X, Y,
00204                           typename simplify_type<Y>::SimpleType>::doit(Val);
00205 }

00218 // dyn_cast<X> - Return the argument parameter cast to the specified type.  This
00219 // casting operator returns null if the argument is of the wrong type, so it can
00220 // be used to test for a type as well as cast if successful.  This should be
00221 // used in the context of an if statement like this:
00222 //
00223 //  if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
00224 //
00225 
00226 template <class X, class Y>
00227 inline typename cast_retty<X, Y>::ret_type dyn_cast(const Y &Val) {
00228   return isa<X>(Val) ? cast<X, Y>(Val) : 0;
00229 }
Run Code Online (Sandbox Code Playgroud)