// 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))]>; | |