[llvm] 3889ccc - [llvm-ml] make TEXTEQU directive not to eagerly expand macros in arguments (#209526)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 29 17:46:21 PDT 2026


Author: wieDasDing
Date: 2026-07-29T20:46:15-04:00
New Revision: 3889ccc1a35fdd3f18eecbc01663534fb3f64478

URL: https://github.com/llvm/llvm-project/commit/3889ccc1a35fdd3f18eecbc01663534fb3f64478
DIFF: https://github.com/llvm/llvm-project/commit/3889ccc1a35fdd3f18eecbc01663534fb3f64478.diff

LOG: [llvm-ml] make TEXTEQU directive not to eagerly expand macros in arguments (#209526)

We observed a crash in `TEXTEQU` that pastes two macros into one.

```masm
.data
part1 TEXTEQU <1>
part2 TEXTEQU <0>
joined TEXTEQU part1, part2 ; crash
```

`part1` is immediately rewritten into `1` as integer, which is rejected
by `TEXTEQU` parser. We need to keep `part1` as identifier for `TEXTEQU`
to pick up later.

Added: 
    llvm/test/tools/llvm-ml/textequ_expansion.asm

Modified: 
    llvm/lib/MC/MCParser/MasmParser.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp
index f0ab2b876225e..4125c492db940 100644
--- a/llvm/lib/MC/MCParser/MasmParser.cpp
+++ b/llvm/lib/MC/MCParser/MasmParser.cpp
@@ -624,6 +624,9 @@ class MasmParser : public MCAsmParser {
   StringRef parseStringToEndOfStatement() override;
 
   bool parseTextItem(std::string &Data);
+  bool parseTextList(std::string &Result, StringRef IDVal);
+  bool setTextVariable(Variable &Var, StringRef Name, StringRef Value,
+                       SMLoc NameLoc, Variable::RedefinableKind Redefinable);
 
   unsigned getBinOpPrecedence(AsmToken::TokenKind K,
                               MCBinaryExpr::Opcode &Kind);
@@ -2197,9 +2200,11 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
     break;
   case DK_ASSIGN:
   case DK_EQU:
-  case DK_TEXTEQU:
     Lex();
     return parseDirectiveEquate(nextVal, IDVal, DirKind, IDLoc);
+  case DK_TEXTEQU:
+    Lex(DoNotExpandMacros);
+    return parseDirectiveEquate(nextVal, IDVal, DirKind, IDLoc);
   case DK_BYTE:
     if (afterNextTok.is(AsmToken::Identifier) &&
         afterNextTok.getString().equals_insensitive("ptr")) {
@@ -2923,46 +2928,27 @@ bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
   }
 
   SMLoc StartLoc = Lexer.getLoc();
-  if (DirKind == DK_EQU || DirKind == DK_TEXTEQU) {
-    // "equ" and "textequ" both allow text expressions.
-    std::string Value;
-    std::string TextItem;
-    if (!parseTextItem(TextItem)) {
-      Value += TextItem;
-
-      // Accept a text-list, not just one text-item.
-      auto parseItem = [&]() -> bool {
-        if (parseTextItem(TextItem))
-          return TokError("expected text item");
-        Value += TextItem;
-        return false;
-      };
-      if (parseOptionalToken(AsmToken::Comma) && parseMany(parseItem))
-        return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
-
-      if (!Var.IsText || Var.TextValue != Value) {
-        switch (Var.Redefinable) {
-        case Variable::NOT_REDEFINABLE:
-          return Error(getTok().getLoc(), "invalid variable redefinition");
-        case Variable::WARN_ON_REDEFINITION:
-          if (Warning(NameLoc, "redefining '" + Name +
-                                   "', already defined on the command line")) {
-            return true;
-          }
-          break;
-        default:
-          break;
-        }
-      }
-      Var.IsText = true;
-      Var.TextValue = Value;
-      Var.Redefinable = Variable::REDEFINABLE;
 
-      return false;
-    }
-  }
-  if (DirKind == DK_TEXTEQU)
+  switch (DirKind) {
+  case DK_TEXTEQU: {
+    // textMacroDir: TEXTEQU/CATSTR accept a textList.
+    std::string Value;
+    if (!parseTextList(Value, IDVal))
+      return setTextVariable(Var, Name, Value, NameLoc, Variable::REDEFINABLE);
     return TokError("expected <text> in '" + Twine(IDVal) + "' directive");
+  }
+  case DK_EQU: {
+    // equDir: EQU accepts equType ::= immExpr | textLiteral.
+    // Only try textLiteral (angle-bracket syntax) for the text path;
+    // otherwise fall through to expression parsing.
+    std::string Value;
+    if (!parseAngleBracketString(Value))
+      return setTextVariable(Var, Name, Value, NameLoc, Variable::REDEFINABLE);
+    break;
+  }
+  default:
+    break;
+  }
 
   // Parse as expression assignment.
   const MCExpr *Expr;
@@ -2981,26 +2967,8 @@ bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
           {StartLoc, EndLoc});
 
     // Not an absolute expression; define as a text replacement.
-    if (!Var.IsText || Var.TextValue != ExprAsString) {
-      switch (Var.Redefinable) {
-      case Variable::NOT_REDEFINABLE:
-        return Error(getTok().getLoc(), "invalid variable redefinition");
-      case Variable::WARN_ON_REDEFINITION:
-        if (Warning(NameLoc, "redefining '" + Name +
-                                 "', already defined on the command line")) {
-          return true;
-        }
-        break;
-      default:
-        break;
-      }
-    }
-
-    Var.IsText = true;
-    Var.TextValue = ExprAsString.str();
-    Var.Redefinable = Variable::REDEFINABLE;
-
-    return false;
+    return setTextVariable(Var, Name, ExprAsString, NameLoc,
+                           Variable::REDEFINABLE);
   }
 
   auto *Sym = static_cast<MCSymbolCOFF *>(getContext().parseSymbol(Var.Name));
@@ -3014,9 +2982,8 @@ bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
       return Error(getTok().getLoc(), "invalid variable redefinition");
     case Variable::WARN_ON_REDEFINITION:
       if (Warning(NameLoc, "redefining '" + Name +
-                               "', already defined on the command line")) {
+                               "', already defined on the command line"))
         return true;
