Let's take an example of implementing a 32-bit immediate load with high/low pairs, where MOVW implies moving a 16-bit low immediate and a clear 16 high bit, and MOVT implies moving a 16-bit high immediate.
There can be various ways to implement this multiple instruction lowering. We can do this by using pseudo-instructions or in the selection DAG-to-DAG phase.
def MOVLOi16 : MOV<0b1000, "movw", (ins i32imm:$imm), [(set i32:$dst, i32imm_lo:$imm)]>; def MOVHIi16 : MOV<0b1010, "movt", (ins GRRegs:$src1, i32imm:$imm), [/* No Pattern */]>;
The second way is to define a pseudo-instruction in the .td
file:
def MOVi32 : InstTOY<(outs GRRegs:$dst), (ins i32imm:$src), "", [(set i32:$dst, (movei32 imm:$src))]> { let isPseudo = 1; }
TOYInstrInfo.cpp
file:bool TOYInstrInfo::expandPostRAPseudo(MachineBasicBlock::iterato r MI) const { if (MI->getOpcode() == TOY::MOVi32){ DebugLoc DL = MI->getDebugLoc(); MachineBasicBlock &MBB = *MI->getParent(); const unsigned DstReg = MI->getOperand(0).getReg(); const bool DstIsDead = MI->getOperand(0).isDead(); const MachineOperand &MO = MI->getOperand(1); auto LO16 = BuildMI(MBB, MI, DL, get(TOY::MOVLOi16), DstReg); auto HI16 = BuildMI(MBB, MI, DL, get(TOY::MOVHIi16)) .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) .addReg(DstReg); MBB.erase(MI); return true; } }
For example, an ex.ll
file with IR will look like this:
define i32 @foo(i32 %a) #0 { %b = add nsw i32 %a, 65537 ; 0x00010001 ret i32 %b }
The assembly generated will look like this:
movw r1, #1 movt r1, #1 add r0, r0, r1 b lr
The first instruction, movw
, will move 1 in the lower 16 bits and clear the high 16 bits. So in r1, 0x00000001
will be written by the first instruction. In the next instruction, movt
, the higher 16 bits will be written. So in r1, 0x0001XXXX
will be written, without disturbing the lower bits. Finally, the r1 register will have 0x00010001
in it. Whenever a pseudo-instruction is encountered as specified in the .td
file, its expand function is called to specify what the pseudo-instruction will expand to.
In the preceding case, the mov32
immediate was to be implemented by two instructions: movw
(the lower 16 bits) and movt
(the higher 16 bits). It was marked as a pseudo-instruction in the .td
file. When this pseudo-instruction needs to be emitted, its expand function is called, which builds two machine instructions: MOVLOi16
and MOVHIi16
. These map to the movw
and movt
instructions of the target architecture.
3.149.234.188