使用英特尔SSE执行分支的最佳方法是什么?

时间:2012-03-04 19:43:41

标签: assembly compiler-construction intel sse

我正在编写编译器,我必须输出浮点值的分支条件代码。例如,要编译这种代码:

if(a <= b){
    //1. DO something
} else {
    //2. Do something else
}

当a和b是浮点变量时。如果条件不正确,我只需要跳到2,否则降到1.我正在考虑编译器级别的优化,考虑1和2中的内容。

我需要适用于所有比较运算符的内容&gt;,&gt; =,&lt;,&lt; =,==和!=

我发现进行比较的方法是使用CMPLTSD(以及其他关系运算符的其他等效指令)。但是,我必须使用SSE寄存器,特别是结果,然后我必须在通用寄存器(例如eax)上移动它的值,最后将值与0进行比较。

我还看到UCOMISD指令应该正确设置标志,但显然它不像我想象的那样工作。

那么,处理这样的代码的最佳方法是什么?有没有比我的第一个解决方案更好的指示?

最好的,我的意思是,这个问题的一般解决方案。如果可能的话,我希望代码的行为与对整数进行比较的方式相同(cmp a,b; jge label)。当然,我更希望用最快的指令来实现这一目标。

2 个答案:

答案 0 :(得分:7)

ucomisd的条件代码不对应有符号整数比较代码,而是对应无符号代码(奇偶校验标志中为“无序”)。我承认,这有点奇怪,但都清楚地记录在案。 如果您真的想要分支的代码可能类似于<=

  ucomisd a,b
  ja else     ; greater
  jp else     ; unordered
  ; code for //1 goes here
  jmp end
else:
  ; code for //2 goes here
end:

<

jae else   ; greater or equal
jp else    ; unordered

如果你真的想要,我可以列出所有这些,但你可以查看ucomisd的条件代码,并将它们与你需要的跳跃相匹配。

答案 1 :(得分:2)

重要:@ harold的答案几乎完全正确,但有一个微妙的错误方面可能会让你在以后的一个非常重要的边缘情况下疯狂 - NaN处理从大多数语言倒退(如C ++)。

正如@harold所说,无序比较结果存储在奇偶校验标志中。

然而,如this stack overflow post中所述,当任何操作数为NaN 时,无序比较为真。这意味着NaN将小于,等于且大于绝对每个数字包括 NaN

因此,如果您希望您的语言与c ++与NaN的任何比较返回false的行为相匹配,那么您需要:

<=

ucomisd xmm0, xmm1
jbe else_label

<

ucomisd xmm0, xmm1
jb else_label

在以下gcc反汇编中确认,我return a >= b

144e:       66 0f 2e c8             ucomisd %xmm0,%xmm1
1452:       0f 93 c0                setae  %al

这里它使用setae,它是与jae等效的寄存器修改。然后立即返回而不检查奇偶校验标志。

为什么ja而不是jg,@ harold的答案仍然是一个明确而正确的解释。

当然,您不必使用有序比较,如果您希望绝对每个数字都小于,大于,,您可以使用前面答案中显示的无序比较在您的程序/语言中等于NaN(即使NaN < NaN为真!)。当然,正如您所看到的,它可能会慢一点,因为它需要额外的检查。