1*d415bd75Srobert //===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===//
2*d415bd75Srobert //
3*d415bd75Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d415bd75Srobert // See https://llvm.org/LICENSE.txt for license information.
5*d415bd75Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d415bd75Srobert //
7*d415bd75Srobert //===----------------------------------------------------------------------===//
8*d415bd75Srobert //
9*d415bd75Srobert // This file implements the SPIRVMCCodeEmitter class.
10*d415bd75Srobert //
11*d415bd75Srobert //===----------------------------------------------------------------------===//
12*d415bd75Srobert 
13*d415bd75Srobert #include "MCTargetDesc/SPIRVMCTargetDesc.h"
14*d415bd75Srobert #include "llvm/CodeGen/Register.h"
15*d415bd75Srobert #include "llvm/MC/MCCodeEmitter.h"
16*d415bd75Srobert #include "llvm/MC/MCFixup.h"
17*d415bd75Srobert #include "llvm/MC/MCInst.h"
18*d415bd75Srobert #include "llvm/MC/MCInstrInfo.h"
19*d415bd75Srobert #include "llvm/MC/MCRegisterInfo.h"
20*d415bd75Srobert #include "llvm/MC/MCSubtargetInfo.h"
21*d415bd75Srobert #include "llvm/Support/Debug.h"
22*d415bd75Srobert #include "llvm/Support/Endian.h"
23*d415bd75Srobert #include "llvm/Support/EndianStream.h"
24*d415bd75Srobert 
25*d415bd75Srobert using namespace llvm;
26*d415bd75Srobert 
27*d415bd75Srobert #define DEBUG_TYPE "spirv-mccodeemitter"
28*d415bd75Srobert 
29*d415bd75Srobert namespace {
30*d415bd75Srobert 
31*d415bd75Srobert class SPIRVMCCodeEmitter : public MCCodeEmitter {
32*d415bd75Srobert   const MCInstrInfo &MCII;
33*d415bd75Srobert 
34*d415bd75Srobert public:
SPIRVMCCodeEmitter(const MCInstrInfo & mcii)35*d415bd75Srobert   SPIRVMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
36*d415bd75Srobert   SPIRVMCCodeEmitter(const SPIRVMCCodeEmitter &) = delete;
37*d415bd75Srobert   void operator=(const SPIRVMCCodeEmitter &) = delete;
38*d415bd75Srobert   ~SPIRVMCCodeEmitter() override = default;
39*d415bd75Srobert 
40*d415bd75Srobert   // getBinaryCodeForInstr - TableGen'erated function for getting the
41*d415bd75Srobert   // binary encoding for an instruction.
42*d415bd75Srobert   uint64_t getBinaryCodeForInstr(const MCInst &MI,
43*d415bd75Srobert                                  SmallVectorImpl<MCFixup> &Fixups,
44*d415bd75Srobert                                  const MCSubtargetInfo &STI) const;
45*d415bd75Srobert 
46*d415bd75Srobert   void encodeInstruction(const MCInst &MI, raw_ostream &OS,
47*d415bd75Srobert                          SmallVectorImpl<MCFixup> &Fixups,
48*d415bd75Srobert                          const MCSubtargetInfo &STI) const override;
49*d415bd75Srobert };
50*d415bd75Srobert 
51*d415bd75Srobert } // end anonymous namespace
52*d415bd75Srobert 
createSPIRVMCCodeEmitter(const MCInstrInfo & MCII,MCContext & Ctx)53*d415bd75Srobert MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII,
54*d415bd75Srobert                                               MCContext &Ctx) {
55*d415bd75Srobert   return new SPIRVMCCodeEmitter(MCII);
56*d415bd75Srobert }
57*d415bd75Srobert 
58*d415bd75Srobert using EndianWriter = support::endian::Writer;
59*d415bd75Srobert 
60*d415bd75Srobert // Check if the instruction has a type argument for operand 1, and defines an ID
61*d415bd75Srobert // output register in operand 0. If so, we need to swap operands 0 and 1 so the
62*d415bd75Srobert // type comes first in the output, despide coming second in the MCInst.
hasType(const MCInst & MI,const MCInstrInfo & MII)63*d415bd75Srobert static bool hasType(const MCInst &MI, const MCInstrInfo &MII) {
64*d415bd75Srobert   const MCInstrDesc &MCDesc = MII.get(MI.getOpcode());
65*d415bd75Srobert   // If we define an output, and have at least one other argument.
66*d415bd75Srobert   if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) {
67*d415bd75Srobert     // Check if we define an ID, and take a type as operand 1.
68*d415bd75Srobert     auto &DefOpInfo = MCDesc.operands()[0];
69*d415bd75Srobert     auto &FirstArgOpInfo = MCDesc.operands()[1];
70*d415bd75Srobert     return (DefOpInfo.RegClass == SPIRV::IDRegClassID ||
71*d415bd75Srobert             DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) &&
72*d415bd75Srobert            FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID;
73*d415bd75Srobert   }
74*d415bd75Srobert   return false;
75*d415bd75Srobert }
76*d415bd75Srobert 
emitOperand(const MCOperand & Op,EndianWriter & OSE)77*d415bd75Srobert static void emitOperand(const MCOperand &Op, EndianWriter &OSE) {
78*d415bd75Srobert   if (Op.isReg()) {
79*d415bd75Srobert     // Emit the id index starting at 1 (0 is an invalid index).
80*d415bd75Srobert     OSE.write<uint32_t>(Register::virtReg2Index(Op.getReg()) + 1);
81*d415bd75Srobert   } else if (Op.isImm()) {
82*d415bd75Srobert     OSE.write<uint32_t>(Op.getImm());
83*d415bd75Srobert   } else {
84*d415bd75Srobert     llvm_unreachable("Unexpected operand type in VReg");
85*d415bd75Srobert   }
86*d415bd75Srobert }
87*d415bd75Srobert 
88*d415bd75Srobert // Emit the type in operand 1 before the ID in operand 0 it defines, and all
89*d415bd75Srobert // remaining operands in the order they come naturally.
emitTypedInstrOperands(const MCInst & MI,EndianWriter & OSE)90*d415bd75Srobert static void emitTypedInstrOperands(const MCInst &MI, EndianWriter &OSE) {
91*d415bd75Srobert   unsigned NumOps = MI.getNumOperands();
92*d415bd75Srobert   emitOperand(MI.getOperand(1), OSE);
93*d415bd75Srobert   emitOperand(MI.getOperand(0), OSE);
94*d415bd75Srobert   for (unsigned i = 2; i < NumOps; ++i)
95*d415bd75Srobert     emitOperand(MI.getOperand(i), OSE);
96*d415bd75Srobert }
97*d415bd75Srobert 
98*d415bd75Srobert // Emit operands in the order they come naturally.
emitUntypedInstrOperands(const MCInst & MI,EndianWriter & OSE)99*d415bd75Srobert static void emitUntypedInstrOperands(const MCInst &MI, EndianWriter &OSE) {
100*d415bd75Srobert   for (const auto &Op : MI)
101*d415bd75Srobert     emitOperand(Op, OSE);
102*d415bd75Srobert }
103*d415bd75Srobert 
encodeInstruction(const MCInst & MI,raw_ostream & OS,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const104*d415bd75Srobert void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
105*d415bd75Srobert                                            SmallVectorImpl<MCFixup> &Fixups,
106*d415bd75Srobert                                            const MCSubtargetInfo &STI) const {
107*d415bd75Srobert   EndianWriter OSE(OS, support::little);
108*d415bd75Srobert 
109*d415bd75Srobert   // Encode the first 32 SPIR-V bytes with the number of args and the opcode.
110*d415bd75Srobert   const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI);
111*d415bd75Srobert   const uint32_t NumWords = MI.getNumOperands() + 1;
112*d415bd75Srobert   const uint32_t FirstWord = (NumWords << 16) | OpCode;
113*d415bd75Srobert   OSE.write<uint32_t>(FirstWord);
114*d415bd75Srobert 
115*d415bd75Srobert   // Emit the instruction arguments (emitting the output type first if present).
116*d415bd75Srobert   if (hasType(MI, MCII))
117*d415bd75Srobert     emitTypedInstrOperands(MI, OSE);
118*d415bd75Srobert   else
119*d415bd75Srobert     emitUntypedInstrOperands(MI, OSE);
120*d415bd75Srobert }
121*d415bd75Srobert 
122*d415bd75Srobert #include "SPIRVGenMCCodeEmitter.inc"
123