Compiler construction LLVM如何将三地址LLVM IR`add`转换为X86两地址`add`?

Compiler construction LLVM如何将三地址LLVM IR`add`转换为X86两地址`add`?,compiler-construction,llvm,Compiler Construction,Llvm,编辑: 我找到了处理此问题的TwoAddressInstructionPasspass。现在我正在研究实现 LLVM IR的add是一条三地址指令: %x = add i32 %y %z 但是X86add是两个地址: add eax 2 翻译似乎需要某种形式的: mov dst src1 add dst src2 所以我很好奇LLVM是怎么做到的。我查看了X86InstrArithmetic.td,找到了arithbiop\u RF的定义: multiclass ArithBinOp_RF

编辑:

我找到了处理此问题的
TwoAddressInstructionPass
pass。现在我正在研究实现

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

%x = add i32 %y %z
但是X86
add
是两个地址:

add eax 2
翻译似乎需要某种形式的:

mov dst src1
add dst src2
所以我很好奇LLVM是怎么做到的。我查看了
X86InstrArithmetic.td
,找到了
arithbiop\u 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
多类ARITHBONP\u RF{
设Defs=[EFLAGS]in{
将约束设置为“$src1=$dst”{
让isCommutable=in中的可交换RR{
设isConvertibleToThreeAddress=ConvertibleToThreeAddressRR in{
def名称#8rr:BINOPRU RF;
def名称#16rr:BINOPRU RF;
def名称#32rr:BINOPRU RF;
def名称#64rr:BINOPRU RF;
}//可转换为三种格式
}//不可交换
我怀疑
约束
与翻译有关:

  • 如果是这种情况,
    约束如何工作
  • 如果不是,LLVM如何以及在何处处理翻译(除了
    lea
    案例)?是否插入
    mov
    ?如果是这种情况,LLVM如何处理冗余
    mov
    案例

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

    static bool isTwoAddrUse(MachineInstr&MI、unsigned Reg、unsigned&DstReg){
    for(无符号i=0,NumOps=MI.getNumOperands();i!=NumOps;++i){
    常量machineeperand&MO=MI.getOperand(i);
    如果(!MO.isReg()| |!MO.isUse()| | MO.getReg()!=Reg)
    继续;
    无符号ti;
    if(MI.isRegTiedToDefOperand(i和ti)){
    DstReg=MI.getOperator(ti.getReg();
    返回true;
    }
    }
    返回false;
    }
    
    “绑定”表示两个操作数映射到同一寄存器。当我们在tablegen中写入
    约束=“$src1=$dst”
    时,生成的代码将
    $src1
    $dst
    设置为“绑定”(GlobalISel/Utils.cpp):

    bool llvm::ConstrainedSelectedInStregorAnds(机器安装和维护),
    const TargetInfo&TII,
    const TargetRegisterInfo和TRI,
    常量注册表(银行信息和RBI){
    断言(!isPreISelGenericOpcode(I.getOpcode())&&
    “预期会有选定的指令”);
    MachineBasicBlock&MBB=*I.getParent();
    MachineFunction&MF=*MBB.getParent();
    MachineRegisterInfo&MRI=MF.getRegInfo();
    for(未签名的OpI=0,OpE=I.getNumExplicitOperands();OpI!=OpE;++OpI){
    MachinePerand&MO=I.GetOperator(OpI);
    //在非寄存器操作数上无需执行任何操作。
    如果(!MO.isReg())
    继续;
    
    LLVM_调试(dbgs()为什么LLVM首先要或需要插入那些
    mov
    s?记住LLVM使用SSA,没有办法说
    %x=add i32%x%y
    @arnt这是我想到的问题。LLVM没有办法表达
    %x=add i32%x%y
    ,但在x86中这是
    add
    的方法。所以我的问题是LLVM怎么做Transforms
    %z=add i32%x%y
    到x86
    add dst src
    中。一个寄存器包含添加前的
    %y
    ,以及添加后的
    %z
    。添加后可能没有任何寄存器包含
    %y
    %y
    不是寄存器,你看,它是一个值,具有自己的活性概念。寄存器允许tor(有几个)可能会决定一直将其保存在寄存器中,将其保存在两个不同位置的不同寄存器中,从内存中读取,无论什么。@arnt感谢您的解释。IIUC,您所说的是寄存器分配短语。但IMO指令选择是第一位的,不是吗?在选择短语中(在寄存器分配(?)之前),LLVM选择LLVM IR进入x86机器指令,我的问题是LLVM将如何选择LLVM IR
    add
    ?如果寄存器分配器决定一直将其保留在寄存器中,正如您所说,它必须发出
    mov
    指令?