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 51 } // end anonymous namespace 52 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. 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 77 static void emitOperand(const MCOperand &Op, EndianWriter &OSE) { 78 if (Op.isReg()) { 79 // Emit the id index starting at 1 (0 is an invalid index). 80 OSE.write<uint32_t>(Register::virtReg2Index(Op.getReg()) + 1); 81 } else if (Op.isImm()) { 82 OSE.write<uint32_t>(Op.getImm()); 83 } else { 84 llvm_unreachable("Unexpected operand type in VReg"); 85 } 86 } 87 88 // Emit the type in operand 1 before the ID in operand 0 it defines, and all 89 // remaining operands in the order they come naturally. 90 static void emitTypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { 91 unsigned NumOps = MI.getNumOperands(); 92 emitOperand(MI.getOperand(1), OSE); 93 emitOperand(MI.getOperand(0), OSE); 94 for (unsigned i = 2; i < NumOps; ++i) 95 emitOperand(MI.getOperand(i), OSE); 96 } 97 98 // Emit operands in the order they come naturally. 99 static void emitUntypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { 100 for (const auto &Op : MI) 101 emitOperand(Op, OSE); 102 } 103 104 void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, 105 SmallVectorImpl<MCFixup> &Fixups, 106 const MCSubtargetInfo &STI) const { 107 EndianWriter OSE(OS, support::little); 108 109 // Encode the first 32 SPIR-V bytes with the number of args and the opcode. 110 const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI); 111 const uint32_t NumWords = MI.getNumOperands() + 1; 112 const uint32_t FirstWord = (NumWords << 16) | OpCode; 113 OSE.write<uint32_t>(FirstWord); 114 115 // Emit the instruction arguments (emitting the output type first if present). 116 if (hasType(MI, MCII)) 117 emitTypedInstrOperands(MI, OSE); 118 else 119 emitUntypedInstrOperands(MI, OSE); 120 } 121 122 #include "SPIRVGenMCCodeEmitter.inc" 123