Compiler considerations — The Linux Kernel 5.7.0+ documentation
本文为摘录(或转载),侵删,原文为: https://people.redhat.com/~jolawren/klp-compiler-notes/livepatch/compiler-considerations.html
1 Interprocedural optimization
函数内联可能是影响动态修补最常见的编译器优化。在一个简单的例子中,内联将原始代码转换为:
|
|
变为:
|
|
内联类似于宏展开,然而编译器可能会内联它认为值得的情况(同时保留其他情况下的原始调用/返回语义),甚至部分内联函数的部分(见下文“GCC 函数后缀”中的冷函数)。
要安全地对上述示例中的 foo()进行动态修补,需要考虑到所有调用它的地方。对于那些编译器已经内联了 foo()的调用者,动态修补应该包含一个调用新的被修补内联函数版本的调用函数的新版本,使得它:
- 调用内联函数的新的被修补版本,或者
- 提供调用者的更新版本,其中包含其自己的内联并更新的内联函数版本
2 GCC function suffixes
GCC 可能会根据应用的优化将原始、复制和克隆函数进行重命名。以下是编译器可能应用于内核函数的名称后缀的部分列表:
冷子函数(Cold subfunctions)
:.code
或者.cold.<N>
:
由属性或优化确定不太可能执行的函数(子函数)的部分。例如,
irq_do_set_affinity()
的不太可能执行的部分可能移出到子函数irq_do_set_affinity.cold.49()
。从 GCC 9 开始,编号后缀已被移除。因此,在上一个例子中,冷子函数简单地为irq_do_set_affinity.cold()
。部分内:联
.part.<N>
:
从原始函数主体中分离出来的函数部分,在整体内联决策上有所改进。 函数cdev_put()
提供了一个有趣的部分克隆的例子。GCC 会将源函数:1 2 3 4 5 6 7 8
void cdev_put(struct cdev *p) { if (p) { struct module *owner = p->owner; kobject_put(&p->kobj); module_put(owner); } }
拆成两个函数:
cdev_put.part.0()
为部分内联,cdev_put
会调用cdev_put.part.0()
将 cdev_put()
中的条件测试和 cdev_put.part.0()
中的 kobject_put()
和 module_put()
调用分成两个函数:
|
|
常量传播 (Constant propagation)
:.constprop.<N>
:
function copies to enable constant propagation when conflicting arguments exist.For example, consider
cpumask_weight()
and its copies forcpumask_weight(cpu_possible_mask)
andcpumask_weight (__cpu_online_mask)
.Note how the .constprop copies implicitly assign the function parameter:
|
|
- IPA-SRA : .isra.0 : TODO