美文网首页软件测试自动化攻防Linux
【LLVM】AliasAnalysis别名分析的介绍与使用

【LLVM】AliasAnalysis别名分析的介绍与使用

作者: bsauce | 来源:发表于2019-06-05 18:59 被阅读0次

    一、介绍

    Alias Analysis (又名 Pointer Analysis)是用于确定两个指针是否指向内存中的同一对象,这里有很多不同的别名分析算法,分为几种类型:流敏感vs流非敏感、上下文敏感vs上下文非敏感、域敏感vs域非敏感、基于一致性的vs基于子集的。传统的别名分析用于回答must、may、no的问题,也即两个指针总是指向同一对象,可能指向同一对象以及绝不会指向同一对象。(SSA—静态单一赋值,将同一变量名用多个名表示,被赋值的变量名不会重复,便于寻找变量的产生与使用点)。

    LLVM AliasAnalysis类是实现别名分析的基础类,能够提供简单的别名分析信息,且能提供Mod/Ref信息,有利于进行更复杂的分析。本文介绍了该接口的实现与使用。

    首先,我们来了解一下别名分析,以及别名分析该如何使用。

    1.别名分析的作用

    例如以下c代码:

        int foo (int __attribute__((address_space(0)))* a,
                 int __attribute__((address_space(1)))* b) {
            *a = 42;
            *b = 20;
            return *a;
        }
    

    转换成llvm如下:

        define i32 @foo(i32 addrspace(0)* %a, i32 addrspace(1)* %b) #0 {
        entry:
          store i32 42, i32 addrspace(0)* %a, align 4
          store i32 20, i32 addrspace(1)* %b, align 4
          %0 = load i32, i32* %a, align 4
          ret i32 %0
        }
    

    现在需要对foo进行优化,去掉不必要的load:

        define i32 @foo(i32 addrspace(0)* %a, i32 addrspace(1)* %b) #0 {
        entry:
          store i32 42, i32 addrspace(0)* %a, align 4
          store i32 20, i32 addrspace(1)* %b, align 4
          ret i32 42
        }
    

    但是这个优化的前提是,a和b不能别名,否则会导致错误如下:

        int i = 0;
        int result = foo(&i, &i);
    

    以上可以看到,以上调用会使a和b别名,本应该返回20,结果因为优化的缘故返回了42,导致错误。所以编译器只有确定两个指针不会产生别名时,才能进行以上优化。

    2.使用方法

    一种实现是利用 llvm::AAResultBase,如果我们的目标是TAR,则可以创建一个从AAResultBase<TARAAResult>继承的类TARAAResult:

        class TARAAResult : public AAResultBase<TARAAResult> {
        public:
          explicit TARAAResult() : AAResultBase() {}
          TARAAResult(TARAAResult &&Arg) : AAResultBase(std::move(Arg)) {}
        
          AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
        };
    

    alias函数的输入是两个MemoryLocation,返回AliasResult。返回结果显示内存对象绝不别名、可能别名、部分别名或正好别名。

        AliasResult TARAAResult::alias(const MemoryLocation &LocA,
                                       const MemoryLocation &LocB) {
          auto AsA = LocA.Ptr->getType()->getPointerAddressSpace();
          auto AsB = LocB.Ptr->getType()->getPointerAddressSpace();
        
          if (AsA != AsB) {
            return NoAlias;
          }
        
          // Forward the query to the next analysis.
          return AAResultBase::alias(LocA, LocB);
        }
    

    二、AliasAnalysis类总览

    AliasAnalysis定义了别名分析必须支持的接口,并且导出了两个重要方法,AliasResult和ModRefResult输出别名结果和mod/ref结果。

    AliasAnalysis接口能够用不同的方式输出内存信息,例如,内存对象表示成开始地址和size,函数调用表示成实际的call和invoke指令。AliasAnalysis接口也提供了helper方法,允许你获取任意指令的mod/ref信息。

    1.指针表示

    AliasAnalysis类提供了不同的方法,来query两个内存对象是否别名,以及函数调用是否可以修改或读内存对象。对于所有query,内存对象表示为开始地址(符号化的LLVM值)+size。

    内存对象的表示对于正确的别名分析至关重要,例如以下c代码:

        int i;
        char C[2];
        char A[10];
        /* ... */
        for (i = 0; i != 10; ++i) {
          C[0] = A[i];          /* One byte store */
          C[1] = A[9-i];        /* One byte store */
        }
    

    对于以上代码,basicaa pass将消除对C[0]和C[1]的store,因为他们都是访问不同地址的单个字节,互不干扰,Loop Invariant Code Motion (LICM) pass会使用store motion移除循环中的store。

        int i;
        char C[2];
        char A[10];
        /* ... */
        for (i = 0; i != 10; ++i) {
          ((short*)C)[0] = A[i];  /* Two byte store! */
          C[1] = A[9-i];          /* One byte store */
        }
    

    相反,对于以上代码,两个对C的store会分开,因为访问&C[0]元素是2个字节访问;如果query中没有size信息,第一个案例也会别名。

    2.alias方法

    alias方法用于确定两个内存对象是否别名,它输入两个内存对象,输出MustAlias, PartialAlias, MayAlias, 或 NoAlias。对于所有AliasAnalysis接口,alias方法要求两个指针值在同一个函数中定义,或者至少1个值是常数。

    NoAlias表示两个内存对象没有任何重叠区域;MayAlias表示两个指针可能指向同一对象;PartialAlias表示两个内存对象可能有重叠;MustAlias表示两个内存对象总是从同一位置开始。

    3.GetModRefInfo方法

    GetModRefInfo方法返回信息是,指令是否可以读或修改某个内存区域。Mod/Ref信息是保守的,如果一条指令可能读或写某区域,就返回ModRef。

    4.其他AliasAnalysis方法

    (1)pointsToConstantMemory方法

    若能确定指针仅指向不变的内存区域(函数,全局常量,null指针)则返回true,该信息可以优化mod/ref信息:因为不变的内存区域是不能被修改的。

    (2)doesNotAccessMemory和onlyReadsMemory方法

    若确定函数从不读写内存,或者函数仅从常量内存读,则doesNotAccessMemory返回true。

    若确定函数仅从非易失性内存读,则onlyReadsMemory返回true。注意,满足doesNotAccessMemory方法的所有函数也都满足onlyReadsMemory。


    三、实现新的AliasAnalysis

    1.不同的Pass类型

    选择使用哪种LLVM pass来做别名分析取决于你想要解决哪种问题。

    • 做过程间分析,用Pass
    • 做局部函数的分析,用FunctionPass子类
    • 若不需要看程序,则选择ImmutablePass

    除了继承以上pass,你也需要继承AliasAnalysis接口,当然,也可以用RegisterAnalysisGroup模板去注册AliasAnalysis实现。

    2.进行初始化所需的调用

    所写的AliasAnalysis的子类需调用AliasAnalysis基类的两个方法:getAnalysisUsage和InitializeAliasAnalysis。例如,实现你的getAnalysisUsage时,除了声明pass依赖,还需显式调用AliasAnalysis::getAnalysisUsage方法。

        void getAnalysisUsage(AnalysisUsage &AU) const {
          AliasAnalysis::getAnalysisUsage(AU);
          // declare your dependencies here.
        }
    

    另外,在你的run方法中需调用InitializeAliasAnalysis方法(Pass—run;FunctionPass—runOnFunction;ImmutablePass—InitializePass)。

        bool run(Module &M) {
          InitializeAliasAnalysis(this);
          // Perform analysis here...
          return false;
        }
    
    3.需要覆盖的方法

    在你的AliasAnalysis子类中必须覆盖getAdjustedAnalysisPointer方法,例如:

        void *getAdjustedAnalysisPointer(const void* ID) override {
          if (ID == &AliasAnalysis::ID)
            return (AliasAnalysis*)this;
          return this;
        }
    
    4.可指定的接口

    所有AliasAnalysis虚方法都默认为其他别名分析提供一个链接,最终能返回正确结果(为alias query和mod/ref query返回May和Mod/Ref),根据你分析的功能,覆盖相应的接口即可。

    5.AliasAnalysis链接行为

    除了-no-aa pass,每个分析pass都链接到另一个别名分析的实现(例如,可以使用"-basicaa -ds-aa -licm"来从多个别名分析达到最大优化)。别名分析会自动处理你未覆盖的方法,对于已覆盖的方法,若需返回AmayAlias或Mod/Ref结果,只需返回超类计算的结果。

        AliasResult alias(const Value *V1, unsigned V1Size,
                          const Value *V2, unsigned V2Size) {
          if (...)
            return NoAlias;
          ...
        
          // Couldn't determine a must or no-alias result.
          return AliasAnalysis::alias(V1, V1Size, V2, V2Size);
        }
    

    除了分析查询,如果你需要覆盖某方法,需将更新通知传给超类,这样就允许所有别名分析进行更新。

    6.更新代码转换后的分析结果

    别名分析最初用于建立程序的静态快照,但用户也用于进行代码转换。所有别名分析都需要更新分析结果,以反映代码转换所做的变换。AliasAnalysis接口提供了4个方法来更新程序变化后的分析结果。

    (1)deleteValue方法

    当从程序删除某个指令或值(包括不使用指针的值)时调用deleteValue方法。通常,别名分析会保留数据结构,这些数据结构包含程序中每个值的条目。调用此方法时,则删除指定值的任何条目(如果存在)。

    (2)copyValue方法

    当程序引入新的值时调用copyValue方法。通常不会引入程序中不存在的值(编译器安全转换),所以这是引入新值的唯一方法,这个方法指示新值和拷贝值有相同的属性。

    (3)replaceWithNewValue方法

    用新值替换旧值,该方法不能被别名分析实现所覆盖。

    (4)addEscapingUse方法

    当指针的使用导致之前计算的分析结果无效时,调用addEscapingUse方法。该方法会提供保守的返回,或者重新计算分析结果。

    总之,只要有新的指针使用,就需要调用该方法,除了一下3种情况:

    • 指针的bitcast或getelementptr
    • 通过指针来store,但并非存指针
    • 通过指针load

    四、使用别名分析结果

    1.使用MemoryDependenceAnalysis Pass

    memdep pass使用别名分析来提供内存使用指令的高级依赖信息,例如,告诉你哪个store提供给了load。它也使用缓存等技术提高效率,一般用于Dead Store Elimination, GVN 和 memcpy优化。

    2.使用AliasSetTracker类

    有些代码转换需要某个代码区域内的活跃的别名集,而不是成对的别名信息。AliasSetTracker类可以根据AliasAnalysis接口提供的成对的别名分析信息,高效的构建所需的别名集。

    首先需要调用add方法对AliasSetTracker进行初始化,添加该代码区域内可能导致别名的指令。当别名集构建完成后,你可以利用AliasSetTrackerbegin() / end()方法迭代访问该别名集。

    AliasSetTracker构造的AliasSets是不相交的,计算mod/ref信息,并跟踪记录该集合所有指针是否是Must aliases。

    例如,Loop Invariant Code Motion pass使用AliasSetTracker来计算每个循环嵌套的别名集,如果循环的某个AliasSet没有被修改,则该集合所有的load指令可能被提出循环。如果所有别名集是store to 和must alias集,则store 在循环外部使用,这样在循环时将内存位置放入进村器。只有当指针参数是循环不变的,才采用这些转换。

    3.直接使用AliasAnalysis接口

    如果这些功能类都用不到,你可以直接使用AliasAnalysis类。尽量使用高级方法,以获取更高的准确率和效率。


    五、现有的别名分析实现

    1.可用的AliasAnalysis实现

    (1)-no-aa pass

    不做别名分析。

    (2)-basicaa pass

    (3)-globalsmodref-aa pass

    (4)-steens-aa pass

    (5)-ds-aa pass

    (6)-scev-aa pass

    注意:basicaa和steens-aa这类标准的LLVM pass太耗时了,Anderson Analysis也很耗时耗内存,已经有一些工作在优化别名分析。

    2.别名分析驱动的转换

    (1)-adce pass

    (2)-licm pass

    (3)-argpromotion pass

    (4)-gvn -memcpyopt -dse pass

    3.用于调试和评估的client

    命令:% opt -ds-aa -aa-eval foo.bc -disable-output -stats

    (1)-print-alias-sets pass

    % opt -ds-aa -print-alias-sets -disable-output

    (2)-aa-eval pass

    4.内存依赖分析

    正在将MemoryDependenceAnalysis迁移到MemorySSA。


    参考:

    https://llvm.org/docs/AliasAnalysis.html

    https://blog.tartanllama.xyz/llvm-alias-analysis/

    http://james0zan.github.io/resource/GSoC15-Proposal-AA.pdf

    相关文章

      网友评论

        本文标题:【LLVM】AliasAnalysis别名分析的介绍与使用

        本文链接:https://www.haomeiwen.com/subject/lewsxctx.html