相关疑难解决方法(0)

Why does Windows64 use a different calling convention from all other OSes on x86-64?

AMD has an ABI specification that describes the calling convention to use on x86-64. All OSes follow it, except for Windows which has it's own x86-64 calling convention. Why?

Does anyone know the technical, historical, or political reasons for this difference, or is it purely a matter of NIHsyndrome?

I understand that different OSes may have different needs for higher level things, but that doesn't explain why for example the register parameter passing order on Windows is rcx - rdx …

windows x86-64 calling-convention

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

为什么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万
查看次数

为什么T *可以在寄存器中传递,但unique_ptr <T>无法传递?

我正在观看Chandler Carruth在CppCon 2019中的演讲:

没有零成本抽象

在该示例中,他举例说明了您对使用std::unique_ptr<int>over和会产生多少开销而感到惊讶int*。该段大约在时间点17:25开始。

您可以看一下他的示例代码对(godbolt.org)的编译结果 -可以看到,确实,编译器似乎不愿意传递unique_ptr值-实际上,底线是只是一个地址-在寄存器内,仅在直接内存中。

Carruth先生在27:00左右提出的观点之一是,C ++ ABI要求按值传递参数(某些但不是全部;也许-非基本类型?而不是在寄存器中。

我的问题:

  1. 这实际上是某些平台上的ABI要求吗?(哪个?)或者在某些情况下可能只是一些悲观?
  2. 为什么ABI这样?也就是说,如果结构/类的字段适合寄存器,甚至单个寄存器,为什么我们不能在寄存器中传递它呢?
  3. 近年来,C ++标准委员会是否曾经讨论过这一点?

PS-为了不给这个问题留下代码:

普通指针:

void bar(int* ptr) noexcept;
void baz(int* ptr) noexcept;

void foo(int* ptr) noexcept {
    if (*ptr > 42) {
        bar(ptr); 
        *ptr = 42; 
    }
    baz(ptr);
}
Run Code Online (Sandbox Code Playgroud)

唯一指针:

using std::unique_ptr;
void bar(int* ptr) noexcept;
void baz(unique_ptr<int> ptr) noexcept;

void foo(unique_ptr<int> ptr) noexcept {
    if (*ptr > 42) { 
        bar(ptr.get());
        *ptr = 42; 
    }
    baz(std::move(ptr));
}
Run Code Online (Sandbox Code Playgroud)

c++ assembly abi calling-convention unique-ptr

80
推荐指数
2
解决办法
2943
查看次数

功能声明:K&R与ANSI

K&R函数声明和ANSI函数声明之间有什么区别?

c kr-c

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

为什么它使用movl而不是push?

注意这段代码:

#include <stdio.h>
void a(int a, int b, int c)
{
    char buffer1[5];
    char buffer2[10];
}

int main()
{
    a(1,2,3); 
}
Run Code Online (Sandbox Code Playgroud)

之后 :

gcc -S a.c
Run Code Online (Sandbox Code Playgroud)

该命令在程序集中显示我们的源代码.

现在我们可以在main函数中看到,我们从不使用"push"命令将函数的参数压入堆栈.它用"movel"而不是那个

main:
 pushl %ebp
 movl %esp, %ebp
 andl $-16, %esp
 subl $16, %esp
 movl $3, 8(%esp)
 movl $2, 4(%esp)
 movl $1, (%esp)
 call a
 leave
Run Code Online (Sandbox Code Playgroud)

为什么会这样?他们之间有什么区别?

c assembly gcc callstack calling-convention

17
推荐指数
3
解决办法
4259
查看次数

将32位偏移量添加到x86-64 ABI的指针时是否需要符号或零扩展?

简介:我正在查看汇编代码来指导我的优化,并在将int32添加到指针时看到许多符号或零扩展.

void Test(int *out, int offset)
{
    out[offset] = 1;
}
-------------------------------------
movslq  %esi, %rsi
movl    $1, (%rdi,%rsi,4)
ret
Run Code Online (Sandbox Code Playgroud)

起初,我认为我的编译器在添加32位到64位整数时遇到了挑战,但我已经用Intel ICC 11,ICC 14和GCC 5.3证实了这种行为.

这个帖子证实了我的发现,但不清楚是否需要符号或零扩展.仅当尚未设置高32位时,才需要此符号/零扩展.但x86-64 ABI难道不够聪明吗?

我有点不愿意将所有指针偏移更改为ssize_t,因为寄存器溢出会增加代码的缓存占用空间.

assembly x86-64 abi compiler-optimization sign-extension

16
推荐指数
1
解决办法
2591
查看次数

当我没有指定操作数大小时,push指令压入堆栈的字节数是多少?

我可以通过这样做将4个字节压入堆栈:

push DWORD 123
Run Code Online (Sandbox Code Playgroud)

但我发现我可以在push不指定操作数大小的情况下使用:

push 123
Run Code Online (Sandbox Code Playgroud)

在这种情况下,push指令将多少字节压入堆栈?推送的字节数是否取决于操作数大小(因此在我的示例中它将推送1个字节)?

x86 assembly

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

如何使用printf打印单精度浮点数

我正在尝试在x86_64程序集中打印浮点数,但它只是将值打印为零.

关于这个问题已经存在一些问题.通过确保设置在%al中使用的向量寄存器的数量,似乎可以解决一个问题.另一个表明你需要一个16字节的堆栈对齐.但是,我正在做这两件事而仍然没有得到正确的输出.

这是我的计划:

# prints a floating point value
.section .rodata
.fmt: .string "num: %f\n"
.num: .float 123.4

.section .text
.global main
.type   main, @function
main:
  subq $8, %rsp     # 16-byte alignment

  # print my number
  movss .num, %xmm0 # load float value
  movq $.fmt, %rdi  # load format string
  movb $1, %al      # use 1 vector register
  call printf

  # exit
  addq $8, %rsp     # undo alignment
  movq $0, %rax     # return 0
  ret
Run Code Online (Sandbox Code Playgroud)

assembly printf x86-64

6
推荐指数
1
解决办法
2978
查看次数

为什么函数参数占用x86上至少4个字节的堆栈?

如果函数参数push/pop在x86上的堆栈中分配,则至少分配4个字节.如果每个函数调用的参数大小小于4个字节,则会浪费内存.一个原因可能是推送和弹出工作最少4个字节,但为什么不esp直接操作以节省堆栈空间,可以将1个字节中的4个参数打包到一个4字节内存,如下所示?

sub esp, 4
mov byte ptr [esp], para1
mov byte ptr [esp+1], para2
mov byte ptr [esp+2], para3
mov byte ptr [esp+3], para4
call func
Run Code Online (Sandbox Code Playgroud)

x86 assembly calling-convention

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