我做了一个玩具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如此努力地优化程序,几乎没有剩下任何东西。初始状态是已知的,没有指向它的指针,并且容易检测顺序增量。显然,循环更难以优化,但我可以理解。
令我失望的是,由此产生的代码仍然是getelementptr
,load
和store
的混乱。这些都不是为了更简单的事情而被删除。
我不确定我是否只是做错了,所以我采用了一个hello world程序并将其转换为C,基本上用匹配的C代码替换每个Brainfuck字符,如上所示,用{{{ 1}}并抛弃生成的IR,发现它非常等效。看起来Clang不再比我糟糕的玩具编译器更能应对这种情况了。
但是,如果我从结构中取出O3
并使其成为本地结构,Clang能够将其大部分用途优化为IR寄存器。那么这里的交易是什么?为什么LLVM无法优化对结构的访问模式?有没有办法告诉LLVM这个内存是100%私有的,它能以任何方式优化它的使用吗?
如果这有重要区别,那我就是LLVM 3.7 svn,这是上周某个时候的最新版本。
答案 0 :(得分:0)
很可能您没有提供数据布局字符串。没有这个,许多优化器都无法产生不错的结果 - 它们无法知道指针的大小等等。
有关详细信息,请参阅http://llvm.org/docs/LangRef.html#data-layout。我建议抓取平台上clang生成的数据布局字符串,然后粘贴到.ll作为第一步。