diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp index 711a20ba0c8..d44515fe2e1 100644 --- a/utils/TableGen/AsmWriterEmitter.cpp +++ b/utils/TableGen/AsmWriterEmitter.cpp @@ -25,6 +25,154 @@ static bool isIdentChar(char C) { 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; + + /// OpVT - For isMachineInstrOperand, this is the value type for the + /// operand. + MVT::ValueType OpVT; + + AsmWriterOperand(const std::string &LitStr) + : OperandType(isLiteralTextOperand), Str(LitStr) {} + + AsmWriterOperand(const std::string &Printer, unsigned OpNo, + MVT::ValueType VT) : OperandType(isMachineInstrOperand), + Str(Printer), MIOpNo(OpNo), OpVT(VT){} + + void EmitCode(std::ostream &OS) const; + }; + + struct AsmWriterInst { + std::vector Operands; + + void ParseAsmString(const CodeGenInstruction &CGI, unsigned Variant); + void EmitCode(std::ostream &OS) const { + for (unsigned i = 0, e = Operands.size(); i != e; ++i) + Operands[i].EmitCode(OS); + } + 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 << ", MVT::" << getName(OpVT) << "); "; +} + + +/// ParseAsmString - Parse the specified Instruction's AsmString into this +/// AsmWriterInst. +/// +void AsmWriterInst::ParseAsmString(const CodeGenInstruction &CGI, + unsigned Variant) { + 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.Name + "'!"; + 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.Name + "'!"; + 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.Name + "'!"; + // 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.Name + "'!"; + LastEmitted = NP+1; + inVariant = false; + } else if (AsmString[DollarPos] == '}') { + if (!inVariant) + throw "'}' character found outside of a variant in instruction '" + + CGI.Name + "'!"; + 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. + // TODO: should eventually handle ${foo}bar as $foo + std::string::size_type VarEnd = DollarPos+1; + while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) + ++VarEnd; + std::string VarName(AsmString.begin()+DollarPos+1, + AsmString.begin()+VarEnd); + if (VarName.empty()) + throw "Stray '$' in '" + CGI.Name + "' asm string, maybe you want $$?"; + + unsigned OpNo = CGI.getOperandNamed(VarName); + + // If this is a two-address instruction and we are not accessing the + // 0th operand, remove an operand. + unsigned MIOp = CGI.OperandList[OpNo].MIOperandNo; + if (CGI.isTwoAddress && MIOp != 0) { + if (MIOp == 1) + throw "Should refer to operand #0 instead of #1 for two-address" + " instruction '" + CGI.Name + "'!"; + --MIOp; + } + + Operands.push_back(AsmWriterOperand(CGI.OperandList[OpNo].PrinterMethodName, + MIOp, CGI.OperandList[OpNo].Ty)); + LastEmitted = VarEnd; + } + } + + AddLiteralString("\\n"); +} + + void AsmWriterEmitter::run(std::ostream &O) { EmitSourceFileHeader("Assembly Writer Source Fragment", O); @@ -45,96 +193,15 @@ void AsmWriterEmitter::run(std::ostream &O) { std::string Namespace = Target.inst_begin()->second.Namespace; - bool inVariant = false; // True if we are inside a {.|.|.} region. - for (CodeGenTarget::inst_iterator I = Target.inst_begin(), E = Target.inst_end(); I != E; ++I) if (!I->second.AsmString.empty()) { - const std::string &AsmString = I->second.AsmString; - O << " case " << Namespace << "::" << I->first << ": O "; + O << " case " << Namespace << "::" << I->first << ": "; - 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. - O << " << \"" << std::string(AsmString.begin()+LastEmitted, - AsmString.begin()+DollarPos) << "\""; - LastEmitted = DollarPos; - } else if (AsmString[DollarPos] == '{') { - if (inVariant) - throw "Nested variants found for instruction '" + I->first + "'!"; - 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 '" + I->first + "'!"; - 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 '" - + I->first + "'!"; - // 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 '" + I->first + "'!"; - LastEmitted = NP+1; - inVariant = false; - } else if (AsmString[DollarPos] == '}') { - if (!inVariant) - throw "'}' character found outside of a variant in instruction '" - + I->first + "'!"; - LastEmitted = DollarPos+1; - inVariant = false; - } else if (DollarPos+1 != AsmString.size() && - AsmString[DollarPos+1] == '$') { - O << " << '$'"; // "$$" -> $ - LastEmitted = DollarPos+2; - } else { - // Get the name of the variable. - // TODO: should eventually handle ${foo}bar as $foo - std::string::size_type VarEnd = DollarPos+1; - while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) - ++VarEnd; - std::string VarName(AsmString.begin()+DollarPos+1, - AsmString.begin()+VarEnd); - if (VarName.empty()) - throw "Stray '$' in '" + I->first + - "' asm string, maybe you want $$?"; - unsigned OpNo = I->second.getOperandNamed(VarName); - - // If this is a two-address instruction and we are not accessing the - // 0th operand, remove an operand. - unsigned MIOp = I->second.OperandList[OpNo].MIOperandNo; - if (I->second.isTwoAddress && MIOp != 0) { - if (MIOp == 1) - throw "Should refer to operand #0 instead of #1 for two-address" - " instruction '" + I->first + "'!"; - --MIOp; - } - - O << "; " << I->second.OperandList[OpNo].PrinterMethodName - << "(MI, " << MIOp << ", MVT::" - << getName(I->second.OperandList[OpNo].Ty) << "); O "; - LastEmitted = VarEnd; - } - } - - O << " << '\\n'; break;\n"; + AsmWriterInst AWI; + AWI.ParseAsmString(I->second, Variant); + AWI.EmitCode(O); + O << " break;\n"; } O << " }\n"