返回元组时 GCC/Clang x86_64 C++ ABI 不匹配?

2024-03-15

当尝试时优化 x86_64 上的返回值 https://stackoverflow.com/q/25381736/3919155,我注意到一个奇怪的事情。即,给定代码:

#include <cstdint>
#include <tuple>
#include <utility>

using namespace std;

constexpr uint64_t a = 1u;
constexpr uint64_t b = 2u;

pair<uint64_t, uint64_t> f() { return {a, b}; }
tuple<uint64_t, uint64_t> g() { return tuple<uint64_t, uint64_t>{a, b}; }

Clang 3.8 输出 https://gcc.godbolt.org/#compilers:!((compiler:clang380,options:'-std%3Dc%2B%2B11+-O2',sourcez:MQSwdgxgNgrgJgUwAQB4IGcAudyYHwBQoksiqmMADlAocdPMijJiFCJgJ6EEzrgBzJGACGAWwTpKIiMixwA3AQIQA9mCwIAHpQBOSGLgBsAFgD6mJCKQBeJAEYYStRsza9B4%2BcsAjW0gAmJ2VpEF1mLwsAGk8wTFMLPCQAMwAKAEokAG8kXQQKXTBskRifAF8FJDKCCmoECLiEzBjDRu8kgQzs3PyYQqRamgb47xbI/CySpHLK6oIsEVYIMxF0dARdTFT%2BAC8EVTTQ8NaR6NjT/HSCJBvbu7ubO139tJOmzIBqJGeD1LfvdIxABEQPSSgWSxWaw2Wx%2BaUG9X%2BZyRl2u93RN0e3xAe1%2BR2GTTGbUSgKQILBQAAA%3D%3D)),filterAsm:(commentOnly:!t,directives:!t,labels:!t),version:3该汇编代码为f:

movl $1, %eax
movl $2, %edx
retq

这对于g:

movl $2, %eax
movl $1, %edx
retq

看起来最优。然而,当使用 GCC 6.1 编译 https://gcc.godbolt.org/#compilers:!((compiler:g6,options:'-std%3Dc%2B%2B11+-O2',sourcez:MQSwdgxgNgrgJgUwAQB4IGcAudyYHwBQoksiqmMADlAocdPMijJiFCJgJ6EEzrgBzJGACGAWwTpKIiMixwA3AQIQA9mCwIAHpQBOSGLgBsAFgD6mJCKQBeJAEYYStRsza9B4%2BcsAjW0gAmJ2VpEF1mLwsAGk8wTFMLPCQAMwAKAEokAG8kXQQKXTBskRifAF8FJDKCCmoECLiEzBjDRu8kgQzs3PyYQqRamgb47xbI/CySpHLK6oIsEVYIMxF0dARdTFT%2BAC8EVTTQ8NaR6NjT/HSCJBvbu7ubO139tJOmzIBqJGeD1LfvdIxABEQPSSgWSxWaw2Wx%2BaUG9X%2BZyRl2u93RN0e3xAe1%2BR2GTTGbUSgKQILBQAAA%3D%3D)),filterAsm:(commentOnly:!t,directives:!t,labels:!t),version:3,而生成的程序集为f与 Clang 输出相同,生成的程序集g is:

movq %rdi, %rax
movq $2, (%rdi)
movq $1, 8(%rdi)
ret

看起来返回值的类型被 GCC 分类为 MEMORY,但被 Clang 分类为 INTEGER。我可以确认将 Clang 代码与 GCC 代码链接,此类代码可能会导致分段错误(Clang 调用 GCC 编译的g()写入任何地方%rdi碰巧指向)并且返回无效值(GCC 调用 Clang 编译g())。哪个编译器有问题?

Related:

  • 构建共享库时,G++ 和 clang++ 与标准库不兼容? https://stackoverflow.com/q/35586332/3919155
  • [cxx-abi-dev] 不平凡的移动构造函数 http://sourcerytools.com/pipermail/cxx-abi-dev/2016-February/002884.html

See also

  • System V 应用程序二进制接口。 AMD64 架构处理器补充。草案版本0.99.5 http://www.x86-64.org/documentation/abi.pdf

ABI 规定参数值根据特定算法进行分类。这里相关的是:

  1. 如果聚合的大小超过一个八字节,则每个字节将被单独分类。每个八字节被初始化为 NO_CLASS 类。

  2. 对象的每个字段都被递归分类,以便始终考虑两个字段。根据八字节中字段的类别计算得到的类别:

在这种情况下,每个字段(对于元组或对)都是类型uint64_t因此占据了整个“八字节”。那么,每八字节中要考虑的“两个字段”是“NO_CLASS”(按照 3)和uint64_t字段,被分类为 INTEGER。

还有,与参数相关passing:

