DSA算法(DataStructure Analysis的首字母缩写)是LLVM的发起人Chris Latter在其硕士、博士系列论文中提出的一个上下文感知(context sensitivity)的、过程间(inter-procedure)的数据结构分析算法。这个算法的强大之处在于可以分析像C这样拥有指针类型的复杂语言,并拥有可观的效率(在http://llvm.org/pubs/可以找到这篇论文“DataStructure Analysis: An Efficient Context-Sensitive Heap Analysis”)。
DSA算法的实现目前在llvm的poolalloc项目下(poolalloc的源代码可通过SVN从http://llvm.org/svn/llvm-project/poolalloc/trunk获取),poolalloc是应用DSA的一个强大的分配池框架。ChrisLatter的论文对此有详尽的描述。据注释显示,DSA的实现尚未稳定,还在剧烈改动中。
DSA算法在llvm的中间表达形式(llvm-IR)的基础上实现,这个中间表达形式的特点是保存了尽可能多的类型信息。而这是DSA能够实现的重要条件(llvm-IR的详尽说明可以参考http://llvm.org/docs/LangRef.html)。
1. DSA算法的架构
1.1. BasicDataStructures的注册
BasicDataStructures是一个近似“玩具”的DSA算法(它假定指针可以指向所有可能的地方,这个近似粗糙但简单,poolalloc还有其它更为精确的实现,后面会看到)。它可以用于测试DSA的框架,另外就没有太多的用处。它的注册入口是:
30 static RegisterPass<BasicDataStructures>
31 X("dsa-basic","Basic Data Structure Analysis(No Analysis)");
Llvm系统一个重要的部分是LLVMPass框架。遍(pass)执行构成编译器的转换与优化,它们构建这些转换所使用的分析结果,并且最重要的是它们是构造编译器代码的一个技术。在llvm中“遍”就是由Pass来抽象,所有的llvm pass都是Pass类的子类,并通过上面的RegisterPass函数向llvm注册。其中,第二个参数是调试用的“打印名”,这个名字明确告诉我们BasicDataStructures实际没做任何实际分析。
202 template<typenamepassName>
203 struct RegisterPass: public PassInfo {
204
205 // Register Passusing default constructor...
206 RegisterPass(constchar *PassArg, const char *Name, bool CFGOnly =false,
207 bool is_analysis = false)
208 : PassInfo(Name,PassArg, &passName::ID,
209 PassInfo::NormalCtor_t(callDefaultCtor<passName>),
210 CFGOnly, is_analysis) {
211 PassRegistry::getPassRegistry()->registerPass(*this);
212 }
213 };
209行的PassInfo::NormalCtor_t是一个定义在PassInfo中函数指针,其定义为:typedefPass* (*NormalCtor_t)(),它与callDefaultCtor的实例绑定。callDefaultCtor在堆上构建模板参数passName所指定类型的一个实例:
182 template<typenamePassName>
183 Pass *callDefaultCtor(){ return newPassName(); }
208行的基类PassInfo的构造函数定义如下:
57 PassInfo(const char *name, constchar *arg, const void *pi,
58 NormalCtor_t normal, bool isCFGOnly,bool is_analysis)
59 : PassName(name), PassArgument(arg),PassID(pi),
60 IsCFGOnlyPass(isCFGOnly),
61 IsAnalysis(is_analysis),IsAnalysisGroup(false), NormalCtor(normal) { }
这个构造函数初始化了PassInfo的所有成员:
44 constchar *constPassName; //Nice name for Pass
45 constchar *constPassArgument; // Command Line argument to run thispass
46 const void *PassID;
47 const boolIsCFGOnlyPass; // Pass only looks at the CFG.
48 const boolIsAnalysis; // True if an analysis pass.
49 const boolIsAnalysisGroup; // True if an analysis group.
50 std::vector<constPassInfo*> ItfImpl;// Interfaces implemented bythis pass
51
52 NormalCtor_t NormalCtor;
注意46行的PassID,llvm使用&passName::ID(即地址)来识别不同的Pass。在RegisterPass的211行,PassRegistry::getPassRegistry()返回自己的一个实例:
34 PassRegistry *PassRegistry::getPassRegistry() {
35 return &*PassRegistryObj;
36 }
其中,PassRegistryObj是一个静态的对象,它被声明为:
33 static ManagedStatic<PassRegistry>PassRegistryObj;
类ManagedStatic会按需创建它所封装的全局或静态对象(由成员指针Ptr指向,在援引时创建),因此PassRegistryObj必须被声明为全局或静态的。这样做的好处可以减少链接了llvm组件的动态库的启动时间。ManagedStatic的方法主要是重载对指针的*及->操作符。例如非const的操作符:
60 template<classC>
61 class ManagedStatic: public ManagedStaticBase{
…
65 C &operator*(){
66 void* tmp = Ptr;
67 if (llvm_is_multithreaded())sys::MemoryFence();
68 if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
69 TsanHappensAfter(this);
70
71 return *static_cast<C*>(Ptr);
72 }
73 C *operator->(){
74 void* tmp = Ptr;
75 if (llvm_is_multithreaded())sys::MemoryFence();
76 if (!tmp) RegisterManagedStatic(object_creator<C>, object_deleter<C>::call);
77 TsanHappensAfter(this);
78
79 return static_cast<C*>(Ptr);
80 }
其中68行的object_creator与object_deleter是两个简单的模板类型,对指定类型分配实例及销毁实例。
24 template<class C>
25 void* object_creator(){
26 return new C();
27 }
31 template<typenameT> struct object_deleter{
32 static voidcall(void * Ptr) { delete (T*)Ptr; }
33 };
而ManagedStatic的基类ManagedStaticBase的定义如下:
39 class ManagedStaticBase{
40 protected:
41 // This should onlybe used as a static variable, which guarantees that this
42 // will be zeroinitialized.
43 mutable void*Ptr;
44 mutable void(*DeleterFn)(void*);
45 mutable const ManagedStaticBase *Next;
46
47 void RegisterManagedStatic(void*(*creator)(), void (*deleter)(void*)) const;
48 public:
49 /// isConstructed -Return true if this object has not been created yet.
50 bool isConstructed() const{ return Ptr != 0; }
51
52 void destroy() const;
53 };
其中最重要的一个方法是RegisterManagedStatic——注册受控全局/静态对象。它由ManagedStatic重载的*及->操作符调用。
22 void ManagedStaticBase::RegisterManagedStatic(void*(*Creator)(),
23 void (*Deleter)(void*)) const {
24 if (llvm_is_multithreaded()) {
25 llvm_acquire_global_lock();
26
27 if (Ptr == 0) {
28 void* tmp = Creator ? Creator() : 0;
29
30 TsanHappensBefore(this);
31 sys::MemoryFence();
32
33 // This writeis racy against the first read in the ManagedStatic
34 // accessors.The race is benign because it does a second read after a
35 // memoryfence, at which point it isn't possible to get a partial value.
36 TsanIgnoreWritesBegin();
37 Ptr = tmp;
38 TsanIgnoreWritesEnd();
39 DeleterFn = Deleter;
40
41 // Add to listof managed statics.
42 Next = StaticList;
43 StaticList = this;
44 }
45
46 llvm_release_global_lock();
47 } else {
48 assert(Ptr== 0 && DeleterFn == 0 && Next == 0 &&
49 "Partially initializedManagedStatic!?");
50 Ptr = Creator ? Creator() : 0;
51 DeleterFn = Deleter;
52
53 // Add to list ofmanaged statics.
54 Next = StaticList;
55 StaticList = this;
56 }
57 }
24~46行是对多线程的处理。llvm及前端clang有一个feature,计划让这些软件运行在多线程环境下。这会使得这些程序更加强大,能更好地利用今后的多核环境。不过多线程带来了开发,尤其是调试上的困难,为了能够尽可能自动地检测竞争情形,llvm实现了一个实验性的编译时测试遍(instrumentation pass),并允许直接把代码与ThreadSanitizer(Tsan)链接(http://code.google.com/p/data-race-test/wiki/CompileTimeInstrumentation参考更多信息)。这里使用了其中的动态修饰(dynamic annotation)机制(更多信息参考http://code.google.com/p/data-race-test/wiki/DynamicAnnotations),插入同步代码以检测是否违反同步条件。
除去同步代码,RegisterManagedStatic做的事情就是调用传入的构建函数(即创建注册的Pass),记录删除用的方法,并把自己所在的实例链入StaticList——这是一个静态的ManagedStaticBase链表。为了控制析构该链表及其包含的对象的时机,llvm定义了llvm_shutdown函数:
76 void llvm::llvm_shutdown() {
77 while (StaticList)
78 StaticList->destroy();
79
80 if (llvm_is_multithreaded())llvm_stop_multithreaded();
81 }
而这个函数进而由llvm_shutdown_obj类型的对象在析构时调用:
105 struct llvm_shutdown_obj{
106 llvm_shutdown_obj() { }
107 explicitllvm_shutdown_obj(bool multithreaded) {
108 if (multithreaded)llvm_start_multithreaded();
109 }
110 ~llvm_shutdown_obj() { llvm_shutdown(); }
111 };
通常程序会在main函数中声明一个局部的llvm_shutdown_obj对象,比如与Pass有密切关系的opt的程序(opt可以运行指定的Pass)。下面是具体的注册方法。
105 void PassRegistry::registerPass(const PassInfo &PI, bool ShouldFree) {
106 sys::SmartScopedLock<true> Guard(*Lock);
107 PassRegistryImpl *Impl = static_cast<PassRegistryImpl*>(getImpl());
108 bool Inserted =
109 Impl->PassInfoMap.insert(std::make_pair(PI.getTypeInfo(),&PI)).second;
110 assert(Inserted&& "Pass registered multiple times!");
111 (void)Inserted;
112 Impl->PassInfoStringMap[PI.getPassArgument()] = &PI;
113
114 // Notify anylisteners.
115 for(std::vector<PassRegistrationListener*>::iterator
116 I = Impl->Listeners.begin(), E =Impl->Listeners.end(); I != E; ++I)
117 (*I)->passRegistered(&PI);
118
119 if (ShouldFree)Impl->ToFree.push_back(&PI);
120 }
PassRegistry本身只实现方法,相关的数据由PassRegistryImpl封装,llvm代码中有很多这样的适配器。PassRegistryImpl的实例由107行的getImpl()分配。注意,PassRegistryImpl声明在匿名名字空间中,除了在本文件中,其它地方不可见。
44 namespace {
45 struct PassRegistryImpl{
46 /// PassInfoMap -Keep track of the PassInfo object for each registered pass.
47 typedefDenseMap<const void*, const PassInfo*> MapType;
48 MapType PassInfoMap;
49
50 typedefStringMap<const PassInfo*> StringMapType;
51 StringMapType PassInfoStringMap;
52
53 ///AnalysisGroupInfo - Keep track of information for each analysis group.
54 structAnalysisGroupInfo {
55 SmallPtrSet<constPassInfo *, 8> Implementations;
56 };
57 DenseMap<constPassInfo*, AnalysisGroupInfo> AnalysisGroupInfoMap;
58
59 std::vector<constPassInfo*> ToFree;
60 std::vector<PassRegistrationListener*>Listeners;
61 };
62 } //end anonymous namespace
在registerPass的109行,PassInfo::getTypeInfo()返回的是PassInfo::PassID,在这里即是BasicDataStructures::ID的地址。如果对某个Pass感兴趣,可以从PassRegistrationListener派生,这样在每个Pass注册时,将会得到通知(117行的passRegistered)。
112行的getPassArgument返回了注册BasicDataStructures时的字符串"dsa-basic"的地址,以这个地址作为键值在Map中保存注册项(另外,它也可作为opt工具的一个选项,表示选用BasicDataStructure进行分析)。