diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp --- a/llvm/lib/MC/MCParser/MasmParser.cpp +++ b/llvm/lib/MC/MCParser/MasmParser.cpp @@ -502,6 +502,10 @@ const AsmToken &Lex(ExpandKind ExpandNextToken); const AsmToken &Lex() override { return Lex(ExpandMacros); } + using MCAsmParser::parseToken; + bool parseToken(AsmToken::TokenKind T, const Twine &Msg, + ExpandKind ExpandNextToken); + void setParsingMSInlineAsm(bool V) override { ParsingMSInlineAsm = V; // When parsing MS inline asm, we must lex 0b1101 and 0ABCH as binary and @@ -600,6 +604,7 @@ void printMacroInstantiations(); + bool expandString(std::string Body, SMLoc StartLoc, SMLoc EndLoc); bool expandStatement(SMLoc Loc); void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, @@ -1206,6 +1211,14 @@ return false; } +bool MasmParser::parseToken(AsmToken::TokenKind T, const Twine &Msg, + ExpandKind ExpandNextToken) { + if (getTok().getKind() != T) + return Error(getTok().getLoc(), Msg); + Lex(ExpandNextToken); + return false; +} + const AsmToken &MasmParser::Lex(ExpandKind ExpandNextToken) { if (Lexer.getTok().is(AsmToken::Error)) Error(Lexer.getErrLoc(), Lexer.getErr()); @@ -3090,7 +3103,7 @@ if (Lexer.is(AsmToken::Space)) { SpaceEaten = true; - Lex(); // Eat spaces. + Lex(DoNotExpandMacros); // Eat spaces. } // Spaces can delimit parameters, but could also be part an expression. @@ -3099,11 +3112,11 @@ if (!IsDarwin) { if (isOperator(Lexer.getKind()) && Lexer.isNot(EndTok)) { MA.push_back(getTok()); - Lex(); + Lex(DoNotExpandMacros); // Whitespace after an operator can be ignored. if (Lexer.is(AsmToken::Space)) - Lex(); + Lex(DoNotExpandMacros); continue; } @@ -3125,7 +3138,7 @@ // Append the token to the current argument list. MA.push_back(getTok()); - Lex(); + Lex(DoNotExpandMacros); } if (ParenLevel != 0) @@ -3168,7 +3181,7 @@ if (Lexer.isNot(AsmToken::Equal)) return TokError("expected '=' after formal parameter identifier"); - Lex(); + Lex(DoNotExpandMacros); NamedParametersFound = true; } @@ -3195,28 +3208,76 @@ MP = &M->Parameters[PI]; SMLoc StrLoc = Lexer.getLoc(); - SMLoc EndLoc; + bool Expand = false; if (Lexer.is(AsmToken::Percent)) { - const MCExpr *AbsoluteExp; - int64_t Value; - /// Eat '%'. - Lex(); - if (parseExpression(AbsoluteExp, EndLoc)) - return false; - if (!AbsoluteExp->evaluateAsAbsolute(Value, - getStreamer().getAssemblerPtr())) - return Error(StrLoc, "expected absolute expression"); - const char *StrChar = StrLoc.getPointer(); - const char *EndChar = EndLoc.getPointer(); - AsmToken newToken(AsmToken::Integer, - StringRef(StrChar, EndChar - StrChar), Value); - FA.Value.push_back(newToken); - } else if (parseMacroArgument(MP, FA.Value, EndTok)) { + // Eat initial '%', and then expand the argument. + Lex(DoNotExpandMacros); + Expand = true; + } + if (parseMacroArgument(MP, FA.Value, EndTok)) { if (M) return addErrorSuffix(" in '" + M->Name + "' macro"); else return true; } + if (Expand) { + // Unless the argument was a single text macro, we also want to try to + // evaluate the expanded result as an absolute expression. + bool Evaluate = true; + if (FA.Value.size() == 1 && FA.Value.front().is(AsmToken::Identifier)) { + std::string IDLower = FA.Value.front().getIdentifier().lower(); + auto BuiltinIt = BuiltinSymbolMap.find(IDLower); + auto VarIt = Variables.find(IDLower); + if (BuiltinIt != BuiltinSymbolMap.end()) { + Evaluate = + (evaluateBuiltinValue(BuiltinIt->getValue(), StrLoc) != nullptr); + } else if (VarIt != Variables.end()) { + Evaluate = !VarIt->getValue().IsText; + } + } + + // Copy the argument into a new buffer to expand it. + SmallString<256> Buf; + raw_svector_ostream OS(Buf); + for (const AsmToken &Token : FA.Value) { + OS << Token.getString(); + } + SMLoc StrEndLoc = getTok().getLoc(); + unsigned OriginalBuffer = CurBuffer; + if (expandString(OS.str().str(), StrLoc, StrEndLoc)) + return true; + unsigned ExpandedBuffer = CurBuffer; + StringRef ExpandedArgument = + SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(); + FA.Value.clear(); + FA.Value.push_back(AsmToken(AsmToken::Identifier, ExpandedArgument)); + + // Unless the argument was a single text macro, try to parse the expanded + // version as an absolute expression; if successful, replace it by its + // evaluation. + printPendingErrors(); + const MCExpr *AbsoluteExp; + int64_t Value; + SMLoc EndLoc; + if (Evaluate && !parseExpression(AbsoluteExp, EndLoc) && + AbsoluteExp->evaluateAsAbsolute(Value, + getStreamer().getAssemblerPtr()) && + CurBuffer == ExpandedBuffer && Lexer.is(AsmToken::EndOfStatement) && + Lexer.Lex().is(AsmToken::Eof)) { + const char *StrChar = StrLoc.getPointer(); + const char *EndChar = EndLoc.getPointer(); + FA.Value.clear(); + FA.Value.push_back(AsmToken( + AsmToken::Integer, StringRef(StrChar, EndChar - StrChar), Value)); + } + + // Disregard any errors that occurred while trying to parse the + // argument as an expression; resume parsing at the original location. + clearPendingErrors(); + EndStatementAtEOFStack.pop_back(); + jumpToLoc(StrEndLoc, OriginalBuffer, EndStatementAtEOFStack.back()); + Lex(DoNotExpandMacros); + } if (!FA.Value.empty()) { if (A.size() <= PI) @@ -3252,7 +3313,7 @@ } if (Lexer.is(AsmToken::Comma)) - Lex(); + Lex(DoNotExpandMacros); } return TokError("too many positional arguments"); @@ -3404,27 +3465,26 @@ // eat $ or @ Lexer.Lex(); // Lexer's Lex guarantees consecutive token. - // Construct the joined identifier and consume the token. + // Construct the joined identifier. Res = StringRef(PrefixLoc.getPointer(), getTok().getIdentifier().size() + 1); - Lex(); // Parser Lex to maintain invariants. - return false; + } else { + if (Lexer.isNot(AsmToken::Identifier) && Lexer.isNot(AsmToken::String)) + return true; + Res = getTok().getIdentifier(); } - if (Lexer.isNot(AsmToken::Identifier) && Lexer.isNot(AsmToken::String)) - return true; - - Res = getTok().getIdentifier(); - - // Consume the identifier token - but if parsing certain directives, avoid - // lexical expansion of the next token. + // Consume the identifier token - but if parsing certain directives or a macro + // invocation, avoid lexical expansion of the next token. ExpandKind ExpandNextToken = ExpandMacros; - if (Position == StartOfStatement && - StringSwitch(Res) - .CaseLower("echo", true) - .CasesLower("ifdef", "ifndef", "elseifdef", "elseifndef", true) - .Default(false)) { - ExpandNextToken = DoNotExpandMacros; + if (StartOfStatement) { + std::string IDLower = Res.lower(); + if (IDLower == "echo" || IDLower == "ifdef" || IDLower == "ifndef" || + IDLower == "elseifdef" || IDLower == "elseifndef") { + ExpandNextToken = DoNotExpandMacros; + } else if (getContext().lookupMacro(Res.lower())) { + ExpandNextToken = DoNotExpandMacros; + } } Lex(ExpandNextToken); @@ -6892,44 +6952,35 @@ return &MacroLikeBodies.back(); } -bool MasmParser::expandStatement(SMLoc Loc) { - std::string Body = parseStringTo(AsmToken::EndOfStatement); - SMLoc EndLoc = getTok().getLoc(); - +bool MasmParser::expandString(std::string Body, SMLoc StartLoc, SMLoc EndLoc) { MCAsmMacroParameters Parameters; MCAsmMacroArguments Arguments; - StringMap BuiltinValues; + StringMap ExpandedValues; for (const auto &S : BuiltinSymbolMap) { const BuiltinSymbol &Sym = S.getValue(); - if (llvm::Optional Text = evaluateBuiltinTextMacro(Sym, Loc)) { - BuiltinValues[S.getKey().lower()] = std::move(*Text); + if (llvm::Optional Text = + evaluateBuiltinTextMacro(Sym, StartLoc)) { + ExpandedValues[S.getKey().lower()] = std::move(*Text); + } + } + for (const auto &V : Variables) { + const Variable &Var = V.getValue(); + if (Var.IsText) { + ExpandedValues[Var.Name] = Var.TextValue; } } - for (const auto &B : BuiltinValues) { + for (const auto &Expanded : ExpandedValues) { MCAsmMacroParameter P; MCAsmMacroArgument A; - P.Name = B.getKey(); + P.Name = Expanded.getKey(); P.Required = true; - A.push_back(AsmToken(AsmToken::String, B.getValue())); + A.push_back(AsmToken(AsmToken::String, Expanded.getValue())); Parameters.push_back(std::move(P)); Arguments.push_back(std::move(A)); } - for (const auto &V : Variables) { - const Variable &Var = V.getValue(); - if (Var.IsText) { - MCAsmMacroParameter P; - MCAsmMacroArgument A; - P.Name = Var.Name; - P.Required = true; - A.push_back(AsmToken(AsmToken::String, Var.TextValue)); - - Parameters.push_back(std::move(P)); - Arguments.push_back(std::move(A)); - } - } MacroLikeBodies.emplace_back(StringRef(), Body, Parameters); MCAsmMacro M = MacroLikeBodies.back(); @@ -6949,6 +7000,12 @@ return false; } +bool MasmParser::expandStatement(SMLoc Loc) { + std::string Body = parseStringTo(AsmToken::EndOfStatement); + SMLoc EndLoc = getTok().getLoc(); + return expandString(Body, /*StartLoc=*/Loc, EndLoc); +} + void MasmParser::instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc, raw_svector_ostream &OS) { instantiateMacroLikeBody(M, DirectiveLoc, /*ExitLoc=*/getTok().getLoc(), OS); diff --git a/llvm/test/tools/llvm-ml/macro.asm b/llvm/test/tools/llvm-ml/macro.asm --- a/llvm/test/tools/llvm-ml/macro.asm +++ b/llvm/test/tools/llvm-ml/macro.asm @@ -172,4 +172,89 @@ ; CHECK-NEXT: jmp "??0002" purge_test ENDP +SubstitutionInStringMacro MACRO x + BYTE "&x"; +ENDM + +substitution_in_string_test PROC + + Var TEXTEQU <2+3> + +does_not_evaluate: +; should expand to BYTE "2+3" + SubstitutionInStringMacro %Var +; CHECK-LABEL: does_not_evaluate: +; CHECK-NEXT: .byte 50 +; CHECK-NEXT: .byte 43 +; CHECK-NEXT: .byte 51 +; CHECK-NOT: .byte + +does_evaluate_left: +; should expand to BYTE "9" + SubstitutionInStringMacro %Var+4 +; CHECK-LABEL: does_evaluate_left: +; CHECK-NEXT: .byte 57 +; CHECK-NOT: .byte + +does_evaluate_right: +; should expand to BYTE "9" + SubstitutionInStringMacro %4+Var +; CHECK-LABEL: does_evaluate_right: +; CHECK-NEXT: .byte 57 +; CHECK-NOT: .byte + + Var TEXTEQU + +expands: +; should expand to BYTE "t" + SubstitutionInStringMacro %Var +; CHECK-LABEL: expands: +; CHECK-NEXT: .byte 116 +; CHECK-NOT: .byte + +does_not_evaluate_left: +; should expand to BYTE "t+4" + SubstitutionInStringMacro %Var+4 +; CHECK-LABEL: does_not_evaluate_left: +; CHECK-NEXT: .byte 116 +; CHECK-NEXT: .byte 43 +; CHECK-NEXT: .byte 52 +; CHECK-NOT: .byte + +does_not_evaluate_right: +; should expand to BYTE "4+t" + SubstitutionInStringMacro %4+Var +; CHECK-LABEL: does_not_evaluate_right: +; CHECK-NEXT: .byte 52 +; CHECK-NEXT: .byte 43 +; CHECK-NEXT: .byte 116 +; CHECK-NOT: .byte + +; Mimic @Date format + Var TEXTEQU <06/03/02> + +date_expands: +; should expand to BYTE "06/03/02" + SubstitutionInStringMacro %Var +; CHECK-LABEL: date_expands: +; CHECK-NEXT: .byte 48 +; CHECK-NEXT: .byte 54 +; CHECK-NEXT: .byte 47 +; CHECK-NEXT: .byte 48 +; CHECK-NEXT: .byte 51 +; CHECK-NEXT: .byte 47 +; CHECK-NEXT: .byte 48 +; CHECK-NEXT: .byte 50 +; CHECK-NOT: .byte + +pseudo_date_evaluates: +; should expand to BYTE "3" + SubstitutionInStringMacro %Var+2 +; CHECK-LABEL: pseudo_date_evaluates: +; CHECK-NEXT: .byte 51 +; CHECK-NOT: .byte + +substitution_in_string_test ENDP + + END