[ms] [llvm-ml] Add support for .radix directive, and accept all radix specifiers
authorEric Astor <epastor@google.com>
Tue, 29 Sep 2020 20:17:47 +0000 (16:17 -0400)
committerEric Astor <epastor@google.com>
Tue, 29 Sep 2020 20:55:51 +0000 (16:55 -0400)
Add support for .radix directive, and radix specifiers [yY] (binary), [oOqQ] (octal), and [tT] (decimal).

Also, when lexing MASM integers, require radix specifier; MASM requires that all literals without a radix specifier be treated as in the default radix. (e.g., 0100 = 100)

Relanding D87400, now with fewer ms-inline-asm tests broken!

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D88337

llvm/include/llvm/MC/MCParser/MCAsmLexer.h
llvm/lib/MC/MCParser/AsmLexer.cpp
llvm/lib/MC/MCParser/COFFMasmParser.cpp
llvm/lib/MC/MCParser/MasmParser.cpp
llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
llvm/test/tools/llvm-ml/radix.test [new file with mode: 0644]
llvm/test/tools/llvm-ml/radix_errors.test [new file with mode: 0644]
llvm/tools/llvm-ml/llvm-ml.cpp

index e89abea..1e449a7 100644 (file)
@@ -50,6 +50,8 @@ protected: // Can only create subclasses.
   bool AllowAtInIdentifier;
   bool IsAtStartOfStatement = true;
   bool LexMasmIntegers = false;
+  bool UseMasmDefaultRadix = false;
+  unsigned DefaultRadix = 10;
   AsmCommentConsumer *CommentConsumer = nullptr;
 
   MCAsmLexer();
@@ -147,9 +149,16 @@ public:
     this->CommentConsumer = CommentConsumer;
   }
 
-  /// Set whether to lex masm-style binary and hex literals. They look like
-  /// 0b1101 and 0ABCh respectively.
+  /// Set whether to lex masm-style binary (e.g., 0b1101) and radix-specified
+  /// literals (e.g., 0ABCh [hex], 576t [decimal], 77o [octal], 1101y [binary]).
   void setLexMasmIntegers(bool V) { LexMasmIntegers = V; }
+
+  /// Set whether to use masm-style default-radix integer literals. If disabled,
+  /// assume decimal unless prefixed (e.g., 0x2c [hex], 077 [octal]).
+  void useMasmDefaultRadix(bool V) { UseMasmDefaultRadix = V; }
+
+  unsigned getMasmDefaultRadix() const { return DefaultRadix; }
+  void setMasmDefaultRadix(unsigned Radix) { DefaultRadix = Radix; }
 };
 
 } // end namespace llvm
index 5a571c7..12a71d6 100644 (file)
@@ -18,6 +18,7 @@
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/MC/MCAsmInfo.h"
 #include "llvm/MC/MCParser/MCAsmLexer.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SaveAndRestore.h"
 #include <cassert>
@@ -271,13 +272,34 @@ static unsigned doHexLookAhead(const char *&CurPtr, unsigned DefaultRadix,
   return DefaultRadix;
 }
 
