gpt4 book ai didi

assembly - 在此编译器输出中,为什么 func(int) 使用其第一个 arg 作为指针,将指向内存的 24 字节归零? arg 不是指针

转载 作者:行者123 更新时间:2023-12-02 09:12:16 24 4
gpt4 key购买 nike

我在理解此汇编代码的作用时遇到问题(这是较大汇编代码的一小部分,这是英特尔语法):

vector<int> func(int i) { ...}  // C++ source

clang 输出 from the Godbolt compiler explorer :

func(int): # @func(int)
push rbp
push rbx
push rax
mov ebp, esi
mov rbx, rdi
xorps xmm0, xmm0
movups xmmword ptr [rbx], xmm0
mov qword ptr [rbx + 16], 0

这是在 Linux 上编译的,遵循官方 System V AMD64 ABI。根据this link ,rdi 寄存器用于将第一个参数传递给函数。所以在这一行

mov rbx, rdi

我们将参数的值(本例中为 int)移至 rbx。不久之后,我们做到了:

movups xmmword ptr [rbx], xmm0

这就是我不明白的地方。 rbx 包含参数的值,它是一个 int,这里我们将 xmm0 的内容复制到 rbx 指向的地址(但 rbx 不包含任何地址,只包含函数的参数!)

我犯了一些错误,但我不明白为什么。

最佳答案

在 Linux 和 Windows 之外的大多数其他 64 位 x86 操作系统使用的 SysV 64 位 ABI 中, structclass返回值要么在 rax 中返回或rdx寄存器,或通过作为第一个参数传递的隐藏指针

这两个选项之间的决定主要取决于返回结构的大小:大于 16 字节的结构通常使用隐藏指针方法,但还有其他因素,我建议 this answer以获得更全面的治疗。

当使用隐藏指针方法时,我们需要一种方法将此指针传递给函数。在这种情况下,指针的行为就好像它是第一个参数(在 rdi 中传递),它将其他参数移动到后面的位置2

通过检查返回 struct 的函数生成的代码,我们可以清楚地看到这一点。 1 到 5 的对象 int值(因此在此平台上为 4 到 20 字节)。 C++代码:

struct one {
int x;
};

struct two {
int x1, x2;
};

struct three {
int x1, x2, x3;
};

struct four {
int x1, x2, x3, x4;
};

struct five {
int x1, x2, x3, x4, x5;
};


one makeOne() {
return {42};
}

two makeTwo() {
return {42, 52};
}

three makeThree() {
return {42, 52, 62};
}

four makeFour() {
return {42, 52, 62, 72};
}

five makeFive() {
return {42, 52, 62, 72, 82};
}

结果为following assemblyclang 6.0(但其他编译器的行为类似:

makeOne():                            # @makeOne()
mov eax, 42
ret
makeTwo(): # @makeTwo()
movabs rax, 223338299434
ret
makeThree(): # @makeThree()
movabs rax, 223338299434
mov edx, 62
ret
makeFour(): # @makeFour()
movabs rax, 223338299434
movabs rdx, 309237645374
ret
.LCPI4_0:
.long 42 # 0x2a
.long 52 # 0x34
.long 62 # 0x3e
.long 72 # 0x48
makeFive(): # @makeFive()
movaps xmm0, xmmword ptr [rip + .LCPI4_0] # xmm0 = [42,52,62,72]
movups xmmword ptr [rdi], xmm0
mov dword ptr [rdi + 16], 82
mov rax, rdi
ret

基本模式是最多 8 个字节(含 8 个字节),即 struct完全返回于 rax (包括在 64 位寄存器中打包多个较小的值),对于最多 16 字节的对象,raxrdx使用1

之后,策略完全改变,我们看到rdi指向的位置发生了内存写入。 - 这就是上面提到的隐藏指针方法。

最后,总结一下,我们注意到 sizeof(vector<int>)usually 24 bytes在 64 位平台上,是 definitely Linux 上的主要 C++ 编译器上为 24 个字节 - 因此隐藏指针方法适用于向量。


感谢 Jester,他已经以简短的形式回答了这个问题,in the comments .

1223338299434 这样奇怪的常量存储到 64 位寄存器中的只是一种优化:编译器只是将两个 32 位存储组合成一个 64 位常量,如 52ul << 32 | 42ul 中所示。结果是 223338299434 .

2 这与传递 this 的方法相同。对于成员函数:如果成员函数返回通过隐藏指针方法传递的值,则隐藏指针首先出现(在 rdi 中),然后是 this指针(在 rsi 中),最后是第一个用户提供的参数(通常在 rdx 中 - 但这取决于类型)。这是an example .

关于assembly - 在此编译器输出中,为什么 func(int) 使用其第一个 arg 作为指针,将指向内存的 24 字节归零? arg 不是指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50846969/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com