LLVM笔记(7) - 指令的side effect
-
什么是指令的side effect
在后端优化中常常见到MI.hasUnmodeledSideEffects()这个接口, 其代表该指令具有无法衡量的副作用. 对于这类指令, 编译器在优化时会保守处理, 比如指令调度会以此为边界(在其之后的指令不会调度到之前). -
查看指令的side effect属性
通常情况下不同架构的指令定义在[llvm_build_dir]/lib/Target/[arch]/[arch]GenInstrInfo.inc文件中(其中llvm_build_dir为构建目录, arch为具体架构). 以下列举了(ARM架构下)几种常见的包含side effect指令: 栈缩减(伪指令), 32位CAS(伪指令), 跳转, 独占访问, 访问状态寄存器等. 可见所有对于编译器无法理解的约束(栈增长/减少必须在函数起始/结束, 原子操作, 独占访问)都被描述为side effect, 而load/store等指令则不带有该标记.
{ 179, 4, 0, 0, 1028, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::Predicable)|(1ULL<<MCID::UnmodeledSideEffects), 0x0ULL, ImplicitList2, ImplicitList2, OperandInfo42, -1 ,nullptr }, // Inst #179 = ADJCALLSTACKDOWN
{ 195, 5, 2, 0, 1029, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::MayStore)|(1ULL<<MCID::UnmodeledSideEffects), 0x0ULL, nullptr, nullptr, OperandInfo54, -1 ,nullptr }, // Inst #195 = CMP_SWAP_32
{ 626, 1, 0, 4, 855, 0|(1ULL<<MCID::Call)|(1ULL<<MCID::UnmodeledSideEffects), 0x180ULL, nullptr, nullptr, OperandInfo45, -1 ,nullptr }, // Inst #626 = BLXi
{ 635, 0, 0, 4, 841, 0|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::MayStore)|(1ULL<<MCID::UnmodeledSideEffects), 0xd00ULL, nullptr, nullptr, nullptr, -1 ,nullptr }, // Inst #635 = CLREX
{ 746, 8, 0, 4, 847, 0|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::MayStore)|(1ULL<<MCID::Predicable)|(1ULL<<MCID::UnmodeledSideEffects), 0x100ULL, nullptr, nullptr, OperandInfo164, -1 ,&getMCRDeprecationInfo }, // Inst #746 = MCR
{ 1793, 7, 1, 4, 946, 0|(1ULL<<MCID::MayStore)|(1ULL<<MCID::Predicable), 0x3c2ULL, nullptr, nullptr, OperandInfo271, -1 ,nullptr }, // Inst #1793 = STRB_POST_REG
- 设置指令的side effect属性
默认情况下基础指令(指无pattern匹配的指令)默认包含side effect属性. 这是因为在基础的Instruction定义中hasSideEffects为?, 即默认带有side effect(见include/llvm/Target/Target.td). 如果指令不需要该属性可以显式指定该属性为0(反之亦然), 以ARM指令为例, 栈增长/缩减指令需要显式指定side effect, 而常量则无需指定side effect:
let hasSideEffects = 0, isNotDuplicable = 1, hasNoSchedulingInfo = 1 in
def CONSTPOOL_ENTRY :
PseudoInst<(outs), (ins cpinst_operand:$instid, cpinst_operand:$cpidx,
i32imm:$size), NoItinerary, []>;
let Defs = [SP], Uses = [SP], hasSideEffects = 1 in {
def ADJCALLSTACKUP :
PseudoInst<(outs), (ins i32imm:$amt1, i32imm:$amt2, pred:$p), NoItinerary,
[(ARMcallseq_end timm:$amt1, timm:$amt2)]>;
def ADJCALLSTACKDOWN :
PseudoInst<(outs), (ins i32imm:$amt, i32imm:$amt2, pred:$p), NoItinerary,
[(ARMcallseq_start timm:$amt, timm:$amt2)]>;
}
当未指定hasSideEffects属性时table-gen保守处理默认包含side effect属性, 但有些时候默认属性并不起效. 这是由于pattern匹配的side effect属性会默认覆盖指令的side effect属性. 以ARM store指令为例:
multiclass AI2_stridx<bit isByte, string opc,
InstrItinClass iii, InstrItinClass iir> {
...
def _POST_REG : AI2ldstidx<0, isByte, 0, (outs GPR:$Rn_wb),
(ins GPR:$Rt, addr_offset_none:$addr, am2offset_reg:$offset),
IndexModePost, StFrm, iir,
opc, "\t$Rt, $addr, $offset",
"$addr.base = $Rn_wb,@earlyclobber $Rn_wb", []> {
// {12} isAdd
// {11-0} imm12/Rm
bits<14> offset;
bits<4> addr;
let Inst{25} = 1;
let Inst{23} = offset{12};
let Inst{19-16} = addr;
let Inst{11-0} = offset{11-0};
let Inst{4} = 0;
let DecoderMethod = "DecodeAddrMode2IdxInstruction";
}
...
}
defm STRB : AI2_stridx<1, "strb", IIC_iStore_bh_iu, IIC_iStore_bh_ru>;
def : ARMPat<(post_store GPR:$Rt, addr_offset_none:$addr,
am2offset_reg:$offset),
(STR_POST_REG GPR:$Rt, addr_offset_none:$addr,
am2offset_reg:$offset)>;
store指令并未指定hasSideEffects, 按理生成的inc文件中会包含该属性, 然而上文已经显示该指令并不存在side effect. 这是因为pattern post_store对应的node是ISD::STORE, 该node并无side effect.
关于pattern与instr属性设置的具体的实现可以参见table-gen代码. 在utils/TableGen/CodeGenDAGPatterns.cpp中定义了CodeGenDAGPattern::InferInstructionFlags(), 该接口会根据pattern设置对应指令的side effect属性并检测该指令对应的所有pattern的side effect属性是否一致.
static bool InferFromPattern(CodeGenInstruction &InstInfo,
const InstAnalyzer &PatInfo,
Record *PatDef) {
...
if (InstInfo.hasSideEffects != PatInfo.hasSideEffects &&
!InstInfo.hasSideEffects_Unset) {
if (!InstInfo.hasSideEffects) {
Error = true;
PrintError(PatDef->getLoc(), "Pattern doesn't match hasSideEffects = " +
Twine(InstInfo.hasSideEffects));
}
}
...
InstInfo.hasSideEffects |= PatInfo.hasSideEffects;
}
void CodeGenDAGPatterns::InferInstructionFlags() {
...
for (const PatternToMatch &PTM : ptms()) {
...
Errors += InferFromPattern(InstInfo, PatInfo, PTM.getSrcRecord());
}
if (Target.guessInstructionProperties()) {
...
if (InstInfo->hasSideEffects_Unset)
InstInfo->hasSideEffects = true;
}
}
关于side effect的小结:
- 指令定义与pattern定义时都可以显式声明该属性.
- table-gen在自动生成时会优先将pattern的side effect属性拷贝给指令.
- 只有未存在pattern的指令时才会使用指令的side effect属性.
- 若未存在pattern的指令未显式声明会默认添加side effect属性.
- 在存在对应pattern的指令上显式声明side effect是合法的(即使对应的pattern无side effect), 但对有side effect的pattern声明对应指令无side effect会报错.