-static AsmToken intToken(StringRef Ref, APInt &Value)
-{
+static const char *findLastDigit(const char *CurPtr, unsigned DefaultRadix) {
+  while (hexDigitValue(*CurPtr) < DefaultRadix) {
+    ++CurPtr;
+  }
+  return CurPtr;
+}
+
+static AsmToken intToken(StringRef Ref, APInt &Value) {
   if (Value.isIntN(64))
     return AsmToken(AsmToken::Integer, Ref, Value);
   return AsmToken(AsmToken::BigNum, Ref, Value);
 }
 
+static std::string radixName(unsigned Radix) {
+  switch (Radix) {
+  case 2:
+    return "binary";
+  case 8:
+    return "octal";
+  case 10:
+    return "decimal";
+  case 16:
+    return "hexadecimal";
+  default:
+    return "base-" + std::to_string(Radix);
+  }
+}
+
 /// LexDigit: First character is [0-9].
 ///   Local Label: [0-9][:]
 ///   Forward/Backward Label: [0-9][fb]
@@ -286,16 +308,46 @@ static AsmToken intToken(StringRef Ref, APInt &Value)
 ///   Hex integer: 0x[0-9a-fA-F]+ or [0x]?[0-9][0-9a-fA-F]*[hH]
 ///   Decimal integer: [1-9][0-9]*
 AsmToken AsmLexer::LexDigit() {
-  // MASM-flavor binary integer: [01]+[bB]
+  // MASM-flavor binary integer: [01]+[yY] (if DefaultRadix < 16, [bByY])
+  // MASM-flavor octal integer: [0-7]+[oOqQ]
+  // MASM-flavor decimal integer: [0-9]+[tT] (if DefaultRadix < 16, [dDtT])
   // MASM-flavor hexadecimal integer: [0-9][0-9a-fA-F]*[hH]
   if (LexMasmIntegers && isdigit(CurPtr[-1])) {
-    const char *FirstNonBinary = (CurPtr[-1] != '0' && CurPtr[-1] != '1') ?
-                                   CurPtr - 1 : nullptr;
+    const char *FirstNonBinary =
+        (CurPtr[-1] != '0' && CurPtr[-1] != '1') ? CurPtr - 1 : nullptr;
+    const char *FirstNonDecimal =
+        (CurPtr[-1] < '0' || CurPtr[-1] > '9') ? CurPtr - 1 : nullptr;
     const char *OldCurPtr = CurPtr;
     while (isHexDigit(*CurPtr)) {
-      if (*CurPtr != '0' && *CurPtr != '1' && !FirstNonBinary)
-        FirstNonBinary = CurPtr;
+      switch (*CurPtr) {
+      default:
+        if (!FirstNonDecimal) {
+          FirstNonDecimal = CurPtr;
+        }
+        LLVM_FALLTHROUGH;
+      case '9':
+      case '8':
+      case '7':
+      case '6':
+      case '5':
+      case '4':
+      case '3':
+      case '2':
+        if (!FirstNonBinary) {
+          FirstNonBinary = CurPtr;
+        }
+        break;
+      case '1':
+      case '0':
+        break;
+      }
+      ++CurPtr;
+    }
+    if (*CurPtr == '.') {
+      // MASM float literals (other than hex floats) always contain a ".", and
+      // are always written in decimal.
       ++CurPtr;
+      return LexFloatLiteral();
     }
 
     unsigned Radix = 0;
@@ -303,28 +355,61 @@ AsmToken AsmLexer::LexDigit() {
       // hexadecimal number
       ++CurPtr;
       Radix = 16;
+    } else if (*CurPtr == 't' || *CurPtr == 'T') {
+      // decimal number
+      ++CurPtr;
+      Radix = 10;
+    } else if (*CurPtr == 'o' || *CurPtr == 'O' || *CurPtr == 'q' ||
+               *CurPtr == 'Q') {
+      // octal number
+      ++CurPtr;
+      Radix = 8;
+    } else if (*CurPtr == 'y' || *CurPtr == 'Y') {
+      // binary number
+      ++CurPtr;
+      Radix = 2;
+    } else if (FirstNonDecimal && FirstNonDecimal + 1 == CurPtr &&
+               DefaultRadix < 14 &&
+               (*FirstNonDecimal == 'd' || *FirstNonDecimal == 'D')) {
+      Radix = 10;
     } else if (FirstNonBinary && FirstNonBinary + 1 == CurPtr &&
-               (*FirstNonBinary == 'b' || *FirstNonBinary == 'B'))
+               DefaultRadix < 12 &&
+               (*FirstNonBinary == 'b' || *FirstNonBinary == 'B')) {
       Radix = 2;
+    }
 
-    if (Radix == 2 || Radix == 16) {
+    if (Radix) {
       StringRef Result(TokStart, CurPtr - TokStart);
       APInt Value(128, 0, true);
 
       if (Result.drop_back().getAsInteger(Radix, Value))
-        return ReturnError(TokStart, Radix == 2 ? "invalid binary number" :
-                             "invalid hexdecimal number");
+        return ReturnError(TokStart, "invalid " + radixName(Radix) + " number");
 
       // MSVC accepts and ignores type suffices on integer literals.
       SkipIgnoredIntegerSuffix(CurPtr);
 
       return intToken(Result, Value);
-   }
+    }
 
-    // octal/decimal integers, or floating point numbers, fall through
+    // default-radix integers, or floating point numbers, fall through
     CurPtr = OldCurPtr;
   }
 
+  // MASM default-radix integers: [0-9a-fA-F]+
+  // (All other integer literals have a radix specifier.)
+  if (LexMasmIntegers && UseMasmDefaultRadix) {
+    CurPtr = findLastDigit(CurPtr, 16);
+    StringRef Result(TokStart, CurPtr - TokStart);
+
+    APInt Value(128, 0, true);
+    if (Result.getAsInteger(DefaultRadix, Value)) {
+      return ReturnError(TokStart,
+                         "invalid " + radixName(DefaultRadix) + " number");
+    }
+
+    return intToken(Result, Value);
+  }
+
   // Decimal integer: [1-9][0-9]*
   if (CurPtr[-1] != '0' || CurPtr[0] == '.') {
     unsigned Radix = doHexLookAhead(CurPtr, 10, LexMasmIntegers);
@@ -339,13 +424,9 @@ AsmToken AsmLexer::LexDigit() {
     StringRef Result(TokStart, CurPtr - TokStart);
 
     APInt Value(128, 0, true);
-    if (Result.getAsInteger(Radix, Value))
-      return ReturnError(TokStart, !isHex ? "invalid decimal number" :
-                           "invalid hexdecimal number");
-
-    // Consume the [hH].
-    if (LexMasmIntegers && Radix == 16)
-      ++CurPtr;
+    if (Result.getAsInteger(Radix, Value)) {
+      return ReturnError(TokStart, "invalid " + radixName(Radix) + " number");
+    }
 
     // The darwin/x86 (and x86-64) assembler accepts and ignores type
     // suffices on integer literals.
@@ -416,11 +497,9 @@ AsmToken AsmLexer::LexDigit() {
   // Either octal or hexadecimal.
   APInt Value(128, 0, true);
   unsigned Radix = doHexLookAhead(CurPtr, 8, LexMasmIntegers);
-  bool isHex = Radix == 16;
   StringRef Result(TokStart, CurPtr - TokStart);
   if (Result.getAsInteger(Radix, Value))
-    return ReturnError(TokStart, !isHex ? "invalid octal number" :
-                       "invalid hexdecimal number");
+    return ReturnError(TokStart, "invalid " + radixName(Radix) + " number");
 
   // Consume the [hH].
   if (Radix == 16)
index 532ded0..575e6ee 100644 (file)
@@ -132,7 +132,6 @@ class COFFMasmParser : public MCAsmParserExtension {
     // option
     // popcontext
     // pushcontext
-    // .radix
     // .safeseh
 
     // Procedure directives
index ca9b2df..352d947 100644 (file)
@@ -732,6 +732,7 @@ private:
     DK_SAVEREG,
     DK_SAVEXMM128,
     DK_SETFRAME,
+    DK_RADIX,
   };
 
   /// Maps directive name --> DirectiveKind enum, for directives parsed by this
@@ -964,6 +965,9 @@ private:
   // ".erre" or ".errnz", depending on ExpectZero.
   bool parseDirectiveErrorIfe(SMLoc DirectiveLoc, bool ExpectZero);
 
+  // ".radix"
+  bool parseDirectiveRadix(SMLoc DirectiveLoc);
+
   // "echo"
   bool parseDirectiveEcho();
 
@@ -2284,6 +2288,8 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
       return parseDirectiveErrorIfe(IDLoc, true);
     case DK_ERRNZ:
       return parseDirectiveErrorIfe(IDLoc, false);
+    case DK_RADIX:
+      return parseDirectiveRadix(IDLoc);
     case DK_ECHO:
       return parseDirectiveEcho();
     }
@@ -6343,6 +6349,7 @@ void MasmParser::initializeDirectiveKindMap() {
   DirectiveKindMap[".savereg"] = DK_SAVEREG;
   DirectiveKindMap[".savexmm128"] = DK_SAVEXMM128;
   DirectiveKindMap[".setframe"] = DK_SETFRAME;
+  DirectiveKindMap[".radix"] = DK_RADIX;
   // DirectiveKindMap[".altmacro"] = DK_ALTMACRO;
   // DirectiveKindMap[".noaltmacro"] = DK_NOALTMACRO;
   DirectiveKindMap["db"] = DK_DB;
@@ -6584,6 +6591,22 @@ bool MasmParser::parseDirectiveMSAlign(SMLoc IDLoc, ParseStatementInfo &Info) {
   return false;
 }
 
+bool MasmParser::parseDirectiveRadix(SMLoc DirectiveLoc) {
+  const SMLoc Loc = getLexer().getLoc();
+  StringRef RadixString = parseStringToEndOfStatement().trim();
+  unsigned Radix;
+  if (RadixString.getAsInteger(10, Radix)) {
+    return Error(Loc,
+                 "radix must be a decimal number in the range 2 to 16; was " +
+                     RadixString);
+  }
+  if (Radix < 2 || Radix > 16)
+    return Error(Loc, "radix must be in the range 2 to 16; was " +
+                          std::to_string(Radix));
+  getLexer().setMasmDefaultRadix(Radix);
+  return false;
+}
+
 bool MasmParser::parseDirectiveEcho() {
   StringRef Message = parseStringToEndOfStatement();
   Lex();  // eat end of statement
index 2b48d4b..7a7c810 100644 (file)
@@ -1662,6 +1662,9 @@ bool X86AsmParser::ParseIntelExpression(IntelExprStateMachine &SM, SMLoc &End) {
       if ((Done = SM.isValidEndState()))
         break;
       return Error(Tok.getLoc(), "unknown token in expression");
+    case AsmToken::Error:
+      return Error(getLexer().getErrLoc(), getLexer().getErr());
+      break;
     case AsmToken::EndOfStatement:
       Done = true;
       break;
@@ -2453,21 +2456,26 @@ bool X86AsmParser::HandleAVX512Operand(OperandVector &Operands) {
       // Parse memory broadcasting ({1to<NUM>}).
       if (getLexer().getTok().getIntVal() != 1)
         return TokError("Expected 1to<NUM> at this point");
-      Parser.Lex();  // Eat "1" of 1to8
-      if (!getLexer().is(AsmToken::Identifier) ||
-          !getLexer().getTok().getIdentifier().startswith("to"))
+      StringRef Prefix = getLexer().getTok().getString();
+      Parser.Lex(); // Eat first token of 1to8
+      if (!getLexer().is(AsmToken::Identifier))
         return TokError("Expected 1to<NUM> at this point");
       // Recognize only reasonable suffixes.
+      SmallVector<char, 5> BroadcastVector;
+      StringRef BroadcastString = (Prefix + getLexer().getTok().getIdentifier())
+                                      .toStringRef(BroadcastVector);
+      if (!BroadcastString.startswith("1to"))
+        return TokError("Expected 1to<NUM> at this point");
       const char *BroadcastPrimitive =
-        StringSwitch<const char*>(getLexer().getTok().getIdentifier())
-          .Case("to2",  "{1to2}")
-          .Case("to4",  "{1to4}")
-          .Case("to8",  "{1to8}")
-          .Case("to16", "{1to16}")
-          .Default(nullptr);
+          StringSwitch<const char *>(BroadcastString)
+              .Case("1to2", "{1to2}")
+              .Case("1to4", "{1to4}")
+              .Case("1to8", "{1to8}")
+              .Case("1to16", "{1to16}")
+              .Default(nullptr);
       if (!BroadcastPrimitive)
         return TokError("Invalid memory broadcast primitive.");
-      Parser.Lex();  // Eat "toN" of 1toN
+      Parser.Lex(); // Eat trailing token of 1toN
       if (!getLexer().is(AsmToken::RCurly))
         return TokError("Expected } at this point");
       Parser.Lex();  // Eat "}"
diff --git a/llvm/test/tools/llvm-ml/radix.test b/llvm/test/tools/llvm-ml/radix.test
new file mode 100644 (file)
index 0000000..6433370
--- /dev/null
@@ -0,0 +1,97 @@
+# RUN: llvm-ml -filetype=asm %s | FileCheck %s
+
+.code
+
+t1:
+mov eax, 100b
+mov eax, 100y
+
+; CHECK-LABEL: t1:
+; CHECK-NEXT: mov eax, 4
+; CHECK-NEXT: mov eax, 4
+
+t2:
+mov eax, 100o
+mov eax, 100q
+
+; CHECK-LABEL: t2:
+; CHECK-NEXT: mov eax, 64
+; CHECK-NEXT: mov eax, 64
+
+t3:
+mov eax, 100d
+mov eax, 100t
+
+; CHECK-LABEL: t3:
+; CHECK-NEXT: mov eax, 100
+; CHECK-NEXT: mov eax, 100
+
+t4:
+mov eax, 100h
+
+; CHECK-LABEL: t4:
+; CHECK-NEXT: mov eax, 256
+
+t5:
+mov eax, 100
+.radix 2
+mov eax, 100
+.radix 16
+mov eax, 100
+.radix 10
+mov eax, 100
+
+; CHECK-LABEL: t5:
+; CHECK: mov eax, 100
+; CHECK: mov eax, 4
+; CHECK: mov eax, 256
+; CHECK: mov eax, 100
+
+t6:
+.radix 9
+mov eax, 100
+.radix 10
+
+; CHECK-LABEL: t6:
+; CHECK: mov eax, 81
+
+t7:
+.radix 12
+mov eax, 100b
+mov eax, 100y
+.radix 10
+
+; CHECK-LABEL: t7:
+; CHECK: mov eax, 1739
+; CHECK: mov eax, 4
+
+t8:
+.radix 16
+mov eax, 100d
+mov eax, 100t
+.radix 10
+
+; CHECK-LABEL: t8:
+; CHECK: mov eax, 4109
+; CHECK: mov eax, 100
+
+t9:
+.radix 12
+mov eax, 102b
+.radix 16
+mov eax, 10fd
+.radix 10
+
+; CHECK-LABEL: t9:
+; CHECK: mov eax, 1763
+; CHECK: mov eax, 4349
+
+t10:
+.radix 16
+mov eax, 1e1
+.radix 10
+
+; CHECK-LABEL: t10:
+; CHECK: mov eax, 481
+
+END
diff --git a/llvm/test/tools/llvm-ml/radix_errors.test b/llvm/test/tools/llvm-ml/radix_errors.test
new file mode 100644 (file)
index 0000000..4745e79
--- /dev/null
@@ -0,0 +1,55 @@
+; RUN: not llvm-ml -filetype=asm %s 2>&1 | FileCheck %s --implicit-check-not=error:
+
+.code
+
+t1:
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 120b
+; CHECK: :[[# @LINE + 1]]:10: error: invalid binary number
+mov eax, 120y
+.radix 11
+; CHECK: :[[# @LINE + 1]]:10: error: invalid base-11 number
+mov eax, 120b
+; CHECK: :[[# @LINE + 1]]:10: error: invalid binary number
+mov eax, 120y
+.radix 10
+
+t2:
+; CHECK: :[[# @LINE + 1]]:10: error: invalid octal number
+mov eax, 190o
+; CHECK: :[[# @LINE + 1]]:10: error: invalid octal number
+mov eax, 190q
+.radix 13
+; CHECK: :[[# @LINE + 1]]:10: error: invalid octal number
+mov eax, 190o
+; CHECK: :[[# @LINE + 1]]:10: error: invalid octal number
+mov eax, 190q
+.radix 10
+
+t3:
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 1f0d
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 1f0t
+.radix 13
+; CHECK: :[[# @LINE + 1]]:10: error: invalid base-13 number
+mov eax, 1f0d
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 1f0t
+.radix 10
+
+t4:
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 10e
+.radix 16
+.radix 10
+; CHECK: :[[# @LINE + 1]]:10: error: invalid decimal number
+mov eax, 10e
+
+t5:
+.radix 9
+; CHECK: :[[# @LINE + 1]]:10: error: invalid base-9 number
+mov eax, 9
+.radix 10
+
+END
index 5abf22d..460a566 100644 (file)
@@ -176,6 +176,7 @@ static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) {
   AsmLexer Lexer(MAI);
   Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer());
   Lexer.setLexMasmIntegers(true);
+  Lexer.useMasmDefaultRadix(true);
 
   bool Error = false;
   while (Lexer.Lex().isNot(AsmToken::Eof)) {
@@ -206,6 +207,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
   Parser->setShowParsedOperands(ShowInstOperands);
   Parser->setTargetParser(*TAP);
   Parser->getLexer().setLexMasmIntegers(true);
+  Parser->getLexer().useMasmDefaultRadix(true);
 
   int Res = Parser->Run(/*NoInitialTextSection=*/true);