[TableGen] Add a new `encoder` directive into VarLenCodeEmitterGen

The new encoder directive can be used to specify custom encoder for a
single operand or slice. This is different from the EncoderMethod field
within an Operand, which affects every operands in the target.

In addition, this patch also changes the function signature of the
encoder method -- a new argument, InsertPost, is added to both the
default one (i.e. getMachineValue) and the custom one. This argument
provides the bit position where the operand will eventually be inserted.

Differential Revision: https://reviews.llvm.org/D119100
This commit is contained in:
Min-Yih Hsu 2021-12-14 20:59:22 +08:00
parent d241ce0f97
commit b99365a7f4
4 changed files with 82 additions and 40 deletions

View File

@ -778,6 +778,11 @@ def operand;
/// Both DAG represent bit 6 to 8 (total of 3 bits) in the encoding of operand /// Both DAG represent bit 6 to 8 (total of 3 bits) in the encoding of operand
/// `$src`. /// `$src`.
def slice; def slice;
/// You can use `encoder` to specify a custom encoder function for a specific
/// `operand` or `encoder` directive. For example:
/// (operand "$src", 4, (encoder "encodeMyImm"))
/// (slice "$src", 8, 6, (encoder "encodeMyReg"))
def encoder;
/// PointerLikeRegClass - Values that are designed to have pointer width are /// PointerLikeRegClass - Values that are designed to have pointer width are
/// derived from this. TableGen treats the register class as having a symbolic /// derived from this. TableGen treats the register class as having a symbolic

View File

