xref: /freebsd/contrib/llvm-project/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVMCCodeEmitter.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
181ad6265SDimitry Andric //===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This file implements the SPIRVMCCodeEmitter class.
1081ad6265SDimitry Andric //
1181ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1281ad6265SDimitry Andric 
1381ad6265SDimitry Andric #include "MCTargetDesc/SPIRVMCTargetDesc.h"
1481ad6265SDimitry Andric #include "llvm/CodeGen/Register.h"
1581ad6265SDimitry Andric #include "llvm/MC/MCCodeEmitter.h"
1681ad6265SDimitry Andric #include "llvm/MC/MCFixup.h"
1781ad6265SDimitry Andric #include "llvm/MC/MCInst.h"
1881ad6265SDimitry Andric #include "llvm/MC/MCInstrInfo.h"
1981ad6265SDimitry Andric #include "llvm/MC/MCRegisterInfo.h"
2081ad6265SDimitry Andric #include "llvm/MC/MCSubtargetInfo.h"
2181ad6265SDimitry Andric #include "llvm/Support/Debug.h"
2281ad6265SDimitry Andric #include "llvm/Support/Endian.h"
2381ad6265SDimitry Andric #include "llvm/Support/EndianStream.h"
2481ad6265SDimitry Andric 
2581ad6265SDimitry Andric using namespace llvm;
2681ad6265SDimitry Andric 
2781ad6265SDimitry Andric #define DEBUG_TYPE "spirv-mccodeemitter"
2881ad6265SDimitry Andric 
2981ad6265SDimitry Andric namespace {
3081ad6265SDimitry Andric 
3181ad6265SDimitry Andric class SPIRVMCCodeEmitter : public MCCodeEmitter {
3281ad6265SDimitry Andric   const MCInstrInfo &MCII;
3381ad6265SDimitry Andric 
3481ad6265SDimitry Andric public:
3581ad6265SDimitry Andric   SPIRVMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
3681ad6265SDimitry Andric   SPIRVMCCodeEmitter(const SPIRVMCCodeEmitter &) = delete;
3781ad6265SDimitry Andric   void operator=(const SPIRVMCCodeEmitter &) = delete;
3881ad6265SDimitry Andric   ~SPIRVMCCodeEmitter() override = default;
3981ad6265SDimitry Andric 
4081ad6265SDimitry Andric   // getBinaryCodeForInstr - TableGen'erated function for getting the
4181ad6265SDimitry Andric   // binary encoding for an instruction.
4281ad6265SDimitry Andric   uint64_t getBinaryCodeForInstr(const MCInst &MI,
4381ad6265SDimitry Andric                                  SmallVectorImpl<MCFixup> &Fixups,
4481ad6265SDimitry Andric                                  const MCSubtargetInfo &STI) const;
4581ad6265SDimitry Andric 
46*5f757f3fSDimitry Andric   void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
4781ad6265SDimitry Andric                          SmallVectorImpl<MCFixup> &Fixups,
4881ad6265SDimitry Andric                          const MCSubtargetInfo &STI) const override;
4981ad6265SDimitry Andric };
5081ad6265SDimitry Andric 
5181ad6265SDimitry Andric } // end anonymous namespace
5281ad6265SDimitry Andric 
5381ad6265SDimitry Andric MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII,
5481ad6265SDimitry Andric                                               MCContext &Ctx) {
5581ad6265SDimitry Andric   return new SPIRVMCCodeEmitter(MCII);
5681ad6265SDimitry Andric }
5781ad6265SDimitry Andric 
5881ad6265SDimitry Andric using EndianWriter = support::endian::Writer;
5981ad6265SDimitry Andric 
6081ad6265SDimitry Andric // Check if the instruction has a type argument for operand 1, and defines an ID
6181ad6265SDimitry Andric // output register in operand 0. If so, we need to swap operands 0 and 1 so the
6281ad6265SDimitry Andric // type comes first in the output, despide coming second in the MCInst.
6381ad6265SDimitry Andric static bool hasType(const MCInst &MI, const MCInstrInfo &MII) {
64bdd1243dSDimitry Andric   const MCInstrDesc &MCDesc = MII.get(MI.getOpcode());
6581ad6265SDimitry Andric   // If we define an output, and have at least one other argument.
6681ad6265SDimitry Andric   if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) {
6781ad6265SDimitry Andric     // Check if we define an ID, and take a type as operand 1.
68bdd1243dSDimitry Andric     auto &DefOpInfo = MCDesc.operands()[0];
69bdd1243dSDimitry Andric     auto &FirstArgOpInfo = MCDesc.operands()[1];
70bdd1243dSDimitry Andric     return (DefOpInfo.RegClass == SPIRV::IDRegClassID ||
71bdd1243dSDimitry Andric             DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) &&
72bdd1243dSDimitry Andric            FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID;
7381ad6265SDimitry Andric   }
7481ad6265SDimitry Andric   return false;
7581ad6265SDimitry Andric }
7681ad6265SDimitry Andric 
77*5f757f3fSDimitry Andric static void emitOperand(const MCOperand &Op, SmallVectorImpl<char> &CB) {
7881ad6265SDimitry Andric   if (Op.isReg()) {
7981ad6265SDimitry Andric     // Emit the id index starting at 1 (0 is an invalid index).
80*5f757f3fSDimitry Andric     support::endian::write<uint32_t>(
81*5f757f3fSDimitry Andric         CB, Register::virtReg2Index(Op.getReg()) + 1, llvm::endianness::little);
8281ad6265SDimitry Andric   } else if (Op.isImm()) {
83*5f757f3fSDimitry Andric     support::endian::write(CB, static_cast<uint32_t>(Op.getImm()),
84*5f757f3fSDimitry Andric                            llvm::endianness::little);
8581ad6265SDimitry Andric   } else {
8681ad6265SDimitry Andric     llvm_unreachable("Unexpected operand type in VReg");
8781ad6265SDimitry Andric   }
8881ad6265SDimitry Andric }
8981ad6265SDimitry Andric 
9081ad6265SDimitry Andric // Emit the type in operand 1 before the ID in operand 0 it defines, and all
9181ad6265SDimitry Andric // remaining operands in the order they come naturally.
92*5f757f3fSDimitry Andric static void emitTypedInstrOperands(const MCInst &MI,
93*5f757f3fSDimitry Andric                                    SmallVectorImpl<char> &CB) {
9481ad6265SDimitry Andric   unsigned NumOps = MI.getNumOperands();
95*5f757f3fSDimitry Andric   emitOperand(MI.getOperand(1), CB);
96*5f757f3fSDimitry Andric   emitOperand(MI.getOperand(0), CB);
9781ad6265SDimitry Andric   for (unsigned i = 2; i < NumOps; ++i)
98*5f757f3fSDimitry Andric     emitOperand(MI.getOperand(i), CB);
9981ad6265SDimitry Andric }
10081ad6265SDimitry Andric 
10181ad6265SDimitry Andric // Emit operands in the order they come naturally.
102*5f757f3fSDimitry Andric static void emitUntypedInstrOperands(const MCInst &MI,
103*5f757f3fSDimitry Andric                                      SmallVectorImpl<char> &CB) {
10481ad6265SDimitry Andric   for (const auto &Op : MI)
105*5f757f3fSDimitry Andric     emitOperand(Op, CB);
10681ad6265SDimitry Andric }
10781ad6265SDimitry Andric 
108*5f757f3fSDimitry Andric void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI,
109*5f757f3fSDimitry Andric                                            SmallVectorImpl<char> &CB,
11081ad6265SDimitry Andric                                            SmallVectorImpl<MCFixup> &Fixups,
11181ad6265SDimitry Andric                                            const MCSubtargetInfo &STI) const {
11281ad6265SDimitry Andric   // Encode the first 32 SPIR-V bytes with the number of args and the opcode.
11381ad6265SDimitry Andric   const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI);
11481ad6265SDimitry Andric   const uint32_t NumWords = MI.getNumOperands() + 1;
11581ad6265SDimitry Andric   const uint32_t FirstWord = (NumWords << 16) | OpCode;
116*5f757f3fSDimitry Andric   support::endian::write(CB, FirstWord, llvm::endianness::little);
11781ad6265SDimitry Andric 
11881ad6265SDimitry Andric   // Emit the instruction arguments (emitting the output type first if present).
11981ad6265SDimitry Andric   if (hasType(MI, MCII))
120*5f757f3fSDimitry Andric     emitTypedInstrOperands(MI, CB);
12181ad6265SDimitry Andric   else
122*5f757f3fSDimitry Andric     emitUntypedInstrOperands(MI, CB);
12381ad6265SDimitry Andric }
12481ad6265SDimitry Andric 
12581ad6265SDimitry Andric #include "SPIRVGenMCCodeEmitter.inc"
126