aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/AsmWriterEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/TableGen/AsmWriterEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/AsmWriterEmitter.cpp274
1 files changed, 173 insertions, 101 deletions
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index b5c7f35be0e5..58c0d32d44eb 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -29,6 +29,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
@@ -274,13 +275,14 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) {
StringRef ClassName = AsmWriter->getValueAsString("AsmWriterClassName");
bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget");
- O <<
- "/// printInstruction - This method is automatically generated by tablegen\n"
- "/// from the instruction set description.\n"
- "void " << Target.getName() << ClassName
- << "::printInstruction(const MCInst *MI, "
- << (PassSubtarget ? "const MCSubtargetInfo &STI, " : "")
- << "raw_ostream &O) {\n";
+ O << "/// printInstruction - This method is automatically generated by "
+ "tablegen\n"
+ "/// from the instruction set description.\n"
+ "void "
+ << Target.getName() << ClassName
+ << "::printInstruction(const MCInst *MI, uint64_t Address, "
+ << (PassSubtarget ? "const MCSubtargetInfo &STI, " : "")
+ << "raw_ostream &O) {\n";
// Build an aggregate string, and build a table of offsets into it.
SequenceToOffsetTable<std::string> StringTable;
@@ -616,17 +618,22 @@ namespace {
// they both have the same conditionals. In which case, we cannot print out the
// alias for that pattern.
class IAPrinter {
- std::vector<std::string> Conds;
std::map<StringRef, std::pair<int, int>> OpMap;
+ std::vector<std::string> Conds;
+
std::string Result;
std::string AsmString;
+ unsigned NumMIOps;
+
public:
- IAPrinter(std::string R, std::string AS)
- : Result(std::move(R)), AsmString(std::move(AS)) {}
+ IAPrinter(std::string R, std::string AS, unsigned NumMIOps)
+ : Result(std::move(R)), AsmString(std::move(AS)), NumMIOps(NumMIOps) {}
- void addCond(const std::string &C) { Conds.push_back(C); }
+ void addCond(std::string C) { Conds.push_back(std::move(C)); }
+ ArrayRef<std::string> getConds() const { return Conds; }
+ size_t getCondCount() const { return Conds.size(); }
void addOperand(StringRef Op, int OpIdx, int PrintMethodIdx = -1) {
assert(OpIdx >= 0 && OpIdx < 0xFE && "Idx out of range");
@@ -635,6 +642,10 @@ public:
OpMap[Op] = std::make_pair(OpIdx, PrintMethodIdx);
}
+ unsigned getNumMIOps() { return NumMIOps; }
+
+ StringRef getResult() { return Result; }
+
bool isOpMapped(StringRef Op) { return OpMap.find(Op) != OpMap.end(); }
int getOpIndex(StringRef Op) { return OpMap[Op].first; }
std::pair<int, int> &getOpData(StringRef Op) { return OpMap[Op]; }
@@ -664,35 +675,16 @@ public:
return std::make_pair(StringRef(Start, I - Start), Next);
}
- void print(raw_ostream &O) {
- if (Conds.empty()) {
- O.indent(6) << "return true;\n";
- return;
- }
-
- O << "if (";
-
- for (std::vector<std::string>::iterator
- I = Conds.begin(), E = Conds.end(); I != E; ++I) {
- if (I != Conds.begin()) {
- O << " &&\n";
- O.indent(8);
- }
-
- O << *I;
- }
-
- O << ") {\n";
- O.indent(6) << "// " << Result << "\n";
-
+ std::string formatAliasString(uint32_t &UnescapedSize) {
// Directly mangle mapped operands into the string. Each operand is
// identified by a '$' sign followed by a byte identifying the number of the
// operand. We add one to the index to avoid zero bytes.
StringRef ASM(AsmString);
- SmallString<128> OutString;
- raw_svector_ostream OS(OutString);
+ std::string OutString;
+ raw_string_ostream OS(OutString);
for (StringRef::iterator I = ASM.begin(), E = ASM.end(); I != E;) {
OS << *I;
+ ++UnescapedSize;
if (*I == '$') {
StringRef Name;
std::tie(Name, I) = parseName(++I, E);
@@ -703,23 +695,25 @@ public:
if (PrintIndex == -1) {
// Can use the default printOperand route.
OS << format("\\x%02X", (unsigned char)OpIndex + 1);
- } else
+ ++UnescapedSize;
+ } else {
// 3 bytes if a PrintMethod is needed: 0xFF, the MCInst operand
// number, and which of our pre-detected Methods to call.
OS << format("\\xFF\\x%02X\\x%02X", OpIndex + 1, PrintIndex + 1);
+ UnescapedSize += 3;
+ }
} else {
++I;
}
}
- // Emit the string.
- O.indent(6) << "AsmString = \"" << OutString << "\";\n";
-
- O.indent(6) << "break;\n";
- O.indent(4) << '}';
+ OS.flush();
+ return OutString;
}
bool operator==(const IAPrinter &RHS) const {
+ if (NumMIOps != RHS.NumMIOps)
+ return false;
if (Conds.size() != RHS.Conds.size())
return false;
@@ -799,6 +793,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
DenseMap<const Record*, unsigned> MCOpPredicateMap;
for (auto &Aliases : AliasMap) {
+ // Collection of instruction alias rules. May contain ambiguous rules.
+ std::vector<IAPrinter> IAPs;
+
for (auto &Alias : Aliases.second) {
const CodeGenInstAlias &CGA = Alias.first;
unsigned LastOpNo = CGA.ResultInstOperandIndex.size();
@@ -808,33 +805,18 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
unsigned NumResultOps = CountNumOperands(FlatInstAsmString, Variant);
std::string FlatAliasAsmString =
- CodeGenInstruction::FlattenAsmStringVariants(CGA.AsmString,
- Variant);
+ CodeGenInstruction::FlattenAsmStringVariants(CGA.AsmString, Variant);
// Don't emit the alias if it has more operands than what it's aliasing.
if (NumResultOps < CountNumOperands(FlatAliasAsmString, Variant))
continue;
- IAPrinter IAP(CGA.Result->getAsString(), FlatAliasAsmString);
-
StringRef Namespace = Target.getName();
- std::vector<Record *> ReqFeatures;
- if (PassSubtarget) {
- // We only consider ReqFeatures predicates if PassSubtarget
- std::vector<Record *> RF =
- CGA.TheDef->getValueAsListOfDefs("Predicates");
- copy_if(RF, std::back_inserter(ReqFeatures), [](Record *R) {
- return R->getValueAsBit("AssemblerMatcherPredicate");
- });
- }
-
unsigned NumMIOps = 0;
for (auto &ResultInstOpnd : CGA.ResultInst->Operands)
NumMIOps += ResultInstOpnd.MINumOperands;
- std::string Cond;
- Cond = std::string("MI->getNumOperands() == ") + utostr(NumMIOps);
- IAP.addCond(Cond);
+ IAPrinter IAP(CGA.Result->getAsString(), FlatAliasAsmString, NumMIOps);
bool CantHandle = false;
@@ -858,7 +840,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
break;
}
- std::string Op = "MI->getOperand(" + utostr(MIOpNum) + ")";
+ // Ignore unchecked result operands.
+ while (IAP.getCondCount() < MIOpNum)
+ IAP.addCond("AliasPatternCond::K_Ignore, 0");
const CodeGenInstAlias::ResultOperand &RO = CGA.ResultOperands[i];
@@ -885,19 +869,17 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (Rec->isSubClassOf("RegisterOperand"))
Rec = Rec->getValueAsDef("RegClass");
if (Rec->isSubClassOf("RegisterClass")) {
- IAP.addCond(Op + ".isReg()");
-
if (!IAP.isOpMapped(ROName)) {
IAP.addOperand(ROName, MIOpNum, PrintMethodIdx);
Record *R = CGA.ResultOperands[i].getRecord();
if (R->isSubClassOf("RegisterOperand"))
R = R->getValueAsDef("RegClass");
- Cond = std::string("MRI.getRegClass(") + Target.getName().str() +
- "::" + R->getName().str() + "RegClassID).contains(" + Op +
- ".getReg())";
+ IAP.addCond(formatv(
+ "AliasPatternCond::K_RegClass, {0}::{1}RegClassID", Namespace,
+ R->getName()));
} else {
- Cond = Op + ".getReg() == MI->getOperand(" +
- utostr(IAP.getOpIndex(ROName)) + ").getReg()";
+ IAP.addCond(formatv("AliasPatternCond::K_TiedReg, {0}",
+ IAP.getOpIndex(ROName)));
}
} else {
// Assume all printable operands are desired for now. This can be
@@ -914,21 +896,19 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
} else
break; // No conditions on this operand at all
}
- Cond = (Target.getName() + ClassName + "ValidateMCOperand(" + Op +
- ", STI, " + utostr(Entry) + ")")
- .str();
+ IAP.addCond(formatv("AliasPatternCond::K_Custom, {0}", Entry));
}
- // for all subcases of ResultOperand::K_Record:
- IAP.addCond(Cond);
break;
}
case CodeGenInstAlias::ResultOperand::K_Imm: {
// Just because the alias has an immediate result, doesn't mean the
// MCInst will. An MCExpr could be present, for example.
- IAP.addCond(Op + ".isImm()");
-
- Cond = Op + ".getImm() == " + itostr(CGA.ResultOperands[i].getImm());
- IAP.addCond(Cond);
+ auto Imm = CGA.ResultOperands[i].getImm();
+ int32_t Imm32 = int32_t(Imm);
+ if (Imm != Imm32)
+ PrintFatalError("Matching an alias with an immediate out of the "
+ "range of int32_t is not supported");
+ IAP.addCond(formatv("AliasPatternCond::K_Imm, uint32_t({0})", Imm32));
break;
}
case CodeGenInstAlias::ResultOperand::K_Reg:
@@ -939,9 +919,9 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
break;
}
- Cond = Op + ".getReg() == " + Target.getName().str() + "::" +
- CGA.ResultOperands[i].getRegister()->getName().str();
- IAP.addCond(Cond);
+ StringRef Reg = CGA.ResultOperands[i].getRegister()->getName();
+ IAP.addCond(
+ formatv("AliasPatternCond::K_Reg, {0}::{1}", Namespace, Reg));
break;
}
@@ -950,6 +930,16 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (CantHandle) continue;
+ std::vector<Record *> ReqFeatures;
+ if (PassSubtarget) {
+ // We only consider ReqFeatures predicates if PassSubtarget
+ std::vector<Record *> RF =
+ CGA.TheDef->getValueAsListOfDefs("Predicates");
+ copy_if(RF, std::back_inserter(ReqFeatures), [](Record *R) {
+ return R->getValueAsBit("AssemblerMatcherPredicate");
+ });
+ }
+
for (auto I = ReqFeatures.cbegin(); I != ReqFeatures.cend(); I++) {
Record *R = *I;
StringRef AsmCondString = R->getValueAsString("AssemblerCondString");
@@ -959,16 +949,12 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
SplitString(AsmCondString, Ops, ",");
assert(!Ops.empty() && "AssemblerCondString cannot be empty");
- for (auto &Op : Ops) {
+ for (StringRef Op : Ops) {
assert(!Op.empty() && "Empty operator");
- if (Op[0] == '!')
- Cond = ("!STI.getFeatureBits()[" + Namespace + "::" + Op.substr(1) +
- "]")
- .str();
- else
- Cond =
- ("STI.getFeatureBits()[" + Namespace + "::" + Op + "]").str();
- IAP.addCond(Cond);
+ bool IsNeg = Op[0] == '!';
+ StringRef Feature = Op.drop_front(IsNeg ? 1 : 0);
+ IAP.addCond(formatv("AliasPatternCond::K_{0}Feature, {1}::{2}",
+ IsNeg ? "Neg" : "", Namespace, Feature));
}
}
@@ -988,13 +974,32 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
<< " *MI, " << (PassSubtarget ? "const MCSubtargetInfo &STI, " : "")
<< "raw_ostream &OS) {\n";
- std::string Cases;
- raw_string_ostream CasesO(Cases);
+ std::string PatternsForOpcode;
+ raw_string_ostream OpcodeO(PatternsForOpcode);
+
+ unsigned PatternCount = 0;
+ std::string Patterns;
+ raw_string_ostream PatternO(Patterns);
+
+ unsigned CondCount = 0;
+ std::string Conds;
+ raw_string_ostream CondO(Conds);
+
+ // All flattened alias strings.
+ std::map<std::string, uint32_t> AsmStringOffsets;
+ std::vector<std::pair<uint32_t, std::string>> AsmStrings;
+ size_t AsmStringsSize = 0;
- for (auto &Entry : IAPrinterMap) {
- std::vector<IAPrinter> &IAPs = Entry.second;
+ // Iterate over the opcodes in enum order so they are sorted by opcode for
+ // binary search.
+ for (const CodeGenInstruction *Inst : NumberedInstructions) {
+ auto It = IAPrinterMap.find(getQualifiedName(Inst->TheDef));
+ if (It == IAPrinterMap.end())
+ continue;
+ std::vector<IAPrinter> &IAPs = It->second;
std::vector<IAPrinter*> UniqueIAPs;
+ // Remove any ambiguous alias rules.
for (auto &LHS : IAPs) {
bool IsDup = false;
for (const auto &RHS : IAPs) {
@@ -1010,18 +1015,43 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (UniqueIAPs.empty()) continue;
- CasesO.indent(2) << "case " << Entry.first << ":\n";
+ unsigned PatternStart = PatternCount;
+
+ // Insert the pattern start and opcode in the pattern list for debugging.
+ PatternO << formatv(" // {0} - {1}\n", It->first, PatternStart);
for (IAPrinter *IAP : UniqueIAPs) {
- CasesO.indent(4);
- IAP->print(CasesO);
- CasesO << '\n';
+ // Start each condition list with a comment of the resulting pattern that
+ // we're trying to match.
+ unsigned CondStart = CondCount;
+ CondO << formatv(" // {0} - {1}\n", IAP->getResult(), CondStart);
+ for (const auto &Cond : IAP->getConds())
+ CondO << " {" << Cond << "},\n";
+ CondCount += IAP->getCondCount();
+
+ // After operands have been examined, re-encode the alias string with
+ // escapes indicating how operands should be printed.
+ uint32_t UnescapedSize = 0;
+ std::string EncodedAsmString = IAP->formatAliasString(UnescapedSize);
+ auto Insertion =
+ AsmStringOffsets.insert({EncodedAsmString, AsmStringsSize});
+ if (Insertion.second) {
+ // If the string is new, add it to the vector.
+ AsmStrings.push_back({AsmStringsSize, EncodedAsmString});
+ AsmStringsSize += UnescapedSize + 1;
+ }
+ unsigned AsmStrOffset = Insertion.first->second;
+
+ PatternO << formatv(" {{{0}, {1}, {2}, {3} },\n", AsmStrOffset,
+ CondStart, IAP->getNumMIOps(), IAP->getCondCount());
+ ++PatternCount;
}
- CasesO.indent(4) << "return false;\n";
+ OpcodeO << formatv(" {{{0}, {1}, {2} },\n", It->first, PatternStart,
+ PatternCount - PatternStart);
}
- if (CasesO.str().empty()) {
+ if (OpcodeO.str().empty()) {
O << HeaderO.str();
O << " return false;\n";
O << "}\n\n";
@@ -1029,6 +1059,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
return;
}
+ // Forward declare the validation method if needed.
if (!MCOpPredicates.empty())
O << "static bool " << Target.getName() << ClassName
<< "ValidateMCOperand(const MCOperand &MCOp,\n"
@@ -1036,11 +1067,52 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
<< " unsigned PredicateIndex);\n";
O << HeaderO.str();
- O.indent(2) << "const char *AsmString;\n";
- O.indent(2) << "switch (MI->getOpcode()) {\n";
- O.indent(2) << "default: return false;\n";
- O << CasesO.str();
- O.indent(2) << "}\n\n";
+ O.indent(2) << "static const PatternsForOpcode OpToPatterns[] = {\n";
+ O << OpcodeO.str();
+ O.indent(2) << "};\n\n";
+ O.indent(2) << "static const AliasPattern Patterns[] = {\n";
+ O << PatternO.str();
+ O.indent(2) << "};\n\n";
+ O.indent(2) << "static const AliasPatternCond Conds[] = {\n";
+ O << CondO.str();
+ O.indent(2) << "};\n\n";
+ O.indent(2) << "static const char AsmStrings[] =\n";
+ for (const auto &P : AsmStrings) {
+ O.indent(4) << "/* " << P.first << " */ \"" << P.second << "\\0\"\n";
+ }
+
+ O.indent(2) << ";\n\n";
+
+ // Assert that the opcode table is sorted. Use a static local constructor to
+ // ensure that the check only happens once on first run.
+ O << "#ifndef NDEBUG\n";
+ O.indent(2) << "static struct SortCheck {\n";
+ O.indent(2) << " SortCheck(ArrayRef<PatternsForOpcode> OpToPatterns) {\n";
+ O.indent(2) << " assert(std::is_sorted(\n";
+ O.indent(2) << " OpToPatterns.begin(), OpToPatterns.end(),\n";
+ O.indent(2) << " [](const PatternsForOpcode &L, const "
+ "PatternsForOpcode &R) {\n";
+ O.indent(2) << " return L.Opcode < R.Opcode;\n";
+ O.indent(2) << " }) &&\n";
+ O.indent(2) << " \"tablegen failed to sort opcode patterns\");\n";
+ O.indent(2) << " }\n";
+ O.indent(2) << "} sortCheckVar(OpToPatterns);\n";
+ O << "#endif\n\n";
+
+ O.indent(2) << "AliasMatchingData M {\n";
+ O.indent(2) << " makeArrayRef(OpToPatterns),\n";
+ O.indent(2) << " makeArrayRef(Patterns),\n";
+ O.indent(2) << " makeArrayRef(Conds),\n";
+ O.indent(2) << " StringRef(AsmStrings, array_lengthof(AsmStrings)),\n";
+ if (MCOpPredicates.empty())
+ O.indent(2) << " nullptr,\n";
+ else
+ O.indent(2) << " &" << Target.getName() << ClassName << "ValidateMCOperand,\n";
+ O.indent(2) << "};\n";
+
+ O.indent(2) << "const char *AsmString = matchAliasPatterns(MI, "
+ << (PassSubtarget ? "&STI" : "nullptr") << ", M);\n";
+ O.indent(2) << "if (!AsmString) return false;\n\n";
// Code that prints the alias, replacing the operands with the ones from the
// MCInst.