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, SmallVectorImpl<char> &CB, 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, 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. 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. 102 static void emitUntypedInstrOperands(const MCInst &MI, 103 SmallVectorImpl<char> &CB) { 104 for (const auto &Op : MI) 105 emitOperand(Op, CB); 106 } 107 108 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