为什么std :: tuple破坏了C ++中的小型结构调用约定优化?

如何解决为什么std :: tuple破坏了C ++中的小型结构调用约定优化?

C ++具有小型结构调用约定优化功能,其中,编译器通过函数参数传递小型结构的效率与传递原始类型(例如通过寄存器)的效率一样。例如:

class MyInt { int n; public: MyInt(int x) : n(x){} };
void foo(int);
void foo(MyInt);
void bar1() { foo(1); }
void bar2() { foo(MyInt(1)); }

bar1()bar2()生成几乎相同的汇编代码,除了分别调用foo(int)foo(MyInt)。特别是在x86_64上,它看起来像:

        mov     edi,1
        jmp     foo(MyInt) ;tail-call optimization jmp instead of call ret

但是如果我们测试std::tuple<int>,它将有所不同:

void foo(std::tuple<int>);
void bar3() { foo(std::tuple<int>(1)); }

struct MyIntTuple : std::tuple<int> { using std::tuple<int>::tuple; };
void foo(MyIntTuple);
void bar4() { foo(MyIntTuple(1)); }

生成的汇编代码看起来完全不同,小型结构(std::tuple<int>)通过指针传递:

        sub     rsp,24
        lea     rdi,[rsp+12]
        mov     DWORD PTR [rsp+12],1
        call    foo(std::tuple<int>)
        add     rsp,24
        ret

我挖得更深一些,试图使我的int更加脏(这应该接近一个不完整的朴素元组impl):

class Empty {};
class MyDirtyInt : protected Empty,MyInt {public: using MyInt::MyInt; };
void foo(MyDirtyInt);
void bar5() { foo(MyDirtyInt(1)); }

但应用了调用约定优化:

        mov     edi,1
        jmp     foo(MyDirtyInt)

我尝试了GCC / Clang / MSVC,它们都表现出相同的行为。 (Godbolt link here)所以我猜这一定是C ++标准中的东西吗? (我相信C ++标准没有指定任何ABI约束吗?)

我知道,只要foo(std::tuple<int>)的定义是可见的并且没有标记为noinline,编译器就应该能够对其进行优化。但是我想知道标准或实施的哪一部分导致此优化无效。

仅供参考,如果您对std::tuple的使用感到好奇,我想创建一个包装器类(即 strong typedef ),并且不想我自己声明比较运算符(运算符在C ++ 20之前),并且不想打扰Boost,所以我认为std::tuple是一个很好的基类,因为所有内容都在那里。

解决方法

这似乎与ABI有关。例如,Itanium C++ ABI reads

如果出于调用目的参数类型非平凡,则调用方必须为临时变量分配空间并通过引用传递该临时变量。

然后,further

如果类型具有非平凡的副本构造函数,移动构造函数或析构函数或所有副本,则出于调用目的将其视为非平凡的和移动构造函数被删除。

AMD64 ABI Draft 1.0中的要求相同。

例如,在 libstdc ++ 中,std::tuple具有简单的移动构造函数:https://godbolt.org/z/4j8vds。该标准规定了both copy and move constructor as defaulted,在此满足。但是,同时tuple inherits from _Tuple_impl_Tuple_impl has a user-defined move constructor。因此,tuple本身的move构造函数不能太琐碎。

相反,在 libc ++ 中,std::tuple<int>的复制和移动构造函数都是微不足道的。因此,该参数在https://godbolt.org/z/WcTjM9的寄存器中传递。

对于 Microsoft STL std::tuple<int>既不可复制构造,也不可移动构造。甚至似乎违反了C ++标准规则。 std::tuple是递归定义的,在递归结束时,std::tuple<>专业化定义了non-defaulted copy constructor。关于此问题有一条评论:// TRANSITION,ABI: should be defaulted。由于tuple<>没有move构造函数,因此tuple<class...>的copy和move构造函数都是不平凡的。

,

如@StoryTeller所建议,它可能与std::tuple中用户定义的move构造函数有关,从而导致此行为。

例如参见:https://godbolt.org/z/3M9KWo

具有用户定义的move构造函数会导致未优化程序集:

bar_my_tuple():
        sub     rsp,24
        lea     rdi,[rsp+12]
        mov     DWORD PTR [rsp+12],1
        call    foo(MyTuple<int>)
        add     rsp,24
        ret

例如在libcxx中,复制和移动构造函数都被声明为默认for tuple_leaffor tuple,并且您获得了小型结构调用约定优化for std::tuple<int>但{{3} }持有一个不可微动的成员,因此自然就变得不可微动。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-