如果 C++ 对象具有非平凡的复制构造函数或非平凡的析构函数,则它通过不可见引用传递(该对象在参数列表中被具有类 INTEGER 的指针替换)

不满足这些要求的对象必须有一个地址,因此需要位于内存中,这就是上述要求存在的原因。返回值也是如此,尽管规范中似乎省略了这一点(可能是偶然的)。

最后还有:

(c) 如果聚合的大小超过两个八字节,并且第一个八字节不是 SSE 或任何其他八字节不是 SSEUP,则整个参数 是在内存中传递的。

显然,这并不适用于此。聚合的大小正好是两个八字节。

在返回值时,文本显示:

  1. 使用分类算法对返回类型进行分类

如上所述,这意味着元组应归类为 INTEGER。然后:

  1. 如果类别为 INTEGER,则序列的下一个可用寄存器 使用%rax、%rdx。

这是很清楚的。

唯一仍然悬而未决的问题是这些类型是否是非平凡复制构造/可破坏的。如上所述,这种类型的值不能在寄存器中传递或返回,即使规范似乎没有认识到返回值的问题。然而,我们可以使用以下程序轻松证明元组和对都是可平凡复制构造和平凡破坏的:

测试程序:

#include <utility>
#include <cstdint>
#include <tuple>
#include <iostream>

using namespace std;

int main(int argc, char **argv)
{
    cout << "pair is trivial? : " << is_trivial<pair<uint64_t, uint64_t> >::value << endl;
    cout << "pair is trivially_copy_constructible? : " << is_trivially_copy_constructible<pair<uint64_t, uint64_t> >::value << endl;
    cout << "pair is standard_layout? : " << is_standard_layout<pair<uint64_t, uint64_t> >::value << endl;
    cout << "pair is pod? : " << is_pod<pair<uint64_t, uint64_t> >::value << endl;
    cout << "pair is trivially_destructable? : " << is_trivially_destructible<pair<uint64_t, uint64_t> >::value << endl;
    cout << "pair is trivially_move_constructible? : " << is_trivially_move_constructible<pair<uint64_t, uint64_t> >::value << endl;

    cout << "tuple is trivial? : " << is_trivial<tuple<uint64_t, uint64_t> >::value << endl;
    cout << "tuple is trivially_copy_constructible? : " << is_trivially_copy_constructible<tuple<uint64_t, uint64_t> >::value << endl;
    cout << "tuple is standard_layout? : " << is_standard_layout<tuple<uint64_t, uint64_t> >::value << endl;
    cout << "tuple is pod? : " << is_pod<tuple<uint64_t, uint64_t> >::value << endl;
    cout << "tuple is trivially_destructable? : " << is_trivially_destructible<tuple<uint64_t, uint64_t> >::value << endl;
    cout << "tuple is trivially_move_constructible? : " << is_trivially_move_constructible<tuple<uint64_t, uint64_t> >::value << endl;
    return 0;
}

使用 GCC 或 Clang 编译时的输出:

pair is trivial? : 0
pair is trivially_copy_constructible? : 1
pair is standard_layout? : 1
pair is pod? : 0
pair is trivially_destructable? : 1
pair is trivially_move_constructible? : 1
tuple is trivial? : 0
tuple is trivially_copy_constructible? : 1
tuple is standard_layout? : 0
tuple is pod? : 0
tuple is trivially_destructable? : 1
tuple is trivially_move_constructible? : 0

这意味着海湾合作委员会的做法是错误的。返回值应以%rax,%rdx 形式传递。

(这些类型之间的主要显着差异是pair是标准布局,并且可以简单地移动构造,而tuple不是,所以 GCC 可能总是通过指针返回非平凡移动构造的值,例如)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

返回元组时 GCC/Clang x86_64 C++ ABI 不匹配? 的相关文章

