llvm-6502/utils/TableGen/AsmWriterEmitter.cpp
Nate Begeman 391c5d231a No longer track value types for asm printer operands, and remove them as
an argument to every operand printing function.  Requires some slight
tweaks to x86, the only user.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@24541 91177308-0d34-0410-b5e6-96231b3b80d8
2005-11-30 18:54:35 +00:00

393 lines
14 KiB
C++

//===- AsmWriterEmitter.cpp - Generate an assembly writer -----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend is emits an assembly printer for the current target.
// Note that this is currently fairly skeletal, but will grow over time.
//
//===----------------------------------------------------------------------===//
#include "AsmWriterEmitter.h"
#include "CodeGenTarget.h"
#include "Record.h"
#include <algorithm>
#include <ostream>
using namespace llvm;
static bool isIdentChar(char C) {
return (C >= 'a' && C <= 'z') ||
(C >= 'A' && C <= 'Z') ||
(C >= '0' && C <= '9') ||
C == '_';
}
namespace {
struct AsmWriterOperand {
enum { isLiteralTextOperand, isMachineInstrOperand } OperandType;
/// Str - For isLiteralTextOperand, this IS the literal text. For
/// isMachineInstrOperand, this is the PrinterMethodName for the operand.
std::string Str;
/// MiOpNo - For isMachineInstrOperand, this is the operand number of the
/// machine instruction.
unsigned MIOpNo;
AsmWriterOperand(const std::string &LitStr)
: OperandType(isLiteralTextOperand), Str(LitStr) {}
AsmWriterOperand(const std::string &Printer, unsigned OpNo)
: OperandType(isMachineInstrOperand), Str(Printer), MIOpNo(OpNo) {}
bool operator!=(const AsmWriterOperand &Other) const {
if (OperandType != Other.OperandType || Str != Other.Str) return true;
if (OperandType == isMachineInstrOperand)
return MIOpNo != Other.MIOpNo;
return false;
}
bool operator==(const AsmWriterOperand &Other) const {
return !operator!=(Other);
}
void EmitCode(std::ostream &OS) const;
};
struct AsmWriterInst {
std::vector<AsmWriterOperand> Operands;
const CodeGenInstruction *CGI;
AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant);
/// MatchesAllButOneOp - If this instruction is exactly identical to the
/// specified instruction except for one differing operand, return the
/// differing operand number. Otherwise return ~0.
unsigned MatchesAllButOneOp(const AsmWriterInst &Other) const;
private:
void AddLiteralString(const std::string &Str) {
// If the last operand was already a literal text string, append this to
// it, otherwise add a new operand.
if (!Operands.empty() &&
Operands.back().OperandType == AsmWriterOperand::isLiteralTextOperand)
Operands.back().Str.append(Str);
else
Operands.push_back(AsmWriterOperand(Str));
}
};
}
void AsmWriterOperand::EmitCode(std::ostream &OS) const {
if (OperandType == isLiteralTextOperand)
OS << "O << \"" << Str << "\"; ";
else
OS << Str << "(MI, " << MIOpNo << "); ";
}
/// ParseAsmString - Parse the specified Instruction's AsmString into this
/// AsmWriterInst.
///
AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
this->CGI = &CGI;
bool inVariant = false; // True if we are inside a {.|.|.} region.
const std::string &AsmString = CGI.AsmString;
std::string::size_type LastEmitted = 0;
while (LastEmitted != AsmString.size()) {
std::string::size_type DollarPos =
AsmString.find_first_of("${|}", LastEmitted);
if (DollarPos == std::string::npos) DollarPos = AsmString.size();
// Emit a constant string fragment.
if (DollarPos != LastEmitted) {
// TODO: this should eventually handle escaping.
AddLiteralString(std::string(AsmString.begin()+LastEmitted,
AsmString.begin()+DollarPos));
LastEmitted = DollarPos;
} else if (AsmString[DollarPos] == '{') {
if (inVariant)
throw "Nested variants found for instruction '" +
CGI.TheDef->getName() + "'!";
LastEmitted = DollarPos+1;
inVariant = true; // We are now inside of the variant!
for (unsigned i = 0; i != Variant; ++i) {
// Skip over all of the text for an irrelevant variant here. The
// next variant starts at |, or there may not be text for this
// variant if we see a }.
std::string::size_type NP =
AsmString.find_first_of("|}", LastEmitted);
if (NP == std::string::npos)
throw "Incomplete variant for instruction '" +
CGI.TheDef->getName() + "'!";
LastEmitted = NP+1;
if (AsmString[NP] == '}') {
inVariant = false; // No text for this variant.
break;
}
}
} else if (AsmString[DollarPos] == '|') {
if (!inVariant)
throw "'|' character found outside of a variant in instruction '"
+ CGI.TheDef->getName() + "'!";
// Move to the end of variant list.
std::string::size_type NP = AsmString.find('}', LastEmitted);
if (NP == std::string::npos)
throw "Incomplete variant for instruction '" +
CGI.TheDef->getName() + "'!";
LastEmitted = NP+1;
inVariant = false;
} else if (AsmString[DollarPos] == '}') {
if (!inVariant)
throw "'}' character found outside of a variant in instruction '"
+ CGI.TheDef->getName() + "'!";
LastEmitted = DollarPos+1;
inVariant = false;
} else if (DollarPos+1 != AsmString.size() &&
AsmString[DollarPos+1] == '$') {
AddLiteralString("$"); // "$$" -> $
LastEmitted = DollarPos+2;
} else {
// Get the name of the variable.
std::string::size_type VarEnd = DollarPos+1;
// handle ${foo}bar as $foo by detecting whether the character following
// the dollar sign is a curly brace. If so, advance VarEnd and DollarPos
// so the variable name does not contain the leading curly brace.
bool hasCurlyBraces = false;
if (VarEnd < AsmString.size() && '{' == AsmString[VarEnd]) {
hasCurlyBraces = true;
++DollarPos;
++VarEnd;
}
while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd]))
++VarEnd;
std::string VarName(AsmString.begin()+DollarPos+1,
AsmString.begin()+VarEnd);
// In order to avoid starting the next string at the terminating curly
// brace, advance the end position past it if we found an opening curly
// brace.
if (hasCurlyBraces) {
if (VarEnd >= AsmString.size())
throw "Reached end of string before terminating curly brace in '"
+ CGI.TheDef->getName() + "'";
if (AsmString[VarEnd] != '}')
throw "Variant name beginning with '{' did not end with '}' in '"
+ CGI.TheDef->getName() + "'";
++VarEnd;
}
if (VarName.empty())
throw "Stray '$' in '" + CGI.TheDef->getName() +
"' asm string, maybe you want $$?";
unsigned OpNo = CGI.getOperandNamed(VarName);
CodeGenInstruction::OperandInfo OpInfo = CGI.OperandList[OpNo];
// If this is a two-address instruction and we are not accessing the
// 0th operand, remove an operand.
unsigned MIOp = OpInfo.MIOperandNo;
if (CGI.isTwoAddress && MIOp != 0) {
if (MIOp == 1)
throw "Should refer to operand #0 instead of #1 for two-address"
" instruction '" + CGI.TheDef->getName() + "'!";
--MIOp;
}
Operands.push_back(AsmWriterOperand(OpInfo.PrinterMethodName, MIOp));
LastEmitted = VarEnd;
}
}
AddLiteralString("\\n");
}
/// MatchesAllButOneOp - If this instruction is exactly identical to the
/// specified instruction except for one differing operand, return the differing
/// operand number. If more than one operand mismatches, return ~1, otherwise
/// if the instructions are identical return ~0.
unsigned AsmWriterInst::MatchesAllButOneOp(const AsmWriterInst &Other)const{
if (Operands.size() != Other.Operands.size()) return ~1;
unsigned MismatchOperand = ~0U;
for (unsigned i = 0, e = Operands.size(); i != e; ++i) {
if (Operands[i] != Other.Operands[i])
if (MismatchOperand != ~0U) // Already have one mismatch?
return ~1U;
else
MismatchOperand = i;
}
return MismatchOperand;
}
static void PrintCases(std::vector<std::pair<std::string,
AsmWriterOperand> > &OpsToPrint, std::ostream &O) {
O << " case " << OpsToPrint.back().first << ": ";
AsmWriterOperand TheOp = OpsToPrint.back().second;
OpsToPrint.pop_back();
// Check to see if any other operands are identical in this list, and if so,
// emit a case label for them.
for (unsigned i = OpsToPrint.size(); i != 0; --i)
if (OpsToPrint[i-1].second == TheOp) {
O << "\n case " << OpsToPrint[i-1].first << ": ";
OpsToPrint.erase(OpsToPrint.begin()+i-1);
}
// Finally, emit the code.
TheOp.EmitCode(O);
O << "break;\n";
}
/// EmitInstructions - Emit the last instruction in the vector and any other
/// instructions that are suitably similar to it.
static void EmitInstructions(std::vector<AsmWriterInst> &Insts,
std::ostream &O) {
AsmWriterInst FirstInst = Insts.back();
Insts.pop_back();
std::vector<AsmWriterInst> SimilarInsts;
unsigned DifferingOperand = ~0;
for (unsigned i = Insts.size(); i != 0; --i) {
unsigned DiffOp = Insts[i-1].MatchesAllButOneOp(FirstInst);
if (DiffOp != ~1U) {
if (DifferingOperand == ~0U) // First match!
DifferingOperand = DiffOp;
// If this differs in the same operand as the rest of the instructions in
// this class, move it to the SimilarInsts list.
if (DifferingOperand == DiffOp || DiffOp == ~0U) {
SimilarInsts.push_back(Insts[i-1]);
Insts.erase(Insts.begin()+i-1);
}
}
}
std::string Namespace = FirstInst.CGI->Namespace;
O << " case " << Namespace << "::"
<< FirstInst.CGI->TheDef->getName() << ":\n";
for (unsigned i = 0, e = SimilarInsts.size(); i != e; ++i)
O << " case " << Namespace << "::"
<< SimilarInsts[i].CGI->TheDef->getName() << ":\n";
for (unsigned i = 0, e = FirstInst.Operands.size(); i != e; ++i) {
if (i != DifferingOperand) {
// If the operand is the same for all instructions, just print it.
O << " ";
FirstInst.Operands[i].EmitCode(O);
} else {
// If this is the operand that varies between all of the instructions,
// emit a switch for just this operand now.
O << " switch (MI->getOpcode()) {\n";
std::vector<std::pair<std::string, AsmWriterOperand> > OpsToPrint;
OpsToPrint.push_back(std::make_pair(Namespace+"::"+
FirstInst.CGI->TheDef->getName(),
FirstInst.Operands[i]));
for (unsigned si = 0, e = SimilarInsts.size(); si != e; ++si) {
AsmWriterInst &AWI = SimilarInsts[si];
OpsToPrint.push_back(std::make_pair(Namespace+"::"+
AWI.CGI->TheDef->getName(),
AWI.Operands[i]));
}
std::reverse(OpsToPrint.begin(), OpsToPrint.end());
while (!OpsToPrint.empty())
PrintCases(OpsToPrint, O);
O << " }";
}
O << "\n";
}
O << " break;\n";
}
void AsmWriterEmitter::run(std::ostream &O) {
EmitSourceFileHeader("Assembly Writer Source Fragment", O);
CodeGenTarget Target;
Record *AsmWriter = Target.getAsmWriter();
std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName");
unsigned Variant = AsmWriter->getValueAsInt("Variant");
O <<
"/// printInstruction - This method is automatically generated by tablegen\n"
"/// from the instruction set description. This method returns true if the\n"
"/// machine instruction was sufficiently described to print it, otherwise\n"
"/// it returns false.\n"
"bool " << Target.getName() << ClassName
<< "::printInstruction(const MachineInstr *MI) {\n";
std::string Namespace = Target.inst_begin()->second.Namespace;
std::vector<AsmWriterInst> Instructions;
for (CodeGenTarget::inst_iterator I = Target.inst_begin(),
E = Target.inst_end(); I != E; ++I)
if (!I->second.AsmString.empty())
Instructions.push_back(AsmWriterInst(I->second, Variant));
// If all of the instructions start with a constant string (a very very common
// occurance), emit all of the constant strings as a big table lookup instead
// of requiring a switch for them.
bool AllStartWithString = true;
for (unsigned i = 0, e = Instructions.size(); i != e; ++i)
if (Instructions[i].Operands.empty() ||
Instructions[i].Operands[0].OperandType !=
AsmWriterOperand::isLiteralTextOperand) {
AllStartWithString = false;
break;
}
if (AllStartWithString) {
// Compute the CodeGenInstruction -> AsmWriterInst mapping. Note that not
// all machine instructions are necessarily being printed, so there may be
// target instructions not in this map.
std::map<const CodeGenInstruction*, AsmWriterInst*> CGIAWIMap;
for (unsigned i = 0, e = Instructions.size(); i != e; ++i)
CGIAWIMap.insert(std::make_pair(Instructions[i].CGI, &Instructions[i]));
// Emit a table of constant strings.
std::vector<const CodeGenInstruction*> NumberedInstructions;
Target.getInstructionsByEnumValue(NumberedInstructions);
O << " static const char * const OpStrs[] = {\n";
for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) {
AsmWriterInst *AWI = CGIAWIMap[NumberedInstructions[i]];
if (AWI == 0) {
// Something not handled by the asmwriter printer.
O << " 0,\t// ";
} else {
O << " \"" << AWI->Operands[0].Str << "\",\t// ";
// Nuke the string from the operand list. It is now handled!
AWI->Operands.erase(AWI->Operands.begin());
}
O << NumberedInstructions[i]->TheDef->getName() << "\n";
}
O << " };\n\n"
<< " // Emit the opcode for the instruction.\n"
<< " if (const char *AsmStr = OpStrs[MI->getOpcode()])\n"
<< " O << AsmStr;\n\n";
}
// Because this is a vector we want to emit from the end. Reverse all of the
// elements in the vector.
std::reverse(Instructions.begin(), Instructions.end());
O << " switch (MI->getOpcode()) {\n"
" default: return false;\n";
while (!Instructions.empty())
EmitInstructions(Instructions, O);
O << " }\n"
" return true;\n"
"}\n";
}