【问题标题】:How does LLVM translate three address LLVM IR `add` into X86 two address `add`?LLVM 如何将三个地址 LLVM IR `add` 转换为 X86 两个地址`add`?
【发布时间】:2019-12-24 08:13:16
【问题描述】:

编辑:

我找到了处理这个问题的TwoAddressInstructionPass 通行证。现在我正在研究实现。

LLVM IR 的add 是一个三地址指令:

%x = add i32 %y %z

但是X86add是两个地址:

add eax 2

似乎翻译需要某种:

mov dst src1
add dst src2

所以我很好奇 LLVM 是如何做到的。我查看了X86InstrArithmetic.td,找到了ArithBinOp_RF的定义:

multiclass ArithBinOp_RF<bits<8> BaseOpc, bits<8> BaseOpc2, bits<8> BaseOpc4,
                         string mnemonic, Format RegMRM, Format MemMRM,
                         SDNode opnodeflag, SDNode opnode,
                         bit CommutableRR, bit ConvertibleToThreeAddress,
                         bit ConvertibleToThreeAddressRR> {
  let Defs = [EFLAGS] in {
    let Constraints = "$src1 = $dst" in {
      let isCommutable = CommutableRR in {
        let isConvertibleToThreeAddress = ConvertibleToThreeAddressRR in {
          def NAME#8rr  : BinOpRR_RF<BaseOpc, mnemonic, Xi8 , opnodeflag>;
          def NAME#16rr : BinOpRR_RF<BaseOpc, mnemonic, Xi16, opnodeflag>;
          def NAME#32rr : BinOpRR_RF<BaseOpc, mnemonic, Xi32, opnodeflag>;
          def NAME#64rr : BinOpRR_RF<BaseOpc, mnemonic, Xi64, opnodeflag>;
        } // isConvertibleToThreeAddress
      } // isCommutable

我怀疑Constraints与翻译有关:

  1. 如果是这样,Constraints 是如何工作的?
  2. 如果不是,LLVM 如何以及在哪里处理翻译(lea 情况除外)?它是否插入mov?如果是这样,LLVM 如何处理冗余的movs?

【问题讨论】:

  • 为什么 LLVM 想要或需要首先插入那些 movs?记住LLVM用的是SSA,没办法说%x = add i32 %x %y
  • @arnt 这就是我想到的问题。 LLVM 无法表达%x = add i32 %x %y,但这是在x86 中add 的方式。所以我的问题是 LLVM 如何将 %z = add i32 %x %y 转换为 x86 add dst src
  • 一个寄存器在添加之前包含%y,在添加之后包含%z。添加后可能没有任何寄存器包含%y%y 不是一个寄存器,你看,它是一个值,有它自己的活性概念。寄存器分配器(有几个)可能决定一直将它保存在一个寄存器中,将它保存在不同位置的两个不同的寄存器中,从内存中读取它,等等。
  • @arnt 感谢您的解释。 IIUC,您在谈论寄存器分配短语。但是 IMO 指令选择是第一位的,不是吗?在选择短语(在寄存器分配(?)之前),LLVM 将 LLVM IR 选择为 x86 机器指令,我的问题是 LLVM 如何选择 LLVM IR add?如果寄存器分配器决定一直将其保存在寄存器中,如您所说,它必须发出mov 指令...?

标签: compiler-construction llvm


【解决方案1】:

有一个TwoAddressInstructionPass,如果MachineInstr 中的一些MachineOperands 被绑定(TwoAddressInstructionPass.cpp),它会将三地址模式指令转换为两地址模式:

static bool isTwoAddrUse(MachineInstr &MI, unsigned Reg, unsigned &DstReg) {
  for (unsigned i = 0, NumOps = MI.getNumOperands(); i != NumOps; ++i) {
    const MachineOperand &MO = MI.getOperand(i);
    if (!MO.isReg() || !MO.isUse() || MO.getReg() != Reg)
      continue;
    unsigned ti;
    if (MI.isRegTiedToDefOperand(i, &ti)) {
      DstReg = MI.getOperand(ti).getReg();
      return true;
    }
  }
  return false;
}

“绑定”表示两个操作数映射到同一个寄存器。当我们在 tablegen 中写入Constraints = "$src1 = $dst" 时,生成的代码会将$src1$dst 设置为“绑定”(GlobalISel/Utils.cpp):

bool llvm::constrainSelectedInstRegOperands(MachineInstr &I,
                                            const TargetInstrInfo &TII,
                                            const TargetRegisterInfo &TRI,
                                            const RegisterBankInfo &RBI) {
  assert(!isPreISelGenericOpcode(I.getOpcode()) &&
         "A selected instruction is expected");
  MachineBasicBlock &MBB = *I.getParent();
  MachineFunction &MF = *MBB.getParent();
  MachineRegisterInfo &MRI = MF.getRegInfo();

  for (unsigned OpI = 0, OpE = I.getNumExplicitOperands(); OpI != OpE; ++OpI) {
    MachineOperand &MO = I.getOperand(OpI);

    // There's nothing to be done on non-register operands.
    if (!MO.isReg())
      continue;

    LLVM_DEBUG(dbgs() << "Converting operand: " << MO << '\n');
    assert(MO.isReg() && "Unsupported non-reg operand");

    Register Reg = MO.getReg();
    // Physical registers don't need to be constrained.
    if (Register::isPhysicalRegister(Reg))
      continue;

    // Register operands with a value of 0 (e.g. predicate operands) don't need
    // to be constrained.
    if (Reg == 0)
      continue;

    // If the operand is a vreg, we should constrain its regclass, and only
    // insert COPYs if that's impossible.
    // constrainOperandRegClass does that for us.
    MO.setReg(constrainOperandRegClass(MF, TRI, MRI, TII, RBI, I, I.getDesc(),
                                       MO, OpI));

    // Tie uses to defs as indicated in MCInstrDesc if this hasn't already been
    // done.
    if (MO.isUse()) {
      int DefIdx = I.getDesc().getOperandConstraint(OpI, MCOI::TIED_TO);
      if (DefIdx != -1 && !I.isRegTiedToUseOperand(DefIdx))
        I.tieOperands(DefIdx, OpI);
    }
  }
  return true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 2017-03-05
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多