@ -43,7 +43,8 @@ class M68kMCCodeEmitter : public MCCodeEmitter {
APInt &Inst, APInt &Scratch, APInt &Inst, APInt &Scratch,
const MCSubtargetInfo &STI) const; const MCSubtargetInfo &STI) const;
void getMachineOpValue(const MCInst &MI, const MCOperand &Op, APInt &Value, void getMachineOpValue(const MCInst &MI, const MCOperand &Op,
unsigned InsertPos, APInt &Value,
SmallVectorImpl<MCFixup> &Fixups, SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const; const MCSubtargetInfo &STI) const;
@ -83,7 +84,7 @@ public:
#include "M68kGenMCCodeEmitter.inc" #include "M68kGenMCCodeEmitter.inc"
void M68kMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &Op, void M68kMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &Op,
APInt &Value, unsigned InsertPos, APInt &Value,
SmallVectorImpl<MCFixup> &Fixups, SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const { const MCSubtargetInfo &STI) const {
// Register // Register

View File

@ -35,7 +35,9 @@ class MyVarInst<MyMemOperand memory_op> : Instruction {
// Testing operand referencing. // Testing operand referencing.
(operand "$dst", 4), (operand "$dst", 4),
// Testing operand referencing with a certain bit range. // Testing operand referencing with a certain bit range.
(slice "$dst", 3, 1) (slice "$dst", 3, 1),
// Testing custom encoder
(operand "$dst", 2, (encoder "myCustomEncoder"))
); );
} }
@ -55,39 +57,45 @@ def FOO16 : MyVarInst<MemOp16<"src">>;
def FOO32 : MyVarInst<MemOp32<"src">>; def FOO32 : MyVarInst<MemOp32<"src">>;
// The fixed bits part // The fixed bits part
// CHECK: {/*NumBits*/39, // CHECK: {/*NumBits*/41,
// CHECK-SAME: // FOO16 // CHECK-SAME: // FOO16
// CHECK: {/*NumBits*/55, // CHECK: {/*NumBits*/57,
// CHECK-SAME: // FOO32 // CHECK-SAME: // FOO32
// CHECK: UINT64_C(46848), // FOO16 // CHECK: UINT64_C(46848), // FOO16
// CHECK: UINT64_C(46848), // FOO32 // CHECK: UINT64_C(46848), // FOO32
// CHECK-LABEL: case ::FOO16: { // CHECK-LABEL: case ::FOO16: {
// CHECK: Scratch = Scratch.zextOrSelf(39); // CHECK: Scratch = Scratch.zextOrSelf(41);
// src.reg // src.reg
// CHECK: getMachineOpValue(MI, MI.getOperand(1), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(1), /*Pos=*/0, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(8, 0), 0); // CHECK: Inst.insertBits(Scratch.extractBits(8, 0), 0);
// src.offset // src.offset
// CHECK: getMachineOpValue(MI, MI.getOperand(2), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(2), /*Pos=*/16, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(16, 0), 16); // CHECK: Inst.insertBits(Scratch.extractBits(16, 0), 16);
// 1st dst // 1st dst
// CHECK: getMachineOpValue(MI, MI.getOperand(0), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(0), /*Pos=*/32, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(4, 0), 32); // CHECK: Inst.insertBits(Scratch.extractBits(4, 0), 32);
// 2nd dst // 2nd dst
// CHECK: getMachineOpValue(MI, MI.getOperand(0), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(0), /*Pos=*/36, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(3, 1), 36); // CHECK: Inst.insertBits(Scratch.extractBits(3, 1), 36);
// dst w/ custom encoder
// CHECK: myCustomEncoder(MI, /*OpIdx=*/0, /*Pos=*/39, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(2, 0), 39);
// CHECK-LABEL: case ::FOO32: { // CHECK-LABEL: case ::FOO32: {
// CHECK: Scratch = Scratch.zextOrSelf(55); // CHECK: Scratch = Scratch.zextOrSelf(57);
// src.reg // src.reg
// CHECK: getMachineOpValue(MI, MI.getOperand(1), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(1), /*Pos=*/0, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(8, 0), 0); // CHECK: Inst.insertBits(Scratch.extractBits(8, 0), 0);
// src.offset // src.offset
// CHECK: getMachineOpValue(MI, MI.getOperand(2), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(2), /*Pos=*/16, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(32, 0), 16); // CHECK: Inst.insertBits(Scratch.extractBits(32, 0), 16);
// 1st dst // 1st dst
// CHECK: getMachineOpValue(MI, MI.getOperand(0), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(0), /*Pos=*/48, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(4, 0), 48); // CHECK: Inst.insertBits(Scratch.extractBits(4, 0), 48);
// 2nd dst // 2nd dst
// CHECK: getMachineOpValue(MI, MI.getOperand(0), Scratch, Fixups, STI); // CHECK: getMachineOpValue(MI, MI.getOperand(0), /*Pos=*/52, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(3, 1), 52); // CHECK: Inst.insertBits(Scratch.extractBits(3, 1), 52);
// dst w/ custom encoder
// CHECK: myCustomEncoder(MI, /*OpIdx=*/0, /*Pos=*/55, Scratch, Fixups, STI);
// CHECK: Inst.insertBits(Scratch.extractBits(2, 0), 55);

View File

