181ad6265SDimitry Andric //===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This file implements the SPIRVMCCodeEmitter class. 1081ad6265SDimitry Andric // 1181ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1281ad6265SDimitry Andric 1381ad6265SDimitry Andric #include "MCTargetDesc/SPIRVMCTargetDesc.h" 1481ad6265SDimitry Andric #include "llvm/CodeGen/Register.h" 1581ad6265SDimitry Andric #include "llvm/MC/MCCodeEmitter.h" 1681ad6265SDimitry Andric #include "llvm/MC/MCFixup.h" 1781ad6265SDimitry Andric #include "llvm/MC/MCInst.h" 1881ad6265SDimitry Andric #include "llvm/MC/MCInstrInfo.h" 1981ad6265SDimitry Andric #include "llvm/MC/MCRegisterInfo.h" 2081ad6265SDimitry Andric #include "llvm/MC/MCSubtargetInfo.h" 2181ad6265SDimitry Andric #include "llvm/Support/Debug.h" 2281ad6265SDimitry Andric #include "llvm/Support/Endian.h" 2381ad6265SDimitry Andric #include "llvm/Support/EndianStream.h" 2481ad6265SDimitry Andric 2581ad6265SDimitry Andric using namespace llvm; 2681ad6265SDimitry Andric 2781ad6265SDimitry Andric #define DEBUG_TYPE "spirv-mccodeemitter" 2881ad6265SDimitry Andric 2981ad6265SDimitry Andric namespace { 3081ad6265SDimitry Andric 3181ad6265SDimitry Andric class SPIRVMCCodeEmitter : public MCCodeEmitter { 3281ad6265SDimitry Andric const MCInstrInfo &MCII; 3381ad6265SDimitry Andric 3481ad6265SDimitry Andric public: 3581ad6265SDimitry Andric SPIRVMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {} 3681ad6265SDimitry Andric SPIRVMCCodeEmitter(const SPIRVMCCodeEmitter &) = delete; 3781ad6265SDimitry Andric void operator=(const SPIRVMCCodeEmitter &) = delete; 3881ad6265SDimitry Andric ~SPIRVMCCodeEmitter() override = default; 3981ad6265SDimitry Andric 4081ad6265SDimitry Andric // getBinaryCodeForInstr - TableGen'erated function for getting the 4181ad6265SDimitry Andric // binary encoding for an instruction. 4281ad6265SDimitry Andric uint64_t getBinaryCodeForInstr(const MCInst &MI, 4381ad6265SDimitry Andric SmallVectorImpl<MCFixup> &Fixups, 4481ad6265SDimitry Andric const MCSubtargetInfo &STI) const; 4581ad6265SDimitry Andric 46*5f757f3fSDimitry Andric void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB, 4781ad6265SDimitry Andric SmallVectorImpl<MCFixup> &Fixups, 4881ad6265SDimitry Andric const MCSubtargetInfo &STI) const override; 4981ad6265SDimitry Andric }; 5081ad6265SDimitry Andric 5181ad6265SDimitry Andric } // end anonymous namespace 5281ad6265SDimitry Andric 5381ad6265SDimitry Andric MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII, 5481ad6265SDimitry Andric MCContext &Ctx) { 5581ad6265SDimitry Andric return new SPIRVMCCodeEmitter(MCII); 5681ad6265SDimitry Andric } 5781ad6265SDimitry Andric 5881ad6265SDimitry Andric using EndianWriter = support::endian::Writer; 5981ad6265SDimitry Andric 6081ad6265SDimitry Andric // Check if the instruction has a type argument for operand 1, and defines an ID 6181ad6265SDimitry Andric // output register in operand 0. If so, we need to swap operands 0 and 1 so the 6281ad6265SDimitry Andric // type comes first in the output, despide coming second in the MCInst. 6381ad6265SDimitry Andric static bool hasType(const MCInst &MI, const MCInstrInfo &MII) { 64bdd1243dSDimitry Andric const MCInstrDesc &MCDesc = MII.get(MI.getOpcode()); 6581ad6265SDimitry Andric // If we define an output, and have at least one other argument. 6681ad6265SDimitry Andric if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) { 6781ad6265SDimitry Andric // Check if we define an ID, and take a type as operand 1. 68bdd1243dSDimitry Andric auto &DefOpInfo = MCDesc.operands()[0]; 69bdd1243dSDimitry Andric auto &FirstArgOpInfo = MCDesc.operands()[1]; 70bdd1243dSDimitry Andric return (DefOpInfo.RegClass == SPIRV::IDRegClassID || 71bdd1243dSDimitry Andric DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) && 72bdd1243dSDimitry Andric FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID; 7381ad6265SDimitry Andric } 7481ad6265SDimitry Andric return false; 7581ad6265SDimitry Andric } 7681ad6265SDimitry Andric 77*5f757f3fSDimitry Andric static void emitOperand(const MCOperand &Op, SmallVectorImpl<char> &CB) { 7881ad6265SDimitry Andric if (Op.isReg()) { 7981ad6265SDimitry Andric // Emit the id index starting at 1 (0 is an invalid index). 80*5f757f3fSDimitry Andric support::endian::write<uint32_t>( 81*5f757f3fSDimitry Andric CB, Register::virtReg2Index(Op.getReg()) + 1, llvm::endianness::little); 8281ad6265SDimitry Andric } else if (Op.isImm()) { 83*5f757f3fSDimitry Andric support::endian::write(CB, static_cast<uint32_t>(Op.getImm()), 84*5f757f3fSDimitry Andric llvm::endianness::little); 8581ad6265SDimitry Andric } else { 8681ad6265SDimitry Andric llvm_unreachable("Unexpected operand type in VReg"); 8781ad6265SDimitry Andric } 8881ad6265SDimitry Andric } 8981ad6265SDimitry Andric 9081ad6265SDimitry Andric // Emit the type in operand 1 before the ID in operand 0 it defines, and all 9181ad6265SDimitry Andric // remaining operands in the order they come naturally. 92*5f757f3fSDimitry Andric static void emitTypedInstrOperands(const MCInst &MI, 93*5f757f3fSDimitry Andric SmallVectorImpl<char> &CB) { 9481ad6265SDimitry Andric unsigned NumOps = MI.getNumOperands(); 95*5f757f3fSDimitry Andric emitOperand(MI.getOperand(1), CB); 96*5f757f3fSDimitry Andric emitOperand(MI.getOperand(0), CB); 9781ad6265SDimitry Andric for (unsigned i = 2; i < NumOps; ++i) 98*5f757f3fSDimitry Andric emitOperand(MI.getOperand(i), CB); 9981ad6265SDimitry Andric } 10081ad6265SDimitry Andric 10181ad6265SDimitry Andric // Emit operands in the order they come naturally. 102*5f757f3fSDimitry Andric static void emitUntypedInstrOperands(const MCInst &MI, 103*5f757f3fSDimitry Andric SmallVectorImpl<char> &CB) { 10481ad6265SDimitry Andric for (const auto &Op : MI) 105*5f757f3fSDimitry Andric emitOperand(Op, CB); 10681ad6265SDimitry Andric } 10781ad6265SDimitry Andric 108*5f757f3fSDimitry Andric void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, 109*5f757f3fSDimitry Andric SmallVectorImpl<char> &CB, 11081ad6265SDimitry Andric SmallVectorImpl<MCFixup> &Fixups, 11181ad6265SDimitry Andric const MCSubtargetInfo &STI) const { 11281ad6265SDimitry Andric // Encode the first 32 SPIR-V bytes with the number of args and the opcode. 11381ad6265SDimitry Andric const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI); 11481ad6265SDimitry Andric const uint32_t NumWords = MI.getNumOperands() + 1; 11581ad6265SDimitry Andric const uint32_t FirstWord = (NumWords << 16) | OpCode; 116*5f757f3fSDimitry Andric support::endian::write(CB, FirstWord, llvm::endianness::little); 11781ad6265SDimitry Andric 11881ad6265SDimitry Andric // Emit the instruction arguments (emitting the output type first if present). 11981ad6265SDimitry Andric if (hasType(MI, MCII)) 120*5f757f3fSDimitry Andric emitTypedInstrOperands(MI, CB); 12181ad6265SDimitry Andric else 122*5f757f3fSDimitry Andric emitUntypedInstrOperands(MI, CB); 12381ad6265SDimitry Andric } 12481ad6265SDimitry Andric 12581ad6265SDimitry Andric #include "SPIRVGenMCCodeEmitter.inc" 126