[ms] [llvm-ml] Make variable redefinition match ML.EXE
authorEric Astor <epastor@google.com>
Thu, 10 Jun 2021 12:35:41 +0000 (08:35 -0400)
committerEric Astor <epastor@google.com>
Thu, 10 Jun 2021 12:36:15 +0000 (08:36 -0400)
MASM specifies that all variable definitions are redefinable, except for EQU definitions to expressions. (TEXTEQU is unspecified, but appears to be fully redefinable as well.)

Also, in practice, ML.EXE allows redefinitions where the value doesn't change.

Make variable redefinition possible for text macros, suppressing expansion if written as the first argument to an EQU or TEXTEQU directive.

Reviewed By: thakis

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

llvm/lib/MC/MCParser/MasmParser.cpp
llvm/test/tools/llvm-ml/variable_redef.asm [new file with mode: 0644]
llvm/test/tools/llvm-ml/variable_redef_errors.asm [new file with mode: 0644]

index 2c0e3a2..9d5f33e 100644 (file)
@@ -1122,8 +1122,22 @@ const AsmToken &MasmParser::Lex() {
   }
 
   const AsmToken *tok = &Lexer.Lex();
+  bool StartOfStatement = Lexer.isAtStartOfStatement();
 
   while (tok->is(AsmToken::Identifier)) {
+    if (StartOfStatement) {
+      AsmToken NextTok;
+
+      MutableArrayRef<AsmToken> Buf(NextTok);
+      size_t ReadCount = Lexer.peekTokens(Buf);
+      if (ReadCount && NextTok.is(AsmToken::Identifier) &&
+          (NextTok.getString().equals_lower("equ") ||
+           NextTok.getString().equals_lower("textequ"))) {
+        // This looks like an EQU or TEXTEQU directive; don't expand the
+        // identifier, allowing for redefinitions.
+        break;
+      }
+    }
     auto it = Variables.find(tok->getIdentifier().lower());
     const llvm::MCAsmMacro *M =
         getContext().lookupMacro(tok->getIdentifier().lower());
@@ -3327,35 +3341,38 @@ bool MasmParser::parseIdentifier(StringRef &Res) {
 ///  ::= name "=" expression
 ///    | name "equ" expression    (not redefinable)
 ///    | name "equ" text-list
-///    | name "textequ" text-list
+///    | name "textequ" text-list (redefinability unspecified)
 bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
                                       DirectiveKind DirKind) {
   Variable &Var = Variables[Name.lower()];
   if (Var.Name.empty()) {
     Var.Name = Name;
-  } else if (!Var.Redefinable) {
-    return TokError("invalid variable redefinition");
   }
-  Var.Redefinable = (DirKind != DK_EQU);
 
   SMLoc StartLoc = Lexer.getLoc();
   if (DirKind == DK_EQU || DirKind == DK_TEXTEQU) {
     // "equ" and "textequ" both allow text expressions.
     std::string Value;
-    if (!parseTextItem(Value)) {
-      Var.IsText = true;
-      Var.TextValue = Value;
+    std::string TextItem;
+    if (!parseTextItem(TextItem)) {
+      Value += TextItem;
 
       // Accept a text-list, not just one text-item.
       auto parseItem = [&]() -> bool {
-        if (parseTextItem(Value))
+        if (parseTextItem(TextItem))
           return TokError("expected text item");
-        Var.TextValue += Value;
+        Value += TextItem;
         return false;
       };
       if (parseOptionalToken(AsmToken::Comma) && parseMany(parseItem))
         return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
 
+      if (!Var.Redefinable && (!Var.IsText || Var.TextValue != Value))
+        return Error(getTok().getLoc(), "invalid variable redefinition");
+      Var.IsText = true;
+      Var.TextValue = Value;
+      Var.Redefinable = true;
+
       return false;
     }
   }
@@ -3367,19 +3384,28 @@ bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
   SMLoc EndLoc;
   if (parseExpression(Expr, EndLoc))
     return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
+
+  int64_t Value;
+  if (!Expr->evaluateAsAbsolute(Value, getStreamer().getAssemblerPtr())) {
+    // Not an absolute expression; define as a text replacement.
+    StringRef ExprAsString = StringRef(
+        StartLoc.getPointer(), EndLoc.getPointer() - StartLoc.getPointer());
+    if (!Var.Redefinable && (!Var.IsText && Var.TextValue != ExprAsString))
+      return Error(getTok().getLoc(), "invalid variable redefinition");
+    Var.IsText = true;
+    Var.TextValue = ExprAsString.str();
+  } else {
+    if (!Var.Redefinable && (Var.IsText || Var.NumericValue != Value))
+      return Error(getTok().getLoc(), "invalid variable redefinition");
+    Var.NumericValue = Value;
+  }
+  Var.Redefinable = (DirKind == DK_ASSIGN);
+
   MCSymbol *Sym = getContext().getOrCreateSymbol(Var.Name);
   Sym->setRedefinable(Var.Redefinable);
   Sym->setVariableValue(Expr);
   Sym->setExternal(false);
 
-  if (Expr->evaluateAsAbsolute(Var.NumericValue,
-                               getStreamer().getAssemblerPtr()))
-    return false;
-
-  // Not an absolute expression; define as a text replacement.
-  Var.IsText = true;
-  Var.TextValue = StringRef(StartLoc.getPointer(),
-                            EndLoc.getPointer() - StartLoc.getPointer()).str();
   return false;
 }
 
diff --git a/llvm/test/tools/llvm-ml/variable_redef.asm b/llvm/test/tools/llvm-ml/variable_redef.asm
new file mode 100644 (file)
index 0000000..f21f8f7
--- /dev/null
@@ -0,0 +1,76 @@
+; RUN: llvm-ml -filetype=s %s /Fo - | FileCheck %s
+
+.data
+
+; <var> = <expression> can be redefined at any time.
+assigned_number = 1
+t1_original BYTE assigned_number
+assigned_number = 1
+t1_reset BYTE assigned_number
+assigned_number = 2
+t1_changed BYTE assigned_number
+
+; CHECK-LABEL: t1_original:
+; CHECK-NEXT: .byte 1
+
+; CHECK-LABEL: t1_reset:
+; CHECK-NEXT: .byte 1
+
+; CHECK-LABEL: t1_changed:
+; CHECK-NEXT: .byte 2
+
+; <var> EQU <expression> can be redundantly set, but can't be changed.
+equated_number equ 3
+t2_original BYTE equated_number
+equated_number equ 3
+t2_reset BYTE equated_number
+
+; CHECK-LABEL: t2_original:
+; CHECK-NEXT: .byte 3
+
+; CHECK-LABEL: t2_reset:
+; CHECK-NEXT: .byte 3
+
+; <var> EQU <text> can be redefined at any time.
+equated_text equ <4, 5>
+t3_original BYTE equated_text
+equated_text equ <4, 5>
+t3_reset BYTE equated_text
+equated_text equ <5, 6>
+t3_changed BYTE equated_text
+
+; CHECK-LABEL: t3_original:
+; CHECK-NEXT: .byte 4
+; CHECK-NEXT: .byte 5
+
+; CHECK-LABEL: t3_reset:
+; CHECK-NEXT: .byte 4
+; CHECK-NEXT: .byte 5
+
+; CHECK-LABEL: t3_changed:
+; CHECK-NEXT: .byte 5
+; CHECK-NEXT: .byte 6
+
+; <var> TEXTEQU <text> can be redefined at any time.
+textequated_text textequ <7, 8>
+t4_original BYTE textequated_text
+textequated_text textequ <7, 8>
+t4_reset BYTE textequated_text
+textequated_text textequ <9, 10>
+t4_changed BYTE textequated_text
+
+; CHECK-LABEL: t4_original:
+; CHECK-NEXT: .byte 7
+; CHECK-NEXT: .byte 8
+
+; CHECK-LABEL: t4_reset:
+; CHECK-NEXT: .byte 7
+; CHECK-NEXT: .byte 8
+
+; CHECK-LABEL: t4_changed:
+; CHECK-NEXT: .byte 9
+; CHECK-NEXT: .byte 10
+
+.code
+
+end
diff --git a/llvm/test/tools/llvm-ml/variable_redef_errors.asm b/llvm/test/tools/llvm-ml/variable_redef_errors.asm
new file mode 100644 (file)
index 0000000..c5a0844
--- /dev/null
@@ -0,0 +1,12 @@
+; RUN: not llvm-ml -filetype=s %s /Fo - 2>&1 | FileCheck %s --implicit-check-not=error:
+
+.data
+
+; <var> EQU <expression> can't be redefined to a new value
+equated_number equ 3
+; CHECK: :[[# @LINE + 1]]:21: error: invalid variable redefinition
+equated_number equ 4
+
+.code
+
+end