[ms] [llvm-ml] Support built-in text macros

Add support for all built-in text macros supported by ML64:
@Date, @Time, @FileName, @FileCur, and @CurSeg.

Reviewed By: thakis

Differential Revision: https://reviews.llvm.org/D104965
This commit is contained in:
Eric Astor 2021-07-21 11:43:25 -04:00
parent 7dfafaa71e
commit 2b759c7f74
6 changed files with 205 additions and 54 deletions

View File

@ -18,6 +18,7 @@
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/Support/SMLoc.h"
#include <cstdint>
#include <ctime>
#include <string>
#include <utility>
@ -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

View File

@ -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 <climits>
#include <cstddef>
#include <cstdint>
#include <ctime>
#include <deque>
#include <memory>
#include <sstream>
@ -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<bool> 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<std::string> 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<MemoryBuffer> Instantiation =
MemoryBuffer::getMemBufferCopy(VarIt->second.TextValue,
"<instantiation>");
// 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<std::string> 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<MemoryBuffer> Instantiation =
MemoryBuffer::getMemBufferCopy(*ExpandedValue, "<instantiation>");
// 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<std::string> 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<std::string> BuiltinValues;
for (const auto &S : BuiltinSymbolMap) {
const BuiltinSymbol &Sym = S.getValue();
if (llvm::Optional<std::string> 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<std::string>
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);
}

View File

@ -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

View File

@ -0,0 +1,2 @@
%echo FileCur = @FileCur
%echo FileName = @FileName

View File

@ -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<utc>;
def help : MLFlag<"?">,
HelpText<"Display available options">;

View File

@ -42,6 +42,7 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include <ctime>
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<MCAsmParser> Parser(
createMCMasmParser(SrcMgr, Ctx, Str, MAI, 0));
createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, 0));
std::unique_ptr<MCTargetAsmParser> TAP(
TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));