llvm-project/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVMCCodeEmitter.cpp
David Green 3e0bf1c7a9 [CodeGen] Move instruction predicate verification to emitInstruction
D25618 added a method to verify the instruction predicates for an
emitted instruction, through verifyInstructionPredicates added into
<Target>MCCodeEmitter::encodeInstruction. This is a very useful idea,
but the implementation inside MCCodeEmitter made it only fire for object
files, not assembly which most of the llvm test suite uses.

This patch moves the code into the <Target>_MC::verifyInstructionPredicates
method, inside the InstrInfo.  The allows it to be called from other
places, such as in this patch where it is called from the
<Target>AsmPrinter::emitInstruction methods which should trigger for
both assembly and object files. It can also be called from other places
such as verifyInstruction, but that is not done here (it tends to catch
errors earlier, but in reality just shows all the mir tests that have
incorrect feature predicates). The interface was also simplified
slightly, moving computeAvailableFeatures into the function so that it
does not need to be called externally.

The ARM, AMDGPU (but not R600), AVR, Mips and X86 backends all currently
show errors in the test-suite, so have been disabled with FIXME
comments.

Recommitted with some fixes for the leftover MCII variables in release
builds.

Differential Revision: https://reviews.llvm.org/D129506
2022-07-14 09:33:28 +01:00

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) {
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.opInfo_begin();
auto FirstArgOpInfo = MCDesc.opInfo_begin() + 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"