笔者在查看PGO优化时看到了本站的这篇文章,其中代码和命令行部分贴上了序号,且命令行带上了$符号,不便于读者调试。
 遂将代码重新整理到gitee,链接在此。
汇编代码分析
目前笔者使用的llvm版本为llvm-19,主要改动发生在生成的汇编文件上,可以看到,在使用PGO优化的版本里,已经没有了分支指令(不再存在两个不同的分支)。 优化前的汇编代码:
优化前的汇编代码:
main:                                   # @main.cfi_startproc
# %bb.0:movl	XX(%rip), %eaxmovl	YY(%rip), %ecxcmpl	$2, %edijl	.LBB0_2
# %bb.1:addl	$2, %eaximull	%eax, %ecx
.LBB0_3:movl	%eax, XX(%rip)movl	%ecx, YY(%rip)xorl	%eax, %eaxretq
.LBB0_2:decl	%eaxsubl	%eax, %ecxjmp	.LBB0_3
可以看到,首先将XX分配给eax寄存器,然后将YY分配给ecx寄存器,分别有%bb.1和.LBB0_2两处不同的处理,对应着源代码中true分支和false分支的处理。
优化后的汇编代码:
main:                                   # @main.cfi_startproc
# %bb.0:movl	XX(%rip), %eaxmovl	YY(%rip), %ecxleal	2(%rax), %edxmovl	%ecx, %esiimull	%edx, %esidecl	%eaxsubl	%eax, %ecxcmpl	$2, %edicmovgel	%edx, %eaxcmovgel	%esi, %ecxmovl	%eax, XX(%rip)movl	%ecx, YY(%rip)xorl	%eax, %eaxretq
关键在于true分支的值存放在edx和esi中,false分支的值放置在eax和ecx中,当程序进行到判断结构时,如果为true的情况,就将edx移动到eax,将esi移动到ecx中,使得最终的结果始终分别存放在eax和ecx两个寄存器当中。
涉及代码分析
lib/CodeGen/MachineBlockPlacement.cpp
 此文件的部分主体逻辑基本未变化,把BranchProbability SuccProb = getAdjustedProbability(RealSuccProb, AdjustedSumProb);看成计算出一个可用的概率的结果就可以了,其他部分基本无障碍。
/// The helper function returns the branch probability that is adjusted
/// or normalized over the new total \p AdjustedSumProb.
static BranchProbability
getAdjustedProbability(BranchProbability OrigProb,BranchProbability AdjustedSumProb) {BranchProbability SuccProb;uint32_t SuccProbN = OrigProb.getNumerator();uint32_t SuccProbD = AdjustedSumProb.getNumerator();if (SuccProbN >= SuccProbD)SuccProb = BranchProbability::getOne();elseSuccProb = BranchProbability(SuccProbN, SuccProbD);return SuccProb;
}
贴一个Chatgpt的解释:
 这个函数的作用是根据给定的原始分支概率(OrigProb)和调整后的总和概率(AdjustedSumProb),返回一个调整后的分支概率(SuccProb)。
具体来说,函数的步骤如下:
- 获取原始分支概率的分子 SuccProbN,这是原始概率的分子部分。
- 获取调整后总和概率的分子 SuccProbD,这是调整后总和概率的分子部分。
- 检查原始概率的分子部分 SuccProbN 是否大于等于调整后总和概率的分子部分 SuccProbD。如果是,则将调整后的分支概率 SuccProb 设置为最大可能的分支概率,即 BranchProbability::getOne(),这表示概率为100%。
- 否则,根据 SuccProbN 和 SuccProbD 创建一个新的 BranchProbability 对象 SuccProb,这个对象将成为函数的返回值。
因此,这个函数的核心目的是根据原始分支概率和调整后的总和概率,计算并返回一个调整后的分支概率,确保它在有效的概率范围内(0到1之间),以反映新的条件或上下文下的分支可能性。