R600/SI: Allow f64 inline immediates in i64 operands
[oota-llvm.git] / lib / Target / R600 / MCTargetDesc / SIMCCodeEmitter.cpp
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 /// \file
11 /// \brief The SI code emitter produces machine code that can be executed
12 /// directly on the GPU device.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include "AMDGPU.h"
17 #include "MCTargetDesc/AMDGPUFixupKinds.h"
18 #include "MCTargetDesc/AMDGPUMCCodeEmitter.h"
19 #include "MCTargetDesc/AMDGPUMCTargetDesc.h"
20 #include "SIDefines.h"
21 #include "llvm/MC/MCCodeEmitter.h"
22 #include "llvm/MC/MCContext.h"
23 #include "llvm/MC/MCFixup.h"
24 #include "llvm/MC/MCInst.h"
25 #include "llvm/MC/MCInstrInfo.h"
26 #include "llvm/MC/MCRegisterInfo.h"
27 #include "llvm/MC/MCSubtargetInfo.h"
28 #include "llvm/Support/raw_ostream.h"
29
30 using namespace llvm;
31
32 namespace {
33
34 /// \brief Helper type used in encoding
35 typedef union {
36   int64_t I;
37   double F;
38 } IntFloatUnion;
39
40 class SIMCCodeEmitter : public  AMDGPUMCCodeEmitter {
41   SIMCCodeEmitter(const SIMCCodeEmitter &) LLVM_DELETED_FUNCTION;
42   void operator=(const SIMCCodeEmitter &) LLVM_DELETED_FUNCTION;
43   const MCInstrInfo &MCII;
44   const MCRegisterInfo &MRI;
45   MCContext &Ctx;
46
47   /// \brief Can this operand also contain immediate values?
48   bool isSrcOperand(const MCInstrDesc &Desc, unsigned OpNo) const;
49
50   /// \brief Encode an fp or int literal
51   uint32_t getLitEncoding(const MCOperand &MO, unsigned OpSize) const;
52
53 public:
54   SIMCCodeEmitter(const MCInstrInfo &mcii, const MCRegisterInfo &mri,
55                   MCContext &ctx)
56     : MCII(mcii), MRI(mri), Ctx(ctx) { }
57
58   ~SIMCCodeEmitter() { }
59
60   /// \brief Encode the instruction and write it to the OS.
61   void EncodeInstruction(const MCInst &MI, raw_ostream &OS,
62                          SmallVectorImpl<MCFixup> &Fixups,
63                          const MCSubtargetInfo &STI) const override;
64
65   /// \returns the encoding for an MCOperand.
66   uint64_t getMachineOpValue(const MCInst &MI, const MCOperand &MO,
67                              SmallVectorImpl<MCFixup> &Fixups,
68                              const MCSubtargetInfo &STI) const override;
69
70   /// \brief Use a fixup to encode the simm16 field for SOPP branch
71   ///        instructions.
72   unsigned getSOPPBrEncoding(const MCInst &MI, unsigned OpNo,
73                              SmallVectorImpl<MCFixup> &Fixups,
74                              const MCSubtargetInfo &STI) const override;
75 };
76
77 } // End anonymous namespace
78
79 MCCodeEmitter *llvm::createSIMCCodeEmitter(const MCInstrInfo &MCII,
80                                            const MCRegisterInfo &MRI,
81                                            const MCSubtargetInfo &STI,
82                                            MCContext &Ctx) {
83   return new SIMCCodeEmitter(MCII, MRI, Ctx);
84 }
85
86 bool SIMCCodeEmitter::isSrcOperand(const MCInstrDesc &Desc,
87                                    unsigned OpNo) const {
88   unsigned OpType = Desc.OpInfo[OpNo].OperandType;
89
90   return OpType == AMDGPU::OPERAND_REG_IMM32 ||
91          OpType == AMDGPU::OPERAND_REG_INLINE_C;
92 }
93
94 // Returns the encoding value to use if the given integer is an integer inline
95 // immediate value, or 0 if it is not.
96 template <typename IntTy>
97 static uint32_t getIntInlineImmEncoding(IntTy Imm) {
98   if (Imm >= 0 && Imm <= 64)
99     return 128 + Imm;
100
101   if (Imm >= -16 && Imm <= -1)
102     return 192 + std::abs(Imm);
103
104   return 0;
105 }
106
107 static uint32_t getLit32Encoding(uint32_t Val) {
108   uint32_t IntImm = getIntInlineImmEncoding(static_cast<int32_t>(Val));
109   if (IntImm != 0)
110     return IntImm;
111
112   if (Val == FloatToBits(0.5f))
113     return 240;
114
115   if (Val == FloatToBits(-0.5f))
116     return 241;
117
118   if (Val == FloatToBits(1.0f))
119     return 242;
120
121   if (Val == FloatToBits(-1.0f))
122     return 243;
123
124   if (Val == FloatToBits(2.0f))
125     return 244;
126
127   if (Val == FloatToBits(-2.0f))
128     return 245;
129
130   if (Val == FloatToBits(4.0f))
131     return 246;
132
133   if (Val == FloatToBits(-4.0f))
134     return 247;
135
136   return 255;
137 }
138
139 static uint32_t getLit64Encoding(uint64_t Val) {
140   uint32_t IntImm = getIntInlineImmEncoding(static_cast<int64_t>(Val));
141   if (IntImm != 0)
142     return IntImm;
143
144   if (Val == DoubleToBits(0.5))
145     return 240;
146
147   if (Val == DoubleToBits(-0.5))
148     return 241;
149
150   if (Val == DoubleToBits(1.0))
151     return 242;
152
153   if (Val == DoubleToBits(-1.0))
154     return 243;
155
156   if (Val == DoubleToBits(2.0))
157     return 244;
158
159   if (Val == DoubleToBits(-2.0))
160     return 245;
161
162   if (Val == DoubleToBits(4.0))
163     return 246;
164
165   if (Val == DoubleToBits(-4.0))
166     return 247;
167
168   return 255;
169 }
170
171 uint32_t SIMCCodeEmitter::getLitEncoding(const MCOperand &MO,
172                                          unsigned OpSize) const {
173   if (MO.isExpr())
174     return 255;
175
176   assert(!MO.isFPImm());
177
178   if (!MO.isImm())
179     return ~0;
180
181   if (OpSize == 4)
182     return getLit32Encoding(static_cast<uint32_t>(MO.getImm()));
183
184   assert(OpSize == 8);
185
186   return getLit64Encoding(static_cast<uint64_t>(MO.getImm()));
187 }
188
189 void SIMCCodeEmitter::EncodeInstruction(const MCInst &MI, raw_ostream &OS,
190                                        SmallVectorImpl<MCFixup> &Fixups,
191                                        const MCSubtargetInfo &STI) const {
192
193   uint64_t Encoding = getBinaryCodeForInstr(MI, Fixups, STI);
194   const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
195   unsigned bytes = Desc.getSize();
196
197   for (unsigned i = 0; i < bytes; i++) {
198     OS.write((uint8_t) ((Encoding >> (8 * i)) & 0xff));
199   }
200
201   if (bytes > 4)
202     return;
203
204   // Check for additional literals in SRC0/1/2 (Op 1/2/3)
205   for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
206
207     // Check if this operand should be encoded as [SV]Src
208     if (!isSrcOperand(Desc, i))
209       continue;
210
211     int RCID = Desc.OpInfo[i].RegClass;
212     const MCRegisterClass &RC = MRI.getRegClass(RCID);
213
214     // Is this operand a literal immediate?
215     const MCOperand &Op = MI.getOperand(i);
216     if (getLitEncoding(Op, RC.getSize()) != 255)
217       continue;
218
219     // Yes! Encode it
220     IntFloatUnion Imm;
221     if (Op.isImm())
222       Imm.I = Op.getImm();
223     else if (Op.isFPImm())
224       Imm.F = Op.getFPImm();
225     else {
226       assert(Op.isExpr());
227       // This will be replaced with a fixup value.
228       Imm.I = 0;
229     }
230
231     for (unsigned j = 0; j < 4; j++) {
232       OS.write((uint8_t) ((Imm.I >> (8 * j)) & 0xff));
233     }
234
235     // Only one literal value allowed
236     break;
237   }
238 }
239
240 unsigned SIMCCodeEmitter::getSOPPBrEncoding(const MCInst &MI, unsigned OpNo,
241                                             SmallVectorImpl<MCFixup> &Fixups,
242                                             const MCSubtargetInfo &STI) const {
243   const MCOperand &MO = MI.getOperand(OpNo);
244
245   if (MO.isExpr()) {
246     const MCExpr *Expr = MO.getExpr();
247     MCFixupKind Kind = (MCFixupKind)AMDGPU::fixup_si_sopp_br;
248     Fixups.push_back(MCFixup::Create(0, Expr, Kind, MI.getLoc()));
249     return 0;
250   }
251
252   return getMachineOpValue(MI, MO, Fixups, STI);
253 }
254
255 uint64_t SIMCCodeEmitter::getMachineOpValue(const MCInst &MI,
256                                             const MCOperand &MO,
257                                        SmallVectorImpl<MCFixup> &Fixups,
258                                        const MCSubtargetInfo &STI) const {
259   if (MO.isReg())
260     return MRI.getEncodingValue(MO.getReg());
261
262   if (MO.isExpr()) {
263     const MCSymbolRefExpr *Expr = cast<MCSymbolRefExpr>(MO.getExpr());
264     MCFixupKind Kind;
265     const MCSymbol *Sym =
266         Ctx.GetOrCreateSymbol(StringRef(END_OF_TEXT_LABEL_NAME));
267
268     if (&Expr->getSymbol() == Sym) {
269       // Add the offset to the beginning of the constant values.
270       Kind = (MCFixupKind)AMDGPU::fixup_si_end_of_text;
271     } else {
272       // This is used for constant data stored in .rodata.
273      Kind = (MCFixupKind)AMDGPU::fixup_si_rodata;
274     }
275     Fixups.push_back(MCFixup::Create(4, Expr, Kind, MI.getLoc()));
276   }
277
278   // Figure out the operand number, needed for isSrcOperand check
279   unsigned OpNo = 0;
280   for (unsigned e = MI.getNumOperands(); OpNo < e; ++OpNo) {
281     if (&MO == &MI.getOperand(OpNo))
282       break;
283   }
284
285   const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
286   if (isSrcOperand(Desc, OpNo)) {
287     int RCID = Desc.OpInfo[OpNo].RegClass;
288     const MCRegisterClass &RC = MRI.getRegClass(RCID);
289
290     uint32_t Enc = getLitEncoding(MO, RC.getSize());
291     if (Enc != ~0U && (Enc != 255 || Desc.getSize() == 4))
292       return Enc;
293
294   } else if (MO.isImm())
295     return MO.getImm();
296
297   llvm_unreachable("Encoding of this operand type is not supported yet.");
298   return 0;
299 }
300