llvm 对 copy 属性的优化(2)
前言
本文将会回答两个问题:
- 什么场景会调用
objc_copyCppObjectAtomic
函数? 在上篇文章中,我们并没有发现任何场景会调用objc_copyCppObjectAtomic
函数。 - copyHelper 是如何生成的? iOS 中 copy 的原理中曾经提到作者没有找到 copyHelper(dest, src); 的实现方法。
含 c++ 类的复制行为
为了解释前言中的两个问题,我们需要在 CopyMock
新增了一个属性 str
,该属性的类型是 std::string
。
#include <string>
@interface CopyMock : NSObject
@property (readwrite) std::string str;
@end
与第一篇文章类似,我们先将代码编译为中间码。如下所示:
// 赋值函数
; Function Attrs: noinline optnone ssp uwtable
define internal void @" 1-[CopyMock setStr:]"(%0*, i8*, %"class.std::__1::basic_string"*) #0 !dbg !1897 {
%4 = alloca %0*, align 8
%5 = alloca i8*, align 8
store %0* %0, %0** %4, align 8
call void @llvm.dbg.declare(metadata %0** %4, metadata !1900, metadata !DIExpression()), !dbg !1901
store i8* %1, i8** %5, align 8
call void @llvm.dbg.declare(metadata i8** %5, metadata !1902, metadata !DIExpression()), !dbg !1901
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"* %2, metadata !1903, metadata !DIExpression()), !dbg !1901
%6 = load %0*, %0** %4, align 8, !dbg !1904
%7 = bitcast %0* %6 to i8*, !dbg !1904
%8 = getelementptr inbounds i8, i8* %7, i64 32, !dbg !1904
%9 = bitcast i8* %8 to %"class.std::__1::basic_string"*, !dbg !1904
%10 = bitcast %"class.std::__1::basic_string"* %9 to i8*, !dbg !1904
%11 = bitcast %"class.std::__1::basic_string"* %2 to i8*, !dbg !1904
call void @objc_copyCppObjectAtomic(i8* %10, i8* %11, i8* bitcast (void (%"class.std::__1::basic_string"*, %"class.std::__1::basic_string"*)* @__assign_helper_atomic_property_ to i8*)), !dbg !1904
ret void, !dbg !1904
}
编译器会先帮开发者增加一个普通的赋值方法 -[CopyMock setStr:]
。
该方法最后会调用 objc_copyCppObjectAtomic
函数,
三个参数分别是:
- 属性str的存储地址,CopyMock 实例的偏移 32 位
`%8 = getelementptr inbounds i8, i8* %7, i64 32, !dbg !1904`
- 被复制
str
的地址`%11 = bitcast %"class.std::__1::basic_string"* %2 to i8*, !dbg !1904`
- 由编译器生成的辅助复制函数
`__assign_helper_atomic_property_`
__assign_helper_atomic_property_
在分析 __assign_helper_atomic_property_
的内部逻辑前,我们需要再看一遍 objc_copyCppObjectAtomic
函数的声明: void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source))
。
通过原型我们可以发现 copyHelper
是 objc_copyCppObjectAtomic
的第三个参数。
实际上,void (*copyHelper) (void *dest, const void *source))
是一个函数指针,它的实现是通过调用方传参决定。在本例中,它会指向 __assign_helper_atomic_property_
。
在阅读 __assign_helper_atomic_property_
的代码前,需要先准备几个小知识:
- c++ 的函数名会被 name mangle,所以
_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
实际上代表字符串复制函数std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
- 中间码具有 ssp 特征,阅读比较繁琐,编译后的汇编会被高度优化,下一章会讲到编译器的优化
- c++ 中的
std::string
和class.std::__1::basic_string
是等价的 - alloca 可以在栈中开辟空间,具有效率高,不需要主动释放等特性
小知识准备结束,下面开始对 __assign_helper_atomic_property_
的实现内容进行分析:
// 辅助赋值函数
; Function Attrs: noinline ssp uwtable
define internal void @__assign_helper_atomic_property_(%"class.std::__1::basic_string"*, %"class.std::__1::basic_string"*) #5 !dbg !1891 {
// 在栈中开辟空间
%3 = alloca %"class.std::__1::basic_string"*, align 8
// 在栈中开辟空间,备用
%4 = alloca %"class.std::__1::basic_string"*, align 8
// 存储参数 %0 到 %3
store %"class.std::__1::basic_string"* %0, %"class.std::__1::basic_string"** %3, align 8
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"** %3, metadata !1892, metadata !DIExpression()), !dbg !1893
// 存储参数 %1 到 %4
store %"class.std::__1::basic_string"* %1, %"class.std::__1::basic_string"** %4, align 8
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"** %4, metadata !1894, metadata !DIExpression()), !dbg !1893
// 取出参数 %4 到 %5
%5 = load %"class.std::__1::basic_string"*, %"class.std::__1::basic_string"** %4, align 8, !dbg !1893
// 取出参数 %3 到 %6
%6 = load %"class.std::__1::basic_string"*, %"class.std::__1::basic_string"** %3, align 8, !dbg !1893
// 调用字符串复制方法 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
%7 = call dereferenceable(24) %"class.std::__1::basic_string"* @_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_(%"class.std::__1::basic_string"* %6, %"class.std::__1::basic_string"* dereferenceable(24) %5), !dbg !1893
ret void, !dbg !1895
}
这个函数的逻辑很简单,相当于对 字符串复制函数 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
进行了一次间接调用。
总结
本文通过将代码改造为 cpp 代码,可以得到以下两个信息:
- copyHelper 通常是由编译器生成的辅助函数
- 具有 cpp 相关类复制的场景才会触发对
objc_copyCppObjectAtomic
函数的调用
- JDK容器学习之LinkedHashMap(二):迭代遍历的实现方式
- React Native 实现基于react-native-tab-navigator库Tab切换封装
- 16.4 配置Tomcat监听80端口
- JDK容器学习之Queue:LinkedBlockingQueue
- Linux基础(day59)
- 16.3 安装Tomcat
- 16.2 安装jdk
- UITabBarController实现Tab切换
- React Native库版本升级与降级
- Java并发学习之Volatile及内存模型探究
- Java并发学习之CountDownLatch实现原理及使用姿势
- Linux基础(day58)
- 携程Android App插件化和动态加载实践
- 15.5 使用pure-ftpd搭建ftp服务
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- APP脱壳方法三
- Flink Checkpoint 原理流程以及常见失败原因分析
- Docker原理之 - OverlayFS设计与实现
- 有赞零售跨平台打印库方案
- [Go] Golang练习项目-GO语言实现快速排序-第一个数作为基准更容易理解
- 有赞移动基础设施建设的实践和思考
- 大数据理论篇HDFS的基石——Google File System
- 6. 二十不惑,ObjectMapper使用也不再迷惑
- 接口自动化对比工具实践
- 什么?Java9这些史诗级更新你都不知道?Java9特性一文打尽!
- 利用 Arthas 精准定位 Java 应用 CPU 负载过高问题
- 你想了解的JDK 10版本更新都在这里
- Linux Page Cache调优在 Kafka 中的应用
- 声明式 UIKit 在有赞美业的实践
- 一个@Transaction哪里来这么多坑?