标签: x86-64

为什么uint_least16_t比uint_fast16_t快于x86_64中的乘法?

关于uint_fast*_t类型族,C标准还不太清楚.在gcc-4.4.4 linux x86_64系统上,类型uint_fast16_tuint_fast32_t大小均为8个字节.但是,8字节数的乘法似乎比4字节数的乘法慢得多.以下代码演示了:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int
main ()
{
  uint_least16_t p, x;
  int count;

  p = 1;
  for (count = 100000; count != 0; --count)
    for (x = 1; x != 50000; ++x)
      p*= x;

  printf("%"PRIuLEAST16, p);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我得到了在程序上运行time命令

real 0m7.606s
user 0m7.557s
sys  0m0.019s
Run Code Online (Sandbox Code Playgroud)

如果我将类型更改为uint_fast16_t(和printf修饰符),则时间变为

real 0m12.609s
user 0m12.593s
sys  0m0.009s
Run Code Online (Sandbox Code Playgroud)

那么,如果stdint.h头文件uint_fast16_t(以及uint_fast32_t)定义为4字节类型,那会不会更好?

c unsigned x86-64 multiplication

11
推荐指数
2
解决办法
2983
查看次数

REX.B覆盖是否适用于MOVSS指令?

我正在为64位x86生成以下指令:

41 F3 0F 10 46 10       movss   XMM0,014h[R14]
Run Code Online (Sandbox Code Playgroud)

不幸的是,它在该行上出现故障.gdb将其反汇编为:

0x0000000000402054 <+320>:   rex.B
0x0000000000402055 <+321>:   movss  0x14(%rsi),%xmm0
Run Code Online (Sandbox Code Playgroud)

请注意,无法识别rex.B覆盖,索引是RSI而不是R14.

指令无效吗?在AMD 64位指令参考中,我找不到任何迹象表明此编码无效.

objdump也无法将其识别为有效指令:

41                      rex.B
f3 0f 10 46 10          movss  0x10(%rsi),%xmm0
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

assembly x86-64

11
推荐指数
1
解决办法
629
查看次数

如何在x86_64汇编中读取STDIN的输入?

我正在尝试学习x86_64程序集,我今天正在尝试标准输入输出并偶然发现这个帖子学习程序集 - echo程序名称如何从STDIN读取输入(使用SYSCALL指令)?特别是如果我知道输入将始终是一个整数,我想将其读入寄存器?

编辑: @Daniel Kozar在下面的回答帮助我理解了STDIN和STDOUT如何与Linux上的SYSCALL指令一起工作.我试图编写一个小程序,它从控制台输入中读取一个数字并打印与该数字对应的ascii字符.假如你输入65作为输入,你应该得到A作为输出.还有一个新的线条角色.如果有的话,它可以帮助任何其他人:-)

section .text
    global _start

_start:
    mov rdi, 0x0      ; file descriptor = stdin = 0
    lea rsi, [rsp+8]  ; buffer = address to store the bytes read
    mov rdx, 0x2      ; number of bytes to read
    mov rax, 0x0      ; SYSCALL number for reading from STDIN
    syscall           ; make the syscall

    xor rax, rax      ; clear off rax
    mov rbx, [rsp+8]  ; read the first byte read into rsp+8 by STDIN call …
Run Code Online (Sandbox Code Playgroud)

assembly stdin x86-64

11
推荐指数
1
解决办法
2万
查看次数

syscall是x86_64上的指令吗?

我想检查在glibc中执行系统调用的代码.我发现了这样的事情.

ENTRY (syscall)
    movq %rdi, %rax     /* Syscall number -> rax.  */
    movq %rsi, %rdi     /* shift arg1 - arg5.  */
    movq %rdx, %rsi
    movq %rcx, %rdx
    movq %r8, %r10
    movq %r9, %r8
    movq 8(%rsp),%r9    /* arg6 is on the stack.  */
    syscall         /* Do the system call.  */
    cmpq $-4095, %rax   /* Check %rax for error.  */
    jae SYSCALL_ERROR_LABEL /* Jump to error handler if error.  */
L(pseudo_end):
    ret         /* Return to caller.  */
Run Code Online (Sandbox Code Playgroud)

现在我的问题是系统调用(在cmpq指令之前)是否是指令?其次,如果它是一个指令,ENTRY(系统调用)的含义是什么?ENTRY的同名(我不知道ENTRY是什么)和指令?其次,L(pseudo_end)是什么?

c linux gcc x86-64 machine-instruction

11
推荐指数
2
解决办法
8114
查看次数

指令级分析:指令指针的含义?

在汇编指令级别分析代码时,如果现代CPU不按顺序或按顺序执行指令,那么指令指针的位置真正意味着什么呢?例如,假设以下x64汇编代码:

