[TableGen] More named sub-operands work.
Commit a538d1f13a first added support for named sub-operands in
CodeEmitterGen. We now add a few more features to that, enabling
further target cleanups.
1. Adds support for handling an EncoderMethod in a sub-operand in
CodeEmitterGen. Previously, the specified encoder of a sub-operand was
ignored, and only the default used.
2. Adds support for sub-operands in DecoderEmitter, along with support
for tied sub-operands.
The changes to the decoder required a few minor tweaks to a few
targets, where existing brokeness was exposed. In order to keep this
patch small, I left FIXMEs which will be addressed in upcoming
patches. (Except MIPS16, since its object file emission/decoding is
totally broken).
Differential Revision: https://reviews.llvm.org/D137653
This commit is contained in:
@@ -48,12 +48,6 @@ public:
|
||||
bool SelectAddrModeS9(SDValue Addr, SDValue &Base, SDValue &Offset);
|
||||
bool SelectAddrModeImm(SDValue Addr, SDValue &Base, SDValue &Offset);
|
||||
bool SelectAddrModeFar(SDValue Addr, SDValue &Base, SDValue &Offset);
|
||||
bool SelectCMOVPred(SDValue N, SDValue &Pred, SDValue &Reg) {
|
||||
const ConstantSDNode *CN = cast<ConstantSDNode>(N);
|
||||
Pred = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), MVT::i32);
|
||||
Reg = CurDAG->getRegister(ARC::STATUS32, MVT::i32);
|
||||
return true;
|
||||
}
|
||||
|
||||
StringRef getPassName() const override {
|
||||
return "ARC DAG->DAG Pattern Instruction Selection";
|
||||
|
||||
@@ -964,12 +964,6 @@ class F16_OP_U7<bit i, string asmstr> :
|
||||
}
|
||||
|
||||
// Special types for different instruction operands.
|
||||
def cmovpred : Operand<i32>, PredicateOp,
|
||||
ComplexPattern<i32, 2, "SelectCMOVPred"> {
|
||||
let MIOperandInfo = (ops i32imm, i32imm);
|
||||
let PrintMethod = "printPredicateOperand";
|
||||
}
|
||||
|
||||
def ccond : Operand<i32> {
|
||||
let MIOperandInfo = (ops i32imm);
|
||||
let PrintMethod = "printPredicateOperand";
|
||||
|
||||
@@ -395,9 +395,9 @@ def cmov : PatFrag<(ops node:$op1, node:$op2, node:$cc),
|
||||
let Uses = [STATUS32], isAsCheapAsAMove = 1, isPredicable=1,
|
||||
isReMaterializable = 0, Constraints = "$B = $B2" in {
|
||||
def MOV_cc : F32_DOP_CC_RR<0b00100, 0b001010, 0,
|
||||
(outs GPR32:$B), (ins GPR32:$C, GPR32:$B2, cmovpred:$cc),
|
||||
(outs GPR32:$B), (ins GPR32:$C, GPR32:$B2, CCOp:$cc),
|
||||
"mov.$cc\t$B, $C",
|
||||
[(set GPR32:$B, (cmov i32:$C, i32:$B2, cmovpred:$cc))]>;
|
||||
[(set GPR32:$B, (cmov i32:$C, i32:$B2, timm:$cc))]>;
|
||||
|
||||
def MOV_cc_ru6 : F32_DOP_CC_RU6<0b00100, 0b001010, 0,
|
||||
(outs GPR32:$B), (ins u6:$C, CCOp:$cc, GPR32:$B2),
|
||||
|
||||
@@ -273,6 +273,7 @@ def vpred_r : vpred_ops<(ops (v4i32 undef_tied_input)), (ops MQPR:$inactive)> {
|
||||
def vpred_n : vpred_ops<(ops), (ops)> {
|
||||
let ParserMatchClass = VPTPredNOperand;
|
||||
let OperandType = "OPERAND_VPRED_N";
|
||||
let DecoderMethod = "DecodeVpredNOperand";
|
||||
let vpred_constraint = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -629,6 +629,9 @@ static DecodeStatus DecodeVPTMaskOperand(MCInst &Inst, unsigned Val,
|
||||
static DecodeStatus DecodeVpredROperand(MCInst &Inst, unsigned Val,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
static DecodeStatus DecodeVpredNOperand(MCInst &Inst, unsigned Val,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
static DecodeStatus
|
||||
DecodeRestrictedIPredicateOperand(MCInst &Inst, unsigned Val, uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
@@ -6544,6 +6547,17 @@ static DecodeStatus DecodeVpredROperand(MCInst &Inst, unsigned RegNo,
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static DecodeStatus
|
||||
DecodeVpredNOperand(MCInst &Inst, unsigned RegNo, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
// Similar to above, we want to ensure that no operands are added for the
|
||||
// vpred operands. (This is marked "maybe_unused" for the moment; because
|
||||
// DecoderEmitter currently (wrongly) omits operands with no instruction bits,
|
||||
// the decoder doesn't actually call it yet. That will be addressed in a
|
||||
// future change.)
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
static DecodeStatus
|
||||
DecodeRestrictedIPredicateOperand(MCInst &Inst, unsigned Val, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
|
||||
@@ -486,6 +486,10 @@ static DecodeStatus DecodeMovePOperands(MCInst &Inst, unsigned Insn,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
|
||||
static DecodeStatus DecodeFIXMEInstruction(MCInst &Inst, unsigned Insn,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
|
||||
static MCDisassembler *createMipsDisassembler(
|
||||
const Target &T,
|
||||
const MCSubtargetInfo &STI,
|
||||
@@ -2513,3 +2517,12 @@ static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst &MI, InsnType insn,
|
||||
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
// This instruction does not have a working decoder, and needs to be
|
||||
// fixed. This "fixme" function was introduced to keep the backend compiling,
|
||||
// while making changes to tablegen code.
|
||||
static DecodeStatus DecodeFIXMEInstruction(MCInst &Inst, unsigned Insn,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
return MCDisassembler::Fail;
|
||||
}
|
||||
|
||||
@@ -537,6 +537,7 @@ def AddiuRxRxImmX16: FEXT_2RI16_ins<0b01001, "addiu", IIM16Alu>,
|
||||
let isCodeGenOnly = 1;
|
||||
}
|
||||
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def AddiuRxRyOffMemX16:
|
||||
FEXT_RRI_A16_mem_ins<0, "addiu", mem16_ea, IIM16Alu>;
|
||||
|
||||
@@ -850,6 +851,7 @@ def LwRxRyOffMemX16: FEXT_RRI16_mem_ins<0b10011, "lw", mem16, II_LW>, MayLoad{
|
||||
// Purpose: Load Word (SP-Relative, Extended)
|
||||
// To load an SP-relative word from memory as a signed value.
|
||||
//
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def LwRxSpImmX16: FEXT_RRI16_mem_ins<0b10010, "lw", mem16sp, II_LW>, MayLoad;
|
||||
|
||||
def LwRxPcTcp16: FRI16_TCP_ins<0b10110, "lw", II_LW>, MayLoad;
|
||||
@@ -1006,6 +1008,7 @@ def SaveX16:
|
||||
// Purpose: Store Byte (Extended)
|
||||
// To store a byte to memory.
|
||||
//
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def SbRxRyOffMemX16:
|
||||
FEXT_RRI16_mem2_ins<0b11000, "sb", mem16, II_SB>, MayStore;
|
||||
|
||||
@@ -1144,6 +1147,7 @@ def SelTBtneZSltiu: SeliT<"btnez", "sltiu">;
|
||||
// Purpose: Store Halfword (Extended)
|
||||
// To store a halfword to memory.
|
||||
//
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def ShRxRyOffMemX16:
|
||||
FEXT_RRI16_mem2_ins<0b11001, "sh", mem16, II_SH>, MayStore;
|
||||
|
||||
@@ -1280,6 +1284,7 @@ def SubuRxRyRz16: FRRR16_ins<0b11, "subu", IIM16Alu>, ArithLogic16Defs<0>;
|
||||
// Purpose: Store Word (Extended)
|
||||
// To store a word to memory.
|
||||
//
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def SwRxRyOffMemX16: FEXT_RRI16_mem2_ins<0b11011, "sw", mem16, II_SW>, MayStore;
|
||||
|
||||
//
|
||||
@@ -1287,6 +1292,7 @@ def SwRxRyOffMemX16: FEXT_RRI16_mem2_ins<0b11011, "sw", mem16, II_SW>, MayStore;
|
||||
// Purpose: Store Word rx (SP-Relative)
|
||||
// To store an SP-relative word to memory.
|
||||
//
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in
|
||||
def SwRxSpImmX16: FEXT_RRI16_mem2_ins<0b11010, "sw", mem16sp, II_SW>, MayStore;
|
||||
|
||||
//
|
||||
|
||||
@@ -306,6 +306,9 @@ static DecodeStatus DecodeSWAP(MCInst &Inst, unsigned insn, uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
static DecodeStatus DecodeTRAP(MCInst &Inst, unsigned insn, uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
static DecodeStatus DecodeFIXMEInstruction(MCInst &MI, unsigned insn,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder);
|
||||
|
||||
#include "SparcGenDisassemblerTables.inc"
|
||||
|
||||
@@ -591,6 +594,15 @@ static DecodeStatus DecodeReturn(MCInst &MI, unsigned insn, uint64_t Address,
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
// This instruction does not have a working decoder, and needs to be
|
||||
// fixed. This "fixme" function was introduced to keep the backend compiling,
|
||||
// while making changes to tablegen code.
|
||||
static DecodeStatus DecodeFIXMEInstruction(MCInst &Inst, unsigned Insn,
|
||||
uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
return MCDisassembler::Fail;
|
||||
}
|
||||
|
||||
static DecodeStatus DecodeSWAP(MCInst &MI, unsigned insn, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
|
||||
|
||||
@@ -419,6 +419,7 @@ multiclass LoadA<string OpcStr, bits<6> Op3Val, bits<6> LoadAOp3Val,
|
||||
// The LDSTUB instruction is supported for asm only.
|
||||
// It is unlikely that general-purpose code could make use of it.
|
||||
// CAS is preferred for sparc v9.
|
||||
let DecoderMethod = "DecodeFIXMEInstruction" in {
|
||||
def LDSTUBrr : F3_1<3, 0b001101, (outs IntRegs:$rd), (ins (MEMrr $rs1, $rs2):$addr),
|
||||
"ldstub [$addr], $rd", []>;
|
||||
def LDSTUBri : F3_2<3, 0b001101, (outs IntRegs:$rd), (ins (MEMri $rs1, $simm13):$addr),
|
||||
@@ -426,6 +427,7 @@ def LDSTUBri : F3_2<3, 0b001101, (outs IntRegs:$rd), (ins (MEMri $rs1, $simm13):
|
||||
def LDSTUBArr : F3_1_asi<3, 0b011101, (outs IntRegs:$rd),
|
||||
(ins (MEMrr $rs1, $rs2):$addr, i8imm:$asi),
|
||||
"ldstuba [$addr] $asi, $rd", []>;
|
||||
}
|
||||
|
||||
// Store multiclass - Define both Reg+Reg/Reg+Imm patterns in one shot.
|
||||
multiclass Store<string OpcStr, bits<6> Op3Val, SDPatternOperator OpNode,
|
||||
@@ -1195,7 +1197,7 @@ let rd = 0 in
|
||||
"unimp $imm22", []>;
|
||||
|
||||
// Section B.32 - Flush Instruction Memory
|
||||
let rd = 0 in {
|
||||
let DecoderMethod = "DecodeFIXMEInstruction", rd = 0 in {
|
||||
def FLUSHrr : F3_1<2, 0b111011, (outs), (ins (MEMrr $rs1, $rs2):$addr),
|
||||
"flush $addr", []>;
|
||||
def FLUSHri : F3_2<2, 0b111011, (outs), (ins (MEMri $rs1, $simm13):$addr),
|
||||
@@ -1759,7 +1761,7 @@ let hasSideEffects = 1 in {
|
||||
}
|
||||
|
||||
// Section A.42 - Prefetch Data
|
||||
let Predicates = [HasV9] in {
|
||||
let DecoderMethod = "DecodeFIXMEInstruction", Predicates = [HasV9] in {
|
||||
def PREFETCHr : F3_1<3, 0b101101,
|
||||
(outs), (ins (MEMrr $rs1, $rs2):$addr, shift_imm5:$rd),
|
||||
"prefetch [$addr], $rd", []>;
|
||||
|
||||
63
llvm/test/TableGen/FixedLenDecoderEmitter/MultiOps.td
Normal file
63
llvm/test/TableGen/FixedLenDecoderEmitter/MultiOps.td
Normal file
@@ -0,0 +1,63 @@
|
||||
// RUN: not llvm-tblgen -gen-disassembler -I %p/../../../include %s 2>&1 | FileCheck %s --implicit-check-not=error:
|
||||
|
||||
include "llvm/Target/Target.td"
|
||||
|
||||
def ArchInstrInfo : InstrInfo { }
|
||||
|
||||
def Arch : Target {
|
||||
let InstructionSet = ArchInstrInfo;
|
||||
}
|
||||
|
||||
def Reg : Register<"reg">;
|
||||
|
||||
def Regs : RegisterClass<"foo", [i32], 0, (add Reg)>;
|
||||
|
||||
def complex_nodec : Operand<i32> {
|
||||
let MIOperandInfo = (ops Regs, Regs);
|
||||
}
|
||||
|
||||
def complex_withdec : Operand<i32> {
|
||||
let MIOperandInfo = (ops Regs, Regs);
|
||||
let DecoderMethod = "DecodeComplex";
|
||||
}
|
||||
|
||||
class ArchInstr : Instruction {
|
||||
let Size = 1;
|
||||
bits<8> Inst;
|
||||
}
|
||||
|
||||
// This definition is broken in both directions:
|
||||
// 1. Uses a complex operand without a decoder, and without named sub-ops.
|
||||
// 2. Uses a complex operand with named sub-ops, but with a decoder as well.
|
||||
|
||||
// CHECK: error: DecoderEmitter: operand "r1c" uses MIOperandInfo with multiple ops, but doesn't have a custom decoder!
|
||||
// CHECK: note: Dumping record for previous error:
|
||||
// CHECK: error: DecoderEmitter: operand "r1ab" has type "complex_withdec" with a custom DecoderMethod, but also named sub-operands.
|
||||
def foo1 : ArchInstr {
|
||||
bits<2> r1a;
|
||||
bits<2> r1b;
|
||||
bits<2> r1c;
|
||||
|
||||
let Inst{1-0} = r1a;
|
||||
let Inst{3-2} = r1b;
|
||||
let Inst{5-4} = r1c;
|
||||
let Inst{7-6} = 0b00;
|
||||
|
||||
let OutOperandList = (outs complex_nodec:$r1c);
|
||||
let InOperandList = (ins (complex_withdec $r1a, $r1b):$r1ab);
|
||||
}
|
||||
|
||||
// This definition has no errors.
|
||||
def foo2 : ArchInstr {
|
||||
bits<2> r2a;
|
||||
bits<2> r2b;
|
||||
bits<2> r2c;
|
||||
|
||||
let Inst{1-0} = r2a;
|
||||
let Inst{3-2} = r2b;
|
||||
let Inst{5-4} = r2c;
|
||||
let Inst{7-6} = 0b01;
|
||||
|
||||
let OutOperandList = (outs complex_withdec:$r2c);
|
||||
let InOperandList = (ins (complex_nodec $r2a, $r2b):$r2ab);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ def RegOperand : RegisterOperand<RegClass> {
|
||||
let EncoderMethod = "barEncoder";
|
||||
}
|
||||
|
||||
def foo : Instruction {
|
||||
def foo1 : Instruction {
|
||||
let Size = 1;
|
||||
|
||||
let OutOperandList = (outs);
|
||||
@@ -28,9 +28,39 @@ def foo : Instruction {
|
||||
bits<8> Inst = bar;
|
||||
}
|
||||
|
||||
// CHECK: case ::foo: {
|
||||
// CHECK: case ::foo1: {
|
||||
// CHECK: op = barEncoder
|
||||
// CHECK: op &= UINT64_C(255);
|
||||
// CHECK: Value |= op;
|
||||
// CHECK: break;
|
||||
// CHECK: }
|
||||
|
||||
|
||||
// Also check that it works from a complex operand.
|
||||
|
||||
def RegPair : Operand<i32> {
|
||||
let MIOperandInfo = (ops RegOperand, RegOperand);
|
||||
}
|
||||
|
||||
def foo2 : Instruction {
|
||||
let Size = 1;
|
||||
|
||||
let OutOperandList = (outs);
|
||||
let InOperandList = (ins (RegPair $r1, $r2):$r12);
|
||||
|
||||
bits<4> r1;
|
||||
bits<4> r2;
|
||||
bits<8> Inst;
|
||||
let Inst{3-0} = r1;
|
||||
let Inst{7-4} = r2;
|
||||
}
|
||||
|
||||
// CHECK: case ::foo2: {
|
||||
// CHECK: op = barEncoder
|
||||
// CHECK: op &= UINT64_C(15);
|
||||
// CHECK: Value |= op;
|
||||
// CHECK: op = barEncoder
|
||||
// CHECK: op &= UINT64_C(15);
|
||||
// CHECK: Value |= op;
|
||||
// CHECK: break;
|
||||
// CHECK: }
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "llvm/TableGen/Error.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
#include "llvm/TableGen/TableGenBackend.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -113,8 +112,6 @@ bool CodeEmitterGen::addCodeToMergeInOperand(Record *R, BitsInit *BI,
|
||||
} else if (CGI.Operands.hasOperandNamed(VarName, OpIdx)) {
|
||||
// Get the machine operand number for the indicated operand.
|
||||
OpIdx = CGI.Operands[OpIdx].MIOperandNo;
|
||||
assert(!CGI.Operands.isFlatOperandNotEmitted(OpIdx) &&
|
||||
"Explicitly used operand also marked as not emitted!");
|
||||
} else {
|
||||
// Fall back to positional lookup. By default, we now disable positional
|
||||
// lookup (and print an error, below), but even so, we'll do the lookup to
|
||||
@@ -164,30 +161,30 @@ bool CodeEmitterGen::addCodeToMergeInOperand(Record *R, BitsInit *BI,
|
||||
}
|
||||
}
|
||||
|
||||
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].EncoderMethodName;
|
||||
std::string &EncoderMethodName =
|
||||
CGI.Operands[SO.first].EncoderMethodNames[SO.second];
|
||||
|
||||
if (UseAPInt)
|
||||
Case += " op.clearAllBits();\n";
|
||||
|
||||
// If the source operand has a custom encoder, use it. This will
|
||||
// get the encoding for all of the suboperands.
|
||||
Case += " // op: " + VarName + "\n";
|
||||
|
||||
// If the source operand has a custom encoder, use it.
|
||||
if (!EncoderMethodName.empty()) {
|
||||
// A custom encoder has all of the information for the
|
||||
// sub-operands, if there are more than one, so only
|
||||
// query the encoder once per source operand.
|
||||
if (SO.second == 0) {
|
||||
Case += " // op: " + VarName + "\n";
|
||||
if (UseAPInt) {
|
||||
Case += " " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
||||
Case += ", op";
|
||||
} else {
|
||||
Case += " op = " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
||||
}
|
||||
Case += ", Fixups, STI);\n";
|
||||
if (UseAPInt) {
|
||||
Case += " " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
||||
Case += ", op";
|
||||
} else {
|
||||
Case += " op = " + EncoderMethodName + "(MI, " + utostr(OpIdx);
|
||||
}
|
||||
Case += ", Fixups, STI);\n";
|
||||
} else {
|
||||
Case += " // op: " + VarName + "\n";
|
||||
if (UseAPInt) {
|
||||
Case += " getMachineOpValue(MI, MI.getOperand(" + utostr(OpIdx) + ")";
|
||||
Case += ", op, Fixups, STI";
|
||||
|
||||
@@ -120,10 +120,11 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) {
|
||||
} else if (Rec->isSubClassOf("RegisterClass")) {
|
||||
OperandType = "OPERAND_REGISTER";
|
||||
} else if (!Rec->isSubClassOf("PointerLikeRegClass") &&
|
||||
!Rec->isSubClassOf("unknown_class"))
|
||||
!Rec->isSubClassOf("unknown_class")) {
|
||||
PrintFatalError(R->getLoc(), "Unknown operand class '" + Rec->getName() +
|
||||
"' in '" + R->getName() +
|
||||
"' instruction!");
|
||||
}
|
||||
|
||||
// Check that the operand has a name and that it's unique.
|
||||
if (ArgName.empty())
|
||||
@@ -136,6 +137,10 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) {
|
||||
Twine(i) +
|
||||
" has the same name as a previous operand!");
|
||||
|
||||
OperandInfo &OpInfo = OperandList.emplace_back(
|
||||
Rec, std::string(ArgName), std::string(PrintMethod),
|
||||
OperandNamespace + "::" + OperandType, MIOperandNo, NumOps, MIOpInfo);
|
||||
|
||||
if (SubArgDag) {
|
||||
if (SubArgDag->getNumArgs() != NumOps) {
|
||||
PrintFatalError(R->getLoc(), "In instruction '" + R->getName() +
|
||||
@@ -162,24 +167,30 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) {
|
||||
"In instruction '" + R->getName() + "', operand #" +
|
||||
Twine(i) + " sub-arg #" + Twine(j) +
|
||||
" has the same name as a previous operand!");
|
||||
|
||||
if (auto MaybeEncoderMethod =
|
||||
cast<DefInit>(MIOpInfo->getArg(j))
|
||||
->getDef()
|
||||
->getValueAsOptionalString("EncoderMethod")) {
|
||||
OpInfo.EncoderMethodNames[j] = *MaybeEncoderMethod;
|
||||
}
|
||||
|
||||
OpInfo.SubOpNames[j] = SubArgName;
|
||||
SubOpAliases[SubArgName] = std::make_pair(MIOperandNo, j);
|
||||
}
|
||||
} else if (!EncoderMethod.empty()) {
|
||||
// If we have no explicit sub-op dag, but have an top-level encoder
|
||||
// method, the single encoder will multiple sub-ops, itself.
|
||||
OpInfo.EncoderMethodNames[0] = EncoderMethod;
|
||||
for (unsigned j = 1; j < NumOps; ++j)
|
||||
OpInfo.DoNotEncode[j] = true;
|
||||
}
|
||||
|
||||
OperandList.emplace_back(
|
||||
Rec, std::string(ArgName), std::string(PrintMethod),
|
||||
std::string(EncoderMethod), OperandNamespace + "::" + OperandType,
|
||||
MIOperandNo, NumOps, MIOpInfo);
|
||||
MIOperandNo += NumOps;
|
||||
}
|
||||
|
||||
if (VariadicOuts)
|
||||
--NumDefs;
|
||||
|
||||
// Make sure the constraints list for each operand is large enough to hold
|
||||
// constraint info, even if none is present.
|
||||
for (OperandInfo &OpInfo : OperandList)
|
||||
OpInfo.Constraints.resize(OpInfo.MINumOperands);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,8 +420,6 @@ void CGIOperandList::ProcessDisableEncoding(StringRef DisableEncoding) {
|
||||
std::pair<unsigned,unsigned> Op = ParseOperandName(OpName, false);
|
||||
|
||||
// Mark the operand as not-to-be encoded.
|
||||
if (Op.second >= OperandList[Op.first].DoNotEncode.size())
|
||||
OperandList[Op.first].DoNotEncode.resize(Op.second+1);
|
||||
OperandList[Op.first].DoNotEncode[Op.second] = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,13 +84,16 @@ template <typename T> class ArrayRef;
|
||||
/// otherwise, it's empty.
|
||||
std::string Name;
|
||||
|
||||
/// The names of sub-operands, if given, otherwise empty.
|
||||
std::vector<std::string> SubOpNames;
|
||||
|
||||
/// PrinterMethodName - The method used to print operands of this type in
|
||||
/// the asmprinter.
|
||||
std::string PrinterMethodName;
|
||||
|
||||
/// EncoderMethodName - The method used to get the machine operand value
|
||||
/// for binary encoding. "getMachineOpValue" by default.
|
||||
std::string EncoderMethodName;
|
||||
/// The method used to get the machine operand value for binary
|
||||
/// encoding, per sub-operand. If empty, uses "getMachineOpValue".
|
||||
std::vector<std::string> EncoderMethodNames;
|
||||
|
||||
/// OperandType - A value from MCOI::OperandType representing the type of
|
||||
/// the operand.
|
||||
@@ -119,12 +122,12 @@ template <typename T> class ArrayRef;
|
||||
std::vector<ConstraintInfo> Constraints;
|
||||
|
||||
OperandInfo(Record *R, const std::string &N, const std::string &PMN,
|
||||
const std::string &EMN, const std::string &OT, unsigned MION,
|
||||
unsigned MINO, DagInit *MIOI)
|
||||
: Rec(R), Name(N), PrinterMethodName(PMN), EncoderMethodName(EMN),
|
||||
OperandType(OT), MIOperandNo(MION), MINumOperands(MINO),
|
||||
MIOperandInfo(MIOI) {}
|
||||
|
||||
const std::string &OT, unsigned MION, unsigned MINO,
|
||||
DagInit *MIOI)
|
||||
: Rec(R), Name(N), SubOpNames(MINO), PrinterMethodName(PMN),
|
||||
EncoderMethodNames(MINO), OperandType(OT), MIOperandNo(MION),
|
||||
MINumOperands(MINO), DoNotEncode(MINO), MIOperandInfo(MIOI),
|
||||
Constraints(MINO) {}
|
||||
|
||||
/// getTiedOperand - If this operand is tied to another one, return the
|
||||
/// other operand number. Otherwise, return -1.
|
||||
|
||||
@@ -1906,6 +1906,18 @@ void parseVarLenInstOperand(const Record &Def,
|
||||
}
|
||||
}
|
||||
|
||||
static void debugDumpRecord(const Record &Rec) {
|
||||
// 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 << Rec;
|
||||
PrintNote(E);
|
||||
}
|
||||
|
||||
/// For an operand field named OpName: populate OpInfo.InitValue with the
|
||||
/// constant-valued bit values, and OpInfo.Fields with the ranges of bits to
|
||||
/// insert from the decoded instruction.
|
||||
static void addOneOperandFields(const Record &EncodingDef, const BitsInit &Bits,
|
||||
std::map<std::string, std::string> &TiedNames,
|
||||
StringRef OpName, OperandInfo &OpInfo) {
|
||||
@@ -1991,14 +2003,23 @@ populateInstruction(CodeGenTarget &Target, const Record &EncodingDef,
|
||||
// operands that are not explicitly represented in the encoding.
|
||||
std::map<std::string, std::string> TiedNames;
|
||||
for (unsigned i = 0; i < CGI.Operands.size(); ++i) {
|
||||
int tiedTo = CGI.Operands[i].getTiedRegister();
|
||||
if (tiedTo != -1) {
|
||||
std::pair<unsigned, unsigned> SO =
|
||||
CGI.Operands.getSubOperandNumber(tiedTo);
|
||||
TiedNames[std::string(InOutOperands[i].second)] =
|
||||
std::string(InOutOperands[SO.first].second);
|
||||
TiedNames[std::string(InOutOperands[SO.first].second)] =
|
||||
std::string(InOutOperands[i].second);
|
||||
auto &Op = CGI.Operands[i];
|
||||
for (unsigned j = 0; j < Op.Constraints.size(); ++j) {
|
||||
const CGIOperandList::ConstraintInfo &CI = Op.Constraints[j];
|
||||
if (CI.isTied()) {
|
||||
int tiedTo = CI.getTiedOperand();
|
||||
std::pair<unsigned, unsigned> SO =
|
||||
CGI.Operands.getSubOperandNumber(tiedTo);
|
||||
std::string TiedName = CGI.Operands[SO.first].SubOpNames[SO.second];
|
||||
if (TiedName.empty())
|
||||
TiedName = CGI.Operands[SO.first].Name;
|
||||
std::string MyName = Op.SubOpNames[j];
|
||||
if (MyName.empty())
|
||||
MyName = Op.Name;
|
||||
|
||||
TiedNames[MyName] = TiedName;
|
||||
TiedNames[TiedName] = MyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2054,7 +2075,9 @@ populateInstruction(CodeGenTarget &Target, const Record &EncodingDef,
|
||||
|
||||
// Skip variables that correspond to explicitly-named operands.
|
||||
unsigned OpIdx;
|
||||
if (CGI.Operands.hasOperandNamed(Vals[i].getName(), OpIdx))
|
||||
std::pair<unsigned, unsigned> SubOp;
|
||||
if (CGI.Operands.hasSubOperandAlias(Vals[i].getName(), SubOp) ||
|
||||
CGI.Operands.hasOperandNamed(Vals[i].getName(), OpIdx))
|
||||
continue;
|
||||
|
||||
// Get the bit range for this operand:
|
||||
@@ -2190,15 +2213,75 @@ populateInstruction(CodeGenTarget &Target, const Record &EncodingDef,
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we can locate the decoder field, but we need to know how
|
||||
// to interpret it. As a first step, require the target to provide
|
||||
// callbacks for decoding register classes.
|
||||
// We're ready to find the instruction encoding locations for this operand.
|
||||
|
||||
if (DagInit *Dag = dyn_cast<DagInit>(OpInit))
|
||||
OpInit = Dag->getOperator();
|
||||
OperandInfo OpInfo = getOpInfo(cast<DefInit>(OpInit)->getDef());
|
||||
// First, find the operand type ("OpInit"), and sub-op names
|
||||
// ("SubArgDag") if present.
|
||||
DagInit *SubArgDag = dyn_cast<DagInit>(OpInit);
|
||||
if (SubArgDag)
|
||||
OpInit = SubArgDag->getOperator();
|
||||
Record *OpTypeRec = cast<DefInit>(OpInit)->getDef();
|
||||
// Lookup the sub-operands from the operand type record (note that only
|
||||
// Operand subclasses have MIOperandInfo, see CodeGenInstruction.cpp).
|
||||
DagInit *SubOps = OpTypeRec->isSubClassOf("Operand")
|
||||
? OpTypeRec->getValueAsDag("MIOperandInfo")
|
||||
: nullptr;
|
||||
|
||||
// Lookup the decoder method and construct a new OperandInfo to hold our result.
|
||||
OperandInfo OpInfo = getOpInfo(OpTypeRec);
|
||||
|
||||
// If we have named sub-operands...
|
||||
if (SubArgDag) {
|
||||
// Then there should not be a custom decoder specified on the top-level
|
||||
// type.
|
||||
if (!OpInfo.Decoder.empty()) {
|
||||
PrintError(EncodingDef.getLoc(),
|
||||
"DecoderEmitter: operand \"" + OpName + "\" has type \"" +
|
||||
OpInit->getAsString() +
|
||||
"\" with a custom DecoderMethod, but also named "
|
||||
"sub-operands.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decode each of the sub-ops separately.
|
||||
assert(SubOps && SubArgDag->getNumArgs() == SubOps->getNumArgs());
|
||||
for (unsigned i = 0; i < SubOps->getNumArgs(); ++i) {
|
||||
StringRef SubOpName = SubArgDag->getArgNameStr(i);
|
||||
OperandInfo SubOpInfo =
|
||||
getOpInfo(cast<DefInit>(SubOps->getArg(i))->getDef());
|
||||
|
||||
addOneOperandFields(EncodingDef, Bits, TiedNames, SubOpName,
|
||||
SubOpInfo);
|
||||
InsnOperands.push_back(SubOpInfo);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, if we have an operand with sub-operands, but they aren't
|
||||
// named...
|
||||
if (SubOps && OpInfo.Decoder.empty()) {
|
||||
// If it's a single sub-operand, and no custom decoder, use the decoder
|
||||
// from the one sub-operand.
|
||||
if (SubOps->getNumArgs() == 1)
|
||||
OpInfo = getOpInfo(cast<DefInit>(SubOps->getArg(0))->getDef());
|
||||
|
||||
// If we have multiple sub-ops, there'd better have a custom
|
||||
// decoder. (Otherwise we don't know how to populate them properly...)
|
||||
if (SubOps->getNumArgs() > 1) {
|
||||
PrintError(EncodingDef.getLoc(),
|
||||
"DecoderEmitter: operand \"" + OpName +
|
||||
"\" uses MIOperandInfo with multiple ops, but doesn't "
|
||||
"have a custom decoder!");
|
||||
debugDumpRecord(EncodingDef);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
addOneOperandFields(EncodingDef, Bits, TiedNames, OpName, OpInfo);
|
||||
// FIXME: it should be an error not to find a definition for a given
|
||||
// operand, rather than just failing to add it to the resulting
|
||||
// instruction! (This is a longstanding bug, which will be addressed in an
|
||||
// upcoming change.)
|
||||
if (OpInfo.numFields() > 0)
|
||||
InsnOperands.push_back(OpInfo);
|
||||
}
|
||||
|
||||
@@ -449,7 +449,8 @@ std::string VarLenCodeEmitterGen::getInstructionCaseForEncoding(
|
||||
|
||||
auto OpIdx = CGI.Operands.ParseOperandName(OperandName);
|
||||
unsigned FlatOpIdx = CGI.Operands.getFlattenedOperandNumber(OpIdx);
|
||||
StringRef CustomEncoder = CGI.Operands[OpIdx.first].EncoderMethodName;
|
||||
StringRef CustomEncoder =
|
||||
CGI.Operands[OpIdx.first].EncoderMethodNames[OpIdx.second];
|
||||
if (ES.CustomEncoder.size())
|
||||
CustomEncoder = ES.CustomEncoder;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user