| // This test describes how we eventually want to describe instructions in |
| // the target independent code generators. |
| // RUN: llvm-tblgen %s |
| // XFAIL: vg_leak |
| |
| // Target indep stuff. |
| class Instruction { // Would have other stuff eventually |
| bit isTwoAddress = 0; |
| string AssemblyString; |
| } |
| class RegisterClass; |
| |
| class RTLNode; |
| |
| def ops; // Marker for operand list. |
| |
| // Various expressions used in RTL descriptions. |
| def imm8 : RTLNode; |
| def imm32 : RTLNode; |
| def addr : RTLNode; |
| |
| def set : RTLNode; |
| def signext : RTLNode; |
| def zeroext : RTLNode; |
| def plus : RTLNode; |
| def and : RTLNode; |
| def xor : RTLNode; |
| def shl : RTLNode; |
| def load : RTLNode; |
| def store : RTLNode; |
| def unspec : RTLNode; |
| |
| // Start of X86 specific stuff. |
| |
| def R8 : RegisterClass; |
| def R16 : RegisterClass; |
| def R32 : RegisterClass; |
| |
| def CL; // As are currently defined |
| def AL; |
| def AX; |
| def EDX; |
| |
| class Format<bits<5> val> { |
| bits<5> Value = val; |
| } |
| |
| def Pseudo : Format<0>; def RawFrm : Format<1>; |
| def AddRegFrm : Format<2>; def MRMDestReg : Format<3>; |
| def MRMDestMem : Format<4>; def MRMSrcReg : Format<5>; |
| def MRMSrcMem : Format<6>; |
| def MRM0r : Format<16>; def MRM1r : Format<17>; def MRM2r : Format<18>; |
| def MRM3r : Format<19>; def MRM4r : Format<20>; def MRM5r : Format<21>; |
| def MRM6r : Format<22>; def MRM7r : Format<23>; |
| def MRM0m : Format<24>; def MRM1m : Format<25>; def MRM2m : Format<26>; |
| def MRM3m : Format<27>; def MRM4m : Format<28>; def MRM5m : Format<29>; |
| def MRM6m : Format<30>; def MRM7m : Format<31>; |
| |
| |
| class Inst<dag opnds, string asmstr, bits<8> opcode, |
| Format f, list<dag> rtl> : Instruction { |
| dag Operands = opnds; |
| string AssemblyString = asmstr; |
| bits<8> Opcode = opcode; |
| Format Format = f; |
| list<dag> RTL = rtl; |
| } |
| |
| |
| // Start of instruction definitions, the real point of this file. |
| // |
| // Note that these patterns show a couple of important things: |
| // 1. The order and contents of the operands of the MachineInstr are |
| // described here. Eventually we can do away with this when everything |
| // is generated from the description. |
| // 2. The asm string is captured here, which makes it possible to get rid of |
| // a ton of hacks in the various printers and a bunch of flags. |
| // 3. Target specific properties (e.g. Format) can still be captured as |
| // needed. |
| // 4. We capture the behavior of the instruction with a simplified RTL-like |
| // expression. |
| // 5. The use/def properties for each operand are automatically inferred from |
| // the pattern. |
| // 6. Address expressions should become first-class entities. |
| |
| // Simple copy instruction. |
| def MOV8rr : Inst<(ops R8:$dst, R8:$src), |
| "mov $dst, $src", 0x88, MRMDestReg, |
| [(set R8:$dst, R8:$src)]>; |
| |
| // Simple immediate initialization. |
| def MOV8ri : Inst<(ops R8:$dst, imm8:$src), |
| "mov $dst, $src", 0xB0, AddRegFrm, |
| [(set R8:$dst, imm8:$src)]>; |
| |
| // Two address instructions are described as three-addr instructions, with |
| // the special target-independent isTwoAddress flag set. The asm pattern |
| // should not refer to the $src1, this would be enforced by the |
| // TargetInstrInfo tablegen backend. |
| let isTwoAddress = 1 in |
| def AND8rr : Inst<(ops R8:$dst, R8:$src1, R8:$src2), |
| "and $dst, $src2", 0x20, MRMDestReg, |
| [(set R8:$dst, (and R8:$src1, R8:$src2))]>; |
| |
| // Instructions that have explicit uses/defs make them explicit in the RTL. |
| // Instructions that need extra stuff emitted in the assembly can, trivially. |
| let isTwoAddress = 1 in |
| def SHL32rCL : Inst<(ops R32:$dst, R32:$src), |
| "shl $dst, CL", 0xD2, MRM4r, |
| [(set R32:$dst, (shl R32:$src, CL))]>; |
| |
| // The RTL list is a list, allowing complex instructions to be defined easily. |
| // Temporary 'internal' registers can be used to break instructions apart. |
| let isTwoAddress = 1 in |
| def XOR32mi : Inst<(ops addr:$addr, imm32:$imm), |
| "xor $dst, $src2", 0x81, MRM6m, |
| [(set R32:$tmp1, (load addr:$addr)), |
| (set R32:$tmp2, (xor R32:$tmp1, imm32:$imm)), |
| (store addr:$addr, R32:$tmp2)]>; |
| |
| // Alternatively, if each tmporary register is only used once, the instruction |
| // can just be described in nested form. This would be the canonical |
| // representation the target generator would convert the above into. Pick your |
| // favorite indentation scheme. |
| let isTwoAddress = 1 in |
| def AND32mr : Inst<(ops addr:$addr, R32:$src), |
| "xor $dst, $src2", 0x81, MRM6m, |
| [(store addr:$addr, |
| (and |
| (load addr:$addr), |
| R32:$src) |
| ) |
| ]>; |
| |
| // Describing complex instructions is not too hard! Note how implicit uses/defs |
| // become explicit here. |
| def CBW : Inst<(ops), |
| "cbw", 0x98, RawFrm, |
| [(set AX, (signext AL))]>; |
| |
| // Noop, does nothing. |
| def NOOP : Inst<(ops), "nop", 0x90, RawFrm, []>; |
| |
| |
| // Instructions that don't expect optimization can use unspec. |
| def IN8rr : Inst<(ops), "in AL, EDX", 0xEC, RawFrm, |
| [(set AL, (unspec EDX))]>; |
| |