[TableGen] Support custom decoders for variable length instructions
authorMin-Yih Hsu <minyihh@uci.edu>
Mon, 21 Nov 2022 06:35:27 +0000 (22:35 -0800)
committerMin-Yih Hsu <minyihh@uci.edu>
Wed, 25 Jan 2023 05:59:24 +0000 (21:59 -0800)
Just like the encoder directive for variable-length instructions, this
patch adds a new decoder directive to allow custom decoder function on
an operand.

Right now, due to the design of DecoderEmitter each operand can only
have a single custom decoder in a given instruction.

Differential Revision: https://reviews.llvm.org/D142079

llvm/include/llvm/Target/Target.td
llvm/test/TableGen/VarLenDecoder.td
llvm/utils/TableGen/DecoderEmitter.cpp
llvm/utils/TableGen/VarLenCodeEmitterGen.cpp
llvm/utils/TableGen/VarLenCodeEmitterGen.h

index dccb146..181c8eb 100644 (file)
@@ -796,11 +796,17 @@ def operand;
 /// Both DAG represent bit 6 to 8 (total of 3 bits) in the encoding of operand
 /// `$src`.
 def slice;
-/// You can use `encoder` to specify a custom encoder function for a specific
-/// `operand` or `encoder` directive. For example:
+/// You can use `encoder` or `decoder` to specify a custom encoder or decoder
+/// function for a specific `operand` or `slice` directive. For example:
 ///     (operand "$src", 4, (encoder "encodeMyImm"))
 ///     (slice "$src", 8, 6, (encoder "encodeMyReg"))
+///     (operand "$src", 4, (encoder "encodeMyImm"), (decoder "decodeMyImm"))
+/// The ordering of `encoder` and `decoder` in the same `operand` or `slice`
+/// doesn't matter.
+/// Note that currently we cannot assign different decoders in the same
+/// (instruction) operand.
 def encoder;
+def decoder;
 
 /// PointerLikeRegClass - Values that are designed to have pointer width are
 /// derived from this.  TableGen treats the register class as having a symbolic
