LLVM筆記(7) - 指令的side effect


  1. 什么是指令的side effect
    在后端優化中常常見到MI.hasUnmodeledSideEffects()這個接口, 其代表該指令具有無法衡量的副作用. 對於這類指令, 編譯器在優化時會保守處理, 比如指令調度會以此為邊界(在其之后的指令不會調度到之前).

  2. 查看指令的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
  1. 設置指令的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的小結:

  1. 指令定義與pattern定義時都可以顯式聲明該屬性.
  2. table-gen在自動生成時會優先將pattern的side effect屬性拷貝給指令.
  3. 只有未存在pattern的指令時才會使用指令的side effect屬性.
  4. 若未存在pattern的指令未顯式聲明會默認添加side effect屬性.
  5. 在存在對應pattern的指令上顯式聲明side effect是合法的(即使對應的pattern無side effect), 但對有side effect的pattern聲明對應指令無side effect會報錯.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM