| //===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the SPIRVMCCodeEmitter class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/SPIRVMCTargetDesc.h" |
| #include "llvm/CodeGen/Register.h" |
| #include "llvm/MC/MCCodeEmitter.h" |
| #include "llvm/MC/MCFixup.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Endian.h" |
| #include "llvm/Support/EndianStream.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "spirv-mccodeemitter" |
| |
| namespace { |
| |
| class SPIRVMCCodeEmitter : public MCCodeEmitter { |
| const MCInstrInfo &MCII; |
| |
| public: |
| SPIRVMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {} |
| SPIRVMCCodeEmitter(const SPIRVMCCodeEmitter &) = delete; |
| void operator=(const SPIRVMCCodeEmitter &) = delete; |
| ~SPIRVMCCodeEmitter() override = default; |
| |
| // getBinaryCodeForInstr - TableGen'erated function for getting the |
| // binary encoding for an instruction. |
| uint64_t getBinaryCodeForInstr(const MCInst &MI, |
| SmallVectorImpl<MCFixup> &Fixups, |
| const MCSubtargetInfo &STI) const; |
| |
| void encodeInstruction(const MCInst &MI, raw_ostream &OS, |
| SmallVectorImpl<MCFixup> &Fixups, |
| const MCSubtargetInfo &STI) const override; |
| }; |
| |
| } // end anonymous namespace |
| |
| MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII, |
| MCContext &Ctx) { |
| return new SPIRVMCCodeEmitter(MCII); |
| } |
| |
| using EndianWriter = support::endian::Writer; |
| |
| // Check if the instruction has a type argument for operand 1, and defines an ID |
| // output register in operand 0. If so, we need to swap operands 0 and 1 so the |
| // type comes first in the output, despide coming second in the MCInst. |
| static bool hasType(const MCInst &MI, const MCInstrInfo &MII) { |
| const MCInstrDesc &MCDesc = MII.get(MI.getOpcode()); |
| // If we define an output, and have at least one other argument. |
| if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) { |
| // Check if we define an ID, and take a type as operand 1. |
| auto &DefOpInfo = MCDesc.operands()[0]; |
| auto &FirstArgOpInfo = MCDesc.operands()[1]; |
| return (DefOpInfo.RegClass == SPIRV::IDRegClassID || |
| DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) && |
| FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID; |
| } |
| return false; |
| } |
| |
| static void emitOperand(const MCOperand &Op, EndianWriter &OSE) { |
| if (Op.isReg()) { |
| // Emit the id index starting at 1 (0 is an invalid index). |
| OSE.write<uint32_t>(Register::virtReg2Index(Op.getReg()) + 1); |
| } else if (Op.isImm()) { |
| OSE.write<uint32_t>(Op.getImm()); |
| } else { |
| llvm_unreachable("Unexpected operand type in VReg"); |
| } |
| } |
| |
| // Emit the type in operand 1 before the ID in operand 0 it defines, and all |
| // remaining operands in the order they come naturally. |
| static void emitTypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { |
| unsigned NumOps = MI.getNumOperands(); |
| emitOperand(MI.getOperand(1), OSE); |
| emitOperand(MI.getOperand(0), OSE); |
| for (unsigned i = 2; i < NumOps; ++i) |
| emitOperand(MI.getOperand(i), OSE); |
| } |
| |
| // Emit operands in the order they come naturally. |
| static void emitUntypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { |
| for (const auto &Op : MI) |
| emitOperand(Op, OSE); |
| } |
| |
| void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, |
| SmallVectorImpl<MCFixup> &Fixups, |
| const MCSubtargetInfo &STI) const { |
| EndianWriter OSE(OS, support::little); |
| |
| // Encode the first 32 SPIR-V bytes with the number of args and the opcode. |
| const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI); |
| const uint32_t NumWords = MI.getNumOperands() + 1; |
| const uint32_t FirstWord = (NumWords << 16) | OpCode; |
| OSE.write<uint32_t>(FirstWord); |
| |
| // Emit the instruction arguments (emitting the output type first if present). |
| if (hasType(MI, MCII)) |
| emitTypedInstrOperands(MI, OSE); |
| else |
| emitUntypedInstrOperands(MI, OSE); |
| } |
| |
| #include "SPIRVGenMCCodeEmitter.inc" |