//===----------------------------------------------------------------------===//
#include "MILexer.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include <cctype>
C == '$';
}
+void MIToken::unescapeQuotedStringValue(std::string &Str) const {
+ assert(isStringValueQuoted() && "String value isn't quoted");
+ StringRef Value = Range.drop_front(StringOffset);
+ assert(Value.front() == '"' && Value.back() == '"');
+ Cursor C = Cursor(Value.substr(1, Value.size() - 2));
+
+ Str.clear();
+ Str.reserve(C.remaining().size());
+ while (!C.isEOF()) {
+ char Char = C.peek();
+ if (Char == '\\') {
+ if (C.peek(1) == '\\') {
+ // Two '\' become one
+ Str += '\\';
+ C.advance(2);
+ continue;
+ }
+ if (isxdigit(C.peek(1)) && isxdigit(C.peek(2))) {
+ Str += hexDigitValue(C.peek(1)) * 16 + hexDigitValue(C.peek(2));
+ C.advance(3);
+ continue;
+ }
+ }
+ Str += Char;
+ C.advance();
+ }
+}
+
+/// Lex a string constant using the following regular expression: \"[^\"]*\"
+static Cursor lexStringConstant(
+ Cursor C,
+ function_ref<void(StringRef::iterator Loc, const Twine &)> ErrorCallback) {
+ assert(C.peek() == '"');
+ for (C.advance(); C.peek() != '"'; C.advance()) {
+ if (C.isEOF()) {
+ ErrorCallback(
+ C.location(),
+ "end of machine instruction reached before the closing '\"'");
+ return None;
+ }
+ }
+ C.advance();
+ return C;
+}
+
static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
return StringSwitch<MIToken::TokenKind>(Identifier)
.Case("_", MIToken::underscore)
return C;
}
-static Cursor maybeLexGlobalValue(Cursor C, MIToken &Token) {
+static Cursor maybeLexGlobalValue(
+ Cursor C, MIToken &Token,
+ function_ref<void(StringRef::iterator Loc, const Twine &)> ErrorCallback) {
if (C.peek() != '@')
return None;
auto Range = C;
C.advance(); // Skip the '@'
- // TODO: add support for quoted names.
+ if (C.peek() == '"') {
+ if (Cursor R = lexStringConstant(C, ErrorCallback)) {
+ Token = MIToken(MIToken::QuotedNamedGlobalValue, Range.upto(R),
+ /*StringOffset=*/1); // Drop the '@'
+ return R;
+ }
+ Token = MIToken(MIToken::Error, Range.remaining());
+ return Range;
+ }
if (!isdigit(C.peek())) {
while (isIdentifierChar(C.peek()))
C.advance();
return R.remaining();
if (Cursor R = maybeLexRegister(C, Token))
return R.remaining();
- if (Cursor R = maybeLexGlobalValue(C, Token))
+ if (Cursor R = maybeLexGlobalValue(C, Token, ErrorCallback))
return R.remaining();
if (Cursor R = maybeLexIntegerLiteral(C, Token))
return R.remaining();
StackObject,
FixedStackObject,
NamedGlobalValue,
+ QuotedNamedGlobalValue,
GlobalValue,
// Other tokens
StringRef::iterator location() const { return Range.begin(); }
- StringRef stringValue() const { return Range.drop_front(StringOffset); }
+ bool isStringValueQuoted() const { return Kind == QuotedNamedGlobalValue; }
+
+ /// Return the token's raw string value.
+ ///
+ /// If the string value is quoted, this method returns that quoted string as
+ /// it is, without unescaping the string value.
+ StringRef rawStringValue() const { return Range.drop_front(StringOffset); }
+
+ /// Return token's string value.
+ ///
+ /// Expects the string value to be unquoted.
+ StringRef stringValue() const {
+ assert(!isStringValueQuoted() && "String value is quoted");
+ return Range.drop_front(StringOffset);
+ }
+
+ /// Unescapes the token's string value.
+ ///
+ /// Expects the string value to be quoted.
+ void unescapeQuotedStringValue(std::string &Str) const;
const APSInt &integerValue() const { return IntVal; }
namespace {
+struct StringValueUtility {
+ StringRef String;
+ std::string UnescapedString;
+
+ StringValueUtility(const MIToken &Token) {
+ if (Token.isStringValueQuoted()) {
+ Token.unescapeQuotedStringValue(UnescapedString);
+ String = UnescapedString;
+ return;
+ }
+ String = Token.stringValue();
+ }
+
+ operator StringRef() const { return String; }
+};
+
/// A wrapper struct around the 'MachineOperand' struct that includes a source
/// range.
struct MachineOperandWithLocation {
bool MIParser::parseGlobalAddressOperand(MachineOperand &Dest) {
switch (Token.kind()) {
- case MIToken::NamedGlobalValue: {
- auto Name = Token.stringValue();
+ case MIToken::NamedGlobalValue:
+ case MIToken::QuotedNamedGlobalValue: {
+ StringValueUtility Name(Token);
const Module *M = MF.getFunction()->getParent();
if (const auto *GV = M->getNamedValue(Name)) {
Dest = MachineOperand::CreateGA(GV, /*Offset=*/0);
break;
}
- return error(Twine("use of undefined global value '@") + Name + "'");
+ return error(Twine("use of undefined global value '@") +
+ Token.rawStringValue() + "'");
}
case MIToken::GlobalValue: {
unsigned GVIdx;
return parseFixedStackObjectOperand(Dest);
case MIToken::GlobalValue:
case MIToken::NamedGlobalValue:
+ case MIToken::QuotedNamedGlobalValue:
return parseGlobalAddressOperand(Dest);
case MIToken::JumpTableIndex:
return parseJumpTableIndexOperand(Dest);
ret i32 %b
}
+ @"\01Hello@$%09 \\ World," = external global i32
+
+ define i32 @test2() {
+ entry:
+ %a = load i32, i32* @"\01Hello@$%09 \\ World,"
+ ret i32 %a
+ }
+
...
---
# CHECK: name: inc
- 'MOV32mr killed %rcx, 1, _, 0, _, %eax'
- 'RETQ %eax'
...
+---
+name: test2
+body:
+ - id: 0
+ name: entry
+ instructions:
+ # CHECK: , @"\01Hello@$%09 \5C World,",
+ - '%rax = MOV64rm %rip, 1, _, @"\01Hello@$%09 \\ World,", _'
+ - '%eax = MOV32rm killed %rax, 1, _, 0, _'
+ - 'RETQ %eax'
+...
--- /dev/null
+# RUN: not llc -march=x86-64 -start-after branch-folder -stop-after branch-folder -o /dev/null %s 2>&1 | FileCheck %s
+
+--- |
+
+ @"quoted name" = external global i32
+
+ define i32 @test() {
+ entry:
+ %a = load i32, i32* @"quoted name"
+ ret i32 %a
+ }
+
+...
+---
+name: test
+body:
+ - id: 0
+ name: entry
+ instructions:
+ # CHECK: [[@LINE+1]]:53: end of machine instruction reached before the closing '"'
+ - '%rax = MOV64rm %rip, 1, _, @"quoted name, _'
+ - '%eax = MOV32rm killed %rax, 1, _, 0, _'
+ - 'RETQ %eax'
+...