dsa算法(1)

来源:互联网 发布:经典网络歌曲大全 编辑:程序博客网 时间:2024/05/21 06:27

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      staticRegisterPass<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    structRegisterPass: 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      staticManagedStatic<PassRegistry>PassRegistryObj;

 

类ManagedStatic会按需创建它所封装的全局或静态对象(由成员指针Ptr指向,在援引时创建),因此PassRegistryObj必须被声明为全局或静态的。这样做的好处可以减少链接了llvm组件的动态库的启动时间。ManagedStatic的方法主要是重载对指针的*及->操作符。例如非const的操作符:

 

60      template<classC>

61      classManagedStatic: 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      classManagedStaticBase{

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    structllvm_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      structPassRegistryImpl{

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进行分析)。


0 0