相关疑难解决方法(0)

为什么32位寄存器上的x86-64指令归零整个64位寄存器的上半部分?

x86-64 Tour of Intel Manuals中,我读到了

也许最令人惊讶的事实是,诸如MOV EAX, EBX自动将指令的高32位归零的指令RAX.

同一来源引用的英特尔文档(3.4.1.1 64位手动基本架构中的通用寄存器)告诉我们:

  • 64位操作数在目标通用寄存器中生成64位结果.
  • 32位操作数生成32位结果,在目标通用寄存器中零扩展为64位结果.
  • 8位和16位操作数生成8位或16位结果.目标通用寄存器的高56位或48位(分别)不会被操作修改.如果8位或16位操作的结果用于64位地址计算,则将寄存器显式符号扩展为完整的64位.

在x86-32和x86-64汇编中,16位指令如

mov ax, bx
Run Code Online (Sandbox Code Playgroud)

不要表现出这种"奇怪"的行为,即eax的上层词被归零.

因此:引入这种行为的原因是什么?乍一看似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖).

x86 assembly x86-64 cpu-registers zero-extension

97
推荐指数
3
解决办法
2万
查看次数

x86的MOV真的可以"免费"吗?为什么我不能重现这个呢?

我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.

对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.

例如,这是我用Visual C++编译的代码:

#include <limits.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    unsigned int k, l, j;
    clock_t tstart = clock();
    for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
    {
        ++k;
        k = j;     // <-- comment out this line to remove the MOV instruction
        l += j;
    }
    fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
    fflush(stderr);
    return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)

这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):

LOOP:
    add edi,esi
    mov …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly cpu-registers micro-optimization

23
推荐指数
2
解决办法
2113
查看次数

如何使用SSE/AVX高效执行double/int64转换?

SSE2具有在单精度浮点数和32位整数之间转换向量的指令.

  • _mm_cvtps_epi32()
  • _mm_cvtepi32_ps()

但是没有双精度和64位整数的等价物.换句话说,他们失踪了:

  • _mm_cvtpd_epi64()
  • _mm_cvtepi64_pd()

似乎AVX也没有它们.

模拟这些内在函数的最有效方法是什么?

c++ floating-point sse simd avx

19
推荐指数
2
解决办法
2850
查看次数

将64位整数加载到双精度SSE2寄存器的最佳方法是什么?

xmm在32位模式下,在SSE2寄存器中加载64位整数值的最佳/最快方法是什么?

在64位模式下,cvtsi2sd可以使用,但在32位模式下,它仅支持32位整数.

到目前为止,我还没有发现更多:

  • 使用fild,fstp以堆叠然后movsdxmm寄存器
  • 加载高32位部分,乘以2 ^ 32,加上低32位

第一个解决方案很慢,第二个解决方案可能会引入精度损失(编辑:无论如何它都很慢,因为低32位必须转换为无符号...)

有更好的方法吗?

double assembly sse int64 sse2

14
推荐指数
1
解决办法
2496
查看次数

关于 uint64 到 double 的转换:为什么右移 1 后代码更简单?

为什么AsDouble1比 更简单AsDouble0

// AsDouble0(unsigned long):                          # @AsDouble0(unsigned long)
//         movq    xmm1, rdi
//         punpckldq       xmm1, xmmword ptr [rip + .LCPI0_0] # xmm1 = xmm1[0],mem[0],xmm1[1],mem[1]
//         subpd   xmm1, xmmword ptr [rip + .LCPI0_1]
//         movapd  xmm0, xmm1
//         unpckhpd        xmm0, xmm1                      # xmm0 = xmm0[1],xmm1[1]
//         addsd   xmm0, xmm1
//         addsd   xmm0, xmm0
//         ret
double AsDouble0(uint64_t x) { return x * 2.0; }

// AsDouble1(unsigned long):                          # @AsDouble1(unsigned long)
//         shr     rdi
//         cvtsi2sd        xmm0, rdi …
Run Code Online (Sandbox Code Playgroud)

