如何使LLVM优先使用一条机器指令而不是另一条机器指令?

问题描述 投票:4回答:2

假设我在目标机器中有两个寄存器计算块:I和X。一个只能对I寄存器应用整数运算,对X寄存器同时应用整数和浮点运算。还有两种类型的指令:

def ADDIi32 : MyInstruction< ..., (outs I:$Rs), (ins I:$Rm, I:$Rn), [(set i32:$Rs, (add i32:$Rm, i32:$Rn)]>;
...

def ADDXi32 : MyInstruction< ..., (outs X:$Rs), (ins X:$Rm, X:$Rn), [(set i32:$Rs, (add i32:$Rm, i32:$Rn)]>;
def ADDXf32 : MyInstruction< ..., (outs X:$Rs), (ins X:$Rm, X:$Rn), [(set f32:$Rs, (fadd f32:$Rm, f32:$Rn)]>;
...

它们的编码不同,具有不同的字符串。所以llvm可以映射

int a, b;
a = a + b;

至ADDIi32或ADDXi32,但

float a, b;
a = a + b;

仅映射到ADDXf32。

我希望LLVM在可能的情况下使用ADDIi32,但不幸的是,我发现没有办法告诉它一条指令(或寄存器)比另一条指令“花费更多”。 Register类中的CostPerUse似乎是候选对象,但它定义了组中其他寄存器之间的成本,而不是所有寄存器之间的成本。我看到一些线程声称AddedComplexity(指令类中的字段具有模糊的描述)控制着选择模式,但也无能为力。

似乎LLVM选择了第一个匹配的指令。当我在ADDIi32之前定义ADDXf32和ADDXi32时,它仅对任何类型的数据使用X函数。当我在ADDXf32和ADDXi32之前定义ADDIi32时,它对整数数据使用I指令,但不匹配任何浮点数据(怪异)。我想我可以在ADDXf32和ADDXi32之间插入ADDIi32,但它们当前位于不同的.td文件中,并且它看起来不像是长期解决方案。

有什么想法吗?

c++ llvm llvm-clang
2个回答
8
投票

[注意,声明顺序不影响指令选择:LLVM仅通过首先匹配更复杂的模式进行排序(这是AddedComplexity会影响的-请注意增加AddedComplexity increases匹配模式的机会),通过匹配来决胜具有较小CodeSize的指令,最终不确定地进行平局。因此,您不能依赖声明的顺序。


编辑:我原始答案的以下部分实际上不适用于您的问题-参见http://lists.llvm.org/pipermail/llvm-dev/2007-September/010833.html。特别是:

ISel模式在分配寄存器之前与DAG匹配。所以ISel模式不能考虑注册类。模式中的寄存器类别是一个选择寄存器分配器的约束(如果选择了该指令),不受选择该模式的限制。


除了I和X寄存器类,还应该有一个“ IntRegsRegClass”或类似的类,在其中添加I和X寄存器。 (这是您调用addRegisterClass的reg类。)

在此寄存器类中添加寄存器的顺序确定了首选分配顺序。


2
投票

这看起来与m68k具有可疑的裂脑D(ata)和A(ddress)寄存器相似。为了进行大规模手动处理,D寄存器靠近ALU,用于整数运算,而A寄存器靠近地址计算单元。这意味着,如果指针位于D寄存器中,则在取消引用之前必须将其复制到A寄存器中。

加法是一种对两种寄存器都有意义的操作,因为一个计数器会同时增加计数器和指针,因此有单独的ADD和ADDA指令。它们可能在芯片上完全不同地实现,但是程序员唯一可见的区别是ADD根据结果更新条件代码,而ADDA不会影响它们。

[其他几条指令也以单独的地址特殊格式出现,包括MOVE,这是我在为m68k后端实现单独的MOVE和MOVEA时遇到的相同问题,并烦恼错误的是被挑选。这就是我最终找到此SO帖子的方式,Cheng Sun的回答告诉我我不是真正想听的,但确实需要知道的。

我的新方法是定义三个寄存器类,Dn,An和Xn(Xn是m68k的现有约定),分别由D,A以及D和A组成。我已经将ADD / ADDA合并为一个新的伪指令ADDZ(已经存在ADDX),该伪指令对未定义条件代码的Xn类起作用。我计划以后再添加一个修正程序,以将这种伪指令转换为适当的有效指令,并在条件代码已经设置的情况下删除任何多余的TST指令。

这已停止了来自例如当ADD更有意义时,先发布ADDA,然后发布MOVE。因为寄存器分配器避免了多余的移动,所以即使A寄存器在Xn类的最后列出,也将使用A寄存器执行指针算法。如果我还支持浮点,则将FADD指令定义为使用单独的FPx寄存器类,因此不会与现有的ADD和ADDZ模式冲突。

对于您的I和X寄存器示例,我将分别为I和X以及X类创建GPR和XR类。 GPR类还将首先列出所有I寄存器,因此X寄存器仅用作最后的手段。然后,我将这样编写您的示例模式:

def ADDi32 : MyInstruction< ..., (outs GPR:$Rs), (ins GPR:$Rm, GPR:$Rn), [(set i32:$Rs, (add i32:$Rm, i32:$Rn)]>;
def ADDf32 : MyInstruction< ..., (outs XR:$Rs), (ins XR:$Rm, XR:$Rn), [(set f32:$Rs, (fadd f32:$Rm, f32:$Rn)]>;
© www.soinside.com 2019 - 2024. All rights reserved.