使用LLVM优化使用大型结构的程序

时间:2015-04-16 19:56:53

标签: optimization llvm

我做了一个玩具Brainfuck编译器。它有效,但鉴于已知的初始状态,输出远没有我希望的那么优化。

我有这种状态结构:

struct state {
    unsigned char mem[0x1000];
    unsigned long ip;
    unsigned index;
};

状态结构(在LLVM IR中看起来像type { [4096 x i8], i64, i32 })使用alloca指令进行分配,然后使用memset调用(内在版本)进行归零。

我的操作按照您的预期实施:

  • <state.index--
  • >state.index++
  • -state.mem[state.index]--
  • +state.mem[state.index]++
  • .putchar(state.mem[state.index])
  • ,state.mem[state.index] = getchar()
  • [作为while (state.mem[state.index] != 0) {循环的开头
  • ]作为循环的结尾

对于每个操作,我发出了我能想到的最简单匹配的LLVM IR。例如,+实现为:

; %index = &state.index
  %index = getelementptr inbounds %"state", %"state"* %state, i64 0, i32 1

; %0 = *%index
  %0 = load i64, i64* %index, align 8

; %arrayidx = &state.mem[%0]
  %arrayidx = getelementptr inbounds %"state", %"state"* %state, i64 0, i32 0, i64 %0

; %1 = *%arrayidx
  %1 = load i8, i8* %arrayidx, align 1

; %inc = %1 + 1
  %inc = add i8 %1, 1

; *arrayidx = %inc
  store i8 %inc, i8* %arrayidx, align 1

我认为这将是足够的信息让LLVM如此努力地优化程序,几乎没有剩下任何东西。初始状态是已知的,没有指向它的指针,并且容易检测顺序增量。显然,循环更难以优化,但我可以理解。

令我失望的是,由此产生的代码仍然是getelementptrloadstore的混乱。这些都不是为了更简单的事情而被删除。

我不确定我是否只是做错了,所以我采用了一个hello world程序并将其转换为C,基本上用匹配的C代码替换每个Brainfuck字符,如上所示,用{{{ 1}}并抛弃生成的IR,发现它非常等效。看起来Clang不再比我糟糕的玩具编译器更能应对这种情况了。

但是,如果我从结构中取出O3并使其成为本地结构,Clang能够将其大部分用途优化为IR寄存器。那么这里的交易是什么?为什么LLVM无法优化对结构的访问模式?有没有办法告诉LLVM这个内存是100%私有的,它能以任何方式优化它的使用吗?

如果这有重要区别,那我就是LLVM 3.7 svn,这是上周某个时候的最新版本。

1 个答案:

答案 0 :(得分:0)

很可能您没有提供数据布局字符串。没有这个,许多优化器都无法产生不错的结果 - 它们无法知道指针的大小等等。

有关详细信息,请参阅http://llvm.org/docs/LangRef.html#data-layout。我建议抓取平台上clang生成的数据布局字符串,然后粘贴到.ll作为第一步。