| 1 | //===- AsmWriterInst.h - Classes encapsulating a printable inst -----------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // These classes implement a parser for assembly strings. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "AsmWriterInst.h" |
| 14 | #include "CodeGenInstruction.h" |
| 15 | #include "llvm/ADT/StringExtras.h" |
| 16 | #include "llvm/TableGen/Error.h" |
| 17 | #include "llvm/TableGen/Record.h" |
| 18 | |
| 19 | using namespace llvm; |
| 20 | |
| 21 | static bool isIdentChar(char C) { return isAlnum(C) || C == '_'; } |
| 22 | |
| 23 | std::string AsmWriterOperand::getCode(bool PassSubtarget) const { |
| 24 | if (OperandType == isLiteralTextOperand) { |
| 25 | if (Str.size() == 1) |
| 26 | return "O << '" + Str + "';" ; |
| 27 | return "O << \"" + Str + "\";" ; |
| 28 | } |
| 29 | |
| 30 | if (OperandType == isLiteralStatementOperand) |
| 31 | return Str; |
| 32 | |
| 33 | std::string Result = Str + "(MI" ; |
| 34 | if (PCRel) |
| 35 | Result += ", Address" ; |
| 36 | if (MIOpNo != ~0U) |
| 37 | Result += ", " + utostr(X: MIOpNo); |
| 38 | if (PassSubtarget) |
| 39 | Result += ", STI" ; |
| 40 | Result += ", O" ; |
| 41 | if (!MiModifier.empty()) |
| 42 | Result += ", \"" + MiModifier + '"'; |
| 43 | return Result + ");" ; |
| 44 | } |
| 45 | |
| 46 | /// ParseAsmString - Parse the specified Instruction's AsmString into this |
| 47 | /// AsmWriterInst. |
| 48 | /// |
| 49 | AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned CGIIndex, |
| 50 | unsigned Variant) |
| 51 | : CGI(&CGI), CGIIndex(CGIIndex) { |
| 52 | |
| 53 | // NOTE: Any extensions to this code need to be mirrored in the |
| 54 | // AsmPrinter::printInlineAsm code that executes as compile time (assuming |
| 55 | // that inline asm strings should also get the new feature)! |
| 56 | std::string AsmString = CGI.FlattenAsmStringVariants(AsmString: CGI.AsmString, Variant); |
| 57 | std::string::size_type LastEmitted = 0; |
| 58 | while (LastEmitted != AsmString.size()) { |
| 59 | std::string::size_type DollarPos = |
| 60 | AsmString.find_first_of(s: "$\\" , pos: LastEmitted); |
| 61 | if (DollarPos == std::string::npos) |
| 62 | DollarPos = AsmString.size(); |
| 63 | |
| 64 | // Emit a constant string fragment. |
| 65 | if (DollarPos != LastEmitted) { |
| 66 | for (; LastEmitted != DollarPos; ++LastEmitted) |
| 67 | switch (AsmString[LastEmitted]) { |
| 68 | case '\n': |
| 69 | AddLiteralString(Str: "\\n" ); |
| 70 | break; |
| 71 | case '\t': |
| 72 | AddLiteralString(Str: "\\t" ); |
| 73 | break; |
| 74 | case '"': |
| 75 | AddLiteralString(Str: "\\\"" ); |
| 76 | break; |
| 77 | case '\\': |
| 78 | AddLiteralString(Str: "\\\\" ); |
| 79 | break; |
| 80 | default: |
| 81 | AddLiteralString(Str: std::string(1, AsmString[LastEmitted])); |
| 82 | break; |
| 83 | } |
| 84 | } else if (AsmString[DollarPos] == '\\') { |
| 85 | if (DollarPos + 1 != AsmString.size()) { |
| 86 | if (AsmString[DollarPos + 1] == 'n') { |
| 87 | AddLiteralString(Str: "\\n" ); |
| 88 | } else if (AsmString[DollarPos + 1] == 't') { |
| 89 | AddLiteralString(Str: "\\t" ); |
| 90 | } else if (std::string("${|}\\" ).find(c: AsmString[DollarPos + 1]) != |
| 91 | std::string::npos) { |
| 92 | AddLiteralString(Str: std::string(1, AsmString[DollarPos + 1])); |
| 93 | } else { |
| 94 | PrintFatalError( |
| 95 | ErrorLoc: CGI.TheDef->getLoc(), |
| 96 | Msg: "Non-supported escaped character found in instruction '" + |
| 97 | CGI.TheDef->getName() + "'!" ); |
| 98 | } |
| 99 | LastEmitted = DollarPos + 2; |
| 100 | continue; |
| 101 | } |
| 102 | } else if (DollarPos + 1 != AsmString.size() && |
| 103 | AsmString[DollarPos + 1] == '$') { |
| 104 | AddLiteralString(Str: "$" ); // "$$" -> $ |
| 105 | LastEmitted = DollarPos + 2; |
| 106 | } else { |
| 107 | // Get the name of the variable. |
| 108 | std::string::size_type VarEnd = DollarPos + 1; |
| 109 | |
| 110 | // handle ${foo}bar as $foo by detecting whether the character following |
| 111 | // the dollar sign is a curly brace. If so, advance VarEnd and DollarPos |
| 112 | // so the variable name does not contain the leading curly brace. |
| 113 | bool hasCurlyBraces = false; |
| 114 | if (VarEnd < AsmString.size() && '{' == AsmString[VarEnd]) { |
| 115 | hasCurlyBraces = true; |
| 116 | ++DollarPos; |
| 117 | ++VarEnd; |
| 118 | } |
| 119 | |
| 120 | while (VarEnd < AsmString.size() && isIdentChar(C: AsmString[VarEnd])) |
| 121 | ++VarEnd; |
| 122 | StringRef VarName(AsmString.data() + DollarPos + 1, |
| 123 | VarEnd - DollarPos - 1); |
| 124 | |
| 125 | // Modifier - Support ${foo:modifier} syntax, where "modifier" is passed |
| 126 | // into printOperand. Also support ${:feature}, which is passed into |
| 127 | // PrintSpecial. |
| 128 | std::string Modifier; |
| 129 | |
| 130 | // In order to avoid starting the next string at the terminating curly |
| 131 | // brace, advance the end position past it if we found an opening curly |
| 132 | // brace. |
| 133 | if (hasCurlyBraces) { |
| 134 | if (VarEnd >= AsmString.size()) |
| 135 | PrintFatalError( |
| 136 | ErrorLoc: CGI.TheDef->getLoc(), |
| 137 | Msg: "Reached end of string before terminating curly brace in '" + |
| 138 | CGI.TheDef->getName() + "'" ); |
| 139 | |
| 140 | // Look for a modifier string. |
| 141 | if (AsmString[VarEnd] == ':') { |
| 142 | ++VarEnd; |
| 143 | if (VarEnd >= AsmString.size()) |
| 144 | PrintFatalError( |
| 145 | ErrorLoc: CGI.TheDef->getLoc(), |
| 146 | Msg: "Reached end of string before terminating curly brace in '" + |
| 147 | CGI.TheDef->getName() + "'" ); |
| 148 | |
| 149 | std::string::size_type ModifierStart = VarEnd; |
| 150 | while (VarEnd < AsmString.size() && isIdentChar(C: AsmString[VarEnd])) |
| 151 | ++VarEnd; |
| 152 | Modifier = AsmString.substr(pos: ModifierStart, n: VarEnd - ModifierStart); |
| 153 | if (Modifier.empty()) |
| 154 | PrintFatalError(ErrorLoc: CGI.TheDef->getLoc(), |
| 155 | Msg: "Bad operand modifier name in '" + |
| 156 | CGI.TheDef->getName() + "'" ); |
| 157 | } |
| 158 | |
| 159 | if (AsmString[VarEnd] != '}') |
| 160 | PrintFatalError( |
| 161 | ErrorLoc: CGI.TheDef->getLoc(), |
| 162 | Msg: "Variable name beginning with '{' did not end with '}' in '" + |
| 163 | CGI.TheDef->getName() + "'" ); |
| 164 | ++VarEnd; |
| 165 | } |
| 166 | if (VarName.empty() && Modifier.empty()) |
| 167 | PrintFatalError(ErrorLoc: CGI.TheDef->getLoc(), |
| 168 | Msg: "Stray '$' in '" + CGI.TheDef->getName() + |
| 169 | "' asm string, maybe you want $$?" ); |
| 170 | |
| 171 | if (VarName.empty()) { |
| 172 | // Just a modifier, pass this into PrintSpecial. |
| 173 | Operands.emplace_back(args: "PrintSpecial" , args: ~0U, args&: Modifier); |
| 174 | } else { |
| 175 | // Otherwise, normal operand. |
| 176 | unsigned OpNo = CGI.Operands.getOperandNamed(Name: VarName); |
| 177 | CGIOperandList::OperandInfo OpInfo = CGI.Operands[OpNo]; |
| 178 | |
| 179 | unsigned MIOp = OpInfo.MIOperandNo; |
| 180 | Operands.emplace_back(args: OpInfo.PrinterMethodName.str(), args&: MIOp, args&: Modifier, |
| 181 | args: AsmWriterOperand::isMachineInstrOperand, |
| 182 | args: OpInfo.OperandType == "MCOI::OPERAND_PCREL" ); |
| 183 | } |
| 184 | LastEmitted = VarEnd; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | Operands.emplace_back(args: "return;" , args: AsmWriterOperand::isLiteralStatementOperand); |
| 189 | } |
| 190 | |
| 191 | /// MatchesAllButOneOp - If this instruction is exactly identical to the |
| 192 | /// specified instruction except for one differing operand, return the differing |
| 193 | /// operand number. If more than one operand mismatches, return ~1, otherwise |
| 194 | /// if the instructions are identical return ~0. |
| 195 | unsigned AsmWriterInst::MatchesAllButOneOp(const AsmWriterInst &Other) const { |
| 196 | if (Operands.size() != Other.Operands.size()) |
| 197 | return ~1; |
| 198 | |
| 199 | unsigned MismatchOperand = ~0U; |
| 200 | for (unsigned i = 0, e = Operands.size(); i != e; ++i) { |
| 201 | if (Operands[i] != Other.Operands[i]) { |
| 202 | if (MismatchOperand != ~0U) // Already have one mismatch? |
| 203 | return ~1U; |
| 204 | MismatchOperand = i; |
| 205 | } |
| 206 | } |
| 207 | return MismatchOperand; |
| 208 | } |
| 209 | |