@ -66,17 +66,30 @@ namespace {
class VarLenCodeEmitterGen { class VarLenCodeEmitterGen {
RecordKeeper &Records; RecordKeeper &Records;
struct EncodingSegment {
unsigned BitWidth;
const Init *Value;
StringRef CustomEncoder = "";
};
class VarLenInst { class VarLenInst {
size_t NumBits; size_t NumBits;
// Set if any of the segment is not fixed value. // Set if any of the segment is not fixed value.
bool HasDynamicSegment; bool HasDynamicSegment;
// {Number of bits, Value} SmallVector<EncodingSegment, 4> Segments;
SmallVector<std::pair<unsigned, const Init *>, 4> Segments;
void buildRec(const DagInit *DI); void buildRec(const DagInit *DI);
StringRef getCustomEncoderName(const Init *EI) const {
if (const auto *DI = dyn_cast<DagInit>(EI)) {
if (DI->getNumArgs() && isa<StringInit>(DI->getArg(0)))
return cast<StringInit>(DI->getArg(0))->getValue();
}
return "";
}
public: public:
VarLenInst() : NumBits(0U), HasDynamicSegment(false) {} VarLenInst() : NumBits(0U), HasDynamicSegment(false) {}
@ -117,7 +130,7 @@ public:
VarLenCodeEmitterGen::VarLenInst::VarLenInst(const DagInit *DI) : NumBits(0U) { VarLenCodeEmitterGen::VarLenInst::VarLenInst(const DagInit *DI) : NumBits(0U) {
buildRec(DI); buildRec(DI);
for (const auto &S : Segments) for (const auto &S : Segments)
NumBits += S.first; NumBits += S.BitWidth;
} }
void VarLenCodeEmitterGen::VarLenInst::buildRec(const DagInit *DI) { void VarLenCodeEmitterGen::VarLenInst::buildRec(const DagInit *DI) {
@ -146,9 +159,9 @@ void VarLenCodeEmitterGen::VarLenInst::buildRec(const DagInit *DI) {
} }
} }
} else if (Op == "operand") { } else if (Op == "operand") {
// (operand <operand name>, <# of bits>) // (operand <operand name>, <# of bits>, [(encoder <custom encoder>)])
if (DI->getNumArgs() != 2) if (DI->getNumArgs() < 2)
PrintFatalError("Expecting 2 arguments for `operand`"); PrintFatalError("Expecting at least 2 arguments for `operand`");
HasDynamicSegment = true; HasDynamicSegment = true;
const Init *OperandName = DI->getArg(0), *NumBits = DI->getArg(1); const Init *OperandName = DI->getArg(0), *NumBits = DI->getArg(1);
if (!isa<StringInit>(OperandName) || !isa<IntInit>(NumBits)) if (!isa<StringInit>(OperandName) || !isa<IntInit>(NumBits))
@ -158,11 +171,16 @@ void VarLenCodeEmitterGen::VarLenInst::buildRec(const DagInit *DI) {
if (NumBitsVal <= 0) if (NumBitsVal <= 0)
PrintFatalError("Invalid number of bits for `operand`"); PrintFatalError("Invalid number of bits for `operand`");
Segments.push_back({NumBitsVal, OperandName}); StringRef CustomEncoder;
if (DI->getNumArgs() >= 3)
CustomEncoder = getCustomEncoderName(DI->getArg(2));
Segments.push_back(
{static_cast<unsigned>(NumBitsVal), OperandName, CustomEncoder});
} else if (Op == "slice") { } else if (Op == "slice") {
// (slice <operand name>, <high / low bit>, <low / high bit>) // (slice <operand name>, <high / low bit>, <low / high bit>,
if (DI->getNumArgs() != 3) // [(encoder <custom encoder>)])
PrintFatalError("Expecting 3 arguments for `slice`"); if (DI->getNumArgs() < 3)
PrintFatalError("Expecting at least 3 arguments for `slice`");
HasDynamicSegment = true; HasDynamicSegment = true;
Init *OperandName = DI->getArg(0), *HiBit = DI->getArg(1), Init *OperandName = DI->getArg(0), *HiBit = DI->getArg(1),
*LoBit = DI->getArg(2); *LoBit = DI->getArg(2);
@ -183,13 +201,18 @@ void VarLenCodeEmitterGen::VarLenInst::buildRec(const DagInit *DI) {
NumBits = static_cast<unsigned>(HiBitVal - LoBitVal + 1); NumBits = static_cast<unsigned>(HiBitVal - LoBitVal + 1);
} }
StringRef CustomEncoder;
if (DI->getNumArgs() >= 4)
CustomEncoder = getCustomEncoderName(DI->getArg(3));
if (NeedSwap) { if (NeedSwap) {
// Normalization: Hi bit should always be the second argument. // Normalization: Hi bit should always be the second argument.
Init *const NewArgs[] = {OperandName, LoBit, HiBit}; Init *const NewArgs[] = {OperandName, LoBit, HiBit};
Segments.push_back( Segments.push_back({NumBits,
{NumBits, DagInit::get(DI->getOperator(), nullptr, NewArgs, {})}); DagInit::get(DI->getOperator(), nullptr, NewArgs, {}),
CustomEncoder});
} else { } else {
Segments.push_back({NumBits, DI}); Segments.push_back({NumBits, DI, CustomEncoder});
} }
} }
} }
@ -372,14 +395,14 @@ void VarLenCodeEmitterGen::emitInstructionBaseValues(
auto SI = VLI.begin(), SE = VLI.end(); auto SI = VLI.begin(), SE = VLI.end();
// Scan through all the segments that have fixed-bits values. // Scan through all the segments that have fixed-bits values.
while (i < BitWidth && SI != SE) { while (i < BitWidth && SI != SE) {
unsigned SegmentNumBits = SI->first; unsigned SegmentNumBits = SI->BitWidth;
if (const auto *BI = dyn_cast<BitsInit>(SI->second)) { if (const auto *BI = dyn_cast<BitsInit>(SI->Value)) {
for (unsigned Idx = 0U; Idx != SegmentNumBits; ++Idx) { for (unsigned Idx = 0U; Idx != SegmentNumBits; ++Idx) {
auto *B = cast<BitInit>(BI->getBit(Idx)); auto *B = cast<BitInit>(BI->getBit(Idx));
Value.setBitVal(i + Idx, B->getValue()); Value.setBitVal(i + Idx, B->getValue());
} }
} }
if (const auto *BI = dyn_cast<BitInit>(SI->second)) if (const auto *BI = dyn_cast<BitInit>(SI->Value))
Value.setBitVal(i, BI->getValue()); Value.setBitVal(i, BI->getValue());
i += SegmentNumBits; i += SegmentNumBits;
@ -439,9 +462,9 @@ std::string VarLenCodeEmitterGen::getInstructionCaseForEncoding(
// Process each segment in VLI. // Process each segment in VLI.
size_t Offset = 0U; size_t Offset = 0U;
for (const auto &Pair : VLI) { for (const auto &ES : VLI) {
unsigned NumBits = Pair.first; unsigned NumBits = ES.BitWidth;
const Init *Val = Pair.second; const Init *Val = ES.Value;
// If it's a StringInit or DagInit, it's a reference to an operand // If it's a StringInit or DagInit, it's a reference to an operand
// or part of an operand. // or part of an operand.
if (isa<StringInit>(Val) || isa<DagInit>(Val)) { if (isa<StringInit>(Val) || isa<DagInit>(Val)) {
@ -458,15 +481,20 @@ std::string VarLenCodeEmitterGen::getInstructionCaseForEncoding(
auto OpIdx = CGI.Operands.ParseOperandName(OperandName); auto OpIdx = CGI.Operands.ParseOperandName(OperandName);
unsigned FlatOpIdx = CGI.Operands.getFlattenedOperandNumber(OpIdx); unsigned FlatOpIdx = CGI.Operands.getFlattenedOperandNumber(OpIdx);
StringRef EncoderMethodName = "getMachineOpValue"; StringRef CustomEncoder = CGI.Operands[OpIdx.first].EncoderMethodName;
auto &CustomEncoder = CGI.Operands[OpIdx.first].EncoderMethodName; if (ES.CustomEncoder.size())
if (!CustomEncoder.empty()) CustomEncoder = ES.CustomEncoder;
EncoderMethodName = CustomEncoder;
SS.indent(6) << "Scratch.clearAllBits();\n"; SS.indent(6) << "Scratch.clearAllBits();\n";
SS.indent(6) << "// op: " << OperandName.drop_front(1) << "\n"; SS.indent(6) << "// op: " << OperandName.drop_front(1) << "\n";
SS.indent(6) << EncoderMethodName << "(MI, MI.getOperand(" if (CustomEncoder.empty())
<< utostr(FlatOpIdx) << "), Scratch, Fixups, STI);\n"; SS.indent(6) << "getMachineOpValue(MI, MI.getOperand("
<< utostr(FlatOpIdx) << ")";
else
SS.indent(6) << CustomEncoder << "(MI, /*OpIdx=*/" << utostr(FlatOpIdx);
SS << ", /*Pos=*/" << utostr(Offset) << ", Scratch, Fixups, STI);\n";
SS.indent(6) << "Inst.insertBits(" SS.indent(6) << "Inst.insertBits("
<< "Scratch.extractBits(" << utostr(NumBits) << ", " << "Scratch.extractBits(" << utostr(NumBits) << ", "
<< utostr(LoBit) << ")" << utostr(LoBit) << ")"