mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 21:46:05 +00:00

Also use brace initialization and emplace to avoid explicitly constructing std::pair, and the same for std::tuple.
615 lines
21 KiB
C++
615 lines
21 KiB
C++
//===- CodeEmitterGen.cpp - Code Emitter Generator ------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// CodeEmitterGen uses the descriptions of instructions and their fields to
|
|
// construct an automated code emitter: a function called
|
|
// getBinaryCodeForInstr() that, given a MCInst, returns the value of the
|
|
// instruction - either as an uint64_t or as an APInt, depending on the
|
|
// maximum bit width of all Inst definitions.
|
|
//
|
|
// In addition, it generates another function called getOperandBitOffset()
|
|
// that, given a MCInst and an operand index, returns the minimum of indices of
|
|
// all bits that carry some portion of the respective operand. When the target's
|
|
// encodeInstruction() stores the instruction in a little-endian byte order, the
|
|
// returned value is the offset of the start of the operand in the encoded
|
|
// instruction. Other targets might need to adjust the returned value according
|
|
// to their encodeInstruction() implementation.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Common/CodeGenHwModes.h"
|
|
#include "Common/CodeGenInstruction.h"
|
|
#include "Common/CodeGenTarget.h"
|
|
#include "Common/InfoByHwMode.h"
|
|
#include "Common/VarLenCodeEmitterGen.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class CodeEmitterGen {
|
|
const RecordKeeper &Records;
|
|
|
|
public:
|
|
CodeEmitterGen(const RecordKeeper &R) : Records(R) {}
|
|
|
|
void run(raw_ostream &O);
|
|
|
|
private:
|
|
int getVariableBit(const std::string &VarName, const BitsInit *BI, int Bit);
|
|
std::pair<std::string, std::string>
|
|
getInstructionCases(const Record *R, const CodeGenTarget &Target);
|
|
void addInstructionCasesForEncoding(const Record *R,
|
|
const Record *EncodingDef,
|
|
const CodeGenTarget &Target,
|
|
std::string &Case,
|
|
std::string &BitOffsetCase);
|
|
bool addCodeToMergeInOperand(const Record *R, const BitsInit *BI,
|
|
const std::string &VarName, std::string &Case,
|
|
std::string &BitOffsetCase,
|
|
const CodeGenTarget &Target);
|
|
|
|
void emitInstructionBaseValues(
|
|
raw_ostream &O, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
|
|
const CodeGenTarget &Target, unsigned HwMode = DefaultMode);
|
|
void
|
|
emitCaseMap(raw_ostream &O,
|
|
const std::map<std::string, std::vector<std::string>> &CaseMap);
|
|
unsigned BitWidth = 0u;
|
|
bool UseAPInt = false;
|
|
};
|
|
|
|
// If the VarBitInit at position 'bit' matches the specified variable then
|
|
// return the variable bit position. Otherwise return -1.
|
|
int CodeEmitterGen::getVariableBit(const std::string &VarName,
|
|
const BitsInit *BI, int Bit) {
|
|
if (const VarBitInit *VBI = dyn_cast<VarBitInit>(BI->getBit(Bit))) {
|
|
if (const VarInit *VI = dyn_cast<VarInit>(VBI->getBitVar()))
|
|
if (VI->getName() == VarName)
|
|
return VBI->getBitNum();
|
|
} else if (const VarInit *VI = dyn_cast<VarInit>(BI->getBit(Bit))) {
|
|
if (VI->getName() == VarName)
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Returns true if it succeeds, false if an error.
|
|
bool CodeEmitterGen::addCodeToMergeInOperand(const Record *R,
|
|
const BitsInit *BI,
|
|
const std::string &VarName,
|
|
std::string &Case,
|
|
std::string &BitOffsetCase,
|
|
const CodeGenTarget &Target) {
|
|
CodeGenInstruction &CGI = Target.getInstruction(R);
|
|
|
|
// Determine if VarName actually contributes to the Inst encoding.
|
|
int Bit = BI->getNumBits() - 1;
|
|
|
|
// Scan for a bit that this contributed to.
|
|
for (; Bit >= 0;) {
|
|
if (getVariableBit(VarName, BI, Bit) != -1)
|
|
break;
|
|
|
|
--Bit;
|
|
}
|
|
|
|
// If we found no bits, ignore this value, otherwise emit the call to get the
|
|
// operand encoding.
|
|
if (Bit < 0)
|
|
return true;
|
|
|
|
// If the operand matches by name, reference according to that
|
|
// operand number. Non-matching operands are assumed to be in
|
|
// order.
|
|
unsigned OpIdx;
|
|
std::pair<unsigned, unsigned> SubOp;
|
|
if (CGI.Operands.hasSubOperandAlias(VarName, SubOp)) {
|
|
OpIdx = CGI.Operands[SubOp.first].MIOperandNo + SubOp.second;
|
|
} else if (CGI.Operands.hasOperandNamed(VarName, OpIdx)) {
|
|
// Get the machine operand number for the indicated operand.
|
|
OpIdx = CGI.Operands[OpIdx].MIOperandNo;
|
|
} else {
|
|
PrintError(R, Twine("No operand named ") + VarName + " in record " +
|
|
R->getName());
|
|
return false;
|
|
}
|
|
|
|
if (CGI.Operands.isFlatOperandNotEmitted(OpIdx)) {
|
|
PrintError(R,
|
|
"Operand " + VarName + " used but also marked as not emitted!");
|
|
return false;
|
|
}
|
|
|
|
std::pair<unsigned, unsigned> SO = CGI.Operands.getSubOperandNumber(OpIdx);
|
|
std::string &EncoderMethodName =
|
|
CGI.Operands[SO.first].EncoderMethodNames[SO.second];
|
|
|
|
if (UseAPInt)
|
|
Case += " op.clearAllBits();\n";
|
|
|
|
Case += " // op: " + VarName + "\n";
|
|
|
|
// If the source operand has a custom encoder, use it.
|
|
if (!EncoderMethodName.empty()) {
|
|
if (UseAPInt) {
|
|
Case += " " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
|
Case += ", op";
|
|
} else {
|
|
Case += " op = " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
|
}
|
|
Case += ", Fixups, STI);\n";
|
|
} else {
|
|
if (UseAPInt) {
|
|
Case +=
|
|
" getMachineOpValue(MI, MI.getOperand(" + utostr(OpIdx) + ")";
|
|
Case += ", op, Fixups, STI";
|
|
} else {
|
|
Case += " op = getMachineOpValue(MI, MI.getOperand(" +
|
|
utostr(OpIdx) + ")";
|
|
Case += ", Fixups, STI";
|
|
}
|
|
Case += ");\n";
|
|
}
|
|
|
|
// Precalculate the number of lits this variable contributes to in the
|
|
// operand. If there is a single lit (consecutive range of bits) we can use a
|
|
// destructive sequence on APInt that reduces memory allocations.
|
|
int NumOperandLits = 0;
|
|
for (int TmpBit = Bit; TmpBit >= 0;) {
|
|
int VarBit = getVariableBit(VarName, BI, TmpBit);
|
|
|
|
// If this bit isn't from a variable, skip it.
|
|
if (VarBit == -1) {
|
|
--TmpBit;
|
|
continue;
|
|
}
|
|
|
|
// Figure out the consecutive range of bits covered by this operand, in
|
|
// order to generate better encoding code.
|
|
int BeginVarBit = VarBit;
|
|
int N = 1;
|
|
for (--TmpBit; TmpBit >= 0;) {
|
|
VarBit = getVariableBit(VarName, BI, TmpBit);
|
|
if (VarBit == -1 || VarBit != (BeginVarBit - N))
|
|
break;
|
|
++N;
|
|
--TmpBit;
|
|
}
|
|
++NumOperandLits;
|
|
}
|
|
|
|
unsigned BitOffset = -1;
|
|
for (; Bit >= 0;) {
|
|
int VarBit = getVariableBit(VarName, BI, Bit);
|
|
|
|
// If this bit isn't from a variable, skip it.
|
|
if (VarBit == -1) {
|
|
--Bit;
|
|
continue;
|
|
}
|
|
|
|
// Figure out the consecutive range of bits covered by this operand, in
|
|
// order to generate better encoding code.
|
|
int BeginInstBit = Bit;
|
|
int BeginVarBit = VarBit;
|
|
int N = 1;
|
|
for (--Bit; Bit >= 0;) {
|
|
VarBit = getVariableBit(VarName, BI, Bit);
|
|
if (VarBit == -1 || VarBit != (BeginVarBit - N))
|
|
break;
|
|
++N;
|
|
--Bit;
|
|
}
|
|
|
|
std::string MaskStr;
|
|
int OpShift;
|
|
|
|
unsigned LoBit = BeginVarBit - N + 1;
|
|
unsigned HiBit = LoBit + N;
|
|
unsigned LoInstBit = BeginInstBit - N + 1;
|
|
BitOffset = LoInstBit;
|
|
if (UseAPInt) {
|
|
std::string ExtractStr;
|
|
if (N >= 64) {
|
|
ExtractStr = "op.extractBits(" + itostr(HiBit - LoBit) + ", " +
|
|
itostr(LoBit) + ")";
|
|
Case += " Value.insertBits(" + ExtractStr + ", " +
|
|
itostr(LoInstBit) + ");\n";
|
|
} else {
|
|
ExtractStr = "op.extractBitsAsZExtValue(" + itostr(HiBit - LoBit) +
|
|
", " + itostr(LoBit) + ")";
|
|
Case += " Value.insertBits(" + ExtractStr + ", " +
|
|
itostr(LoInstBit) + ", " + itostr(HiBit - LoBit) + ");\n";
|
|
}
|
|
} else {
|
|
uint64_t OpMask = ~(uint64_t)0 >> (64 - N);
|
|
OpShift = BeginVarBit - N + 1;
|
|
OpMask <<= OpShift;
|
|
MaskStr = "UINT64_C(" + utostr(OpMask) + ")";
|
|
OpShift = BeginInstBit - BeginVarBit;
|
|
|
|
if (NumOperandLits == 1) {
|
|
Case += " op &= " + MaskStr + ";\n";
|
|
if (OpShift > 0) {
|
|
Case += " op <<= " + itostr(OpShift) + ";\n";
|
|
} else if (OpShift < 0) {
|
|
Case += " op >>= " + itostr(-OpShift) + ";\n";
|
|
}
|
|
Case += " Value |= op;\n";
|
|
} else {
|
|
if (OpShift > 0) {
|
|
Case += " Value |= (op & " + MaskStr + ") << " +
|
|
itostr(OpShift) + ";\n";
|
|
} else if (OpShift < 0) {
|
|
Case += " Value |= (op & " + MaskStr + ") >> " +
|
|
itostr(-OpShift) + ";\n";
|
|
} else {
|
|
Case += " Value |= (op & " + MaskStr + ");\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BitOffset != (unsigned)-1) {
|
|
BitOffsetCase += " case " + utostr(OpIdx) + ":\n";
|
|
BitOffsetCase += " // op: " + VarName + "\n";
|
|
BitOffsetCase += " return " + utostr(BitOffset) + ";\n";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::pair<std::string, std::string>
|
|
CodeEmitterGen::getInstructionCases(const Record *R,
|
|
const CodeGenTarget &Target) {
|
|
std::string Case, BitOffsetCase;
|
|
|
|
auto Append = [&](const std::string &S) {
|
|
Case += S;
|
|
BitOffsetCase += S;
|
|
};
|
|
|
|
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
|
|
if (const auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
|
|
const CodeGenHwModes &HWM = Target.getHwModes();
|
|
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
|
|
|
|
// Invoke the interface to obtain the HwMode ID controlling the
|
|
// EncodingInfo for the current subtarget. This interface will
|
|
// mask off irrelevant HwMode IDs.
|
|
Append(" unsigned HwMode = "
|
|
"STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);\n");
|
|
Case += " switch (HwMode) {\n";
|
|
Case += " default: llvm_unreachable(\"Unknown hardware mode!\"); "
|
|
"break;\n";
|
|
for (auto &[ModeId, Encoding] : EBM) {
|
|
if (ModeId == DefaultMode) {
|
|
Case +=
|
|
" case " + itostr(DefaultMode) + ": InstBitsByHw = InstBits";
|
|
} else {
|
|
Case += " case " + itostr(ModeId) +
|
|
": InstBitsByHw = InstBits_" +
|
|
std::string(HWM.getMode(ModeId).Name);
|
|
}
|
|
Case += "; break;\n";
|
|
}
|
|
Case += " };\n";
|
|
|
|
// We need to remodify the 'Inst' value from the table we found above.
|
|
if (UseAPInt) {
|
|
int NumWords = APInt::getNumWords(BitWidth);
|
|
Case += " Inst = APInt(" + itostr(BitWidth);
|
|
Case += ", ArrayRef(InstBitsByHw + opcode * " + itostr(NumWords) +
|
|
", " + itostr(NumWords);
|
|
Case += "));\n";
|
|
Case += " Value = Inst;\n";
|
|
} else {
|
|
Case += " Value = InstBitsByHw[opcode];\n";
|
|
}
|
|
|
|
Append(" switch (HwMode) {\n");
|
|
Append(" default: llvm_unreachable(\"Unhandled HwMode\");\n");
|
|
for (auto &[ModeId, Encoding] : EBM) {
|
|
Append(" case " + itostr(ModeId) + ": {\n");
|
|
addInstructionCasesForEncoding(R, Encoding, Target, Case,
|
|
BitOffsetCase);
|
|
Append(" break;\n");
|
|
Append(" }\n");
|
|
}
|
|
Append(" }\n");
|
|
return {std::move(Case), std::move(BitOffsetCase)};
|
|
}
|
|
}
|
|
addInstructionCasesForEncoding(R, R, Target, Case, BitOffsetCase);
|
|
return {std::move(Case), std::move(BitOffsetCase)};
|
|
}
|
|
|
|
void CodeEmitterGen::addInstructionCasesForEncoding(
|
|
const Record *R, const Record *EncodingDef, const CodeGenTarget &Target,
|
|
std::string &Case, std::string &BitOffsetCase) {
|
|
const BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
|
|
|
|
// Loop over all of the fields in the instruction, determining which are the
|
|
// operands to the instruction.
|
|
bool Success = true;
|
|
size_t OrigBitOffsetCaseSize = BitOffsetCase.size();
|
|
BitOffsetCase += " switch (OpNum) {\n";
|
|
size_t BitOffsetCaseSizeBeforeLoop = BitOffsetCase.size();
|
|
for (const RecordVal &RV : EncodingDef->getValues()) {
|
|
// Ignore fixed fields in the record, we're looking for values like:
|
|
// bits<5> RST = { ?, ?, ?, ?, ? };
|
|
if (RV.isNonconcreteOK() || RV.getValue()->isComplete())
|
|
continue;
|
|
|
|
Success &= addCodeToMergeInOperand(R, BI, std::string(RV.getName()), Case,
|
|
BitOffsetCase, Target);
|
|
}
|
|
// Avoid empty switches.
|
|
if (BitOffsetCase.size() == BitOffsetCaseSizeBeforeLoop)
|
|
BitOffsetCase.resize(OrigBitOffsetCaseSize);
|
|
else
|
|
BitOffsetCase += " }\n";
|
|
|
|
if (!Success) {
|
|
// Dump the record, so we can see what's going on...
|
|
std::string E;
|
|
raw_string_ostream S(E);
|
|
S << "Dumping record for previous error:\n";
|
|
S << *R;
|
|
PrintNote(E);
|
|
}
|
|
|
|
StringRef PostEmitter = R->getValueAsString("PostEncoderMethod");
|
|
if (!PostEmitter.empty()) {
|
|
Case += " Value = ";
|
|
Case += PostEmitter;
|
|
Case += "(MI, Value";
|
|
Case += ", STI";
|
|
Case += ");\n";
|
|
}
|
|
}
|
|
|
|
static void emitInstBits(raw_ostream &OS, const APInt &Bits) {
|
|
for (unsigned I = 0; I < Bits.getNumWords(); ++I)
|
|
OS << ((I > 0) ? ", " : "") << "UINT64_C(" << utostr(Bits.getRawData()[I])
|
|
<< ")";
|
|
}
|
|
|
|
void CodeEmitterGen::emitInstructionBaseValues(
|
|
raw_ostream &O, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
|
|
const CodeGenTarget &Target, unsigned HwMode) {
|
|
const CodeGenHwModes &HWM = Target.getHwModes();
|
|
if (HwMode == DefaultMode)
|
|
O << " static const uint64_t InstBits[] = {\n";
|
|
else
|
|
O << " static const uint64_t InstBits_"
|
|
<< HWM.getModeName(HwMode, /*IncludeDefault=*/true) << "[] = {\n";
|
|
|
|
for (const CodeGenInstruction *CGI : NumberedInstructions) {
|
|
const Record *R = CGI->TheDef;
|
|
|
|
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
|
|
R->getValueAsBit("isPseudo")) {
|
|
O << " ";
|
|
emitInstBits(O, APInt(BitWidth, 0));
|
|
O << ",\n";
|
|
continue;
|
|
}
|
|
|
|
const Record *EncodingDef = R;
|
|
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
|
|
if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
|
|
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
|
|
if (EBM.hasMode(HwMode)) {
|
|
EncodingDef = EBM.get(HwMode);
|
|
} else {
|
|
// If the HwMode does not match, then Encoding '0'
|
|
// should be generated.
|
|
APInt Value(BitWidth, 0);
|
|
O << " ";
|
|
emitInstBits(O, Value);
|
|
O << "," << '\t' << "// " << R->getName() << "\n";
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
const BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
|
|
|
|
// Start by filling in fixed values.
|
|
APInt Value(BitWidth, 0);
|
|
for (unsigned I = 0, E = BI->getNumBits(); I != E; ++I) {
|
|
if (const auto *B = dyn_cast<BitInit>(BI->getBit(I)); B && B->getValue())
|
|
Value.setBit(I);
|
|
}
|
|
O << " ";
|
|
emitInstBits(O, Value);
|
|
O << "," << '\t' << "// " << R->getName() << "\n";
|
|
}
|
|
O << " UINT64_C(0)\n };\n";
|
|
}
|
|
|
|
void CodeEmitterGen::emitCaseMap(
|
|
raw_ostream &O,
|
|
const std::map<std::string, std::vector<std::string>> &CaseMap) {
|
|
for (const auto &[Case, InstList] : CaseMap) {
|
|
bool First = true;
|
|
for (const auto &Inst : InstList) {
|
|
if (!First)
|
|
O << "\n";
|
|
O << " case " << Inst << ":";
|
|
First = false;
|
|
}
|
|
O << " {\n";
|
|
O << Case;
|
|
O << " break;\n"
|
|
<< " }\n";
|
|
}
|
|
}
|
|
|
|
void CodeEmitterGen::run(raw_ostream &O) {
|
|
emitSourceFileHeader("Machine Code Emitter", O);
|
|
|
|
CodeGenTarget Target(Records);
|
|
|
|
// For little-endian instruction bit encodings, reverse the bit order
|
|
Target.reverseBitsForLittleEndianEncoding();
|
|
|
|
ArrayRef<const CodeGenInstruction *> NumberedInstructions =
|
|
Target.getInstructionsByEnumValue();
|
|
|
|
if (Target.hasVariableLengthEncodings()) {
|
|
emitVarLenCodeEmitter(Records, O);
|
|
} else {
|
|
const CodeGenHwModes &HWM = Target.getHwModes();
|
|
// The set of HwModes used by instruction encodings.
|
|
std::set<unsigned> HwModes;
|
|
BitWidth = 0;
|
|
for (const CodeGenInstruction *CGI : NumberedInstructions) {
|
|
const Record *R = CGI->TheDef;
|
|
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
|
|
R->getValueAsBit("isPseudo"))
|
|
continue;
|
|
|
|
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
|
|
if (const DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
|
|
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
|
|
for (const auto &[Key, Value] : EBM) {
|
|
const BitsInit *BI = Value->getValueAsBitsInit("Inst");
|
|
BitWidth = std::max(BitWidth, BI->getNumBits());
|
|
HwModes.insert(Key);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
const BitsInit *BI = R->getValueAsBitsInit("Inst");
|
|
BitWidth = std::max(BitWidth, BI->getNumBits());
|
|
}
|
|
UseAPInt = BitWidth > 64;
|
|
|
|
// Emit function declaration
|
|
if (UseAPInt) {
|
|
O << "void " << Target.getName()
|
|
<< "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
|
|
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
|
|
<< " APInt &Inst,\n"
|
|
<< " APInt &Scratch,\n"
|
|
<< " const MCSubtargetInfo &STI) const {\n";
|
|
} else {
|
|
O << "uint64_t " << Target.getName();
|
|
O << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
|
|
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
|
|
<< " const MCSubtargetInfo &STI) const {\n";
|
|
}
|
|
|
|
// Emit instruction base values
|
|
emitInstructionBaseValues(O, NumberedInstructions, Target, DefaultMode);
|
|
if (!HwModes.empty()) {
|
|
// Emit table for instrs whose encodings are controlled by HwModes.
|
|
for (unsigned HwMode : HwModes) {
|
|
if (HwMode == DefaultMode)
|
|
continue;
|
|
emitInstructionBaseValues(O, NumberedInstructions, Target, HwMode);
|
|
}
|
|
|
|
// This pointer will be assigned to the HwMode table later.
|
|
O << " const uint64_t *InstBitsByHw;\n";
|
|
}
|
|
|
|
// Map to accumulate all the cases.
|
|
std::map<std::string, std::vector<std::string>> CaseMap;
|
|
std::map<std::string, std::vector<std::string>> BitOffsetCaseMap;
|
|
|
|
// Construct all cases statement for each opcode
|
|
for (const Record *R : Records.getAllDerivedDefinitions("Instruction")) {
|
|
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
|
|
R->getValueAsBit("isPseudo"))
|
|
continue;
|
|
std::string InstName =
|
|
(R->getValueAsString("Namespace") + "::" + R->getName()).str();
|
|
std::string Case, BitOffsetCase;
|
|
std::tie(Case, BitOffsetCase) = getInstructionCases(R, Target);
|
|
|
|
CaseMap[Case].push_back(InstName);
|
|
BitOffsetCaseMap[BitOffsetCase].push_back(std::move(InstName));
|
|
}
|
|
|
|
// Emit initial function code
|
|
if (UseAPInt) {
|
|
int NumWords = APInt::getNumWords(BitWidth);
|
|
O << " const unsigned opcode = MI.getOpcode();\n"
|
|
<< " if (Scratch.getBitWidth() != " << BitWidth << ")\n"
|
|
<< " Scratch = Scratch.zext(" << BitWidth << ");\n"
|
|
<< " Inst = APInt(" << BitWidth << ", ArrayRef(InstBits + opcode * "
|
|
<< NumWords << ", " << NumWords << "));\n"
|
|
<< " APInt &Value = Inst;\n"
|
|
<< " APInt &op = Scratch;\n"
|
|
<< " switch (opcode) {\n";
|
|
} else {
|
|
O << " const unsigned opcode = MI.getOpcode();\n"
|
|
<< " uint64_t Value = InstBits[opcode];\n"
|
|
<< " uint64_t op = 0;\n"
|
|
<< " (void)op; // suppress warning\n"
|
|
<< " switch (opcode) {\n";
|
|
}
|
|
|
|
// Emit each case statement
|
|
emitCaseMap(O, CaseMap);
|
|
|
|
// Default case: unhandled opcode
|
|
O << " default:\n"
|
|
<< " std::string msg;\n"
|
|
<< " raw_string_ostream Msg(msg);\n"
|
|
<< " Msg << \"Not supported instr: \" << MI;\n"
|
|
<< " report_fatal_error(Msg.str().c_str());\n"
|
|
<< " }\n";
|
|
if (UseAPInt)
|
|
O << " Inst = Value;\n";
|
|
else
|
|
O << " return Value;\n";
|
|
O << "}\n\n";
|
|
|
|
O << "#ifdef GET_OPERAND_BIT_OFFSET\n"
|
|
<< "#undef GET_OPERAND_BIT_OFFSET\n\n"
|
|
<< "uint32_t " << Target.getName()
|
|
<< "MCCodeEmitter::getOperandBitOffset(const MCInst &MI,\n"
|
|
<< " unsigned OpNum,\n"
|
|
<< " const MCSubtargetInfo &STI) const {\n"
|
|
<< " switch (MI.getOpcode()) {\n";
|
|
emitCaseMap(O, BitOffsetCaseMap);
|
|
O << " }\n"
|
|
<< " std::string msg;\n"
|
|
<< " raw_string_ostream Msg(msg);\n"
|
|
<< " Msg << \"Not supported instr[opcode]: \" << MI << \"[\" << OpNum "
|
|
"<< \"]\";\n"
|
|
<< " report_fatal_error(Msg.str().c_str());\n"
|
|
<< "}\n\n"
|
|
<< "#endif // GET_OPERAND_BIT_OFFSET\n\n";
|
|
}
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
static TableGen::Emitter::OptClass<CodeEmitterGen>
|
|
X("gen-emitter", "Generate machine code emitter");
|