- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在理解此汇编代码的作用时遇到问题(这是较大汇编代码的一小部分,这是英特尔语法):
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 中, struct
或class
返回值要么在 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 assembly在clang
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 字节的对象,rax
和rdx
使用1。
之后,策略完全改变,我们看到rdi
指向的位置发生了内存写入。 - 这就是上面提到的隐藏指针方法。
最后,总结一下,我们注意到 sizeof(vector<int>)
是 usually 24 bytes在 64 位平台上,是 definitely Linux 上的主要 C++ 编译器上为 24 个字节 - 因此隐藏指针方法适用于向量。
感谢 Jester,他已经以简短的形式回答了这个问题,in the comments .
1 像 223338299434
这样奇怪的常量存储到 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/
我有这个代码。为了让它工作,我必须使用 Args&&... 而不是 Args... 或 Args&... 我注意到 args 从 & 转换为 const& 或 && 转换为 &。 Args...Arg
当我定义类时,我总是去 Class A(object): def __init__(self, arg): self.arg = arg def print_arg(s
假设我想定义两个 {Type} 类的变量。构造函数采用 1 个参数。下面两种方式是否完全等价(编译成相同的目标代码)? Type a(arg), b(arg); 和 Type a(arg); Type
(旁白:我是一名 Perl 程序员,正如您所知,这是我的第一个重要的 Java 程序。简单的术语将不胜感激。) 我有以下启动器作为编码工作: import java.lang.reflect.*; i
Math.nextUp(arg) 始终与 arg + Math.ulp(arg) 相同,还是我遗漏了什么? System.out.println( 0.5 + Math.ulp(0.5));
今天我在学习完美转发,我创建了这个代码示例 #include #include template auto toStdFun(Function&& fun, Args&&...ar
我想知道你会选择哪个选项? putStrLn (show randomNum) putStrLn $ show randomNum (putStrLn . show) randomNum 所有选项在语
我试图在 visual studio 2012 中编译一个库,它最初是用 c++ 为 visual studio 2015 编写的。我有一个错误说 'class' missing tag。 错误消息的
我在下面的代码中遇到了运行时异常ArrayIndexOutOfBoundException,行中: if ( args[0].equals("t") || args[0].equals("time")
我有以下代码 import React, { Component } from "react"; import { Accounts } from "meteor/accounts-base"; ex
这个问题已经有答案了: Difference between Arrays and 3 dots (Varargs) in java (3 个回答) 已关闭 5 年前。 受学校线性代数 I 和 II
所以我定义了一个函数: def getDistnace(self, strings, parentD, nodeName, nodeDistance): 我用它来调用: Node.getDistnac
这个问题在这里已经有了答案: subprocess.call() arguments ignored when using shell=True w/ list [duplicate] (2 个答案
我想将参数传递给 java 应用程序,但喜欢 linux 应用程序风格。 java 中的main 方法对所有参数使用一个String 数组。在 Linux 中,大多数应用程序接受如下参数:ls -l
这是我的代码片段 #include void change(int a[]){ printf("%p\n",&a); } int main(){
我需要使用 python 3.6 subprocess.run() 函数发出以下命令: gsettings set org.gnome.shell enabled-extensions "['appl
这两个函数是否有任何有意义的不同?有什么理由通常更喜欢一个而不是另一个吗? void foo(auto x, auto &... y) { /* ... */ } template void foo(
例如: def m(arg, ...args) { println "arg: $arg" println "args: $args" } m('arg', k:'v') 输出: ar
我对 Java 还很陌生。目前正在尝试将 args[] 中给出的文件名传递给此 FileReader,但当我编译时,它说找不到指定的文件。如果我对文件名进行硬编码,它就可以正常工作。这应该如何运作?
为什么这是一个语法错误??做这件事的合适方法是什么? >>> def f(*args, option=None): File "", line 1 def f(*args, option=
我是一名优秀的程序员,十分优秀!