1 //===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the SPIRVMCCodeEmitter class.
11 //===----------------------------------------------------------------------===//
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"
27 #define DEBUG_TYPE "spirv-mccodeemitter"
31 class SPIRVMCCodeEmitter
: public MCCodeEmitter
{
32 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;
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;
46 void encodeInstruction(const MCInst
&MI
, SmallVectorImpl
<char> &CB
,
47 SmallVectorImpl
<MCFixup
> &Fixups
,
48 const MCSubtargetInfo
&STI
) const override
;
51 } // end anonymous namespace
53 MCCodeEmitter
*llvm::createSPIRVMCCodeEmitter(const MCInstrInfo
&MCII
,
55 return new SPIRVMCCodeEmitter(MCII
);
58 using EndianWriter
= support::endian::Writer
;
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.
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 return MCDesc
.operands()[0].RegClass
>= 0 &&
69 MCDesc
.operands()[1].RegClass
>= 0 &&
70 MCDesc
.operands()[0].RegClass
!= SPIRV::TYPERegClassID
&&
71 MCDesc
.operands()[1].RegClass
== SPIRV::TYPERegClassID
;
76 static void emitOperand(const MCOperand
&Op
, SmallVectorImpl
<char> &CB
) {
78 // Emit the id index starting at 1 (0 is an invalid index).
79 support::endian::write
<uint32_t>(
80 CB
, Register::virtReg2Index(Op
.getReg()) + 1, llvm::endianness::little
);
81 } else if (Op
.isImm()) {
82 support::endian::write(CB
, static_cast<uint32_t>(Op
.getImm()),
83 llvm::endianness::little
);
85 llvm_unreachable("Unexpected operand type in VReg");
89 // Emit the type in operand 1 before the ID in operand 0 it defines, and all
90 // remaining operands in the order they come naturally.
91 static void emitTypedInstrOperands(const MCInst
&MI
,
92 SmallVectorImpl
<char> &CB
) {
93 unsigned NumOps
= MI
.getNumOperands();
94 emitOperand(MI
.getOperand(1), CB
);
95 emitOperand(MI
.getOperand(0), CB
);
96 for (unsigned i
= 2; i
< NumOps
; ++i
)
97 emitOperand(MI
.getOperand(i
), CB
);
100 // Emit operands in the order they come naturally.
101 static void emitUntypedInstrOperands(const MCInst
&MI
,
102 SmallVectorImpl
<char> &CB
) {
103 for (const auto &Op
: MI
)
107 void SPIRVMCCodeEmitter::encodeInstruction(const MCInst
&MI
,
108 SmallVectorImpl
<char> &CB
,
109 SmallVectorImpl
<MCFixup
> &Fixups
,
110 const MCSubtargetInfo
&STI
) const {
111 // Encode the first 32 SPIR-V bytes with the number of args and the opcode.
112 const uint64_t OpCode
= getBinaryCodeForInstr(MI
, Fixups
, STI
);
113 const uint32_t NumWords
= MI
.getNumOperands() + 1;
114 const uint32_t FirstWord
= (NumWords
<< 16) | OpCode
;
115 support::endian::write(CB
, FirstWord
, llvm::endianness::little
);
117 // Emit the instruction arguments (emitting the output type first if present).
118 if (hasType(MI
, MCII
))
119 emitTypedInstrOperands(MI
, CB
);
121 emitUntypedInstrOperands(MI
, CB
);
124 #include "SPIRVGenMCCodeEmitter.inc"