-      }
       break;
     default:
       break;
@@ -3158,6 +3125,46 @@ bool MasmParser::parseTextItem(std::string &Data) {
   llvm_unreachable("unhandled token kind");
 }
 
+/// textList ::= textItem | textList , [ ;; ] textItem
+bool MasmParser::parseTextList(std::string &Result, StringRef IDVal) {
+  std::string TextItem;
+  if (parseTextItem(TextItem))
+    return true;
+  Result += TextItem;
+  while (getTok().is(AsmToken::Comma)) {
+    Lex(DoNotExpandMacros);
+    if (getTok().is(AsmToken::EndOfStatement))
+      Lex(DoNotExpandMacros);
+    if (parseTextItem(TextItem))
+      return TokError("expected text item in '" + Twine(IDVal) + "' directive");
+    Result += TextItem;
+  }
+  return false;
+}
+
+/// Check redefinition rules and assign a text variable.
+bool MasmParser::setTextVariable(Variable &Var, StringRef Name, StringRef Value,
+                                 SMLoc NameLoc,
+                                 Variable::RedefinableKind Redefinable) {
+  if (!Var.IsText || Var.TextValue != Value) {
+    switch (Var.Redefinable) {
+    case Variable::NOT_REDEFINABLE:
+      return Error(getTok().getLoc(), "invalid variable redefinition");
+    case Variable::WARN_ON_REDEFINITION:
+      if (Warning(NameLoc, "redefining '" + Name +
+                               "', already defined on the command line"))
+        return true;
+      break;
+    default:
+      break;
+    }
+  }
+  Var.IsText = true;
+  Var.TextValue = Value.str();
+  Var.Redefinable = Redefinable;
+  return false;
+}
+
 /// parseDirectiveAscii:
 ///   ::= ( .ascii | .asciz | .string ) [ "string" ( , "string" )* ]
 bool MasmParser::parseDirectiveAscii(StringRef IDVal, bool ZeroTerminated) {
@@ -5760,19 +5767,10 @@ static int rewritesSort(const AsmRewrite *AsmRewriteA,
 
 bool MasmParser::defineMacro(StringRef Name, StringRef Value) {
   Variable &Var = Variables[Name.lower()];
-  if (Var.Name.empty()) {
+  if (Var.Name.empty())
     Var.Name = Name;
-  } else if (Var.Redefinable == Variable::NOT_REDEFINABLE) {
-    return Error(SMLoc(), "invalid variable redefinition");
-  } else if (Var.Redefinable == Variable::WARN_ON_REDEFINITION &&
-             Warning(SMLoc(), "redefining '" + Name +
-                                  "', already defined on the command line")) {
-    return true;
-  }
-  Var.Redefinable = Variable::WARN_ON_REDEFINITION;
-  Var.IsText = true;
-  Var.TextValue = Value.str();
-  return false;
+  return setTextVariable(Var, Name, Value, SMLoc(),
+                         Variable::WARN_ON_REDEFINITION);
 }
 
 bool MasmParser::lookUpField(StringRef Name, AsmFieldInfo &Info) const {

diff  --git a/llvm/test/tools/llvm-ml/textequ_expansion.asm b/llvm/test/tools/llvm-ml/textequ_expansion.asm
new file mode 100644
index 0000000000000..64720f69d2b26
--- /dev/null
+++ b/llvm/test/tools/llvm-ml/textequ_expansion.asm
@@ -0,0 +1,92 @@
+; RUN: llvm-ml -filetype=s %s /Fo - | FileCheck %s
+
+.data
+
+; TEXTEQU with text macro argument should expand correctly.
+part1 TEXTEQU <1>
+part2 TEXTEQU <0>
+joined TEXTEQU part1, part2
+t1 BYTE joined
+
+; CHECK-LABEL: t1:
+; CHECK-NEXT: .byte 10
+
+; TEXTEQU text-list with 3 elements.
+a2 TEXTEQU <1>
+b2 TEXTEQU <2>
+c2 TEXTEQU <3>
+triple TEXTEQU a2, b2, c2
+t2 BYTE triple
+
+; CHECK-LABEL: t2:
+; CHECK-NEXT: .byte 123
+
+; TEXTEQU where a later element is a text macro.
+; The text macro should be resolved by parseTextItem, not the lexer.
+inner TEXTEQU <5>
+outer TEXTEQU <2>, inner
+t3 BYTE outer
+
+; CHECK-LABEL: t3:
+; CHECK-NEXT: .byte 25
+
+; TEXTEQU with trailing ;;
+ts4 TEXTEQU <42>;;
+t4 BYTE ts4
+
+; CHECK-LABEL: t4:
+; CHECK-NEXT: .byte 42
+
+; TEXTEQU multi-element with trailing ;;
+a5 TEXTEQU <1>
+b5 TEXTEQU <0>
+ts5 TEXTEQU a5, b5;;
+t5 BYTE ts5
+
+; CHECK-LABEL: t5:
+; CHECK-NEXT: .byte 10
+
+; TEXTEQU text-list spanning multiple lines.
+a6 TEXTEQU <1>
+b6 TEXTEQU <0>
+ts6 TEXTEQU a6,
+  b6
+t6 BYTE ts6
+
+; CHECK-LABEL: t6:
+; CHECK-NEXT: .byte 10
+
+; TEXTEQU text-list with ;; between items.
+a7 TEXTEQU <1>
+b7 TEXTEQU <0>
+ts7 TEXTEQU a7,;;
+  b7
+t7 BYTE ts7
+
+; CHECK-LABEL: t7:
+; CHECK-NEXT: .byte 10
+
+; EQU with text literal creates a text macro.
+eq8 EQU <42>
+t8 BYTE eq8
+
+; CHECK-LABEL: t8:
+; CHECK-NEXT: .byte 42
+
+; EQU with numeric expression creates a numeric constant.
+eq9 EQU 7
+t9 DWORD eq9
+
+; CHECK-LABEL: t9:
+; CHECK-NEXT: .long 7
+
+; EQU with text macro identifier: the text macro is expanded by the lexer,
+; then evaluated as a numeric expression (not stored as a text macro).
+txt10 TEXTEQU <99>
+eq10 EQU txt10
+t10 DWORD eq10
+
+; CHECK-LABEL: t10:
+; CHECK-NEXT: .long 99
+
+end


        


More information about the llvm-commits mailing list