随机推荐

  • Python3 + PyGobject + GTK3 和 cx_freeze 缺少 DLL

    当我使用 py python3 pygobject gtk3 应用程序制作 exe 时从 pygobject 站点来看 它缺少一些 DLL 文件 缺少什么文件 我已经手动尝试过需要哪些 DLL 因此 如果这对某人有帮助 the 必须进行编辑
  • 如何将嵌套 Java Collection 中的所有项目展平到单个列表中?

    给定一个复杂的嵌套对象集合 例如 Set
  • WPF 同一系列的多个字体文件

    我有以下字体文件 MyFont Regular tff MyFont Bold tff MyFont Italic tff 我该如何使用它们 我可以做以下事情
  • 显示精灵的另一个实例

    是否可以显示精灵的另一个实例 我想做的是反射动画精灵 到目前为止 我得到的是我的 Sprite 称为 canvas 它内部有使用 AS3 进行动画处理的内容 我想要做的是显示它翻转的副本 在它下面看起来像倒影 我尝试了以下代码 但没有运气
  • 类库(传统便携式)?

    我有一台装有 Microsoft Visual Studio Community 2017 的电脑 版本 15 2 它有一个类库 便携式 的项目模板 另一台版本为 15 3 1 的 PC 有一个类库模板 Legacy Portable PC
  • 请解释如何使用CheckBoxTableCell

    我想了解更多有关如何实际使用或子类化 如果需要 CheckBoxTableCell 的信息 在一种特定情况下 我想使用此类 其中复选框不绑定到基础数据模型属性 假设我有一个名为 选择 的列 其中包含复选框 该列或多或少充当该行的视觉标记 用
  • 亚马逊s3上传多个文件android

    如果有人仍在寻找解决方案 我最终会在代码上使用循环 但我没有找到官方 api 来执行多个文件上传 我有一个 ImageFiles 的 ArrayList 我想将其上传到 Amazon s3 他们的文档提供了以下代码 credentials
  • 通过距离和摩擦力计算速度

    我正在用 Javascript Canvas HTML5 编写一个游戏 我刚刚发现了一个与高等数学相关的大问题 该游戏是平面 2D 游戏 因此您可以从另一个角度看世界 这意味着没有重力 只有摩擦力 CODE var friction 0 9
  • 当没有导航栏时,如何在 EKEventeditViewController 中获得“完成”或“后退”按钮?

    我的 iOS 应用程序中有一个日历事件列表 单击时将在 EKEventViewController 中打开 这是我的代码 void tableView UITableView tableView didSelectRowAtIndexPat
  • 如何在表格行上使用slideDown(或show)函数?

    我正在尝试向表中添加一行并将该行滑入视图中 但是 Slidedown 函数似乎向表行添加了 display block 样式 这会弄乱布局 有什么想法可以解决这个问题吗 这是代码 get some url val1 id function
  • 在 Promise catch 中重新抛出错误

    我在教程中找到了以下代码 promise then function result some code catch function error throw error 我有点困惑 catch 调用有什么作用吗 在我看来 它没有任何效果 因
  • Windows 搜索 sql - 无法访问 System.Search.QueryFocusedSummary

    我正在尝试使用 sql 查询 Windows Search 4 0 该物业 我感兴趣的是 System Search QueryFocusedSummary 我正在尝试从 SystemIndex 读取此属性 我收到 列不存在 错误消息 我能
  • 安装nvm后无法卸载全局npm包

    我发现了与此问题相关的几个线程 但似乎没有一个专门处理我的案例 并且我无法使用我找到的建议来解决 当我跑步时npm uninstall g some package 它只是返回 up to date in 043s 全球一揽子计划仍然存在
  • cakephp 3.x 保存多个实体 - newEntities

    我在保存多条记录方面遇到了最困难的时期 我已经尝试了一百万次 但最终遇到了同样的问题 我的记录没有保存 而且我看不到任何错误 请记住 我是 cakephp 的新手 也是一名新手编码员 我是否遗漏了一些明显且关键的东西 Table this
  • Google 在 React 中使用 firebase 登录 chrome 扩展

    使用 Firebase 进行 Google 登录 并使用 React 创建的 Chrome 扩展程序 我已经使用设置了 oauthGoogleConsole并能够使用 chrome 扩展程序成功登录 chrome identity getA
  • Leaflet Draw spritesheet 图标问题 - 缺失且未对齐

    我已将传单绘制纳入我的一个项目中 我的问题是图标没有显示在工具栏中 它看起来像这样 环顾四周我发现THIS https github com Leaflet Leaflet draw issues 617发布并按照其说明进行操作 我在 Le
  • 在 apache (ubuntu 12) 下将 python 脚本作为 cgi 运行时出现问题

    披露 我搜索了很多 我认为我的问题 针对我的配置 在这里没有得到解答 例如作为 cgi apache 服务器运行 python 脚本 https stackoverflow com questions 15878010 run python
  • Unity android 项目抛出“抱歉,您的硬件不支持此应用程序”错误

    我已经调查了2天了 我读了很多东西 总结一下我读到的内容 非 NEON 设备无法与 UNITY 5 版本一起使用 您应该将安装位置设置为 自动或强制内部 您应该将写入权限设置为 仅限内部 除了上述设置之外 我还尝试了这些纹理压缩设置 不要覆
  • 如何更改 actionPerformed() 内的 Swing Timer Delay

    所以我正在构建这个音乐播放器应用程序 它可以播放拖放到 JLabel 上的音符 当我按下播放按钮时 我希望每个音符都突出显示 并带有与该音符对应的延迟值 我为此使用了 Swing Timer 但问题是 它只是以构造函数中指定的恒定延迟循环
  • 返回元组时 GCC/Clang x86_64 C++ ABI 不匹配?

    当尝试时优化 x86 64 上的返回值 https stackoverflow com q 25381736 3919155 我注意到一个奇怪的事情 即 给定代码 include