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
183 lines
5.9 KiB
C++
183 lines
5.9 KiB
C++
//===- R600MCCodeEmitter.cpp - Code Emitter for R600->Cayman GPU families -===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
/// \file
|
|
///
|
|
/// The R600 code emitter produces machine code that can be executed
|
|
/// directly on the GPU device.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/R600MCTargetDesc.h"
|
|
#include "R600Defines.h"
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/MC/SubtargetFeature.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class R600MCCodeEmitter : public MCCodeEmitter {
|
|
const MCRegisterInfo &MRI;
|
|
const MCInstrInfo &MCII;
|
|
|
|
public:
|
|
R600MCCodeEmitter(const MCInstrInfo &mcii, const MCRegisterInfo &mri)
|
|
: MRI(mri), MCII(mcii) {}
|
|
R600MCCodeEmitter(const R600MCCodeEmitter &) = delete;
|
|
R600MCCodeEmitter &operator=(const R600MCCodeEmitter &) = delete;
|
|
|
|
/// Encode the instruction and write it to the OS.
|
|
void encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const override;
|
|
|
|
/// \returns the encoding for an MCOperand.
|
|
uint64_t getMachineOpValue(const MCInst &MI, const MCOperand &MO,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
|
|
private:
|
|
|
|
void Emit(uint32_t value, raw_ostream &OS) const;
|
|
void Emit(uint64_t value, raw_ostream &OS) const;
|
|
|
|
unsigned getHWReg(unsigned regNo) const;
|
|
|
|
uint64_t getBinaryCodeForInstr(const MCInst &MI,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
enum RegElement {
|
|
ELEMENT_X = 0,
|
|
ELEMENT_Y,
|
|
ELEMENT_Z,
|
|
ELEMENT_W
|
|
};
|
|
|
|
enum FCInstr {
|
|
FC_IF_PREDICATE = 0,
|
|
FC_ELSE,
|
|
FC_ENDIF,
|
|
FC_BGNLOOP,
|
|
FC_ENDLOOP,
|
|
FC_BREAK_PREDICATE,
|
|
FC_CONTINUE
|
|
};
|
|
|
|
MCCodeEmitter *llvm::createR600MCCodeEmitter(const MCInstrInfo &MCII,
|
|
MCContext &Ctx) {
|
|
return new R600MCCodeEmitter(MCII, *Ctx.getRegisterInfo());
|
|
}
|
|
|
|
void R600MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
|
|
if (MI.getOpcode() == R600::RETURN ||
|
|
MI.getOpcode() == R600::FETCH_CLAUSE ||
|
|
MI.getOpcode() == R600::ALU_CLAUSE ||
|
|
MI.getOpcode() == R600::BUNDLE ||
|
|
MI.getOpcode() == R600::KILL) {
|
|
return;
|
|
} else if (IS_VTX(Desc)) {
|
|
uint64_t InstWord01 = getBinaryCodeForInstr(MI, Fixups, STI);
|
|
uint32_t InstWord2 = MI.getOperand(2).getImm(); // Offset
|
|
if (!(STI.getFeatureBits()[R600::FeatureCaymanISA])) {
|
|
InstWord2 |= 1 << 19; // Mega-Fetch bit
|
|
}
|
|
|
|
Emit(InstWord01, OS);
|
|
Emit(InstWord2, OS);
|
|
Emit((uint32_t) 0, OS);
|
|
} else if (IS_TEX(Desc)) {
|
|
int64_t Sampler = MI.getOperand(14).getImm();
|
|
|
|
int64_t SrcSelect[4] = {
|
|
MI.getOperand(2).getImm(),
|
|
MI.getOperand(3).getImm(),
|
|
MI.getOperand(4).getImm(),
|
|
MI.getOperand(5).getImm()
|
|
};
|
|
int64_t Offsets[3] = {
|
|
MI.getOperand(6).getImm() & 0x1F,
|
|
MI.getOperand(7).getImm() & 0x1F,
|
|
MI.getOperand(8).getImm() & 0x1F
|
|
};
|
|
|
|
uint64_t Word01 = getBinaryCodeForInstr(MI, Fixups, STI);
|
|
uint32_t Word2 = Sampler << 15 | SrcSelect[ELEMENT_X] << 20 |
|
|
SrcSelect[ELEMENT_Y] << 23 | SrcSelect[ELEMENT_Z] << 26 |
|
|
SrcSelect[ELEMENT_W] << 29 | Offsets[0] << 0 | Offsets[1] << 5 |
|
|
Offsets[2] << 10;
|
|
|
|
Emit(Word01, OS);
|
|
Emit(Word2, OS);
|
|
Emit((uint32_t) 0, OS);
|
|
} else {
|
|
uint64_t Inst = getBinaryCodeForInstr(MI, Fixups, STI);
|
|
if ((STI.getFeatureBits()[R600::FeatureR600ALUInst]) &&
|
|
((Desc.TSFlags & R600_InstFlag::OP1) ||
|
|
Desc.TSFlags & R600_InstFlag::OP2)) {
|
|
uint64_t ISAOpCode = Inst & (0x3FFULL << 39);
|
|
Inst &= ~(0x3FFULL << 39);
|
|
Inst |= ISAOpCode << 1;
|
|
}
|
|
Emit(Inst, OS);
|
|
}
|
|
}
|
|
|
|
void R600MCCodeEmitter::Emit(uint32_t Value, raw_ostream &OS) const {
|
|
support::endian::write(OS, Value, support::little);
|
|
}
|
|
|
|
void R600MCCodeEmitter::Emit(uint64_t Value, raw_ostream &OS) const {
|
|
support::endian::write(OS, Value, support::little);
|
|
}
|
|
|
|
unsigned R600MCCodeEmitter::getHWReg(unsigned RegNo) const {
|
|
return MRI.getEncodingValue(RegNo) & HW_REG_MASK;
|
|
}
|
|
|
|
uint64_t R600MCCodeEmitter::getMachineOpValue(const MCInst &MI,
|
|
const MCOperand &MO,
|
|
SmallVectorImpl<MCFixup> &Fixups,
|
|
const MCSubtargetInfo &STI) const {
|
|
if (MO.isReg()) {
|
|
if (HAS_NATIVE_OPERANDS(MCII.get(MI.getOpcode()).TSFlags))
|
|
return MRI.getEncodingValue(MO.getReg());
|
|
return getHWReg(MO.getReg());
|
|
}
|
|
|
|
if (MO.isExpr()) {
|
|
// We put rodata at the end of code section, then map the entire
|
|
// code secetion as vtx buf. Thus the section relative address is the
|
|
// correct one.
|
|
// Each R600 literal instruction has two operands
|
|
// We can't easily get the order of the current one, so compare against
|
|
// the first one and adjust offset.
|
|
const unsigned offset = (&MO == &MI.getOperand(0)) ? 0 : 4;
|
|
Fixups.push_back(MCFixup::create(offset, MO.getExpr(), FK_SecRel_4, MI.getLoc()));
|
|
return 0;
|
|
}
|
|
|
|
assert(MO.isImm());
|
|
return MO.getImm();
|
|
}
|
|
|
|
#include "R600GenMCCodeEmitter.inc"
|