[医]脱脩脟茅脕麓陆脱
编译器。[医]脱脩脟茅脕麓陆脱 是一个编译器实用程序模块,旨在分析 朱莉娅的SSA表格IR又名。 IRCode的.
这种逃逸分析旨在:
*利用Julia的高级语义,特别是通过过程间调用来解释转义和别名
*具有足够的通用性,可用于各种优化,包括https://github.com/JuliaLang/julia/pull/43888[别名感知SROA],https://github.com/JuliaLang/julia/pull/44056[早 最后确定 插入],https://github.com/JuliaLang/julia/pull/42465[免拷贝 不变的阵列 构造]、可变对象的堆栈分配等等。
*基于完全向后的数据流分析实现以及结合正交格属性的新格设计实现简单的实现
试试吧!
您可以通过加载 尤蒂尔斯。jl 定义方便项的实用程序脚本 code_escapes 和 @code_escapes 用于测试和调试目的:
每个call自变量和SSA语句侧面的符号表示以下含义:
* ◌ (普通):这个值不会被分析,因为它的转义信息无论如何都不会被使用(当对象是 等位类型 例如)
* ✓ (绿色或青色):此值永远不会逃逸(has_no_escape(结果。状态[x]) 持有),颜色为蓝色,如果它有arg逃脱也(has_arg_escape(结果。状态[x]) 持有)
* ↑ (蓝色或黄色):该值可以通过return(has_return_escape(结果。状态[x]) 持有),颜色为黄色,如果它已经未处理抛出逃脱也(has_thrown_escape(结果。状态[x]) 持有)
* X (红色):这个值可以逃逸到某个地方,逃逸分析不能推理像逃逸到全局内存(has_all_escape(结果。状态[x]) 持有)
* * (粗体):该值的转义状态介于 返回图 和 阿勒斯卡图 在部分顺序 亚太资讯,颜色为黄色,如果它已经未处理抛出逃脱也(has_thrown_escape(结果。状态[x]) 持有)
* ′:此值具有附加的对象字段/数组元素信息在其 AliasInfo 物业
每个调用参数和SSA值的转义信息可以通过编程方式检查,如下所示:
分析设计
格子设计
[医]脱脩脟茅脕麓陆脱 被实现为https://en.wikipedia.org/wiki/Data-flow_analysis[数据流分析]在 x::EscapeInfo,其由以下属性组成:
* X.分析::Bool:不是形式上的格子的一部分,只表示 x 没有分析过
* X.ReturnEscape::BitSet:记录SSA报表 x 可以通过return转义到调用方
* X.ThrownEscape::BitSet:记录SSA报表 x 可以作为异常抛出(用于 异常处理如下所述)
* x.AliasInfo:维护所有可能的值,这些值可以别名为 x (用于 别名分析如下所述)
* X.ArgEscape::Int (尚未实现):表示它将通过 塞特菲尔德! 关于争论
考虑到输入程序具有有限数量语句的不变性,这些属性可以组合起来创建一个具有有限高度的部分格,Julia的语义保证了这一点。 这种格子设计的巧妙之处在于,它允许格子操作单独处理每个格子属性,从而实现了更简单的格子操作。 事实证明,它开始使晶格操作的实现复杂化,主要是因为它经常需要每个晶格元素类型对象之间的转换规则。 我们正在努力https://github.com/JuliaLang/julia/pull/42596[大修我们的类型推断格实现]与 亚太资讯-像格子设计。].
后向逃逸传播
该逃逸分析实现基于paper[1].分析工作在晶格 亚太资讯 并通过维护包含与待分析的剩余SSA语句相对应的程序计数器的(概念)工作集,将晶格元素从底部过渡到顶部,直到每个晶格元素收敛到一个固定点。 分析管理跟踪的单个全局状态 亚太资讯 每个参数和SSA语句,但也要注意,一些流灵敏度被编码为记录在程序计数器 亚太资讯'的 返回图 属性,如有必要,可与支配分析结合,以推理流动敏感性。
这种转义分析的一个独特设计是它完全_backward_,即转义信息从usages流向definitions_。 例如,在下面的代码片段中,EA首先分析语句 回报%1 并施加 返回图 上 %1 (对应于 反对),然后它分析 %1=%new(Base.重新价值{Base.RefValue{String}, _2})) 并传播 返回图 强加于 %1 到调用参数 _2 (对应于 s):
这里的关键观察是,这种后向分析允许转义信息沿着use-def链自然流动,而不是control-flow[2]. 因此,该方案可以简单地实现逃逸分析,例如 [医]菲诺德 例如,可以通过传播强加于a的转义信息来简单地处理 [医]菲诺德 对其前身的价值观:
别名分析
[医]脱脩脟茅脕麓陆脱 实现后向字段分析,以便以一定的精度推理对对象字段施加的转义,以及 x::EscapeInfo'的 x.AliasInfo 财产就是为了这个目的而存在的。 它记录所有可能的值,这些值可以别名为 x 在"使用"站点,然后该记录值的转义信息稍后在"定义"站点传播到实际字段值。 更具体地说,分析记录了一个值,该值可能通过分析而被混叠到对象的字段中 盖菲尔德 调用,然后它在分析时将其转义信息传播到字段 %新(。..) 表达或 塞特菲尔德! callfootnote:Dynamism[然而,在某些情况下,对象字段无法精确分析。 例如,对象可能会逃逸到某个地方 [医]脱脩脟茅脕麓陆脱 无法解释可能对其产生的记忆效应,或者由于缺少类型信息而无法知道对象的字段。 在这种情况下 AliasInfo 属性被提升到其自己的晶格顺序内的最顶层元素,并且它导致后续场分析是保守的,并且对不可分析对象的场施加的转义信息被传播到对象本身。].
在上面的例子中, 返回图 强加于 %3 (对应于 v)是_not_直接传播到 %1 (对应于 反对)但更确切地说 返回图 只传播到 _2 (对应于 s). 这里 %3 被记录在 %1'的 AliasInfo 属性,因为它可以别名到 %1,然后在分析时 基地。塞特菲尔德!(%1,:x,_2)::字符串,该转义信息被传播到 _2 但不是为了 %1.
所以 [医]脱脩脟茅脕麓陆脱 跟踪哪些IR元素可以在 盖菲尔德-%新/塞特菲尔德! 链以便分析对象字段的转义,但实际上这种别名分析也需要泛化处理其他IR元素。 这是因为在Julia IR中,同一个对象有时由不同的IR元素表示,因此我们应该确保那些实际上可以表示同一个对象的不同IR元素共享相同的转义信息。 返回与其操作数相同对象的IR元素,例如 [医]皮诺德 和 打字/打字,可能会导致IR级混叠,因此需要在它们之间共享对任何此类混叠值施加的转义信息。 更有趣的是,它也是正确推理突变所必需的。 [医]菲诺德. 让我们考虑下面的例子:
ϕ1 = %5 和 ϕ2 = %6 被别名,因此 返回图 强加于 %8=基数。getfield(%6,:x)::字符串 (对应于 y=〇1[])需要传播到 基地。塞特菲尔德!(%5,:x,_3)::字符串 (对应于 ф2[]=x). 为了正确传播这种转义信息,分析应该认识到 ϕ1 和 ϕ2 也可以被别名,并均衡他们的逃逸信息。
这种别名信息的一个有趣的特性是,它在"使用"站点不知道,但只能在"定义"站点派生(因为别名在概念上等同于赋值),因此它自然不适合后向分析。 为了有效地在相关值之间传播转义信息,EscapeAnalysis。jl使用了一种受旧JVM paperfootnote中解释的转义分析算法启发的方法:Jvm05[escape Analysis in The Context Of Dynamic Compilation and Deoptimization. Thomas Kotzmann和Hanspeter Mössenböck,2005,6月。 https://dl.acm.org/doi/10.1145/1064979.1064996。]。也就是说,除了管理转义格元素外,分析还维护一个"equi"-别名集,一组不相交的别名参数和SSA语句。 别名集管理可以彼此别名的值,并允许在它们之间均衡对任何此类别名值施加的转义信息。
阵列分析
上面描述的对象字段的别名分析也可以推广到分析数组操作。 [医]脱脩脟茅脕麓陆脱 实现各种原始数组操作的处理,以便它可以通过 arrayref-阵列,阵列 使用-def链,并且不会过于保守地转义分配的数组:
在上面的例子中 [医]脱脩脟茅脕麓陆脱 明白这一点 %20 和 %2 (对应于分配的对象 安全装置)通过 阵列,阵列-arrayref 链和强加 返回图 在他们身上,但不是强加给分配的数组 %1 (对应于 阿利). [医]脱脩脟茅脕麓陆脱 仍然强加 N.倒影,倒影 上 阿利 因为它还需要考虑通过 BoundsError,但也要注意,这样的未处理 N.倒影,倒影 优化时往往可以忽略 阿利 分配。
此外,在数组索引信息和数组维度可以精确地知道的情况下_, [医]脱脩脟茅脕麓陆脱 甚至能够通过 arrayref-阵列,阵列 链,作为 [医]脱脩脟茅脕麓陆脱 是否对对象进行"每字段"别名分析:
请注意 返回图 只强加于 %2 (对应于 安全装置)但不是上 %4 (对应于 安全(t)). 这是因为分配的数组的维度和索引涉及所有 arrayref/阵列,阵列 操作可作为恒定信息和 [医]脱脩脟茅脕麓陆脱 能理解 %6 被别名为 %2 但永远不要被化名为 %4. 在这种情况下,后续的优化传递将能够替换 基地。arrayref(true,%1,1)::任何 与 %2 (a.k.a."load-forwarding")并最终消除阵列的分配 %1 完全(又名"标量替换")。
与对象字段分析相比,对象字段的访问可以使用推理派生的类型信息进行简单分析,数组维度不会被编码为类型信息,因此我们需要额外的分析来推导 [医]脱脩脟茅脕麓陆脱 此时,在启动主分析例程之前,首先执行一个额外的简单线性扫描来分析已分配数组的维度,以便后续的逃逸分析可以精确分析这些数组上的操作。
然而,这种精确的"每个元素"别名分析通常很难。 本质上,数组继承的主要困难是数组维度和索引通常是非常量:
*循环经常产生循环变量,非恒定数组索引 *(特定于向量)数组大小调整会更改数组维度并使其常量无效
让我们用具体的例子来讨论这些困难。
在下面的示例中, [医]脱脩脟茅脕麓陆脱 失败的精确别名分析,因为索引在 基地。arrayset(错误, %4, %8, %6)::向量资料{Any} 不是(平凡的)常数。 特别是 任何[没有,没有] 形成一个循环并调用 阵列,阵列 循环操作,其中 %6 表示为ϕ节点值(其值取决于控制流)。 结果是, 返回图 最终强加于两者 %23 (对应于 安全装置)和 %25 (对应于 安全(t)),虽然理想情况下我们希望它只强加于 %23 但不是在 %25:
下一个示例说明了矢量大小调整如何使精确别名分析变得困难。 基本的困难是分配数组的维数 %1 首先初始化为 0,但它由两个改变 :jl_array_grow_end 之后再打电话。 [医]脱脩脟茅脕麓陆脱 目前只是放弃精确的别名分析,每当它遇到任何数组大小调整操作等 返回图 是强加于两者 %2 (对应于 安全装置)和 %20 (对应于 安全(t)):
为了解决这些困难,我们需要推理来了解数组维度,并在流敏感的wayfootnote中传播数组维度:ArrayDimension[否则,我们需要在转义分析之上进行另一个前向数据流分析。],以及提出循环变量值的良好表示。
[医]脱脩脟茅脕麓陆脱 此时,快速切换到更不精确的分析,该分析在数组维度或索引非常不恒定的情况下不会跟踪精确的索引信息。 开关自然可以实现为晶格连接操作 EscapeInfo。AliasInfo 数据流分析框架中的属性。
异常处理
这也将是值得注意的是如何 [医]脱脩脟茅脕麓陆脱 通过异常处理可能的转义。 天真地,它似乎足以传播强加于人的逃逸信息。 :the_exception 对象到所有可能在相应的值中抛出的值 试试 块。 但实际上还有其他几种方法可以访问Julia中的异常对象,例如 基地。当前概念 和 重新思考. 例如,逃逸分析需要考虑潜在的逃逸。 r 在下面的例子中:
它需要全局分析,以便通过现有异常接口正确推理所有可能的转义。 现在,我们总是保守地将最顶层的转义信息传播给所有可能抛出的对象,因为考虑到异常处理和错误路径通常不需要对性能非常敏感,并且错误路径的优化也可能非常无效,因为它们通常出于延迟原因故意"未优化"。
x::EscapeInfo'的 x.投掷 物业记录SSA报表 x 可以作为异常抛出。 使用此信息 [医]脱脩脟茅脕麓陆脱 可以通过异常传播可能的转义,仅限于可能在每个异常中抛出的转义 试试 区域:
分析用法
分析/分析 是分析SSA-IR元素逃逸信息的切入点。
大多数优化如SROA(sroa_pass!)应用于内联传递的优化源时更有效(ssa_inlining_pass!)通过解决过程间调用和扩展被调用方源来简化。 因此, 分析/分析 还能够分析内联后IR并收集对某些内存相关优化有用的转义信息。
但是,由于像内联这样的某些优化传递可以更改控制流并消除死代码,因此它们可以破坏转义信息的过程间有效性。 具体而言,为了收集程序间有效的转义信息,我们需要分析一个预内联IR。
因为这个原因, 分析/分析 可以分析 IRCode的 在任何Julia级别的优化阶段,特别是,它应该在以下两个阶段使用:
* IPO EA:分析预内联IR生成非有效转义信息缓存
* 本地EA:分析内联后IR以收集本地有效的转义信息
转义信息由 IPO EA 被转化为 [医]银针,银针 数据结构并全局缓存。 通过适当的 get_escape_cache 回拨至 分析/分析,通过利用先前派生的非内联调用的缓存过程间信息,转义分析可以提高分析的准确性 IPO EA. 更有趣的是,它也是有效的使用 IPO EA 用于类型推断的转义信息,例如,可以通过形成来提高推断精度 康斯特/局部结构/穆斯塔利亚斯 可变对象。
# *`基地。编译器。逃逸分析。分析/分析`*-函数
analyze_escapes(ir::IRCode, nargs::Int, get_escape_cache) -> estate::EscapeState
分析转义信息 ir表格:
* 纳格斯:分析调用的实际参数个数
* get_escape_cache(::MethodInstance)->联合{Bool,ArgEscapeCache}:检索缓存的参数转义信息
# *`基地。编译器。逃逸分析。亚太资讯`*-类型
x::EscapeInfo
用于转义信息的格,其中包含以下属性:
* X.分析::Bool:不是形式上的格子的一部分,只表示是否 x 已经分析过
* X.ReturnEscape::Bool:表示 x 可以通过return转义到调用方
* X.ThrownEscape::BitSet:记录SSA报表编号 x 可以作为异常抛出:
** isempty(X.ThrownEscape): x 永远不会在这个调用框架中抛出(底部)
** pc№x.ThrownEscape: x 可能会在SSA声明中抛出 个人电脑
** -1∈x.投掷: x 可以在这个调用帧的任意点(顶部)抛出
+
这些资料将由 escape_exception! 通过异常传播潜在的逃逸。
* x.AliasInfo::联合{Bool,IndexableFields,Unindexable}:维护所有可能的值,这些值可以别名为 x:
** x.AliasInfo===false 指示的字段/元素 x 还没有分析
** x.AliasInfo===真 指示的字段/元素 x 无法分析,例如 x 不知道或不具体,因此它的领域/元素不能精确地知道
** x.AliasInfo::IndexableFields 记录可以别名为对象字段的所有可能值 x 具有精确的索引信息
** x.AliasInfo::不可索引 记录所有可能的值,这些值可以别名为 x 没有精确的索引信息
* X.Liveness::BitSet:记录SSA报表编号 x 应该是活的,例如用作调用参数,返回给调用者,或保留为 :外汇储备:
** isempty(X.Liveness): x 永远不会在这个调用框架中使用(底部)
** 活性 还具有特殊含义,即它是当前分析的调用帧的调用参数(因此它立即从调用方可见)。
** 活性: x 可在SSA声明中使用 个人电脑
** -1∈x.活力: x 可用于此调用帧的任意点(顶部)
有实用程序构造函数来创建公共 亚太资讯s,例如,
* NoEscape():这个格子的底部(-like)元素,意味着它不会逃到任何地方
* AllEscape():这个格子的最上面的元素,意味着它会逃到任何地方
分析/分析 将这些元素从底部过渡到顶部,与Julia的原生类型推断例程的方向相同。 抽象状态将使用底部(-like)元素初始化:
*调用参数初始化为 阿格斯卡普(),其 活,活 物业包括 0 指示它作为调用参数传递,并从调用者立即可见
*其他状态初始化为 NotAnalyzed(),这是一种特殊的晶格元素,略低于 诺斯卡皮,但在同一时间不代表任何意义,除了它还没有分析(因此它不是正式的格子的一部分)