diff options
Diffstat (limited to 'utils/TableGen/EDEmitter.cpp')
| -rw-r--r-- | utils/TableGen/EDEmitter.cpp | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/utils/TableGen/EDEmitter.cpp b/utils/TableGen/EDEmitter.cpp new file mode 100644 index 0000000000000..9aad2f609b489 --- /dev/null +++ b/utils/TableGen/EDEmitter.cpp @@ -0,0 +1,665 @@ +//===- EDEmitter.cpp - Generate instruction descriptions for ED -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend is responsible for emitting a description of each +// instruction in a format that the enhanced disassembler can use to tokenize +// and parse instructions. +// +//===----------------------------------------------------------------------===// + +#include "EDEmitter.h" + +#include "AsmWriterInst.h" +#include "CodeGenTarget.h" +#include "Record.h" + +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include <vector> +#include <string> + +#define MAX_OPERANDS 5 +#define MAX_SYNTAXES 2 + +using namespace llvm; + +/////////////////////////////////////////////////////////// +// Support classes for emitting nested C data structures // +/////////////////////////////////////////////////////////// + +namespace { + + class EnumEmitter { + private: + std::string Name; + std::vector<std::string> Entries; + public: + EnumEmitter(const char *N) : Name(N) { + } + int addEntry(const char *e) { + Entries.push_back(std::string(e)); + return Entries.size() - 1; + } + void emit(raw_ostream &o, unsigned int &i) { + o.indent(i) << "enum " << Name.c_str() << " {" << "\n"; + i += 2; + + unsigned int index = 0; + unsigned int numEntries = Entries.size(); + for(index = 0; index < numEntries; ++index) { + o.indent(i) << Entries[index]; + if(index < (numEntries - 1)) + o << ","; + o << "\n"; + } + + i -= 2; + o.indent(i) << "};" << "\n"; + } + + void emitAsFlags(raw_ostream &o, unsigned int &i) { + o.indent(i) << "enum " << Name.c_str() << " {" << "\n"; + i += 2; + + unsigned int index = 0; + unsigned int numEntries = Entries.size(); + unsigned int flag = 1; + for (index = 0; index < numEntries; ++index) { + o.indent(i) << Entries[index] << " = " << format("0x%x", flag); + if (index < (numEntries - 1)) + o << ","; + o << "\n"; + flag <<= 1; + } + + i -= 2; + o.indent(i) << "};" << "\n"; + } + }; + + class StructEmitter { + private: + std::string Name; + std::vector<std::string> MemberTypes; + std::vector<std::string> MemberNames; + public: + StructEmitter(const char *N) : Name(N) { + } + void addMember(const char *t, const char *n) { + MemberTypes.push_back(std::string(t)); + MemberNames.push_back(std::string(n)); + } + void emit(raw_ostream &o, unsigned int &i) { + o.indent(i) << "struct " << Name.c_str() << " {" << "\n"; + i += 2; + + unsigned int index = 0; + unsigned int numMembers = MemberTypes.size(); + for (index = 0; index < numMembers; ++index) { + o.indent(i) << MemberTypes[index] << " " << MemberNames[index] << ";"; + o << "\n"; + } + + i -= 2; + o.indent(i) << "};" << "\n"; + } + }; + + class ConstantEmitter { + public: + virtual ~ConstantEmitter() { } + virtual void emit(raw_ostream &o, unsigned int &i) = 0; + }; + + class LiteralConstantEmitter : public ConstantEmitter { + private: + std::string Literal; + public: + LiteralConstantEmitter(const char *literal) : Literal(literal) { + } + LiteralConstantEmitter(int literal) { + char buf[256]; + snprintf(buf, 256, "%d", literal); + Literal = buf; + } + void emit(raw_ostream &o, unsigned int &i) { + o << Literal; + } + }; + + class CompoundConstantEmitter : public ConstantEmitter { + private: + std::vector<ConstantEmitter*> Entries; + public: + CompoundConstantEmitter() { + } + ~CompoundConstantEmitter() { + unsigned int index; + unsigned int numEntries = Entries.size(); + for (index = 0; index < numEntries; ++index) { + delete Entries[index]; + } + } + CompoundConstantEmitter &addEntry(ConstantEmitter *e) { + Entries.push_back(e); + return *this; + } + void emit(raw_ostream &o, unsigned int &i) { + o << "{" << "\n"; + i += 2; + + unsigned int index; + unsigned int numEntries = Entries.size(); + for (index = 0; index < numEntries; ++index) { + o.indent(i); + Entries[index]->emit(o, i); + if (index < (numEntries - 1)) + o << ","; + o << "\n"; + } + + i -= 2; + o.indent(i) << "}"; + } + }; + + class FlagsConstantEmitter : public ConstantEmitter { + private: + std::vector<std::string> Flags; + public: + FlagsConstantEmitter() { + } + FlagsConstantEmitter &addEntry(const char *f) { + Flags.push_back(std::string(f)); + return *this; + } + void emit(raw_ostream &o, unsigned int &i) { + unsigned int index; + unsigned int numFlags = Flags.size(); + if (numFlags == 0) + o << "0"; + + for (index = 0; index < numFlags; ++index) { + o << Flags[index].c_str(); + if (index < (numFlags - 1)) + o << " | "; + } + } + }; +} + +EDEmitter::EDEmitter(RecordKeeper &R) : Records(R) { +} + +/// populateOperandOrder - Accepts a CodeGenInstruction and generates its +/// AsmWriterInst for the desired assembly syntax, giving an ordered list of +/// operands in the order they appear in the printed instruction. Then, for +/// each entry in that list, determines the index of the same operand in the +/// CodeGenInstruction, and emits the resulting mapping into an array, filling +/// in unused slots with -1. +/// +/// @arg operandOrder - The array that will be populated with the operand +/// mapping. Each entry will contain -1 (invalid index +/// into the operands present in the AsmString) or a number +/// representing an index in the operand descriptor array. +/// @arg inst - The instruction to use when looking up the operands +/// @arg syntax - The syntax to use, according to LLVM's enumeration +void populateOperandOrder(CompoundConstantEmitter *operandOrder, + const CodeGenInstruction &inst, + unsigned syntax) { + unsigned int numArgs = 0; + + AsmWriterInst awInst(inst, syntax, -1, -1); + + std::vector<AsmWriterOperand>::iterator operandIterator; + + for (operandIterator = awInst.Operands.begin(); + operandIterator != awInst.Operands.end(); + ++operandIterator) { + if (operandIterator->OperandType == + AsmWriterOperand::isMachineInstrOperand) { + char buf[2]; + snprintf(buf, sizeof(buf), "%u", operandIterator->CGIOpNo); + operandOrder->addEntry(new LiteralConstantEmitter(buf)); + numArgs++; + } + } + + for(; numArgs < MAX_OPERANDS; numArgs++) { + operandOrder->addEntry(new LiteralConstantEmitter("-1")); + } +} + +///////////////////////////////////////////////////// +// Support functions for handling X86 instructions // +///////////////////////////////////////////////////// + +#define ADDFLAG(flag) flags->addEntry(flag) + +#define REG(str) if (name == str) { ADDFLAG("kOperandFlagRegister"); return 0; } +#define MEM(str) if (name == str) { ADDFLAG("kOperandFlagMemory"); return 0; } +#define LEA(str) if (name == str) { ADDFLAG("kOperandFlagEffectiveAddress"); \ + return 0; } +#define IMM(str) if (name == str) { ADDFLAG("kOperandFlagImmediate"); \ + return 0; } +#define PCR(str) if (name == str) { ADDFLAG("kOperandFlagMemory"); \ + ADDFLAG("kOperandFlagPCRelative"); \ + return 0; } + +/// X86FlagFromOpName - Processes the name of a single X86 operand (which is +/// actually its type) and translates it into an operand flag +/// +/// @arg flags - The flags object to add the flag to +/// @arg name - The name of the operand +static int X86FlagFromOpName(FlagsConstantEmitter *flags, + const std::string &name) { + REG("GR8"); + REG("GR8_NOREX"); + REG("GR16"); + REG("GR32"); + REG("GR32_NOREX"); + REG("FR32"); + REG("RFP32"); + REG("GR64"); + REG("FR64"); + REG("VR64"); + REG("RFP64"); + REG("RFP80"); + REG("VR128"); + REG("RST"); + REG("SEGMENT_REG"); + REG("DEBUG_REG"); + REG("CONTROL_REG_32"); + REG("CONTROL_REG_64"); + + MEM("i8mem"); + MEM("i8mem_NOREX"); + MEM("i16mem"); + MEM("i32mem"); + MEM("f32mem"); + MEM("ssmem"); + MEM("opaque32mem"); + MEM("opaque48mem"); + MEM("i64mem"); + MEM("f64mem"); + MEM("sdmem"); + MEM("f80mem"); + MEM("opaque80mem"); + MEM("i128mem"); + MEM("f128mem"); + MEM("opaque512mem"); + + LEA("lea32mem"); + LEA("lea64_32mem"); + LEA("lea64mem"); + + IMM("i8imm"); + IMM("i16imm"); + IMM("i16i8imm"); + IMM("i32imm"); + IMM("i32imm_pcrel"); + IMM("i32i8imm"); + IMM("i64imm"); + IMM("i64i8imm"); + IMM("i64i32imm"); + IMM("i64i32imm_pcrel"); + IMM("SSECC"); + + PCR("brtarget8"); + PCR("offset8"); + PCR("offset16"); + PCR("offset32"); + PCR("offset64"); + PCR("brtarget"); + + return 1; +} + +#undef REG +#undef MEM +#undef LEA +#undef IMM +#undef PCR +#undef ADDFLAG + +/// X86PopulateOperands - Handles all the operands in an X86 instruction, adding +/// the appropriate flags to their descriptors +/// +/// @operandFlags - A reference the array of operand flag objects +/// @inst - The instruction to use as a source of information +static void X86PopulateOperands( + FlagsConstantEmitter *(&operandFlags)[MAX_OPERANDS], + const CodeGenInstruction &inst) { + if (!inst.TheDef->isSubClassOf("X86Inst")) + return; + + unsigned int index; + unsigned int numOperands = inst.OperandList.size(); + + for (index = 0; index < numOperands; ++index) { + const CodeGenInstruction::OperandInfo &operandInfo = + inst.OperandList[index]; + Record &rec = *operandInfo.Rec; + + if (X86FlagFromOpName(operandFlags[index], rec.getName())) { + errs() << "Operand type: " << rec.getName().c_str() << "\n"; + errs() << "Operand name: " << operandInfo.Name.c_str() << "\n"; + errs() << "Instruction mame: " << inst.TheDef->getName().c_str() << "\n"; + llvm_unreachable("Unhandled type"); + } + } +} + +/// decorate1 - Decorates a named operand with a new flag +/// +/// @operandFlags - The array of operand flag objects, which don't have names +/// @inst - The CodeGenInstruction, which provides a way to translate +/// between names and operand indices +/// @opName - The name of the operand +/// @flag - The name of the flag to add +static inline void decorate1(FlagsConstantEmitter *(&operandFlags)[MAX_OPERANDS], + const CodeGenInstruction &inst, + const char *opName, + const char *opFlag) { + unsigned opIndex; + + opIndex = inst.getOperandNamed(std::string(opName)); + + operandFlags[opIndex]->addEntry(opFlag); +} + +#define DECORATE1(opName, opFlag) decorate1(operandFlags, inst, opName, opFlag) + +#define MOV(source, target) { \ + instFlags.addEntry("kInstructionFlagMove"); \ + DECORATE1(source, "kOperandFlagSource"); \ + DECORATE1(target, "kOperandFlagTarget"); \ +} + +#define BRANCH(target) { \ + instFlags.addEntry("kInstructionFlagBranch"); \ + DECORATE1(target, "kOperandFlagTarget"); \ +} + +#define PUSH(source) { \ + instFlags.addEntry("kInstructionFlagPush"); \ + DECORATE1(source, "kOperandFlagSource"); \ +} + +#define POP(target) { \ + instFlags.addEntry("kInstructionFlagPop"); \ + DECORATE1(target, "kOperandFlagTarget"); \ +} + +#define CALL(target) { \ + instFlags.addEntry("kInstructionFlagCall"); \ + DECORATE1(target, "kOperandFlagTarget"); \ +} + +#define RETURN() { \ + instFlags.addEntry("kInstructionFlagReturn"); \ +} + +/// X86ExtractSemantics - Performs various checks on the name of an X86 +/// instruction to determine what sort of an instruction it is and then adds +/// the appropriate flags to the instruction and its operands +/// +/// @arg instFlags - A reference to the flags for the instruction as a whole +/// @arg operandFlags - A reference to the array of operand flag object pointers +/// @arg inst - A reference to the original instruction +static void X86ExtractSemantics(FlagsConstantEmitter &instFlags, + FlagsConstantEmitter *(&operandFlags)[MAX_OPERANDS], + const CodeGenInstruction &inst) { + const std::string &name = inst.TheDef->getName(); + + if (name.find("MOV") != name.npos) { + if (name.find("MOV_V") != name.npos) { + // ignore (this is a pseudoinstruction) + } + else if (name.find("MASK") != name.npos) { + // ignore (this is a masking move) + } + else if (name.find("r0") != name.npos) { + // ignore (this is a pseudoinstruction) + } + else if (name.find("PS") != name.npos || + name.find("PD") != name.npos) { + // ignore (this is a shuffling move) + } + else if (name.find("MOVS") != name.npos) { + // ignore (this is a string move) + } + else if (name.find("_F") != name.npos) { + // TODO handle _F moves to ST(0) + } + else if (name.find("a") != name.npos) { + // TODO handle moves to/from %ax + } + else if (name.find("CMOV") != name.npos) { + MOV("src2", "dst"); + } + else if (name.find("PC") != name.npos) { + MOV("label", "reg") + } + else { + MOV("src", "dst"); + } + } + + if (name.find("JMP") != name.npos || + name.find("J") == 0) { + if (name.find("FAR") != name.npos && name.find("i") != name.npos) { + BRANCH("off"); + } + else { + BRANCH("dst"); + } + } + + if (name.find("PUSH") != name.npos) { + if (name.find("FS") != name.npos || + name.find("GS") != name.npos) { + instFlags.addEntry("kInstructionFlagPush"); + // TODO add support for fixed operands + } + else if (name.find("F") != name.npos) { + // ignore (this pushes onto the FP stack) + } + else if (name[name.length() - 1] == 'm') { + PUSH("src"); + } + else if (name.find("i") != name.npos) { + PUSH("imm"); + } + else { + PUSH("reg"); + } + } + + if (name.find("POP") != name.npos) { + if (name.find("POPCNT") != name.npos) { + // ignore (not a real pop) + } + else if (name.find("FS") != name.npos || + name.find("GS") != name.npos) { + instFlags.addEntry("kInstructionFlagPop"); + // TODO add support for fixed operands + } + else if (name.find("F") != name.npos) { + // ignore (this pops from the FP stack) + } + else if (name[name.length() - 1] == 'm') { + POP("dst"); + } + else { + POP("reg"); + } + } + + if (name.find("CALL") != name.npos) { + if (name.find("ADJ") != name.npos) { + // ignore (not a call) + } + else if (name.find("SYSCALL") != name.npos) { + // ignore (doesn't go anywhere we know about) + } + else if (name.find("VMCALL") != name.npos) { + // ignore (rather different semantics than a regular call) + } + else if (name.find("FAR") != name.npos && name.find("i") != name.npos) { + CALL("off"); + } + else { + CALL("dst"); + } + } + + if (name.find("RET") != name.npos) { + RETURN(); + } +} + +#undef MOV +#undef BRANCH +#undef PUSH +#undef POP +#undef CALL +#undef RETURN + +#undef COND_DECORATE_2 +#undef COND_DECORATE_1 +#undef DECORATE1 + +/// populateInstInfo - Fills an array of InstInfos with information about each +/// instruction in a target +/// +/// @arg infoArray - The array of InstInfo objects to populate +/// @arg target - The CodeGenTarget to use as a source of instructions +static void populateInstInfo(CompoundConstantEmitter &infoArray, + CodeGenTarget &target) { + std::vector<const CodeGenInstruction*> numberedInstructions; + target.getInstructionsByEnumValue(numberedInstructions); + + unsigned int index; + unsigned int numInstructions = numberedInstructions.size(); + + for (index = 0; index < numInstructions; ++index) { + const CodeGenInstruction& inst = *numberedInstructions[index]; + + CompoundConstantEmitter *infoStruct = new CompoundConstantEmitter; + infoArray.addEntry(infoStruct); + + FlagsConstantEmitter *instFlags = new FlagsConstantEmitter; + infoStruct->addEntry(instFlags); + + LiteralConstantEmitter *numOperandsEmitter = + new LiteralConstantEmitter(inst.OperandList.size()); + infoStruct->addEntry(numOperandsEmitter); + + CompoundConstantEmitter *operandFlagArray = new CompoundConstantEmitter; + infoStruct->addEntry(operandFlagArray); + + FlagsConstantEmitter *operandFlags[MAX_OPERANDS]; + + for (unsigned operandIndex = 0; operandIndex < MAX_OPERANDS; ++operandIndex) { + operandFlags[operandIndex] = new FlagsConstantEmitter; + operandFlagArray->addEntry(operandFlags[operandIndex]); + } + + unsigned numSyntaxes = 0; + + if (target.getName() == "X86") { + X86PopulateOperands(operandFlags, inst); + X86ExtractSemantics(*instFlags, operandFlags, inst); + numSyntaxes = 2; + } + + CompoundConstantEmitter *operandOrderArray = new CompoundConstantEmitter; + infoStruct->addEntry(operandOrderArray); + + for (unsigned syntaxIndex = 0; syntaxIndex < MAX_SYNTAXES; ++syntaxIndex) { + CompoundConstantEmitter *operandOrder = new CompoundConstantEmitter; + operandOrderArray->addEntry(operandOrder); + + if (syntaxIndex < numSyntaxes) { + populateOperandOrder(operandOrder, inst, syntaxIndex); + } + else { + for (unsigned operandIndex = 0; + operandIndex < MAX_OPERANDS; + ++operandIndex) { + operandOrder->addEntry(new LiteralConstantEmitter("-1")); + } + } + } + } +} + +void EDEmitter::run(raw_ostream &o) { + unsigned int i = 0; + + CompoundConstantEmitter infoArray; + CodeGenTarget target; + + populateInstInfo(infoArray, target); + + o << "InstInfo instInfo" << target.getName().c_str() << "[] = "; + infoArray.emit(o, i); + o << ";" << "\n"; +} + +void EDEmitter::runHeader(raw_ostream &o) { + EmitSourceFileHeader("Enhanced Disassembly Info Header", o); + + o << "#ifndef EDInfo_" << "\n"; + o << "#define EDInfo_" << "\n"; + o << "\n"; + o << "#include <inttypes.h>" << "\n"; + o << "\n"; + o << "#define MAX_OPERANDS " << format("%d", MAX_OPERANDS) << "\n"; + o << "#define MAX_SYNTAXES " << format("%d", MAX_SYNTAXES) << "\n"; + o << "\n"; + + unsigned int i = 0; + + EnumEmitter operandFlags("OperandFlags"); + operandFlags.addEntry("kOperandFlagImmediate"); + operandFlags.addEntry("kOperandFlagRegister"); + operandFlags.addEntry("kOperandFlagMemory"); + operandFlags.addEntry("kOperandFlagEffectiveAddress"); + operandFlags.addEntry("kOperandFlagPCRelative"); + operandFlags.addEntry("kOperandFlagSource"); + operandFlags.addEntry("kOperandFlagTarget"); + operandFlags.emitAsFlags(o, i); + + o << "\n"; + + EnumEmitter instructionFlags("InstructionFlags"); + instructionFlags.addEntry("kInstructionFlagMove"); + instructionFlags.addEntry("kInstructionFlagBranch"); + instructionFlags.addEntry("kInstructionFlagPush"); + instructionFlags.addEntry("kInstructionFlagPop"); + instructionFlags.addEntry("kInstructionFlagCall"); + instructionFlags.addEntry("kInstructionFlagReturn"); + instructionFlags.emitAsFlags(o, i); + + o << "\n"; + + StructEmitter instInfo("InstInfo"); + instInfo.addMember("uint32_t", "instructionFlags"); + instInfo.addMember("uint8_t", "numOperands"); + instInfo.addMember("uint8_t", "operandFlags[MAX_OPERANDS]"); + instInfo.addMember("const char", "operandOrders[MAX_SYNTAXES][MAX_OPERANDS]"); + instInfo.emit(o, i); + + o << "\n"; + o << "#endif" << "\n"; +} |
