错误的 LLVM 别名分析

Incorrect LLVM alias analysis

我在问一个类似于 this post 的问题,关于 LLVM 别名分析似乎给出了不正确的结果。 由于它包含大量重写, 我决定 post 它作为一个单独的问题。 我是 运行 这个非常简单的代码:

char *foo()
{
    int i;
    int size;
    char *s=malloc(5);
    char *p=malloc(8);

    while ((i < size) && (s < p))
    {
        i--;
    }
    return NULL;
}

每次我的代码遇到 icmp 指令时,我都会询问是否 它的操作数可以是彼此的别名。对于第一次比较 它平凡地回答不,但对于之间的第二次比较 %tmp2%tmp3 它回答是。 这是生成的 LLVM 位码:

; Function Attrs: nounwind uwtable
define internal i8* @foo() #0 {
entry:
  %i = alloca i32, align 4
  %s = alloca i8*, align 8
  %p = alloca i8*, align 8
  %size = alloca i32, align 4
  %call = call noalias i8* @malloc(i64 8) #3
  store i8* %call, i8** %s, align 8
  %call1 = call noalias i8* @malloc(i64 13) #3
  store i8* %call1, i8** %p, align 8
  br label %while.cond

while.cond:    ; preds = %while.body, %entry
  %tmp = load i32, i32* %i, align 4
  %tmp1 = load i32, i32* %size, align 4
  %cmp = icmp sgt i32 %tmp, %tmp1
  br i1 %cmp, label %land.rhs, label %while.end

land.rhs:    ; preds = %while.cond
  %tmp2 = load i8*, i8** %p, align 8
  %tmp3 = load i8*, i8** %s, align 8
  %cmp2 = icmp ult i8* %tmp2, %tmp3
  br i1 %cmp2, label %while.body, label %while.end

while.body:    ; preds = %land.rhs
  %tmp5 = load i32, i32* %i, align 4
  %dec = add nsw i32 %tmp5, -1
  store i32 %dec, i32* %i, align 4
  br label %while.cond

while.end:    ; preds = %while.cond, %land.rhs
  ret i8* null
}

当我查看规格时 the LLVM documentation 中的通行证 -basicaa,它明确表示(强调我的):

Distinct globals, stack allocations, and heap allocations can never alias.

但这里就是这种情况,有两个不同的堆分配。 怎么回事?

编辑:

这是 print-alias-sets 的输出

opt -basicaa -aa-eval -globals-aa -scev-aa -loop-simplify -instnamer -print-alias-sets -indvars -simplifycfg -view-cfg -o ./examples/input.ready.bc ./examples/input.bc
Alias Set Tracker: 4 alias sets for 4 pointer values.
  AliasSet[0x563ec3312a90, 1] must alias, Mod       Pointers: (i32* %argc.addr, 4)
  AliasSet[0x563ec3312b30, 1] must alias, Mod       Pointers: (i8*** %argv.addr, 8)
  AliasSet[0x563ec3312bd0, 1] must alias, Mod/Ref   Pointers: (i32* %i, 4)
  AliasSet[0x563ec3312c70, 2] may alias, Mod/Ref   Pointers: (i8* %arrayidx, 1)
  3 Unknown instructions: i8* %call, i32 %call1, i8* %call2

Alias Set Tracker: 3 alias sets for 3 pointer values.
  AliasSet[0x563ec3312a90, 1] must alias, Mod/Ref   Pointers: (i8** %lp.addr, 8)
  AliasSet[0x563ec3312b80, 1] must alias, Mod       Pointers: (i32* %size.addr, 4)
  AliasSet[0x563ec3312ae0, 1] must alias, Mod/Ref   Pointers: (i32* %i, 4)

Alias Set Tracker: 5 alias sets for 4 pointer values.
  AliasSet[0x563ec3312b80, 1] may alias, Mod/Ref   
  2 Unknown instructions: i8* %call, i8* %call1
  AliasSet[0x563ec3312bd0, 1] must alias, Mod/Ref   Pointers: (i8** %s, 8)
  AliasSet[0x563ec33129f0, 1] must alias, Mod/Ref   Pointers: (i8** %p, 8)
  AliasSet[0x563ec33229c0, 1] must alias, Mod/Ref   Pointers: (i32* %i, 4)
  AliasSet[0x563ec3322a60, 1] must alias, Ref       Pointers: (i32* %size, 4)

===== Alias Analysis Evaluator Report =====
  94 Total Alias Queries Performed
  73 no alias responses (77.6%)
  18 may alias responses (19.1%)
  2 partial alias responses (2.1%)
  1 must alias responses (1.0%)
  Alias Analysis Evaluator Pointer Alias Summary: 77%/19%/2%/1%
  54 Total ModRef Queries Performed
  17 no mod/ref responses (31.4%)
  0 mod responses (0.0%)
  3 ref responses (5.5%)
  34 mod & ref responses (62.9%)
  Alias Analysis Evaluator Mod/Ref Summary: 31%/0%/5%/62%

另一个编辑

以下是我在(循环)通道中检查别名的方法:

virtual bool runOnLoop(Loop *loop, LPPassManager &LPM)
{       
    AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();

    for (auto it = loop->block_begin(); it != loop->block_end(); it++)
    {
        for (auto inst = (*it)->begin(); inst != (*it)->end(); inst++)
        {
            Instruction *i = (Instruction *) inst;
            if (strncmp(i->getOpcodeName(),"icmp",4) == 0)
            {
                CmpInst *ci = (CmpInst *) i;
                if (AA->isNoAlias(
                    ci->getOperand(0),
                    ci->getOperand(1)))
                {
                    errs() << ci->getOperand(0)->getName().str();
                    errs() << " and ";
                    errs() << ci->getOperand(1)->getName().str();
                    errs() << " are NOT aliases\n";
                }
                else
                {
                    errs() << ci->getOperand(0)->getName().str();
                    errs() << " and ";
                    errs() << ci->getOperand(1)->getName().str();
                    errs() << " are aliases of one another\n";
                }
            }
        }
    }

不同的堆分配被正确地视为独立的(即非别名),您可以通过在入口基本块的末尾添加此指令来检查:

%cmp3 = icmp ult i8* %call, %call1

为了正确地进行别名分析 "kick in",您需要 运行 优化代码。 在这种情况下,还应该添加 -mem2reg

此外,请确保您正在分析的函数未标记 optnone 属性(反汇编并检查可读的 IR 代码),这将禁用对其的优化。