c++ assembly sse x86-64 floating-point-conversion

9
推荐指数
2
解决办法
288
查看次数

sqint of uint64_t vs. int64_t

我注意到计算平方根的整数部分uint64_t要复杂得多int64_t.请问有人对此有解释吗?为什么处理一个额外的位似乎要困难得多?

下列:

int64_t sqrt_int(int64_t a) {
    return sqrt(a);
}
Run Code Online (Sandbox Code Playgroud)

与铛5.0和编译-mfpmath=sse -msse3 -Wall -O3

sqrt_int(long):                           # @sqrt_int(long)
        cvtsi2sd        xmm0, rdi
        sqrtsd  xmm0, xmm0
        cvttsd2si       rax, xmm0
        ret
Run Code Online (Sandbox Code Playgroud)

但是以下内容:

uint64_t sqrt_int(uint64_t a) {
    return sqrt(a);
}
Run Code Online (Sandbox Code Playgroud)

编译为:

.LCPI0_0:
        .long   1127219200              # 0x43300000
        .long   1160773632              # 0x45300000
        .long   0                       # 0x0
        .long   0                       # 0x0
.LCPI0_1:
        .quad   4841369599423283200     # double 4503599627370496
        .quad   4985484787499139072     # double 1.9342813113834067E+25
.LCPI0_2:
        .quad   4890909195324358656     # double 9.2233720368547758E+18
sqrt_int(unsigned long):                           # @sqrt_int(unsigned long) …
Run Code Online (Sandbox Code Playgroud)

x86 assembly sse compilation square-root

8
推荐指数
1
解决办法
373
查看次数

如何在x86(32位)程序集中将无符号整数转换为浮点数?

我需要将32位和64位无符号整数转换为xmm寄存器中的浮点值.有一些x86指令可以将有符号整数转换为单精度和双精度浮点值,但对于无符号整数则没有.

额外:如何将xmm寄存器中的浮点值转换为32位和64位无符号整数?

x86 assembly sse

5
推荐指数
1
解决办法
4563
查看次数

为什么添加 xorps 指令使这个函数使用 cvtsi2ss 并添加 ~5x 快?

我正在使用 Google Benchmark 优化一个函数,并遇到了我的代码在某些情况下意外变慢的情况。我开始试验它,查看编译后的程序集,并最终想出了一个最小的测试用例来展示这个问题。这是我想出的展示这种放缓的程序集:

    .text
test:
    #xorps  %xmm0, %xmm0
    cvtsi2ss    %edi, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    addss   %xmm0, %xmm0
    retq
    .global test
Run Code Online (Sandbox Code Playgroud)

此函数遵循 GCC/Clang 的 x86-64 函数声明调用约定extern "C" float test(int);注意注释掉的xorps指令。取消注释此指令可显着提高函数的性能。用我的机器有i7-8700K,谷歌基准测试显示的功能测试它,而不xorps指令需要8.54ns(CPU),而功能xorps指令需要1.48ns。我已经在具有不同操作系统、处理器、处理器世代和不同处理器制造商(英特尔和 AMD)的多台计算机上对此进行了测试,它们都表现出类似的性能差异。重复addss指令使减速更加明显(在某种程度上),并且这种减速仍然使用此处的其他指令(例如mulss)或什至混合指令发生,只要它们都%xmm0以某种方式依赖于值。值得指出的是,只调用xorps 每个函数调用会导致性能提升。使用循环对性能进行采样(如 Google Benchmark 所做的那样)和xorps循环外的调用仍然显示出较慢的性能。

由于这是一种专门添加指令可以提高性能的情况,因此这似乎是由 CPU 中的一些非常低级的东西引起的。由于它发生在各种 CPU …

sse x86-64 cpu-architecture clang microbenchmark

3
推荐指数
1
解决办法
167
查看次数