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 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; 72 } 73 return false; 74 } 75 76 static void emitOperand(const MCOperand &Op, SmallVectorImpl<char> &CB) { 77 if (Op.isReg()) { 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); 84 } else { 85 llvm_unreachable("Unexpected operand type in VReg"); 86 } 87 } 88 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); 98 } 99 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) 104 emitOperand(Op, CB); 105 } 106 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); 116 117 // Emit the instruction arguments (emitting the output type first if present). 118 if (hasType(MI, MCII)) 119 emitTypedInstrOperands(MI, CB); 120 else 121 emitUntypedInstrOperands(MI, CB); 122 } 123 124 #include "SPIRVGenMCCodeEmitter.inc" 125