diff --git a/include/llvm/MC/MCParser/MCAsmParser.h b/include/llvm/MC/MCParser/MCAsmParser.h index c9b3ab3256d..dbb76bd3659 100644 --- a/include/llvm/MC/MCParser/MCAsmParser.h +++ b/include/llvm/MC/MCParser/MCAsmParser.h @@ -18,6 +18,7 @@ #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/Support/SMLoc.h" #include +#include #include #include @@ -348,7 +349,7 @@ MCAsmParser *createMCAsmParser(SourceMgr &, MCContext &, MCStreamer &, /// Create an MCAsmParser instance for parsing Microsoft MASM-style assembly MCAsmParser *createMCMasmParser(SourceMgr &, MCContext &, MCStreamer &, - const MCAsmInfo &, unsigned CB = 0); + const MCAsmInfo &, struct tm, unsigned CB = 0); } // end namespace llvm diff --git a/lib/MC/MCParser/MasmParser.cpp b/lib/MC/MCParser/MasmParser.cpp index beb8c3af34f..92ec178e340 100644 --- a/lib/MC/MCParser/MasmParser.cpp +++ b/lib/MC/MCParser/MasmParser.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" @@ -56,6 +57,7 @@ #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" @@ -65,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -372,6 +375,10 @@ private: /// This is the current buffer index we're lexing from as managed by the /// SourceMgr object. unsigned CurBuffer; + + /// time of assembly + struct tm TM; + std::vector EndStatementAtEOFStack; AsmCond TheCondState; @@ -448,7 +455,7 @@ private: public: MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out, - const MCAsmInfo &MAI, unsigned CB); + const MCAsmInfo &MAI, struct tm TM, unsigned CB = 0); MasmParser(const MasmParser &) = delete; MasmParser &operator=(const MasmParser &) = delete; ~MasmParser() override; @@ -823,6 +830,9 @@ private: const MCExpr *evaluateBuiltinValue(BuiltinSymbol Symbol, SMLoc StartLoc); + llvm::Optional evaluateBuiltinTextMacro(BuiltinSymbol Symbol, + SMLoc StartLoc); + // ".ascii", ".asciz", ".string" bool parseDirectiveAscii(StringRef IDVal, bool ZeroTerminated); @@ -1060,9 +1070,9 @@ extern MCAsmParserExtension *createCOFFMasmParser(); enum { DEFAULT_ADDRSPACE = 0 }; MasmParser::MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out, - const MCAsmInfo &MAI, unsigned CB = 0) + const MCAsmInfo &MAI, struct tm TM, unsigned CB) : Lexer(MAI), Ctx(Ctx), Out(Out), MAI(MAI), SrcMgr(SM), - CurBuffer(CB ? CB : SM.getMainFileID()) { + CurBuffer(CB ? CB : SM.getMainFileID()), TM(TM) { HadError = false; // Save the old handler. SavedDiagHandler = SrcMgr.getDiagHandler(); @@ -1154,25 +1164,9 @@ void MasmParser::jumpToLoc(SMLoc Loc, unsigned InBuffer, bool MasmParser::expandMacros() { const AsmToken &Tok = getTok(); + const std::string IDLower = Tok.getIdentifier().lower(); - auto VarIt = Variables.find(Tok.getIdentifier().lower()); - if (VarIt != Variables.end() && VarIt->second.IsText) { - std::unique_ptr Instantiation = - MemoryBuffer::getMemBufferCopy(VarIt->second.TextValue, - ""); - - // Jump to the macro instantiation and prime the lexer. - CurBuffer = - SrcMgr.AddNewSourceBuffer(std::move(Instantiation), Tok.getEndLoc()); - Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(), nullptr, - /*EndStatementAtEOF=*/false); - EndStatementAtEOFStack.push_back(false); - Lexer.Lex(); - return false; - } - - const llvm::MCAsmMacro *M = - getContext().lookupMacro(Tok.getIdentifier().lower()); + const llvm::MCAsmMacro *M = getContext().lookupMacro(IDLower); if (M && M->IsFunction && peekTok().is(AsmToken::LParen)) { // This is a macro function invocation; expand it in place. const SMLoc MacroLoc = Tok.getLoc(); @@ -1185,7 +1179,31 @@ bool MasmParser::expandMacros() { return false; } - return true; + llvm::Optional ExpandedValue; + auto BuiltinIt = BuiltinSymbolMap.find(IDLower); + if (BuiltinIt != BuiltinSymbolMap.end()) { + ExpandedValue = + evaluateBuiltinTextMacro(BuiltinIt->getValue(), Tok.getLoc()); + } else { + auto VarIt = Variables.find(IDLower); + if (VarIt != Variables.end() && VarIt->getValue().IsText) { + ExpandedValue = VarIt->getValue().TextValue; + } + } + + if (!ExpandedValue.hasValue()) + return true; + std::unique_ptr Instantiation = + MemoryBuffer::getMemBufferCopy(*ExpandedValue, ""); + + // Jump to the macro instantiation and prime the lexer. + CurBuffer = + SrcMgr.AddNewSourceBuffer(std::move(Instantiation), Tok.getEndLoc()); + Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(), nullptr, + /*EndStatementAtEOF=*/false); + EndStatementAtEOFStack.push_back(false); + Lexer.Lex(); + return false; } const AsmToken &MasmParser::Lex(ExpandKind ExpandNextToken) { @@ -2938,16 +2956,17 @@ bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body, const char *Begin = Body.data() + Pos; StringRef Argument(Begin, I - Pos); + const std::string ArgumentLower = Argument.lower(); unsigned Index = 0; for (; Index < NParameters; ++Index) - if (Parameters[Index].Name == Argument) + if (Parameters[Index].Name.equals_insensitive(ArgumentLower)) break; if (Index == NParameters) { if (InitialAmpersand) OS << '&'; - auto it = LocalSymbols.find(Argument.lower()); + auto it = LocalSymbols.find(ArgumentLower); if (it != LocalSymbols.end()) OS << it->second; else @@ -3602,28 +3621,50 @@ bool MasmParser::parseTextItem(std::string &Data) { case AsmToken::Identifier: { // This must be a text macro; we need to expand it accordingly. StringRef ID; + SMLoc StartLoc = getTok().getLoc(); if (parseIdentifier(ID)) return true; Data = ID.str(); - auto it = Variables.find(ID.lower()); - if (it == Variables.end()) { - // Not a variable; since we haven't used the token, put it back for better - // error recovery. - getLexer().UnLex(AsmToken(AsmToken::Identifier, ID)); - return true; + bool Expanded = false; + while (true) { + // Try to resolve as a built-in text macro + auto BuiltinIt = BuiltinSymbolMap.find(ID.lower()); + if (BuiltinIt != BuiltinSymbolMap.end()) { + llvm::Optional BuiltinText = + evaluateBuiltinTextMacro(BuiltinIt->getValue(), StartLoc); + if (!BuiltinText.hasValue()) { + // Not a text macro; break without substituting + break; + } + Data = std::move(*BuiltinText); + ID = StringRef(Data); + Expanded = true; + continue; + } + + // Try to resolve as a variable text macro + auto VarIt = Variables.find(ID.lower()); + if (VarIt != Variables.end()) { + const Variable &Var = VarIt->getValue(); + if (!Var.IsText) { + // Not a text macro; break without substituting + break; + } + Data = Var.TextValue; + ID = StringRef(Data); + Expanded = true; + continue; + } + + break; } - while (it != Variables.end()) { - const Variable &Var = it->second; - if (!Var.IsText) { - // Not a text macro; not usable in TextItem context. Since we haven't - // used the token, put it back for better error recovery. - getLexer().UnLex(AsmToken(AsmToken::Identifier, ID)); - return true; - } - Data = Var.TextValue; - it = Variables.find(StringRef(Data).lower()); + if (!Expanded) { + // Not a text macro; not usable in TextItem context. Since we haven't used + // the token, put it back for better error recovery. + getLexer().UnLex(AsmToken(AsmToken::Identifier, ID)); + return true; } return false; } @@ -6857,16 +6898,36 @@ bool MasmParser::expandStatement(SMLoc Loc) { MCAsmMacroParameters Parameters; MCAsmMacroArguments Arguments; + + StringMap BuiltinValues; + for (const auto &S : BuiltinSymbolMap) { + const BuiltinSymbol &Sym = S.getValue(); + if (llvm::Optional Text = evaluateBuiltinTextMacro(Sym, Loc)) { + BuiltinValues[S.getKey().lower()] = std::move(*Text); + } + } + for (const auto &B : BuiltinValues) { + MCAsmMacroParameter P; + MCAsmMacroArgument A; + P.Name = B.getKey(); + P.Required = true; + A.push_back(AsmToken(AsmToken::String, B.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) { - Parameters.emplace_back(); - Arguments.emplace_back(); - MCAsmMacroParameter &P = Parameters.back(); - MCAsmMacroArgument &A = Arguments.back(); + 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); @@ -7595,11 +7656,11 @@ void MasmParser::initializeBuiltinSymbolMap() { BuiltinSymbolMap["@line"] = BI_LINE; // Text built-ins (supported in all versions) - // BuiltinSymbolMap["@date"] = BI_DATE; - // BuiltinSymbolMap["@time"] = BI_TIME; - // BuiltinSymbolMap["@filecur"] = BI_FILECUR; - // BuiltinSymbolMap["@filename"] = BI_FILENAME; - // BuiltinSymbolMap["@curseg"] = BI_CURSEG; + BuiltinSymbolMap["@date"] = BI_DATE; + BuiltinSymbolMap["@time"] = BI_TIME; + BuiltinSymbolMap["@filecur"] = BI_FILECUR; + BuiltinSymbolMap["@filename"] = BI_FILENAME; + BuiltinSymbolMap["@curseg"] = BI_CURSEG; // Some built-ins exist only for MASM32 (32-bit x86) if (getContext().getSubtargetInfo()->getTargetTriple().getArch() == @@ -7641,9 +7702,42 @@ const MCExpr *MasmParser::evaluateBuiltinValue(BuiltinSymbol Symbol, llvm_unreachable("unhandled built-in symbol"); } +llvm::Optional +MasmParser::evaluateBuiltinTextMacro(BuiltinSymbol Symbol, SMLoc StartLoc) { + switch (Symbol) { + default: + return {}; + case BI_DATE: { + // Current local date, formatted MM/DD/YY + char TmpBuffer[sizeof("mm/dd/yy")]; + const size_t Len = strftime(TmpBuffer, sizeof(TmpBuffer), "%D", &TM); + return std::string(TmpBuffer, Len); + } + case BI_TIME: { + // Current local time, formatted HH:MM:SS (24-hour clock) + char TmpBuffer[sizeof("hh:mm:ss")]; + const size_t Len = strftime(TmpBuffer, sizeof(TmpBuffer), "%T", &TM); + return std::string(TmpBuffer, Len); + } + case BI_FILECUR: + return SrcMgr + .getMemoryBuffer( + ActiveMacros.empty() ? CurBuffer : ActiveMacros.front()->ExitBuffer) + ->getBufferIdentifier() + .str(); + case BI_FILENAME: + return sys::path::stem(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID()) + ->getBufferIdentifier()) + .upper(); + case BI_CURSEG: + return getStreamer().getCurrentSectionOnly()->getName().str(); + } + llvm_unreachable("unhandled built-in symbol"); +} + /// Create an MCAsmParser instance. MCAsmParser *llvm::createMCMasmParser(SourceMgr &SM, MCContext &C, MCStreamer &Out, const MCAsmInfo &MAI, - unsigned CB) { - return new MasmParser(SM, C, Out, MAI, CB); + struct tm TM, unsigned CB) { + return new MasmParser(SM, C, Out, MAI, TM, CB); } diff --git a/test/tools/llvm-ml/builtin_symbols.asm b/test/tools/llvm-ml/builtin_symbols.asm index 844f9f260cb..81442c49d7e 100644 --- a/test/tools/llvm-ml/builtin_symbols.asm +++ b/test/tools/llvm-ml/builtin_symbols.asm @@ -1,4 +1,7 @@ -; RUN: llvm-ml -filetype=s %s /Fo /dev/null 2>&1 | FileCheck %s +; RUN: llvm-ml -filetype=s %s /I %S /Fo /dev/null 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-REALTIME +; RUN: llvm-ml -filetype=s %s /I %S /Fo /dev/null --timestamp=0 --utc 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-FIXEDTIME + +.code version_val TEXTEQU %@Version @@ -33,3 +36,24 @@ ECHO t4: %ECHO @Line = line_val ; CHECK-LABEL: t4: ; CHECK-NEXT: @Line = [[# @LINE - 5]] + +ECHO t5: +include builtin_symbols_t5.inc +; CHECK-LABEL: t5: +; CHECK: FileCur = {{.*}}builtin_symbols_t5.inc +; CHECK: FileName = BUILTIN_SYMBOLS +; CHECK-NOT: _T5 + +ECHO t6: +%ECHO Date = @Date +%ECHO Time = @Time + +; CHECK-LABEL: t6: +; CHECK-REALTIME: Date = {{([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{2})}} +; CHECK-FIXEDTIME: Date = 01/01/70 +; CHECK-NOT: {{[[:digit:]]}} +; CHECK-REALTIME: Time = {{([[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2})}} +; CHECK-FIXEDTIME: Time = 00:00:00 +; CHECK-NOT: {{[[:digit:]]}} + +end diff --git a/test/tools/llvm-ml/builtin_symbols_t5.inc b/test/tools/llvm-ml/builtin_symbols_t5.inc new file mode 100644 index 00000000000..4a08674bd75 --- /dev/null +++ b/test/tools/llvm-ml/builtin_symbols_t5.inc @@ -0,0 +1,2 @@ +%echo FileCur = @FileCur +%echo FileName = @FileName diff --git a/tools/llvm-ml/Opts.td b/tools/llvm-ml/Opts.td index 06871b49ad0..631c8566e2a 100644 --- a/tools/llvm-ml/Opts.td +++ b/tools/llvm-ml/Opts.td @@ -51,6 +51,12 @@ def preserve_comments : LLVMFlag<"preserve-comments">, HelpText<"Preserve comments in output assembly">; def save_temp_labels : LLVMFlag<"save-temp-labels">, HelpText<"Don't discard temporary labels">; +def timestamp : LLVMJoined<"timestamp=">, + HelpText<"Specify the assembly timestamp (used for @Date and " + "@Time built-ins)">; +def utc : LLVMFlag<"utc">, + HelpText<"Render @Date and @Time built-ins in GMT/UTC">; +def gmtime : LLVMFlag<"gmtime">, Alias; def help : MLFlag<"?">, HelpText<"Display available options">; diff --git a/tools/llvm-ml/llvm-ml.cpp b/tools/llvm-ml/llvm-ml.cpp index dee5f6ff4e5..0e6b0558378 100644 --- a/tools/llvm-ml/llvm-ml.cpp +++ b/tools/llvm-ml/llvm-ml.cpp @@ -42,6 +42,7 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" +#include using namespace llvm; using namespace llvm::opt; @@ -130,8 +131,31 @@ static int AssembleInput(StringRef ProgName, const Target *TheTarget, MCAsmInfo &MAI, MCSubtargetInfo &STI, MCInstrInfo &MCII, MCTargetOptions &MCOptions, const opt::ArgList &InputArgs) { + struct tm TM; + time_t Timestamp; + if (InputArgs.hasArg(OPT_timestamp)) { + StringRef TimestampStr = InputArgs.getLastArgValue(OPT_timestamp); + int64_t IntTimestamp; + if (TimestampStr.getAsInteger(10, IntTimestamp)) { + WithColor::error(errs(), ProgName) + << "invalid timestamp '" << TimestampStr + << "'; must be expressed in seconds since the UNIX epoch.\n"; + return 1; + } + Timestamp = IntTimestamp; + } else { + Timestamp = time(nullptr); + } + if (InputArgs.hasArg(OPT_utc)) { + // Not thread-safe. + TM = *gmtime(&Timestamp); + } else { + // Not thread-safe. + TM = *localtime(&Timestamp); + } + std::unique_ptr Parser( - createMCMasmParser(SrcMgr, Ctx, Str, MAI, 0)); + createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, 0)); std::unique_ptr TAP( TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));