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会报错.
posted @ 2019-12-23 01:06  Five100Miles  阅读(2560)  评论(1编辑  收藏  举报