LLVM指令选择中的模式定义方法

目标指令选择

目标指令选择阶段会把后端td文件里面的DAG模式和selection DAG的节点相匹配
如果找到一个匹配,则匹配的节点会被有具体机器指令(或者伪指令)的节点代替。
因此你的td模式的指令对于生成好的代码很重要,因此你需要话费很多时间来调试
这些模式。

理解pat用法

模式的record记录从llvm的pat类继承,这个pat类有两个参数,第一个参数是要匹配模式
的dag,第二个参数是机器指令的dag,当模式匹配了dag中的一些节点时,匹配的节点会被第二个参数中的dag替换。

接下来要使用一些类来帮助精简基于pat构建的td模式,这里有这样两个类。

class PatGprGpr<SDPatternOperator OpNode, RWInst Inst>
    : Pat<(OpNode GPR:$rs1, GPR:$rs2), (Inst GPR:$rs1, GPR:$rs2)>;
class PatGprSimm12<SDPatternOperator OpNode, RWInstI Inst>
    : Pat<(OpNode GPR:$rs1, simm12:$imm12), (Inst GPR:$rs1, simm12:$imm12)>;

patgprgpr会把在dag中两通用寄存器为输入指令的节点替换掉,比如下面是对ADD指令的
pattern的定义:

def : PatGprGpr<add, ADD>;

PatGprSimm12会把dag中输入为一个通用寄存器和一个12bit立即数的指令替换掉。为了
达到这个目标,第一个和第二个参数的第二个操作数应该是simm12而不是GPR。注意这里simm12是一个记录,它也是从类Operand继承的,因为可以用于做指令定义。
simm12还继承了ImmLeaf类,因此它也可以在模式中使用。

下面的例子中展示了对ADDI指令的模式定义。

def : PatGprSimm12<add, ADDI>;

NOTE: ImmLeaf类是一个用来匹配立即数的模式,他的参数是立即数的类型,这里的类型
可以是i32, i16等等,和一个predicate,也就是一段c++代码,
这段代码用来表达如何检查模式要匹配一个节点必须满足的条件。
举个例子,simm12的predicate是

return isInt<12>(Imm)

用来检查是否一个整数能够放进12比特中。

def simm12 : Operand<i32>, ImmLeaf<i32, [{return isInt<12>(Imm);}]> {
  let ParserMatchClass = SImmAsmOperand<12>;
  let EncoderMethod = "getImmOpValue";
  let DecoderMethod = "decodeSImmOperand<12>";
  let MCOperandPredicate = [{
    int64_t Imm;
    if (MCOp.evaluateAsConstantImm(Imm))
      return isInt<12>(Imm);
    return MCOp.isBareSymbolRef();
  }];
  let OperandType = "OPERAND_SIMM12";
  let OperandNamespace = "RISCWOp";
}

NOTE:有的时候同一个指令可能会在selectiondag中被匹配到一个或者多个模式,在这种情况下,LLVM的PatFrag和Pat配合来避免代码重复,这个可以参见RISCW架构的bit shift模式。

class shiftop<SDPatternOperator operator>
    : PatFrags<(ops node:$val, node:$count),
               [(operator node:$val, node:$count),
                (operator node:$val, (and node:$count, immbottomwsizeset))]>;
def : PatGprGpr<shiftop<shl>, SLL>;

理解SDNodeXForm

RISCW和RISCV后端都先定义指令然后会单独提供模式记录。然而其他后端比如ARM和XCore会
有所不同,他们把模式参数作为输入,这种方法降低了代码总行数,但是导致代码很难读懂。

Pat的第二个参数,如果找到了dag中的一个匹配,还会对操作数(通常是立即数)做变换。

为了这样做,我们首先需要定义一个继承自SDNodeXForm的操作节点。SDNodeXForm的参数
为一段实现变换的C++代码,我们使用我们新生命的模式中的代码作为例子,在这个例子中,
HI20节点会抽取一个立即数的高20位。

def HI20 : SDNodeXForm<imm, [{
  return CurDAG->getTargetConstant(((N->getZExtValue()+0x800) >> 12) & 0xfffff,
                                   SDLoc(N), N->getValueType(0));
}]>;
def : Pat<(simm32hi20:$imm), (LUI (HI20 imm:$imm))>;

NOTE: SDNodeXForm节点不会插入任何实际的节点,通常他们是被用来对立即数做变换。
这样操作就能够在代码释放之前完全解析。

随着目标指令选择的进行,LLVM会对每个它处理selection DAG节点来调用来自后端SelectionDAGIsel的select方法。这样后端就能够提供代码,在RISCVW的Select方法
中,包含了把常量0替换为x0寄存器的访问的方法。

switch(Opcode) {
  case ISD::Constant: {
    auto ConstNode = cast<ConstantSDNode>(Node);
    if (ConstNode->isNullValue()) {
      SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node),
                                           RISCW::X0, MVT::i32);
      ReplaceNode(Node, New.getNode());
      return;
    }
    break;
  }
  default: break;
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值