index 512cae8..0d36d14 100644 (file)
@@ -34,12 +34,13 @@ class MyVarInst<MyMemOperand memory_op> : Instruction {
 def FOO16 : MyVarInst<MemOp16> {
   let Inst = (ascend
       (descend (operand "$dst", 3), 0b01000, (operand "$src.reg", 3)),
-      (slice "$src.offset", 15, 0)
+      (slice "$src.offset", 15, 0, (decoder "myCustomDecoder"))
   );
 }
 def FOO32 : MyVarInst<MemOp32> {
   let Inst = (ascend
-      (descend (operand "$dst", 3), 0b01001, (operand "$src.reg", 3)),
+      (descend (operand "$dst", 3), 0b01001,
+      (operand "$src.reg", 3, (decoder "myCustomDecoder"))),
       (slice "$src.offset", 31, 16),
       (slice "$src.offset", 15, 0)
   );
@@ -63,13 +64,13 @@ def FOO32 : MyVarInst<MemOp32> {
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
 // CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 11, 16);
-// CHECK-NEXT: MI.addOperand(MCOperand::createImm(tmp));
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
 // CHECK-NEXT: return S;
 // CHECK-NEXT: case 1:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
 // CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
 // CHECK-NEXT: tmp = 0x0;
 // CHECK-NEXT: insertBits(tmp, fieldFromInstruction(insn, 11, 16), 16, 16);
 // CHECK-NEXT: insertBits(tmp, fieldFromInstruction(insn, 27, 16), 0, 16);
index fef574b..8f81674 100644 (file)
@@ -1893,6 +1893,8 @@ void parseVarLenInstOperand(const Record &Def,
               OpName);
       unsigned OpIdx = CGI.Operands.getFlattenedOperandNumber(OpSubOpPair);
       Operands[OpIdx].addField(CurrBitPos, EncodingSegment.BitWidth, Offset);
+      if (!EncodingSegment.CustomDecoder.empty())
+        Operands[OpIdx].Decoder = EncodingSegment.CustomDecoder.str();
 
       int TiedReg = TiedTo[OpSubOpPair.first];
       if (TiedReg != -1) {
index 8120122..2c1acd8 100644 (file)
@@ -83,9 +83,35 @@ public:
 
   void run(raw_ostream &OS);
 };
-
 } // end anonymous namespace
 
+// Get the name of custom encoder or decoder, if there is any.
+// Returns `{encoder name, decoder name}`.
+static std::pair<StringRef, StringRef> getCustomCoders(ArrayRef<Init *> Args) {
+  std::pair<StringRef, StringRef> Result;
+  for (const auto *Arg : Args) {
+    const auto *DI = dyn_cast<DagInit>(Arg);
+    if (!DI)
+      continue;
+    const Init *Op = DI->getOperator();
+    if (!isa<DefInit>(Op))
+      continue;
+    // syntax: `(<encoder | decoder> "function name")`
+    StringRef OpName = cast<DefInit>(Op)->getDef()->getName();
+    if (OpName != "encoder" && OpName != "decoder")
+      continue;
+    if (!DI->getNumArgs() || !isa<StringInit>(DI->getArg(0)))
+      PrintFatalError("expected '" + OpName +
+                      "' directive to be followed by a custom function name.");
+    StringRef FuncName = cast<StringInit>(DI->getArg(0))->getValue();
+    if (OpName == "encoder")
+      Result.first = FuncName;
+    else
+      Result.second = FuncName;
+  }
+  return Result;
+}
+
 VarLenInst::VarLenInst(const DagInit *DI, const RecordVal *TheDef)
     : TheDef(TheDef), NumBits(0U) {
   buildRec(DI);
@@ -123,7 +149,8 @@ void VarLenInst::buildRec(const DagInit *DI) {
       }
     }
   } else if (Op == "operand") {
-    // (operand <operand name>, <# of bits>, [(encoder <custom encoder>)])
+    // (operand <operand name>, <# of bits>,
+    //          [(encoder <custom encoder>)][, (decoder <custom decoder>)])
     if (DI->getNumArgs() < 2)
       PrintFatalError(TheDef->getLoc(),
                       "Expecting at least 2 arguments for `operand`");
@@ -136,14 +163,13 @@ void VarLenInst::buildRec(const DagInit *DI) {
     if (NumBitsVal <= 0)
       PrintFatalError(TheDef->getLoc(), "Invalid number of bits for `operand`");
 
-    StringRef CustomEncoder;
-    if (DI->getNumArgs() >= 3)
-      CustomEncoder = getCustomEncoderName(DI->getArg(2));
-    Segments.push_back(
-        {static_cast<unsigned>(NumBitsVal), OperandName, CustomEncoder});
+    auto [CustomEncoder, CustomDecoder] =
+        getCustomCoders(DI->getArgs().slice(2));
+    Segments.push_back({static_cast<unsigned>(NumBitsVal), OperandName,
+                        CustomEncoder, CustomDecoder});
   } else if (Op == "slice") {
     // (slice <operand name>, <high / low bit>, <low / high bit>,
-    //        [(encoder <custom encoder>)])
+    //        [(encoder <custom encoder>)][, (decoder <custom decoder>)])
     if (DI->getNumArgs() < 3)
       PrintFatalError(TheDef->getLoc(),
                       "Expecting at least 3 arguments for `slice`");
@@ -167,18 +193,17 @@ void VarLenInst::buildRec(const DagInit *DI) {
       NumBits = static_cast<unsigned>(HiBitVal - LoBitVal + 1);
     }
 
-    StringRef CustomEncoder;
-    if (DI->getNumArgs() >= 4)
-      CustomEncoder = getCustomEncoderName(DI->getArg(3));
+    auto [CustomEncoder, CustomDecoder] =
+        getCustomCoders(DI->getArgs().slice(3));
 
     if (NeedSwap) {
       // Normalization: Hi bit should always be the second argument.
       Init *const NewArgs[] = {OperandName, LoBit, HiBit};
       Segments.push_back({NumBits,
                           DagInit::get(DI->getOperator(), nullptr, NewArgs, {}),
-                          CustomEncoder});
+                          CustomEncoder, CustomDecoder});
     } else {
-      Segments.push_back({NumBits, DI, CustomEncoder});
+      Segments.push_back({NumBits, DI, CustomEncoder, CustomDecoder});
     }
   }
 }
index 5bdedee..2b55fd1 100644 (file)
@@ -22,6 +22,7 @@ struct EncodingSegment {
   unsigned BitWidth;
   const Init *Value;
   StringRef CustomEncoder = "";
+  StringRef CustomDecoder = "";
 };
 
 class VarLenInst {
@@ -35,14 +36,6 @@ class VarLenInst {
 
   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:
   VarLenInst() : TheDef(nullptr), NumBits(0U), HasDynamicSegment(false) {}