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:
SPIRVMCCodeEmitter(const MCInstrInfo & mcii)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, SmallVectorImpl<char> &CB,
47 SmallVectorImpl<MCFixup> &Fixups,
48 const MCSubtargetInfo &STI) const override;
49 };
50
51 } // end anonymous namespace
52
createSPIRVMCCodeEmitter(const MCInstrInfo & MCII,MCContext & Ctx)53 MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII,
54 MCContext &Ctx) {
55 return new SPIRVMCCodeEmitter(MCII);
56 }
57
58 using EndianWriter = support::endian::Writer;
59
60 // Check if the instruction has a type argument for operand 1, and defines an ID
61 // output register in operand 0. If so, we need to swap operands 0 and 1 so the
62 // type comes first in the output, despide coming second in the MCInst.
hasType(const MCInst & MI,const MCInstrInfo & MII)63 static bool hasType(const MCInst &MI, const MCInstrInfo &MII) {
64 const MCInstrDesc &MCDesc = MII.get(MI.getOpcode());
65 // If we define an output, and have at least one other argument.
66 if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) {
67 // Check if we define an ID, and take a type as operand 1.
68 auto &DefOpInfo = MCDesc.operands()[0];
69 auto &FirstArgOpInfo = MCDesc.operands()[1];
70 return (DefOpInfo.RegClass == SPIRV::IDRegClassID ||
71 DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) &&
72 FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID;
73 }
74 return false;
75 }
76
emitOperand(const MCOperand & Op,SmallVectorImpl<char> & CB)77 static void emitOperand(const MCOperand &Op, SmallVectorImpl<char> &CB) {
78 if (Op.isReg()) {
79 // Emit the id index starting at 1 (0 is an invalid index).
80 support::endian::write<uint32_t>(
81 CB, Register::virtReg2Index(Op.getReg()) + 1, llvm::endianness::little);
82 } else if (Op.isImm()) {
83 support::endian::write(CB, static_cast<uint32_t>(Op.getImm()),
84 llvm::endianness::little);
85 } else {
86 llvm_unreachable("Unexpected operand type in VReg");
87 }
88 }
89
90 // Emit the type in operand 1 before the ID in operand 0 it defines, and all
91 // remaining operands in the order they come naturally.
emitTypedInstrOperands(const MCInst & MI,SmallVectorImpl<char> & CB)92 static void emitTypedInstrOperands(const MCInst &MI,
93 SmallVectorImpl<char> &CB) {
94 unsigned NumOps = MI.getNumOperands();
95 emitOperand(MI.getOperand(1), CB);
96 emitOperand(MI.getOperand(0), CB);
97 for (unsigned i = 2; i < NumOps; ++i)
98 emitOperand(MI.getOperand(i), CB);
99 }
100
101 // Emit operands in the order they come naturally.
emitUntypedInstrOperands(const MCInst & MI,SmallVectorImpl<char> & CB)102 static void emitUntypedInstrOperands(const MCInst &MI,
103 SmallVectorImpl<char> &CB) {
104 for (const auto &Op : MI)
105 emitOperand(Op, CB);
106 }
107
encodeInstruction(const MCInst & MI,SmallVectorImpl<char> & CB,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const108 void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI,
109 SmallVectorImpl<char> &CB,
110 SmallVectorImpl<MCFixup> &Fixups,
111 const MCSubtargetInfo &STI) const {
112 // Encode the first 32 SPIR-V bytes with the number of args and the opcode.
113 const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI);
114 const uint32_t NumWords = MI.getNumOperands() + 1;
115 const uint32_t FirstWord = (NumWords << 16) | OpCode;
116 support::endian::write(CB, FirstWord, llvm::endianness::little);
117
118 // Emit the instruction arguments (emitting the output type first if present).
119 if (hasType(MI, MCII))
120 emitTypedInstrOperands(MI, CB);
121 else
122 emitUntypedInstrOperands(MI, CB);
123 }
124
125 #include "SPIRVGenMCCodeEmitter.inc"
126