Avoid copying MCInstrDesc instances because a future patch will change them to find their implicit operands and operand info array based on their own "this" pointer, so it will only work for MCInstrDescs in the TargetInsts table, not for a copy of an MCInstrDesc at a different address. Differential Revision: https://reviews.llvm.org/D142214
123 lines
4.5 KiB
C++
123 lines
4.5 KiB
C++
//===-- SPIRVMCCodeEmitter.cpp - Emit SPIR-V machine code -------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the SPIRVMCCodeEmitter class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/SPIRVMCTargetDesc.h"
|
|
#include "llvm/CodeGen/Register.h"
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCFixup.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "spirv-mccodeemitter"
|
|
|
|
namespace {
|
|
|
|
class SPIRVMCCodeEmitter : public MCCodeEmitter {
|
|
const MCInstrInfo &MCII;
|
|
|
|
public:
|
|
SPIRVMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
|
|
SPIRVMCCodeEmitter(const SPIRVMCCodeEmitter &) = delete;
|
|
void operator=(const SPIRVMCCodeEmitter &) = delete;
|
|
~SPIRVMCCodeEmitter() override = default;
|
|
|
|
// getBinaryCodeForInstr - TableGen'erated function for getting the
|
|
// binary encoding for an instruction.
|
|
uint64_t getBinaryCodeForInstr(const MCInst &MI,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
|
|
void encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const override;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
MCCodeEmitter *llvm::createSPIRVMCCodeEmitter(const MCInstrInfo &MCII,
|
|
MCContext &Ctx) {
|
|
return new SPIRVMCCodeEmitter(MCII);
|
|
}
|
|
|
|
using EndianWriter = support::endian::Writer;
|
|
|
|
// Check if the instruction has a type argument for operand 1, and defines an ID
|
|
// output register in operand 0. If so, we need to swap operands 0 and 1 so the
|
|
// type comes first in the output, despide coming second in the MCInst.
|
|
static bool hasType(const MCInst &MI, const MCInstrInfo &MII) {
|
|
const MCInstrDesc &MCDesc = MII.get(MI.getOpcode());
|
|
// If we define an output, and have at least one other argument.
|
|
if (MCDesc.getNumDefs() == 1 && MCDesc.getNumOperands() >= 2) {
|
|
// Check if we define an ID, and take a type as operand 1.
|
|
auto &DefOpInfo = MCDesc.operands()[0];
|
|
auto &FirstArgOpInfo = MCDesc.operands()[1];
|
|
return (DefOpInfo.RegClass == SPIRV::IDRegClassID ||
|
|
DefOpInfo.RegClass == SPIRV::ANYIDRegClassID) &&
|
|
FirstArgOpInfo.RegClass == SPIRV::TYPERegClassID;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void emitOperand(const MCOperand &Op, EndianWriter &OSE) {
|
|
if (Op.isReg()) {
|
|
// Emit the id index starting at 1 (0 is an invalid index).
|
|
OSE.write<uint32_t>(Register::virtReg2Index(Op.getReg()) + 1);
|
|
} else if (Op.isImm()) {
|
|
OSE.write<uint32_t>(Op.getImm());
|
|
} else {
|
|
llvm_unreachable("Unexpected operand type in VReg");
|
|
}
|
|
}
|
|
|
|
// Emit the type in operand 1 before the ID in operand 0 it defines, and all
|
|
// remaining operands in the order they come naturally.
|
|
static void emitTypedInstrOperands(const MCInst &MI, EndianWriter &OSE) {
|
|
unsigned NumOps = MI.getNumOperands();
|
|
emitOperand(MI.getOperand(1), OSE);
|
|
emitOperand(MI.getOperand(0), OSE);
|
|
for (unsigned i = 2; i < NumOps; ++i)
|
|
emitOperand(MI.getOperand(i), OSE);
|
|
}
|
|
|
|
// Emit operands in the order they come naturally.
|
|
static void emitUntypedInstrOperands(const MCInst &MI, EndianWriter &OSE) {
|
|
for (const auto &Op : MI)
|
|
emitOperand(Op, OSE);
|
|
}
|
|
|
|
void SPIRVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
EndianWriter OSE(OS, support::little);
|
|
|
|
// Encode the first 32 SPIR-V bytes with the number of args and the opcode.
|
|
const uint64_t OpCode = getBinaryCodeForInstr(MI, Fixups, STI);
|
|
const uint32_t NumWords = MI.getNumOperands() + 1;
|
|
const uint32_t FirstWord = (NumWords << 16) | OpCode;
|
|
OSE.write<uint32_t>(FirstWord);
|
|
|
|
// Emit the instruction arguments (emitting the output type first if present).
|
|
if (hasType(MI, MCII))
|
|
emitTypedInstrOperands(MI, OSE);
|
|
else
|
|
emitUntypedInstrOperands(MI, OSE);
|
|
}
|
|
|
|
#include "SPIRVGenMCCodeEmitter.inc"
|