1 //===-- SIMCCodeEmitter.cpp - SI Code Emitter -------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // The SI code emitter produces machine code that can be executed directly on
11 // the GPU device.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "MCTargetDesc/AMDGPUMCTargetDesc.h"
16 #include "MCTargetDesc/AMDGPUMCCodeEmitter.h"
17 #include "llvm/MC/MCCodeEmitter.h"
18 #include "llvm/MC/MCContext.h"
19 #include "llvm/MC/MCInst.h"
20 #include "llvm/MC/MCInstrInfo.h"
21 #include "llvm/MC/MCRegisterInfo.h"
22 #include "llvm/MC/MCSubtargetInfo.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 #define LITERAL_REG 255
26 #define VGPR_BIT(src_idx) (1ULL << (9 * src_idx - 1))
27 #define SI_INSTR_FLAGS_ENCODING_MASK 0xf
28 
29 
30 // These must be kept in sync with SIInstructions.td and also the
31 // InstrEncodingInfo array in SIInstrInfo.cpp.
32 //
33 // NOTE: This enum is only used to identify the encoding type within LLVM,
34 // the actual encoding type that is part of the instruction format is different
35 namespace SIInstrEncodingType {
36   enum Encoding {
37     EXP = 0,
38     LDS = 1,
39     MIMG = 2,
40     MTBUF = 3,
41     MUBUF = 4,
42     SMRD = 5,
43     SOP1 = 6,
44     SOP2 = 7,
45     SOPC = 8,
46     SOPK = 9,
47     SOPP = 10,
48     VINTRP = 11,
49     VOP1 = 12,
50     VOP2 = 13,
51     VOP3 = 14,
52     VOPC = 15
53   };
54 }
55 
56 using namespace llvm;
57 
58 namespace {
59 class SIMCCodeEmitter : public  AMDGPUMCCodeEmitter {
60   SIMCCodeEmitter(const SIMCCodeEmitter &); // DO NOT IMPLEMENT
61   void operator=(const SIMCCodeEmitter &); // DO NOT IMPLEMENT
62   const MCInstrInfo &MCII;
63   const MCSubtargetInfo &STI;
64   MCContext &Ctx;
65 
66 public:
SIMCCodeEmitter(const MCInstrInfo & mcii,const MCSubtargetInfo & sti,MCContext & ctx)67   SIMCCodeEmitter(const MCInstrInfo &mcii, const MCSubtargetInfo &sti,
68                   MCContext &ctx)
69     : MCII(mcii), STI(sti), Ctx(ctx) { }
70 
~SIMCCodeEmitter()71   ~SIMCCodeEmitter() { }
72 
73   /// EncodeInstruction - Encode the instruction and write it to the OS.
74   virtual void EncodeInstruction(const MCInst &MI, raw_ostream &OS,
75                          SmallVectorImpl<MCFixup> &Fixups) const;
76 
77   /// getMachineOpValue - Reutrn the encoding for an MCOperand.
78   virtual uint64_t getMachineOpValue(const MCInst &MI, const MCOperand &MO,
79                                      SmallVectorImpl<MCFixup> &Fixups) const;
80 
81 public:
82 
83   /// GPRAlign - Encode a sequence of registers with the correct alignment.
84   unsigned GPRAlign(const MCInst &MI, unsigned OpNo, unsigned shift) const;
85 
86   /// GPR2AlignEncode - Encoding for when 2 consecutive registers are used
87   virtual unsigned GPR2AlignEncode(const MCInst &MI, unsigned OpNo,
88                                    SmallVectorImpl<MCFixup> &Fixup) const;
89 
90   /// GPR4AlignEncode - Encoding for when 4 consectuive registers are used
91   virtual unsigned GPR4AlignEncode(const MCInst &MI, unsigned OpNo,
92                                    SmallVectorImpl<MCFixup> &Fixup) const;
93 
94   /// i32LiteralEncode - Encode an i32 literal this is used as an operand
95   /// for an instruction in place of a register.
96   virtual uint64_t i32LiteralEncode(const MCInst &MI, unsigned OpNo,
97                                    SmallVectorImpl<MCFixup> &Fixup) const;
98 
99   /// SMRDmemriEncode - Encoding for SMRD indexed loads
100   virtual uint32_t SMRDmemriEncode(const MCInst &MI, unsigned OpNo,
101                                    SmallVectorImpl<MCFixup> &Fixup) const;
102 
103   /// VOPPostEncode - Post-Encoder method for VOP instructions
104   virtual uint64_t VOPPostEncode(const MCInst &MI, uint64_t Value) const;
105 
106 private:
107 
108   ///getEncodingType =  Return this SIInstrEncodingType for this instruction.
109   unsigned getEncodingType(const MCInst &MI) const;
110 
111   ///getEncodingBytes - Get then size in bytes of this instructions encoding.
112   unsigned getEncodingBytes(const MCInst &MI) const;
113 
114   /// getRegBinaryCode - Returns the hardware encoding for a register
115   unsigned getRegBinaryCode(unsigned reg) const;
116 
117   /// getHWRegNum - Generated function that returns the hardware encoding for
118   /// a register
119   unsigned getHWRegNum(unsigned reg) const;
120 
121 };
122 
123 } // End anonymous namespace
124 
createSIMCCodeEmitter(const MCInstrInfo & MCII,const MCSubtargetInfo & STI,MCContext & Ctx)125 MCCodeEmitter *llvm::createSIMCCodeEmitter(const MCInstrInfo &MCII,
126                                            const MCSubtargetInfo &STI,
127                                            MCContext &Ctx) {
128   return new SIMCCodeEmitter(MCII, STI, Ctx);
129 }
130 
EncodeInstruction(const MCInst & MI,raw_ostream & OS,SmallVectorImpl<MCFixup> & Fixups) const131 void SIMCCodeEmitter::EncodeInstruction(const MCInst &MI, raw_ostream &OS,
132                                        SmallVectorImpl<MCFixup> &Fixups) const {
133   uint64_t Encoding = getBinaryCodeForInstr(MI, Fixups);
134   unsigned bytes = getEncodingBytes(MI);
135   for (unsigned i = 0; i < bytes; i++) {
136     OS.write((uint8_t) ((Encoding >> (8 * i)) & 0xff));
137   }
138 }
139 
getMachineOpValue(const MCInst & MI,const MCOperand & MO,SmallVectorImpl<MCFixup> & Fixups) const140 uint64_t SIMCCodeEmitter::getMachineOpValue(const MCInst &MI,
141                                             const MCOperand &MO,
142                                        SmallVectorImpl<MCFixup> &Fixups) const {
143   if (MO.isReg()) {
144     return getRegBinaryCode(MO.getReg());
145   } else if (MO.isImm()) {
146     return MO.getImm();
147   } else if (MO.isFPImm()) {
148     // XXX: Not all instructions can use inline literals
149     // XXX: We should make sure this is a 32-bit constant
150     return LITERAL_REG;
151   } else{
152     llvm_unreachable("Encoding of this operand type is not supported yet.");
153   }
154   return 0;
155 }
156 
157 //===----------------------------------------------------------------------===//
158 // Custom Operand Encodings
159 //===----------------------------------------------------------------------===//
160 
GPRAlign(const MCInst & MI,unsigned OpNo,unsigned shift) const161 unsigned SIMCCodeEmitter::GPRAlign(const MCInst &MI, unsigned OpNo,
162                                    unsigned shift) const {
163   unsigned regCode = getRegBinaryCode(MI.getOperand(OpNo).getReg());
164   return regCode >> shift;
165   return 0;
166 }
GPR2AlignEncode(const MCInst & MI,unsigned OpNo,SmallVectorImpl<MCFixup> & Fixup) const167 unsigned SIMCCodeEmitter::GPR2AlignEncode(const MCInst &MI,
168                                           unsigned OpNo ,
169                                         SmallVectorImpl<MCFixup> &Fixup) const {
170   return GPRAlign(MI, OpNo, 1);
171 }
172 
GPR4AlignEncode(const MCInst & MI,unsigned OpNo,SmallVectorImpl<MCFixup> & Fixup) const173 unsigned SIMCCodeEmitter::GPR4AlignEncode(const MCInst &MI,
174                                           unsigned OpNo,
175                                         SmallVectorImpl<MCFixup> &Fixup) const {
176   return GPRAlign(MI, OpNo, 2);
177 }
178 
i32LiteralEncode(const MCInst & MI,unsigned OpNo,SmallVectorImpl<MCFixup> & Fixup) const179 uint64_t SIMCCodeEmitter::i32LiteralEncode(const MCInst &MI,
180                                            unsigned OpNo,
181                                         SmallVectorImpl<MCFixup> &Fixup) const {
182   return LITERAL_REG | (MI.getOperand(OpNo).getImm() << 32);
183 }
184 
185 #define SMRD_OFFSET_MASK 0xff
186 #define SMRD_IMM_SHIFT 8
187 #define SMRD_SBASE_MASK 0x3f
188 #define SMRD_SBASE_SHIFT 9
189 /// SMRDmemriEncode - This function is responsibe for encoding the offset
190 /// and the base ptr for SMRD instructions it should return a bit string in
191 /// this format:
192 ///
193 /// OFFSET = bits{7-0}
194 /// IMM    = bits{8}
195 /// SBASE  = bits{14-9}
196 ///
SMRDmemriEncode(const MCInst & MI,unsigned OpNo,SmallVectorImpl<MCFixup> & Fixup) const197 uint32_t SIMCCodeEmitter::SMRDmemriEncode(const MCInst &MI, unsigned OpNo,
198                                         SmallVectorImpl<MCFixup> &Fixup) const {
199   uint32_t Encoding;
200 
201   const MCOperand &OffsetOp = MI.getOperand(OpNo + 1);
202 
203   //XXX: Use this function for SMRD loads with register offsets
204   assert(OffsetOp.isImm());
205 
206   Encoding =
207       (getMachineOpValue(MI, OffsetOp, Fixup) & SMRD_OFFSET_MASK)
208     | (1 << SMRD_IMM_SHIFT) //XXX If the Offset is a register we shouldn't set this bit
209     | ((GPR2AlignEncode(MI, OpNo, Fixup) & SMRD_SBASE_MASK) << SMRD_SBASE_SHIFT)
210     ;
211 
212   return Encoding;
213 }
214 
215 //===----------------------------------------------------------------------===//
216 // Post Encoder Callbacks
217 //===----------------------------------------------------------------------===//
218 
VOPPostEncode(const MCInst & MI,uint64_t Value) const219 uint64_t SIMCCodeEmitter::VOPPostEncode(const MCInst &MI, uint64_t Value) const{
220   unsigned encodingType = getEncodingType(MI);
221   unsigned numSrcOps;
222   unsigned vgprBitOffset;
223 
224   if (encodingType == SIInstrEncodingType::VOP3) {
225     numSrcOps = 3;
226     vgprBitOffset = 32;
227   } else {
228     numSrcOps = 1;
229     vgprBitOffset = 0;
230   }
231 
232   // Add one to skip over the destination reg operand.
233   for (unsigned opIdx = 1; opIdx < numSrcOps + 1; opIdx++) {
234     const MCOperand &MO = MI.getOperand(opIdx);
235     if (MO.isReg()) {
236       unsigned reg = MI.getOperand(opIdx).getReg();
237       if (AMDGPUMCRegisterClasses[AMDGPU::VReg_32RegClassID].contains(reg) ||
238           AMDGPUMCRegisterClasses[AMDGPU::VReg_64RegClassID].contains(reg)) {
239         Value |= (VGPR_BIT(opIdx)) << vgprBitOffset;
240       }
241     } else if (MO.isFPImm()) {
242       union {
243         float f;
244         uint32_t i;
245       } Imm;
246       // XXX: Not all instructions can use inline literals
247       // XXX: We should make sure this is a 32-bit constant
248       Imm.f = MO.getFPImm();
249       Value |= ((uint64_t)Imm.i) << 32;
250     }
251   }
252   return Value;
253 }
254 
255 //===----------------------------------------------------------------------===//
256 // Encoding helper functions
257 //===----------------------------------------------------------------------===//
258 
getEncodingType(const MCInst & MI) const259 unsigned SIMCCodeEmitter::getEncodingType(const MCInst &MI) const {
260   return MCII.get(MI.getOpcode()).TSFlags & SI_INSTR_FLAGS_ENCODING_MASK;
261 }
262 
getEncodingBytes(const MCInst & MI) const263 unsigned SIMCCodeEmitter::getEncodingBytes(const MCInst &MI) const {
264 
265   // Instructions with literal constants are expanded to 64-bits, and
266   // the constant is stored in bits [63:32]
267   for (unsigned i = 0; i < MI.getNumOperands(); i++) {
268     if (MI.getOperand(i).isFPImm()) {
269       return 8;
270     }
271   }
272 
273   // This instruction always has a literal
274   if (MI.getOpcode() == AMDGPU::S_MOV_IMM_I32) {
275     return 8;
276   }
277 
278   unsigned encoding_type = getEncodingType(MI);
279   switch (encoding_type) {
280     case SIInstrEncodingType::EXP:
281     case SIInstrEncodingType::LDS:
282     case SIInstrEncodingType::MUBUF:
283     case SIInstrEncodingType::MTBUF:
284     case SIInstrEncodingType::MIMG:
285     case SIInstrEncodingType::VOP3:
286       return 8;
287     default:
288       return 4;
289   }
290 }
291 
292 
getRegBinaryCode(unsigned reg) const293 unsigned SIMCCodeEmitter::getRegBinaryCode(unsigned reg) const {
294   switch (reg) {
295     case AMDGPU::M0: return 124;
296     case AMDGPU::SREG_LIT_0: return 128;
297     default: return getHWRegNum(reg);
298   }
299 }
300 
301 #define SIRegisterInfo SIMCCodeEmitter
302 #include "SIRegisterGetHWRegNum.inc"
303 #undef SIRegisterInfo
304