assembly 如何在x86(32位)汇编中将无符号整数转换为浮点数?

zqry0prt  于 2022-11-13  发布在  其他
关注(0)|答案(3)|浏览(265)

我需要将32位和64位的无符号整数转换为xmm寄存器中的浮点值。有x86指令可以将 * 有符号整数 * 转换为单精度和双精度浮点值,但没有用于无符号整数的指令。
额外收获:如何将xmm寄存器中的浮点值转换为32位和64位无符号整数?

fjnneemd

fjnneemd1#

无耻地使用Janus的答案作为模板(毕竟我真的很喜欢C++):
在i7上使用gcc -march=native -O3生成,因此这是使用最多(包括)-mavxuint2float生成的,反之亦然,正如预期的那样,长转换只是对大于263-1的数字有一个特殊情况。

0000000000000000 <ulong2double>:
   0:   48 85 ff                test   %rdi,%rdi
   3:   78 0b                   js     10 <ulong2double+0x10>
   5:   c4 e1 fb 2a c7          vcvtsi2sd %rdi,%xmm0,%xmm0
   a:   c3                      retq   
   b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  10:   48 89 f8                mov    %rdi,%rax
  13:   83 e7 01                and    $0x1,%edi
  16:   48 d1 e8                shr    %rax
  19:   48 09 f8                or     %rdi,%rax
  1c:   c4 e1 fb 2a c0          vcvtsi2sd %rax,%xmm0,%xmm0
  21:   c5 fb 58 c0             vaddsd %xmm0,%xmm0,%xmm0
  25:   c3                      retq   

0000000000000030 <ulong2float>:
  30:   48 85 ff                test   %rdi,%rdi
  33:   78 0b                   js     40 <ulong2float+0x10>
  35:   c4 e1 fa 2a c7          vcvtsi2ss %rdi,%xmm0,%xmm0
  3a:   c3                      retq   
  3b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  40:   48 89 f8                mov    %rdi,%rax
  43:   83 e7 01                and    $0x1,%edi
  46:   48 d1 e8                shr    %rax
  49:   48 09 f8                or     %rdi,%rax
  4c:   c4 e1 fa 2a c0          vcvtsi2ss %rax,%xmm0,%xmm0
  51:   c5 fa 58 c0             vaddss %xmm0,%xmm0,%xmm0
  55:   c3                      retq   

0000000000000060 <uint2double>:
  60:   89 ff                   mov    %edi,%edi
  62:   c4 e1 fb 2a c7          vcvtsi2sd %rdi,%xmm0,%xmm0
  67:   c3                      retq   

0000000000000070 <uint2float>:
  70:   89 ff                   mov    %edi,%edi
  72:   c4 e1 fa 2a c7          vcvtsi2ss %rdi,%xmm0,%xmm0
  77:   c3                      retq
mm5n2pyu

mm5n2pyu2#

下面是GCC生成的代码。我把它们封装在函数中,但是你可以很容易地去掉堆栈处理。并不是所有的都使用SSE来做实际的工作(ulonglong转换没有),如果你找到相应的指令,请告诉我。Clang生成的代码几乎一样。

% cat tofloats.c 
double ulonglong2double(unsigned long long a) {
    return a;
}
float ulonglong2float(unsigned long long a) {
    return a;
}
double uint2double(unsigned int a) {
    return a;
}
float uint2float(unsigned int a) {
    return a;
}
% gcc -msse4.2 -g -Os -c tofloats.c && objdump -d tofloats.o
00000000 <ulonglong2double>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 10                sub    $0x10,%esp
   6:   8b 55 0c                mov    0xc(%ebp),%edx
   9:   8b 45 08                mov    0x8(%ebp),%eax
   c:   89 55 f4                mov    %edx,-0xc(%ebp)
   f:   85 d2                   test   %edx,%edx
  11:   89 45 f0                mov    %eax,-0x10(%ebp)
  14:   df 6d f0                fildll -0x10(%ebp)
  17:   79 06                   jns    1f <ulonglong2double+0x1f>
  19:   d8 05 00 00 00 00       fadds  0x0
  1f:   dd 5d f8                fstpl  -0x8(%ebp)
  22:   dd 45 f8                fldl   -0x8(%ebp)
  25:   c9                      leave  
  26:   c3                      ret    

00000027 <ulonglong2float>:
  27:   55                      push   %ebp
  28:   89 e5                   mov    %esp,%ebp
  2a:   83 ec 10                sub    $0x10,%esp
  2d:   8b 55 0c                mov    0xc(%ebp),%edx
  30:   8b 45 08                mov    0x8(%ebp),%eax
  33:   89 55 f4                mov    %edx,-0xc(%ebp)
  36:   85 d2                   test   %edx,%edx
  38:   89 45 f0                mov    %eax,-0x10(%ebp)
  3b:   df 6d f0                fildll -0x10(%ebp)
  3e:   79 06                   jns    46 <ulonglong2float+0x1f>
  40:   d8 05 00 00 00 00       fadds  0x0
  46:   d9 5d fc                fstps  -0x4(%ebp)
  49:   d9 45 fc                flds   -0x4(%ebp)
  4c:   c9                      leave  
  4d:   c3                      ret    

