[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