Page 91 - 《软件学报》2025年第7期
P. 91
3012 软件学报 2025 年第 36 卷第 7 期
在每个优化级别分类器中, 标签在不同优化级别间存在序列依赖, 如图 6(b) 所示. 例如, O2 中进行内联函数
调用预测的输入是函数调用特征及已为 O0 和 O1 预测的标签. 考虑到在 O0 和 O1 中进行的内联决策通常也会出
现在 O2 中, ECOCCJ48 的架构可以利用优化间的内联关联性来产生更准确的预测结果.
此外, O2NMatcher 还使用集成方法 (ensemble method) 来增强分类器的性能. 在性能方面, 集成学习方法要优
于单模型学习方法 [23] . O2NMatcher 首先在随机选取的训练数据集上训练基分类器, 然后通过聚合基分类器的预
测来预测标签. 由于基本分类器可以在不同的语料库上进行训练, 它们能够捕捉到一些稀有的内联模式.
4.2.2 模型预测
在模式测试阶段, 给定一个开源软件项目, O2NMatcher 首先提取所有函数调用并构建其函数调用图 (function
call graph, FCG). 然后, 对于 FCG 中的每个函数调用, O2NMatcher 提取相应的特征. 将这些特征作为输入到
ECOCCJ48 中, 最后得到每个 O2NMatcher 在所有编译设置下的标签.
例如, 当 O2NMatcher 在两种编译器 (GCC 和 Clang) 和 4 种优化级别 (O0、O1、O2、O3) 上训练模型时, 开
源软件项目中的每个函数调用将有 8 个内联标签, 每个标签对应一个编译设置.
获取每个函数调用的标签后, O2NMatcher 创建与 8 种编译设置相对应的 8 个 FCG. 然后, O2NMatcher 在这
些 8 个 FCG 中标记内联的函数调用. 最后, 将得到 8 个有标签的 FCG 用于进一步的源代码函数集合生成.
4.3 源代码函数集合生成
在获得标记的 FCG 后, 需要在其中生成相应的源代码函数集合. 图 7(a) 展示了一个有标签的 FCG 示例. 图中
用红色边表示内联调用, 蓝色边表示普通调用. 红色圆圈代表有内联调用的函数, 黑色圆圈代表仅有普通调用的
函数.
A A
B A
a a
b1 b2 a
D C C C C D
c1 c1
d c1 c2 d
F E A C D E E F
(a) 有标签的 FCG (b) 根节点选取 (c) 调用边扩展
图 7 源代码函数集合生成示例
在 GCC 和 Clang 的函数内联实施中, 当启发式规则决定对给定的调用图边进行内联时, 被调用者的节点会被
克隆以表示由内联器稍后生成的新函数副本, 因此, 所有被内联的函数实际上是被克隆之后添加到内联该函数的
主函数中, 因此 O2NMatcher 需要将每个内联函数添加至主函数中, 以生成一个由主函数和内联函数组成的新
函数.
另外, 如果一个仅被调用一次的函数被内联了, 那么该函数将不会作为独立的函数在二进制文件中被编译. 这
表明在函数内联过程, 还会删除一些函数, 而这些函数将不能作为主函数内联更多函数.
参照上述 GCC 和 Clang 中的函数内联过程, 并且考虑到函数内联的过程就是将内联函数调用所连接的函数
重新组合成一个函数的过程, 本文将源代码函数集合生成的过程转化为包含根节点选择和边扩展的内联函数调用
图的生成问题. 根节点选择目的在于选择可以作为主函数的源代码函数节点, 而边扩展则是将每个内联函数添加
至主函数中, 以生成对应的源代码函数集合.
4.3.1 根节点选择
首先, 本文将内联子图定义为由内联调用及其关联函数构成的 FCG 的子图. 例如, 在图 7(a) 中有两个内联子
图, 分别是 (D, F) 和 (A, C, E). 简单来说, 只要内联子图中的节点能满足以下两个条件之一, 它就可以作为根节点
来生成子树.
(1) 节点是内联子图的根节点.

