-
什么是指令的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會報錯.