mov RAX, [RBX];         // Assume a cache miss here.
mov RSI, [RBX + RCX];   // Another cache miss.             
xor R8, R8;        
add RDX, RAX;           // Dependent on the load into RAX.
add RDI, RSI;           // Dependent on the load into RSI.
Run Code Online (Sandbox Code Playgroud)

指令指针大部分时间用在哪条指令上?我可以为所有人想出好的论点:

  • mov RAX, [RBX] 大概需要100个周期,因为这是一个缓存未命中.
  • mov RSI, [RBX + RCX]也需要100个周期,但可能与前一个指令并行执行.它甚至意味着指令指针位于其中一个或另一个上?
  • xor R8, R8 可能在内存加载完成之前执行乱序并完成,但指令指针可能会保留在此处,直到所有先前的指令也完成为止.
  • add RDX, RAX生成管道停顿,因为它RAX是在缓慢的缓存未命中加载之后实际使用值的指令.
  • add RDI, RSI也停滞,因为它依赖于负载RSI.

performance assembly profiling x86-64 low-level

11
推荐指数
1
解决办法
891
查看次数

在64位Linux中,内核占用了哪个地址空间?

64位Linux内核的地址空间是多少,也就是说,它所使用的代码,堆栈,堆和数据段的地址范围是多少.

linux x86-64 linux-kernel

11
推荐指数
1
解决办法
8718
查看次数

矢量化模运算

我正在尝试编写一些合理快速的组件向量加法代码.我正在使用(签名,我相信)64位整数.

功能是

void addRq (int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) {
    for(int i = 0; i < dim; i++) {
        a[i] = (a[i]+b[i])%q; // LINE1
    }
}
Run Code Online (Sandbox Code Playgroud)

我在icc -std=gnu99 -O3IvyBridge(SSE4.2和AVX,但不是AVX2)上编译(icc以便我以后可以使用SVML).

我的基线是%q从LINE1中删除.100(迭代)函数调用dim=11221184需要1.6秒.ICC自动矢量化SSE代码; 大.

我真的想做模块化的补充.使用%q,ICC不会自动向量化代码,它在11.8秒(!)内运行.即使忽略了之前尝试的自动矢量化,这似乎仍然过分.

由于我没有AVX2,SSE的矢量化需要SVML,这也许就是为什么ICC没有自动矢量化的原因.无论如何,这是我尝试对内循环进行矢量化:

__m128i qs = _mm_set1_epi64x(q);
for(int i = 0; i < dim; i+=2) {
    __m128i xs = _mm_load_si128((const __m128i*)(a+i));
    __m128i ys = _mm_load_si128((const __m128i*)(b+i));
    __m128i zs = _mm_add_epi64(xs,ys);
    zs = _mm_rem_epi64(zs,qs);
    _mm_store_si128((__m128i*)(a+i),zs);
}
Run Code Online (Sandbox Code Playgroud)

主循环的汇编是: …

c assembly sse x86-64 intrinsics

11
推荐指数
1
解决办法
1401
查看次数

分支预测会导致非法指令吗?

在以下伪代码中:

if (rdtscp supported by hardware) {
    Invoke "rdtscp" instruction
} else {
    Invoke "rdtsc" instruction
}
Run Code Online (Sandbox Code Playgroud)

假设CPU不支持该rdtscp指令,因此我们回退到else语句.

如果CPU错误预测分支,指令管道是否可能尝试执行rdtscp并抛出Illgal Instruction错误?

assembly x86-64 cpu-architecture branch-prediction

11
推荐指数
1
解决办法
542
查看次数

为什么x86-64的jmpq只需要32位长度的地址?

当我objdump -D用来反汇编二进制时,典型的代码jmpq就像e9 7f fe ff ff是,用于表示负偏移.但是,x86-64的地址是64(48)位(据我所知),那么这个32位地址如何7f fe ff ff表示64位绝对地址的负偏移?

此外,还有像任何其他指令jmpjmpq,但有64位地址位移?我怎样才能找到英特尔或AMD手册中的说明(我搜索jmpq但没有找到任何内容)?


当我搜索时,它似乎被称为RIP相对寻址.似乎并非所有说明都这样做.是否有64位相对寻址?如果是间接跳转,64位绝对地址将在寄存器或内存中,对吧?

assembly x86-64 computer-architecture

11
推荐指数
2
解决办法
3954
查看次数

在JIT中处理远程内部函数的调用

