使用“英特尔SSE”执行分支的最佳方式是什么



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

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

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

我需要一个能与所有比较运算符>、>=、<一起工作的东西<=,==和!=

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

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

那么,处理这样的代码的最佳方式是什么呢?有比我的第一个解决方案更好的说明吗?

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

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的条件代码,并将它们与你需要的跳转相匹配。

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

正如@harold正确地说的那样,无序的比较结果存储在奇偶校验标志中。

但是,当任何操作数都是NaN时,无序比较为true,如本堆栈溢出文章中所述。这意味着NaN将小于、等于并大于绝对每个数字,包括NaN

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

对于<=:

ucomisd xmm0, xmm1
jbe else_label

对于<:

ucomisd xmm0, xmm1
jb else_label

在以下gcc拆卸中确认,其中I 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也是真的!),你可以使用无序比较,如前一个答案所示。当然,正如你所看到的,它可能会慢一点,因为它需要额外的检查。

最新更新