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 private: 51 FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) const; 52 void 53 verifyInstructionPredicates(const MCInst &MI, 54 const FeatureBitset &AvailableFeatures) const; 55 }; 56 57 } // end anonymous namespace 58 59 MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII, 60 MCContext &Ctx) { 61 return new SPIRVMCCodeEmitter(MCII); 62 } 63 64 using EndianWriter = support::endian::Writer; 65 66 // Check if the instruction has a type argument for operand 1, and defines an ID 67 // output register in operand 0. If so, we need to swap operands 0 and 1 so the 68 // type comes first in the output, despide coming second in the MCInst. 69 static bool hasType(const MCInst &MI, const MCInstrInfo &MII) { 70 MCInstrDesc MCDesc = MII.get(MI.getOpcode()); 71 // If we define an output, and have at least one other argument. 72 if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) { 73 // Check if we define an ID, and take a type as operand 1. 74 auto DefOpInfo = MCDesc.opInfo_begin(); 75 auto FirstArgOpInfo = MCDesc.opInfo_begin() + 1; 76 return (DefOpInfo->RegClass == SPIRV::IDRegClassID || 77 DefOpInfo->RegClass == SPIRV::ANYIDRegClassID) && 78 FirstArgOpInfo->RegClass == SPIRV::TYPERegClassID; 79 } 80 return false; 81 } 82 83 static void emitOperand(const MCOperand &Op, EndianWriter &OSE) { 84 if (Op.isReg()) { 85 // Emit the id index starting at 1 (0 is an invalid index). 86 OSE.write<uint32_t>(Register::virtReg2Index(Op.getReg()) + 1); 87 } else if (Op.isImm()) { 88 OSE.write<uint32_t>(Op.getImm()); 89 } else { 90 llvm_unreachable("Unexpected operand type in VReg"); 91 } 92 } 93 94 // Emit the type in operand 1 before the ID in operand 0 it defines, and all 95 // remaining operands in the order they come naturally. 96 static void emitTypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { 97 unsigned NumOps = MI.getNumOperands(); 98 emitOperand(MI.getOperand(1), OSE); 99 emitOperand(MI.getOperand(0), OSE); 100 for (unsigned i = 2; i < NumOps; ++i) 101 emitOperand(MI.getOperand(i), OSE); 102 } 103 104 // Emit operands in the order they come naturally. 105 static void emitUntypedInstrOperands(const MCInst &MI, EndianWriter &OSE) { 106 for (const auto &Op : MI) 107 emitOperand(Op, OSE); 108 } 109 110 void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, 111 SmallVectorImpl<MCFixup> &Fixups, 112 const MCSubtargetInfo &STI) const { 113 auto Features = computeAvailableFeatures(STI.getFeatureBits()); 114 verifyInstructionPredicates(MI, Features); 115 116 EndianWriter OSE(OS, support::little); 117 118 // Encode the first 32 SPIR-V bytes with the number of args and the opcode. 119 const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI); 120 const uint32_t NumWords = MI.getNumOperands() + 1; 121 const uint32_t FirstWord = (NumWords << 16) | OpCode; 122 OSE.write<uint32_t>(FirstWord); 123 124 // Emit the instruction arguments (emitting the output type first if present). 125 if (hasType(MI, MCII)) 126 emitTypedInstrOperands(MI, OSE); 127 else 128 emitUntypedInstrOperands(MI, OSE); 129 } 130 131 #define ENABLE_INSTR_PREDICATE_VERIFIER 132 #include "SPIRVGenMCCodeEmitter.inc" 133