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