我正在Rust写一个JIT(尽管Rust只与我的一些问题有关,其中很大一部分与JIT约定有关).我有我在Rust中实现的内部函数,我需要能够call从我的JIT发出的代码.我的JIT mmap(_, _, PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED)是jitted代码的一些页面.我有我的内在函数的地址,但不幸的是它们比32位偏移更远.我现在试图决定如何发出对这些内在函数的调用.我考虑过以下选项:

  1. (特定于Rust)以某种方式使Rust将内在函数放置在堆附近(可能在?上),以便calls将在32位偏移量内.目前还不清楚Rust是否可行(有一种方法可以指定自定义链接器args,但是我无法告诉它们应用了什么以及我是否可以将单个函数作为重定位目标.即使我可以在哪里放置它?).如果堆足够大,似乎这可能会失败.

  2. (特定于Rust)将我的JIT页面分配给内部函数更接近.这可以通过实现mmap(_, _, PROT_EXEC, MAP_FIXED),但我不确定如何选择一个不会破坏现有Rust代码的地址(并保持在arch限制内 - 是否有一种理智的方式来获得这些限制?).

  3. 在JIT页面中创建处理绝对跳转(下面的代码)call的存根,然后是存根.这有利于JITted代码中的(初始)调用站点是一个很好的小型相对调用.但是必须跳过某些东西感觉不对.这似乎对性能有害(可能干扰RAS /跳转地址预测).此外,似乎这种跳转会更慢,因为它的地址是间接的,它取决于mov该地址.

mov rax, {ABSOLUTE_AOTC_FUNCTION_ADDRESS}
jmp rax
Run Code Online (Sandbox Code Playgroud)
  1. 与(3)相反,只是在JITed代码中的每个内部调用站点处内联上述内容.这解决了间接问题,但使JITted代码变大(可能这会产生指令缓存和解码后果).它仍然存在跳跃是间接的并且取决于的问题mov.

  2. 将内在函数的地址放在JIT页面附近的PROT_READ(仅)页面上.使所有呼叫站点接近,绝对间接呼叫(下面的代码).这从(2)中删除了第二级间接.但遗憾的是,该指令的编码很大(6个字节),因此它与(4)具有相同的问题.另外,现在不是依赖于寄存器,而是不必要地跳转(只要地址在JIT时间已知)依赖于内存,这肯定会影响性能(尽管可能这个页面被缓存了?).

aotc_function_address:
    .quad 0xDEADBEEF

# Then at the call site
call qword ptr [rip+aotc_function_address]
Run Code Online (Sandbox Code Playgroud)
  1. 具有段寄存器的Futz使其更接近内在函数,以便可以相对于该段寄存器进行调用.这种调用的编码很长(所以这可能有解码管道问题),但除此之外,这在很大程度上避免了它之前的一切棘手的问题.但是,相对于非cs细分市场的调用可能表现不佳.或者也许这样的未来是不明智的(例如,与Rust运行时混淆).(正如@prl所指出的,没有远程调用就行不通,这对性能来说太糟糕了)

  2. 不是真正的解决方案,但我可以使编译器32位并且根本没有这个问题.这不是一个很好的解决方案,它也会阻止我使用扩展的通用寄存器(我利用它们).

所有选项都有缺点.简而言之,1和2是唯一似乎没有性能影响的,但目前还不清楚是否有一种非hacky方式来实现它们(或者根本没有任何方式).3-5独立于Rust,但具有明显的性能缺陷.

考虑到这一点,一些(Rust)具体问题:

  1. 对于方法(1),是否可以强制Rust链接extern "C"特定地址(堆附近)的某些函数?我应该如何选择这样的地址(在编译时)?假设由mmap(由Rust分配)返回的任何地址都在此位置的32位偏移量内是否安全?

  2. 对于方法(2),我如何找到一个合适的位置放置JIT页面(这样它不会破坏现有的Rust代码)?

还有一些JIT(非Rust)特定问题:

  1. 对于方法(3),存根是否会妨碍我应该关心的性能?间接jmp怎么样?我知道这有点类似于链接器存根,除非我理解链接器存根至少只解析一次(因此它们不需要是间接的?).任何JIT都采用这种技术吗?

  2. 对于方法(4),如果3中的间接呼叫是可以的,那么内联呼叫值得吗?如果JIT通常采用方法(3/4),这个选项更好吗?

  3. 对于方法(5),跳转对内存的依赖性(假设地址在编译时已知)是不是很糟糕?这会使(3)或(4)的性能降低吗?任何JIT都采用这种技术吗?

  4. 对于方法(6),这样的未来是不明智的吗?(特定于Rust)是否有可用的段寄存器(运行时或ABI未使用)?相对于非cs细分市场的调用是否与相对于cs?的调用相同?

  5. 最后(也是最重要的),是否有我在这里失踪更好的方法(可能由即时编译器采用更常见)?

如果没有我的Rust问题有答案,我无法实现(1)或(2).当然,我可以实现和测试3-5(可能是6,虽然事先了解段寄存器会很好),但考虑到这些是截然不同的方法,我希望有关于此的现有文献我找不到,因为我不知道谷歌的正确条款(我目前也正在研究这些基准).或者也许有人深入研究JIT内部人员可以分享他们的经验或他们常见的东西? …

compiler-construction assembly jit x86-64 rust

11
推荐指数
1
解决办法
269
查看次数