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
但是X86add
是两个地址:
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;
}//可转换为三种格式
}//不可交换
我怀疑约束
与翻译有关:
约束如何工作
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
到x86add dst src
中。一个寄存器包含添加前的%y
,以及添加后的%z
。添加后可能没有任何寄存器包含%y
。%y
不是寄存器,你看,它是一个值,具有自己的活性概念。寄存器允许tor(有几个)可能会决定一直将其保存在寄存器中,将其保存在两个不同位置的不同寄存器中,从内存中读取,无论什么。@arnt感谢您的解释。IIUC,您所说的是寄存器分配短语。但IMO指令选择是第一位的,不是吗?在选择短语中(在寄存器分配(?)之前),LLVM选择LLVM IR进入x86机器指令,我的问题是LLVM将如何选择LLVM IRadd
?如果寄存器分配器决定一直将其保留在寄存器中,正如您所说,它必须发出mov
指令?