如何重新排列 LLVM GEP 指令?

How to re-arragne LLVM GEP instructions?

我有如下所示的 LLVM IR:

 for.body:                                         ; preds = %for.cond
  %add = add nsw i32 %i.0, 3
  %idxprom = sext i32 %add to i64
  %arrayidx = getelementptr inbounds i32, i32* %arr, i64 %idxprom
  %0 = load i32, i32* %arrayidx, align 4
  %add1 = add nsw i32 %sum1.0, %0
  %add2 = add nsw i32 %i.0, 2
  %idxprom3 = sext i32 %add2 to i64
  %arrayidx4 = getelementptr inbounds i32, i32* %arr, i64 %idxprom3
  %1 = load i32, i32* %arrayidx4, align 4
  %add5 = add nsw i32 %sum2.0, %1
  %add6 = add nsw i32 %i.0, 1
  %idxprom7 = sext i32 %add6 to i64
  %arrayidx8 = getelementptr inbounds i32, i32* %arr, i64 %idxprom7
  %2 = load i32, i32* %arrayidx8, align 4
  %add9 = add nsw i32 %sum3.0, %2
  %idxprom10 = sext i32 %i.0 to i64
  %arrayidx11 = getelementptr inbounds i32, i32* %arr, i64 %idxprom10
  %3 = load i32, i32* %arrayidx11, align 4
  %add12 = add nsw i32 %sum4.0, %3
  br label %for.inc

我想重新安排上面的 GEP 说明。对于这个例子,它应该像下面这样排列:

  %arrayidx11 = getelementptr inbounds i32, i32* %arr, i64 %idxprom10
  %arrayidx8 = getelementptr inbounds i32, i32* %arr, i64 %idxprom7
  %arrayidx4 = getelementptr inbounds i32, i32* %arr, i64 %idxprom3
  %arrayidx = getelementptr inbounds i32, i32* %arr, i64 %idxprom

我知道即使是数组访问的使用也必须在这种安排之后移动。所以我正在尝试使用以下代码为每个 GEP 指令获取使用链:

    // Get all the use chain instructions
    for (Value::use_iterator i = inst1->use_begin(),e = inst1->use_end(); i!=e;++i) {
      dyn_cast<Instruction>(*i)->dump();
    }

但是我只得到了这段代码的声明指令,我期望得到 %arrayidx4 的以下所有指令:

  %arrayidx4 = getelementptr inbounds i32, i32* %arr, i64 %idxprom3
  %1 = load i32, i32* %arrayidx4, align 4

请帮帮我。提前致谢。

我不太喜欢这个问题,但我今天应该为我的税做文书工作...

您的首要任务是找到 GEP 并将它们按您想要的顺序排序。这样做时,您需要一个单独的列表。 LLVM 的 BasicBlock class 确实提供了一个列表,但作为一般规则,在迭代它时切勿修改该列表。这是允许的,但太容易出错了。

所以在开始时:

std::vector<GetElementPtr *> geps;
for(auto & i : block->getInstList())
    if(GetElementPtrInst * g = dyn_cast<GetElementPTrInst>(&i))
        geps.push_back(g);

您可以使用任何容器 class,您的项目代码标准可能会建议使用 std::whatever 或 LLVM class。

接下来,将 geps 排序为您喜欢的顺序。我省略了那部分。

之后,将每个GEP移动到块中最新的允许点。那是哪一点?好吧,如果这个块是有效的,那么每个 GEP 已经在它使用的值之后和使用它的指令之前,所以将它移动到可能更晚的点,同时保持它在它的用户会做之前。

for(auto g : geps) {
    Instruction * firstUser = nullptr;
    for(auto u : g->users()) {
        Instruction * i = dyn_cast<Instruction>(u);
        if(i &&
           i->getParent() == g->getParent() &&
           (!firstUser ||
            i->comesBefore(firstUser)))
              firstUser = i;
        }
    }
    if(firstUser)
        g->moveBefore(firstUser);
}

对于每个用户,检查它是否是同一基本块内的指令,如果是,则检查它是否比目前看到的其他用户在块中更早。最后,移动GEP。

您可能更喜欢不同的方法。有几种可能。例如,您可以在对 GEP 进行排序后对其重新排序(使用 moveAfter() 将每个 GEP 移到前一个 GEP 之后),然后使用 users()moveAfter() 的组合来确保所有用户都是在他们使用的说明之后。

for(auto u : foo->users))) {
    Instruction * i = dyn_cast<Instruction>(u);
    if(i &&
       i->getParent() == foo->getParent() &&
       i->comesBefore(foo))
        i->moveAfter(foo);
}

再次注意,此代码在遍历时从不修改基本块的列表。如果您有任何神秘错误,请先检查一下。