0000004e <uint2double>:
  4e:   55                      push   %ebp
  4f:   89 e5                   mov    %esp,%ebp
  51:   83 ec 08                sub    $0x8,%esp
  54:   66 0f 6e 45 08          movd   0x8(%ebp),%xmm0
  59:   66 0f d6 45 f8          movq   %xmm0,-0x8(%ebp)
  5e:   df 6d f8                fildll -0x8(%ebp)
  61:   c9                      leave  
  62:   c3                      ret    

00000063 <uint2float>:
  63:   55                      push   %ebp
  64:   89 e5                   mov    %esp,%ebp
  66:   83 ec 08                sub    $0x8,%esp
  69:   66 0f 6e 45 08          movd   0x8(%ebp),%xmm0
  6e:   66 0f d6 45 f8          movq   %xmm0,-0x8(%ebp)
  73:   df 6d f8                fildll -0x8(%ebp)
  76:   c9                      leave  
  77:   c3                      ret

以下是奖励点数(转换为整数):

% cat toints.c                                      
unsigned long long float2ulonglong(float a) {
    return a;
}
unsigned long long double2ulonglong(double a) {
    return a;
}
unsigned int float2uint(float a) {
    return a;
}
unsigned int double2uint(double a) {
    return a;
}
% gcc -msse4.2 -g -Os -c toints.c && objdump -d toints.o  
00000000 <float2ulonglong>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   83 ec 0c                sub    $0xc,%esp
   7:   d9 45 08                flds   0x8(%ebp)
   a:   d9 05 00 00 00 00       flds   0x0
  10:   d9 c9                   fxch   %st(1)
  12:   db e9                   fucomi %st(1),%st
  14:   73 0d                   jae    23 <float2ulonglong+0x23>
  16:   dd d9                   fstp   %st(1)
  18:   dd 4d f0                fisttpll -0x10(%ebp)
  1b:   8b 45 f0                mov    -0x10(%ebp),%eax
  1e:   8b 55 f4                mov    -0xc(%ebp),%edx
  21:   eb 13                   jmp    36 <float2ulonglong+0x36>
  23:   de e1                   fsubp  %st,%st(1)
  25:   dd 4d f0                fisttpll -0x10(%ebp)
  28:   8b 55 f4                mov    -0xc(%ebp),%edx
  2b:   8b 45 f0                mov    -0x10(%ebp),%eax
  2e:   8d 8a 00 00 00 80       lea    -0x80000000(%edx),%ecx
  34:   89 ca                   mov    %ecx,%edx
  36:   83 c4 0c                add    $0xc,%esp
  39:   5b                      pop    %ebx
  3a:   5d                      pop    %ebp
  3b:   c3                      ret    

0000003c <double2ulonglong>:
  3c:   55                      push   %ebp
  3d:   89 e5                   mov    %esp,%ebp
  3f:   53                      push   %ebx
  40:   83 ec 0c                sub    $0xc,%esp
  43:   dd 45 08                fldl   0x8(%ebp)
  46:   d9 05 00 00 00 00       flds   0x0
  4c:   d9 c9                   fxch   %st(1)
  4e:   db e9                   fucomi %st(1),%st
  50:   73 0d                   jae    5f <double2ulonglong+0x23>
  52:   dd d9                   fstp   %st(1)
  54:   dd 4d f0                fisttpll -0x10(%ebp)
  57:   8b 45 f0                mov    -0x10(%ebp),%eax
  5a:   8b 55 f4                mov    -0xc(%ebp),%edx
  5d:   eb 13                   jmp    72 <double2ulonglong+0x36>
  5f:   de e1                   fsubp  %st,%st(1)
  61:   dd 4d f0                fisttpll -0x10(%ebp)
  64:   8b 55 f4                mov    -0xc(%ebp),%edx
  67:   8b 45 f0                mov    -0x10(%ebp),%eax
  6a:   8d 8a 00 00 00 80       lea    -0x80000000(%edx),%ecx
  70:   89 ca                   mov    %ecx,%edx
  72:   83 c4 0c                add    $0xc,%esp
  75:   5b                      pop    %ebx
  76:   5d                      pop    %ebp
  77:   c3                      ret    

