Early if-conversion
用于对于没有很多可预测指令的乱序CPU。目标是消除可能误预测的条件分支。
来自分支两侧的指令都会被推测性地执行,并使用 cmov 指令选择结果。
// SSAIfConv 类在确定可能的情况下,对SSA形式的机器码执行if-conversion。该类不包含任何启发式方法;
// 外部代码应该用于确定何时进行 if-conversion 是个好主意。
//
// SSAIfConv 可以转换三角形和菱形:
//
// 三角形: Head 菱形: Head
// | \ / \_
// | \ / |
// | [TF]BB FBB TBB
// | / \ /
// | / \ /
// Tail Tail
//
// 条件块TBB和/或FBB中的指令被插入到Head块中,Tail块中的phi指令被转换为select指令。
按支配树的后序遍历访问块。后序遍历允许在单次遍历中进行嵌套的if转换。tryConvertIf() 函数可能会擦除块,但仅擦除由头块支配的块。这使得在后序遍历迭代器仍然活动时更新支配树是安全的。
./BiShengKernel/build3/bin/llc -debugify-and-strip-all-safe < ./BiShengKernel/llvm/test/CodeGen/AArch64/arm64-early-ifcvt.ll -stress-early-ifcvt -aarch64-enable-atomic-cfg-tidy=0 -stop-before=early-ifcvt -o 1.mir
./BiShengKernel/build3/bin/llc -debugify-and-strip-all-safe < ./BiShengKernel/llvm/test/CodeGen/AArch64/arm64-early-ifcvt.ll -stress-early-ifcvt -aarch64-enable-atomic-cfg-tidy=0 -stop-after=early-ifcvt -o 2.mir
./build3/bin/llc -run-pass=dot-machine-cfg 1.mir
dot .mm2.dot -T svg -o 1.mm2.dot.svg
./build3/bin/llc -run-pass=dot-machine-cfg 2.mir
dot .mm2.dot -T svg -o 2.mm2.dot.svg
; Function Attrs: nounwind ssp memory(read) uwtable
define i32 @mm2(ptr nocapture %p, i32 %n) #0 {
entry:
br label %do.body
do.body: ; preds = %do.cond, %entry
...
br i1 %cmp, label %do.cond, label %if.else
if.else: ; preds = %do.body
...
br label %do.cond
do.cond: ; preds = %if.else, %do.body
...
br i1 %tobool, label %do.end, label %do.body
do.end: ; preds = %do.cond
%sub = sub nsw i32 %max.1, %min.1
ret i32 %sub
}
最终效果:
类似C语言:
int32_t mm2(int32_t *p, int32_t n) {
for(max=0,min=0; n != 0; n--) {
p = p + 1;
int32_t p0 = *p;
if (p0 > max) {
max = p0;
} else {
min = (p0 < min) ? p0 : min;
}
}
return max - min;
}
// 转换为:
int32_t mm2(int32_t *p, int32_t n) {
for(max=0,min=0; n != 0; n--) {
p = p + 1;
int32_t p0 = *p;
int32_t tmp = p0 < min ? p0 : min;
max = p0 > max ? p0 : max;
min = p0 > max ? min : tmp;
}
return max - min;
}
在 MIR 的表示:
几个重要的接口:
SSAIfConv::canConvertIf(..) {
// ...
TII->analyzeBranch(*Head, TBB, FBB, Cond); // llvm/lib/Target/AArch64/AArch64InstrInfo.cpp:266
// ...
if (!TII->canInsertSelect(*Head, Cond, PI.PHI->getOperand(0).getReg(),
PI.TReg, PI.FReg, PI.CondCycles, PI.TCycles,
PI.FCycles)) {}
// canPredicateInstrs - 如果 MBB 中的所有指令都可以安全地作为谓词,则返回true。不考虑终结指令。
/// 如果指令使用了在头基本块中定义的任何值,则将定义这些值的指令添加到InsertAfter中。
//
// 任何被破坏的寄存器单元都将添加到 ClobberedRegUnits 中。
SSAIfConv::canPredicateInstrs(MachineBasicBlock *MBB);
// canSpeculateInstrs - 如果MBB中的所有指令都可以安全地进行推测,则返回true。不考虑终结指令。
// 如果指令使用了在头基本块中定义的任何值,则将定义这些值的指令添加到InsertAfter中。
//
// 任何被破坏的寄存器单元都将添加到 ClobberedRegUnits 中。
SSAIfConv::canSpeculateInstrs(MachineBasicBlock *MBB);
}