diff --git a/llvm/include/llvm/MC/MCAsmMacro.h b/llvm/include/llvm/MC/MCAsmMacro.h --- a/llvm/include/llvm/MC/MCAsmMacro.h +++ b/llvm/include/llvm/MC/MCAsmMacro.h @@ -144,13 +144,15 @@ StringRef Body; MCAsmMacroParameters Parameters; std::vector Locals; + bool Function = false; public: MCAsmMacro(StringRef N, StringRef B, MCAsmMacroParameters P) : Name(N), Body(B), Parameters(std::move(P)) {} MCAsmMacro(StringRef N, StringRef B, MCAsmMacroParameters P, - std::vector L) - : Name(N), Body(B), Parameters(std::move(P)), Locals(std::move(L)) {} + std::vector L, bool F) + : Name(N), Body(B), Parameters(std::move(P)), Locals(std::move(L)), + Function(F) {} #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) void dump() const { dump(dbgs()); } 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 @@ -579,6 +579,9 @@ AsmToken::TokenKind EndTok = AsmToken::EndOfStatement); void printMacroInstantiations(); + + bool expandStatement(SMLoc Loc); + void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, SMRange Range = None) const { ArrayRef Ranges(Range); @@ -1219,7 +1222,12 @@ } // While we have input, parse each statement. - while (Lexer.isNot(AsmToken::Eof)) { + while (Lexer.isNot(AsmToken::Eof) || + SrcMgr.getParentIncludeLoc(CurBuffer) != SMLoc()) { + // Skip through the EOF at the end of an inclusion. + if (Lexer.is(AsmToken::Eof)) + Lex(); + ParseStatementInfo Info(&AsmStrRewrites); bool Parsed = parseStatement(Info, nullptr); @@ -1880,6 +1888,7 @@ } /// ParseStatement: +/// ::= % statement /// ::= EndOfStatement /// ::= Label* Directive ...Operands... EndOfStatement /// ::= Label* Identifier OperandList* EndOfStatement @@ -1897,6 +1906,15 @@ Lex(); return false; } + + // If preceded by an expansion operator, first expand all text macros and + // macro functions. + if (getTok().is(AsmToken::Percent)) { + SMLoc ExpansionLoc = getTok().getLoc(); + if (parseToken(AsmToken::Percent) || expandStatement(ExpansionLoc)) + return true; + } + // Statements always start with an identifier, unless we're dealing with a // processor directive (.386, .686, etc.) that lexes as a real. AsmToken ID = getTok(); @@ -1948,6 +1966,11 @@ Lex(); // always eat a token if (!IDVal.startswith(".")) return Error(IDLoc, "unexpected token at start of statement"); + } else if (Lexer.is(AsmToken::Identifier) && + getTok().getString().equals_lower("echo")) { + // Intercept echo early to avoid lexical substitution in its message, and + // delegate all handling to the appropriate function. + return parseDirectiveEcho(); } else if (parseIdentifier(IDVal)) { if (!TheCondState.Ignore) { Lex(); // always eat a token @@ -2101,7 +2124,11 @@ // If macros are enabled, check to see if this is a macro instantiation. if (const MCAsmMacro *M = getContext().lookupMacro(IDVal)) { - return handleMacroEntry(M, IDLoc); + if (M->Function) { + return handleMacroInvocation(M, IDLoc); + } else { + return handleMacroEntry(M, IDLoc); + } } // Otherwise, we have a normal instruction or directive. @@ -2327,8 +2354,6 @@ return parseDirectiveErrorIfe(IDLoc, false); case DK_RADIX: return parseDirectiveRadix(IDLoc); - case DK_ECHO: - return parseDirectiveEcho(); } return Error(IDLoc, "unknown directive"); @@ -3070,6 +3095,9 @@ } bool MasmParser::handleMacroInvocation(const MCAsmMacro *M, SMLoc NameLoc) { + if (!M->Function) + return Error(NameLoc, "cannot invoke macro procedure as function"); + if (parseToken(AsmToken::LParen, "invoking macro function '" + M->Name + "' requires arguments in parentheses") || handleMacroEntry(M, NameLoc, AsmToken::RParen)) @@ -3286,15 +3314,19 @@ StringRef ID; if (parseIdentifier(ID)) return true; + Data = ID.str(); auto it = Variables.find(ID); if (it == Variables.end()) return true; - const Variable &Var = it->second; - if (!Var.IsText) - return true; - Data = Var.TextValue; + while (it != Variables.end()) { + const Variable &Var = it->second; + if (!Var.IsText) + return true; + Data = Var.TextValue; + it = Variables.find(Data); + } return false; } } @@ -5473,6 +5505,7 @@ // Consuming deferred text, so use Lexer.Lex to ignore Lexing Errors. AsmToken EndToken, StartToken = getTok(); unsigned MacroDepth = 0; + bool MacroFunction = false; // Lex the macro definition. while (true) { // Ignore Lexing errors in macros. @@ -5484,7 +5517,8 @@ if (getLexer().is(AsmToken::Eof)) return Error(NameLoc, "no matching 'endm' in definition"); - // Otherwise, check whether we have reach the 'endm'. + // Otherwise, check whether we have reached the 'endm'... and determine if + // this is a macro function. if (getLexer().is(AsmToken::Identifier)) { if (getTok().getIdentifier().equals_lower("endm")) { if (MacroDepth == 0) { // Outermost macro. @@ -5498,6 +5532,11 @@ // Otherwise we just found the end of an inner macro. --MacroDepth; } + } else if (getTok().getIdentifier().equals_lower("exitm")) { + if (MacroDepth == 0 && + getLexer().peekTok().isNot(AsmToken::EndOfStatement)) { + MacroFunction = true; + } } else if (isMacroLikeDirective()) { // We allow nested macros. Those aren't instantiated until the // outermost macro is expanded so just ignore them for now. @@ -5516,7 +5555,8 @@ const char *BodyStart = StartToken.getLoc().getPointer(); const char *BodyEnd = EndToken.getLoc().getPointer(); StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart); - MCAsmMacro Macro(Name, Body, std::move(Parameters), std::move(Locals)); + MCAsmMacro Macro(Name, Body, std::move(Parameters), std::move(Locals), + MacroFunction); DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n"; Macro.dump()); getContext().defineMacro(Name, std::move(Macro)); @@ -6458,6 +6498,46 @@ return &MacroLikeBodies.back(); } +bool MasmParser::expandStatement(SMLoc Loc) { + SMLoc StartLoc = getTok().getLoc(); + eatToEndOfStatement(); + SMLoc EndLoc = getTok().getLoc(); + + StringRef Body = StringRef(StartLoc.getPointer(), + EndLoc.getPointer() - StartLoc.getPointer()); + MCAsmMacroParameters Parameters; + MCAsmMacroArguments Arguments; + for (const auto &V : Variables) { + const Variable &Var = V.getValue(); + if (Var.IsText) { + Parameters.emplace_back(); + Arguments.emplace_back(); + MCAsmMacroParameter &P = Parameters.back(); + MCAsmMacroArgument &A = Arguments.back(); + P.Name = Var.Name; + P.Required = true; + A.push_back(AsmToken(AsmToken::String, Var.TextValue)); + } + } + MacroLikeBodies.emplace_back(StringRef(), Body, Parameters); + MCAsmMacro M = MacroLikeBodies.back(); + + // Expand the statement in a new buffer. + SmallString<80> Buf; + raw_svector_ostream OS(Buf); + if (expandMacro(OS, M.Body, M.Parameters, Arguments, M.Locals, EndLoc)) + return true; + std::unique_ptr Expansion = + MemoryBuffer::getMemBufferCopy(OS.str(), ""); + + // Jump to the expanded statement and prime the lexer. + CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(Expansion), EndLoc); + Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer()); + EndStatementAtEOFStack.push_back(false); + Lex(); + return false; +} + void MasmParser::instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc, raw_svector_ostream &OS) { instantiateMacroLikeBody(M, DirectiveLoc, /*ExitLoc=*/getTok().getLoc(), OS); @@ -6741,10 +6821,24 @@ return false; } +/// parseDirectiveEcho +/// ::= "echo" message bool MasmParser::parseDirectiveEcho() { - StringRef Message = parseStringToEndOfStatement(); - Lex(); // eat end of statement - llvm::outs() << Message << '\n'; + // We're called before the directive is parsed, to avoid triggering lexical + // substitutions in the message. Assert that the next token is the directive, + // then eat it without using the Parser's Lex method. + assert(getTok().is(AsmToken::Identifier) && + getTok().getString().equals_lower("echo")); + Lexer.Lex(); + + SMLoc StartLoc = getTok().getLoc(); + eatToEndOfStatement(); + SMLoc EndLoc = getTok().getLoc(); + StringRef Message = StringRef(StartLoc.getPointer(), + EndLoc.getPointer() - StartLoc.getPointer()); + llvm::outs() << Message; + if (Message.back() != '\n') + llvm::outs() << '\n'; return false; } diff --git a/llvm/test/tools/llvm-ml/expansion.test b/llvm/test/tools/llvm-ml/expansion.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ml/expansion.test @@ -0,0 +1,30 @@ +; RUN: llvm-ml -filetype=asm %s 2>&1 | FileCheck %s + +.code + +num EQU 276 +var TEXTEQU %num + +ECHO t1 +ECHO var + ECHO var + +; CHECK-LABEL: t1 +; CHECK: var +; CHECK: var +; CHECK-NOT: var + +ECHO t2 +%ECHO var +% ECHO var + %ECHO var + % ECHO var + +; CHECK-LABEL: t2 +; CHECK: 276 +; CHECK: 276 +; CHECK: 276 +; CHECK: 276 +; CHECK-NOT: 276 + +end