00000078 <float2uint>:
  78:   55                      push   %ebp
  79:   89 e5                   mov    %esp,%ebp
  7b:   83 ec 08                sub    $0x8,%esp
  7e:   d9 45 08                flds   0x8(%ebp)
  81:   dd 4d f8                fisttpll -0x8(%ebp)
  84:   8b 45 f8                mov    -0x8(%ebp),%eax
  87:   c9                      leave  
  88:   c3                      ret    

00000089 <double2uint>:
  89:   55                      push   %ebp
  8a:   89 e5                   mov    %esp,%ebp
  8c:   83 ec 08                sub    $0x8,%esp
  8f:   dd 45 08                fldl   0x8(%ebp)
  92:   dd 4d f8                fisttpll -0x8(%ebp)
  95:   8b 45 f8                mov    -0x8(%ebp),%eax
  98:   c9                      leave  
  99:   c3                      ret

这些函数从堆栈中获取输入并通过堆栈返回。如果在函数结束时需要XMM寄存器中的结果,可以使用movd/movq将其从堆栈中获取到XMM。如果函数返回的是双精度型,则结果为-0x8(%ebp)。如果它是浮点数,则结果为-0x4(%ebp)。Ulonglongs的长度为双精度数,而int的长度为浮点数。

第一个PLL:存储整数并截断

FISTTP使用截断(chop)作为舍入模式将ST中的值转换为有符号整数,将结果传输到目标,然后弹出ST。FISTTP接受字、短整数和长整数目标。

福康米:比较浮点值并设置EFLAGS

对寄存器ST(0)与ST(i)的内容执行无序比较,并根据比较结果设置EFLAGS寄存器中的状态标志ZF、PF及CF(请参阅下表)。比较时忽略零的符号,因此-0.0等于+0.0。

46qrfjad

46qrfjad3#

另请参阅 * How to efficiently perform double/int64 conversions with SSE/AVX? *,了解有限范围或全范围的压缩转换技巧。另请参阅 Strange uint32_t to float array conversion,了解编译器实现该转换的策略分析。关于此问题的其他答案仅显示编译器生成的代码,而不讨论其工作原理。
在AVX-512中,浮点型/双精度型与无符号64位整数之间的转换指令非常等效。此答案主要针对双精度型与uint64_t之间的转换,但vcvtuqq2ps(压缩uint64_t到压缩单精度)和类似的指令确实存在,vcvtusi2sd xmm1, xmm2, r/m64{er}只在64位模式下可用。使用SSE 1中同样不便的合并到某个寄存器语义,而不是零扩展到新的xmm。

floatdouble * 至 * uint64_t(带AVX-512 F)

AVX-512 F增加了对FP与无符号整数(标量vcvttsd2usi或压缩)之间的转换以及 * 压缩 * 与64位整数之间的转换(有符号或无符号,例如vcvttpd2uqq压缩双精度或vcvttps2uqq将float 32转换为uint64_t)的支持。
在AVX-512之前,使用32位无符号整数的标量转换在64位模式下或x87下很容易,使用非负64位有符号整数的零扩展。但即使在64位模式下,64位无符号整数也是一个问题。
vcvttsd2usi eax, xmm0在32或64位模式下工作,使用AVX-512。(或ss版本;浮点型与双精度型)
vcttvsd2usi rax, xmm0当然只能在64位模式下使用AVX-512。因此,我们可以改用压缩转换,因为在32位模式下不会阻止64位SIMD整数元素工作。
我不确定高半部分中的垃圾是否代表了一个次正规浮点数/双精度浮点数,我猜可能不会,因为将其舍入为整数与一个微小的正规化值没有什么不同。

;;; 32-bit mode can use packed 64-bit conversion then get the 2 halves
 vcvttpd2uqq  xmm1, xmm0    ; 2x truncating uint64_t from double conversions

 vmovd       eax, xmm1       ; extract the halves to integer registers
 vpextrd     edx, xmm1, 1    ; edx:eax = (uint64_t)xmm0[0]

或者直接存储到存储器vmovq [esp], xmm1中。
要使用当前舍入模式而不是截断,请在助记符中省略额外的t。(如果您没有更改MXCSR,则默认舍入模式是最接近的,并将even作为平局。)

vcvtpd2uqq  xmm1, xmm0       ; (uint64_t)nearbyint(xmm0)

所有新的AVX-512 FP转换指令都提供截断和电流舍入模式版本,这对压缩转换有意义; EVEX取整模式覆盖是only available for ZMMM vectors,而不是XMM/YMM。
我有点惊讶,他们费心用一个与vcvtsd2usi分开的操作码来制作vcvttsd2usi,而不是仅仅用VCVTSD2USI r64, xmm1/m64{rz-sae}的一个别名来覆盖舍入模式,以便向零截断。(您也可以覆盖为Nearest、Upward或Downward)。这样做的副作用是抑制该指令的FP异常,因此他们可能希望支持通过检查MXCSR标志来检测不准确或溢出转换的代码。

相关问题