1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 04:02:41 +01:00

[ms] [llvm-ml] Enable support for MASM-style macro procedures

Allows the MACRO directive to define macro procedures with parameters and macro-local symbols.

Supports required and optional parameters (including default values), and matches ml64.exe for its macro-local symbol handling (up to 65536 macro-local symbols in any translation unit).

Reviewed By: thakis

Differential Revision: https://reviews.llvm.org/D89729
This commit is contained in:
Eric Astor 2020-11-04 10:00:51 -05:00
parent fab109bb67
commit 5e9623a87c
5 changed files with 302 additions and 214 deletions

View File

@ -143,10 +143,14 @@ struct MCAsmMacro {
StringRef Name;
StringRef Body;
MCAsmMacroParameters Parameters;
std::vector<std::string> Locals;
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<std::string> L)
: Name(N), Body(B), Parameters(std::move(P)), Locals(std::move(L)) {}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() const { dump(dbgs()); }

View File

@ -38,6 +38,11 @@ void MCAsmMacro::dump(raw_ostream &OS) const {
OS << " ";
P.dump();
}
if (!Locals.empty()) {
OS << " Locals:\n";
for (StringRef L : Locals)
OS << " " << L << '\n';
}
OS << " (BEGIN BODY)" << Body << "(END BODY)\n";
}
#endif

View File

@ -51,6 +51,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
@ -432,12 +433,12 @@ private:
/// Did we already inform the user about inconsistent MD5 usage?
bool ReportedInconsistentMD5 = false;
// Is alt macro mode enabled.
bool AltMacroMode = false;
// Current <...> expression depth.
unsigned AngleBracketDepth = 0U;
// Number of locals defined.
uint16_t LocalCounter = 0;
public:
MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
const MCAsmInfo &MAI, unsigned CB);
@ -541,8 +542,8 @@ private:
ArrayRef<MCAsmMacroParameter> Parameters);
bool expandMacro(raw_svector_ostream &OS, StringRef Body,
ArrayRef<MCAsmMacroParameter> Parameters,
ArrayRef<MCAsmMacroArgument> A, bool EnableAtPseudoVariable,
SMLoc L);
ArrayRef<MCAsmMacroArgument> A,
const std::vector<std::string> &Locals, SMLoc L);
/// Are we inside a macro instantiation?
bool isInsideMacroInstantiation() {return !ActiveMacros.empty();}
@ -706,8 +707,6 @@ private:
DK_CFI_REGISTER,
DK_CFI_WINDOW_SAVE,
DK_CFI_B_KEY_FRAME,
DK_ALTMACRO,
DK_NOALTMACRO,
DK_MACRO,
DK_EXITM,
DK_ENDM,
@ -889,9 +888,7 @@ private:
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
bool parseDirectiveExitMacro(StringRef Directive);
bool parseDirectiveEndMacro(StringRef Directive);
bool parseDirectiveMacro(SMLoc DirectiveLoc);
// alternate macro mode directives
bool parseDirectiveAltmacro(StringRef Directive);
bool parseDirectiveMacro(StringRef Name, SMLoc NameLoc);
bool parseDirectiveStruct(StringRef Directive, DirectiveKind DirKind,
StringRef Name, SMLoc NameLoc);
@ -1615,11 +1612,6 @@ bool MasmParser::parseExpression(const MCExpr *&Res) {
/// If the function returns a 'true' value,
/// the End argument will be filled with the last location pointed to the '>'
/// character.
/// There is a gap between the AltMacro's documentation and the single quote
/// implementation. GCC does not fully support this feature and so we will not
/// support it.
/// TODO: Adding single quote as a string.
static bool isAngleBracketString(SMLoc &StrLoc, SMLoc &EndLoc) {
assert((StrLoc.getPointer() != nullptr) &&
"Argument to the function cannot be a NULL value");
@ -1638,12 +1630,12 @@ static bool isAngleBracketString(SMLoc &StrLoc, SMLoc &EndLoc) {
}
/// creating a string without the escape characters '!'.
static std::string angleBracketString(StringRef AltMacroStr) {
static std::string angleBracketString(StringRef BracketContents) {
std::string Res;
for (size_t Pos = 0; Pos < AltMacroStr.size(); Pos++) {
if (AltMacroStr[Pos] == '!')
for (size_t Pos = 0; Pos < BracketContents.size(); Pos++) {
if (BracketContents[Pos] == '!')
Pos++;
Res += AltMacroStr[Pos];
Res += BracketContents[Pos];
}
return Res;
}
@ -2252,11 +2244,6 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
return parseDirectiveCFIRegister(IDLoc);
case DK_CFI_WINDOW_SAVE:
return parseDirectiveCFIWindowSave();
case DK_MACRO:
return parseDirectiveMacro(IDLoc);
case DK_ALTMACRO:
case DK_NOALTMACRO:
return parseDirectiveAltmacro(IDVal);
case DK_EXITM:
return parseDirectiveExitMacro(IDVal);
case DK_ENDM:
@ -2396,6 +2383,9 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
case DK_ENDS:
Lex();
return parseDirectiveEnds(IDVal, IDLoc);
case DK_MACRO:
Lex();
return parseDirectiveMacro(IDVal, IDLoc);
}
// Finally, we check if this is allocating a variable with user-defined type.
@ -2601,33 +2591,29 @@ static bool isIdentifierChar(char c) {
bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body,
ArrayRef<MCAsmMacroParameter> Parameters,
ArrayRef<MCAsmMacroArgument> A,
bool EnableAtPseudoVariable, SMLoc L) {
const std::vector<std::string> &Locals, SMLoc L) {
unsigned NParameters = Parameters.size();
bool HasVararg = NParameters ? Parameters.back().Vararg : false;
if ((!IsDarwin || NParameters != 0) && NParameters != A.size())
if (NParameters != A.size())
return Error(L, "Wrong number of arguments");
StringMap<std::string> LocalSymbols;
std::string Name;
Name.reserve(6);
for (StringRef Local : Locals) {
raw_string_ostream LocalName(Name);
LocalName << "??"
<< format_hex_no_prefix(LocalCounter++, 4, /*Upper=*/true);
LocalSymbols.insert({Local, LocalName.str()});
Name.clear();
}
// A macro without parameters is handled differently on Darwin:
// gas accepts no arguments and does no substitutions
while (!Body.empty()) {
// Scan for the next substitution.
std::size_t End = Body.size(), Pos = 0;
for (; Pos != End; ++Pos) {
// Check for a substitution or escape.
if (IsDarwin && !NParameters) {
// This macro has no parameters, look for $0, $1, etc.
if (Body[Pos] != '$' || Pos + 1 == End)
continue;
char Next = Body[Pos + 1];
if (Next == '$' || Next == 'n' ||
isdigit(static_cast<unsigned char>(Next)))
// Find the next possible identifier
if (Body[Pos] == '&' || isIdentifierChar(Body[Pos]))
break;
} else {
// This macro has parameters, look for \foo, \bar, etc.
if (Body[Pos] == '\\' && Pos + 1 != End)
break;
}
}
// Add the prefix.
@ -2637,90 +2623,56 @@ bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body,
if (Pos == End)
break;
if (IsDarwin && !NParameters) {
switch (Body[Pos + 1]) {
// $$ => $
case '$':
OS << '$';
break;
// $n => number of arguments
case 'n':
OS << A.size();
break;
// $[0-9] => argument
default: {
// Missing arguments are ignored.
unsigned Index = Body[Pos + 1] - '0';
if (Index >= A.size())
break;
// Otherwise substitute with the token values, with spaces eliminated.
for (const AsmToken &Token : A[Index])
OS << Token.getString();
break;
}
}
Pos += 2;
} else {
unsigned I = Pos + 1;
// Check for the \@ pseudo-variable.
if (EnableAtPseudoVariable && Body[I] == '@' && I + 1 != End)
unsigned I = Pos;
bool InitialAmpersand = (Body[I] == '&');
if (InitialAmpersand) {
++I;
else
++Pos;
}
while (isIdentifierChar(Body[I]) && I + 1 != End)
++I;
const char *Begin = Body.data() + Pos + 1;
StringRef Argument(Begin, I - (Pos + 1));
const char *Begin = Body.data() + Pos;
StringRef Argument(Begin, I - Pos);
unsigned Index = 0;
if (Argument == "@") {
OS << NumOfMacroInstantiations;
Pos += 2;
} else {
for (; Index < NParameters; ++Index)
if (Parameters[Index].Name == Argument)
break;
if (Index == NParameters) {
if (Body[Pos + 1] == '(' && Body[Pos + 2] == ')')
Pos += 3;
else {
OS << '\\' << Argument;
if (InitialAmpersand)
OS << '&';
auto it = LocalSymbols.find(Argument.lower());
if (it != LocalSymbols.end())
OS << it->second;
else
OS << Argument;
Pos = I;
}
} else {
bool VarargParameter = HasVararg && Index == (NParameters - 1);
for (const AsmToken &Token : A[Index])
// For altmacro mode, you can write '%expr'.
for (const AsmToken &Token : A[Index]) {
// In MASM, you can write '%expr'.
// The prefix '%' evaluates the expression 'expr'
// and uses the result as a string (e.g. replace %(1+2) with the
// string "3").
// Here, we identify the integer token which is the result of the
// absolute expression evaluation and replace it with its string
// representation.
if (AltMacroMode && Token.getString().front() == '%' &&
Token.is(AsmToken::Integer))
if (Token.getString().front() == '%' && Token.is(AsmToken::Integer))
// Emit an integer value to the buffer.
OS << Token.getIntVal();
// Only Token that was validated as a string and begins with '<'
// is considered altMacroString!!!
else if (AltMacroMode && Token.getString().front() == '<' &&
Token.is(AsmToken::String)) {
OS << angleBracketString(Token.getStringContents());
}
// We expect no quotes around the string's contents when
// parsing for varargs.
else if (Token.isNot(AsmToken::String) || VarargParameter)
OS << Token.getString();
else
OS << Token.getStringContents();
Pos += 1 + Argument.size();
}
Pos += Argument.size();
if (Pos < End && Body[Pos] == '&') {
++Pos;
}
}
// Update the scan point.
@ -2779,7 +2731,6 @@ private:
} // end anonymous namespace
bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
if (Vararg) {
if (Lexer.isNot(AsmToken::EndOfStatement)) {
StringRef Str = parseStringToEndOfStatement();
@ -2788,6 +2739,17 @@ bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
return false;
}
SMLoc StrLoc = Lexer.getLoc(), EndLoc;
if (Lexer.is(AsmToken::Less) && isAngleBracketString(StrLoc, EndLoc)) {
const char *StrChar = StrLoc.getPointer();
const char *EndChar = EndLoc.getPointer();
jumpToLoc(EndLoc, CurBuffer);
/// Eat from '<' to '>'.
Lex();
MA.emplace_back(AsmToken::String, StringRef(StrChar, EndChar - StrChar));
return false;
}
unsigned ParenLevel = 0;
// Darwin doesn't use spaces to delmit arguments.
@ -2801,7 +2763,6 @@ bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
return TokError("unexpected token in macro instantiation");
if (ParenLevel == 0) {
if (Lexer.is(AsmToken::Comma))
break;
@ -2887,7 +2848,7 @@ bool MasmParser::parseMacroArguments(const MCAsmMacro *M,
SMLoc StrLoc = Lexer.getLoc();
SMLoc EndLoc;
if (AltMacroMode && Lexer.is(AsmToken::Percent)) {
if (Lexer.is(AsmToken::Percent)) {
const MCExpr *AbsoluteExp;
int64_t Value;
/// Eat '%'.
@ -2902,16 +2863,6 @@ bool MasmParser::parseMacroArguments(const MCAsmMacro *M,
AsmToken newToken(AsmToken::Integer,
StringRef(StrChar, EndChar - StrChar), Value);
FA.Value.push_back(newToken);
} else if (AltMacroMode && Lexer.is(AsmToken::Less) &&
isAngleBracketString(StrLoc, EndLoc)) {
const char *StrChar = StrLoc.getPointer();
const char *EndChar = EndLoc.getPointer();
jumpToLoc(EndLoc, CurBuffer);
/// Eat from '<' to '>'.
Lex();
AsmToken newToken(AsmToken::String,
StringRef(StrChar, EndChar - StrChar));
FA.Value.push_back(newToken);
} else if (parseMacroArgument(FA.Value, Vararg))
return true;
@ -2992,12 +2943,12 @@ bool MasmParser::handleMacroEntry(const MCAsmMacro *M, SMLoc NameLoc) {
StringRef Body = M->Body;
raw_svector_ostream OS(Buf);
if (expandMacro(OS, Body, M->Parameters, A, true, getTok().getLoc()))
if (expandMacro(OS, Body, M->Parameters, A, M->Locals, getTok().getLoc()))
return true;
// We include the .endmacro in the buffer as our cue to exit the macro
// We include the endm in the buffer as our cue to exit the macro
// instantiation.
OS << ".endmacro\n";
OS << "endm\n";
std::unique_ptr<MemoryBuffer> Instantiation =
MemoryBuffer::getMemBufferCopy(OS.str(), "<instantiation>");
@ -5312,76 +5263,60 @@ bool MasmParser::parseDirectiveCFIUndefined(SMLoc DirectiveLoc) {
return false;
}
/// parseDirectiveAltmacro
/// ::= .altmacro
/// ::= .noaltmacro
bool MasmParser::parseDirectiveAltmacro(StringRef Directive) {
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in '" + Directive + "' directive");
AltMacroMode = (Directive == ".altmacro");
return false;
}
/// parseDirectiveMacro
/// ::= .macro name[,] [parameters]
bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
StringRef Name;
if (parseIdentifier(Name))
return TokError("expected identifier in '.macro' directive");
if (getLexer().is(AsmToken::Comma))
Lex();
/// ::= name macro [parameters]
/// ["LOCAL" identifiers]
/// parameters ::= parameter [, parameter]*
/// parameter ::= name ":" qualifier
/// qualifier ::= "req" | "vararg" | "=" macro_argument
bool MasmParser::parseDirectiveMacro(StringRef Name, SMLoc NameLoc) {
MCAsmMacroParameters Parameters;
while (getLexer().isNot(AsmToken::EndOfStatement)) {
if (!Parameters.empty() && Parameters.back().Vararg)
return Error(Lexer.getLoc(),
"Vararg parameter '" + Parameters.back().Name +
"' should be last one in the list of parameters.");
"' should be last in the list of parameters");
MCAsmMacroParameter Parameter;
if (parseIdentifier(Parameter.Name))
return TokError("expected identifier in '.macro' directive");
return TokError("expected identifier in 'macro' directive");
// Emit an error if two (or more) named parameters share the same name.
for (const MCAsmMacroParameter& CurrParam : Parameters)
if (CurrParam.Name.equals(Parameter.Name))
if (CurrParam.Name.equals_lower(Parameter.Name))
return TokError("macro '" + Name + "' has multiple parameters"
" named '" + Parameter.Name + "'");
if (Lexer.is(AsmToken::Colon)) {
Lex(); // consume ':'
if (parseOptionalToken(AsmToken::Equal)) {
// Default value
SMLoc ParamLoc;
ParamLoc = Lexer.getLoc();
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false))
return true;
} else {
SMLoc QualLoc;
StringRef Qualifier;
QualLoc = Lexer.getLoc();
if (parseIdentifier(Qualifier))
return Error(QualLoc, "missing parameter qualifier for "
"'" + Parameter.Name + "' in macro '" + Name + "'");
"'" +
Parameter.Name + "' in macro '" + Name +
"'");
if (Qualifier == "req")
if (Qualifier.equals_lower("req"))
Parameter.Required = true;
else if (Qualifier == "vararg")
else if (Qualifier.equals_lower("vararg"))
Parameter.Vararg = true;
else
return Error(QualLoc, Qualifier + " is not a valid parameter qualifier "
"for '" + Parameter.Name + "' in macro '" + Name + "'");
return Error(QualLoc,
Qualifier + " is not a valid parameter qualifier for '" +
Parameter.Name + "' in macro '" + Name + "'");
}
if (getLexer().is(AsmToken::Equal)) {
Lex();
SMLoc ParamLoc;
ParamLoc = Lexer.getLoc();
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false ))
return true;
if (Parameter.Required)
Warning(ParamLoc, "pointless default value for required parameter "
"'" + Parameter.Name + "' in macro '" + Name + "'");
}
Parameters.push_back(std::move(Parameter));
@ -5393,6 +5328,24 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
// Eat just the end of statement.
Lexer.Lex();
std::vector<std::string> Locals;
if (getTok().is(AsmToken::Identifier) &&
getTok().getIdentifier().equals_lower("local")) {
Lex(); // Eat the LOCAL directive.
StringRef ID;
while (true) {
if (parseIdentifier(ID))
return true;
Locals.push_back(ID.lower());
// If we see a comma, continue (and allow line continuation).
if (!parseOptionalToken(AsmToken::Comma))
break;
parseOptionalToken(AsmToken::EndOfStatement);
}
}
// Consuming deferred text, so use Lexer.Lex to ignore Lexing Errors.
AsmToken EndToken, StartToken = getTok();
unsigned MacroDepth = 0;
@ -5405,12 +5358,11 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
// Check whether we have reached the end of the file.
if (getLexer().is(AsmToken::Eof))
return Error(DirectiveLoc, "no matching '.endmacro' in definition");
return Error(NameLoc, "no matching 'endm' in definition");
// Otherwise, check whether we have reach the .endmacro.
// Otherwise, check whether we have reach the 'endm'.
if (getLexer().is(AsmToken::Identifier)) {
if (getTok().getIdentifier() == ".endm" ||
getTok().getIdentifier() == ".endmacro") {
if (getTok().getIdentifier().equals_lower("endm")) {
if (MacroDepth == 0) { // Outermost macro.
EndToken = getTok();
Lexer.Lex();
@ -5422,26 +5374,30 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
// Otherwise we just found the end of an inner macro.
--MacroDepth;
}
} else if (getTok().getIdentifier() == ".macro") {
// We allow nested macros. Those aren't instantiated until the outermost
// macro is expanded so just ignore them for now.
} else {
const AsmToken NextTok = getLexer().peekTok();
if (NextTok.is(AsmToken::Identifier) &&
NextTok.getIdentifier().equals_lower("macro")) {
// We allow nested macros. Those aren't instantiated until the
// outermost macro is expanded so just ignore them for now.
++MacroDepth;
}
}
}
// Otherwise, scan til the end of the statement.
eatToEndOfStatement();
}
if (getContext().lookupMacro(Name)) {
return Error(DirectiveLoc, "macro '" + Name + "' is already defined");
return Error(NameLoc, "macro '" + Name + "' is already defined");
}
const char *BodyStart = StartToken.getLoc().getPointer();
const char *BodyEnd = EndToken.getLoc().getPointer();
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
checkForBadMacro(DirectiveLoc, Name, Body, Parameters);
MCAsmMacro Macro(Name, Body, std::move(Parameters));
checkForBadMacro(NameLoc, Name, Body, Parameters);
MCAsmMacro Macro(Name, Body, std::move(Parameters), std::move(Locals));
DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n";
Macro.dump());
getContext().defineMacro(Name, std::move(Macro));
@ -5551,7 +5507,7 @@ void MasmParser::checkForBadMacro(SMLoc DirectiveLoc, StringRef Name,
}
/// parseDirectiveExitMacro
/// ::= .exitm
/// ::= exitm
bool MasmParser::parseDirectiveExitMacro(StringRef Directive) {
if (parseToken(AsmToken::EndOfStatement,
"unexpected token in '" + Directive + "' directive"))
@ -6365,9 +6321,9 @@ void MasmParser::initializeDirectiveKindMap() {
// DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
// DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
// DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
// DirectiveKindMap[".macro"] = DK_MACRO;
// DirectiveKindMap[".exitm"] = DK_EXITM;
// DirectiveKindMap[".endm"] = DK_ENDM;
DirectiveKindMap["macro"] = DK_MACRO;
DirectiveKindMap["exitm"] = DK_EXITM;
DirectiveKindMap["endm"] = DK_ENDM;
// DirectiveKindMap[".purgem"] = DK_PURGEM;
DirectiveKindMap[".err"] = DK_ERR;
DirectiveKindMap[".errb"] = DK_ERRB;
@ -6386,8 +6342,6 @@ void MasmParser::initializeDirectiveKindMap() {
DirectiveKindMap[".savexmm128"] = DK_SAVEXMM128;
DirectiveKindMap[".setframe"] = DK_SETFRAME;
DirectiveKindMap[".radix"] = DK_RADIX;
// DirectiveKindMap[".altmacro"] = DK_ALTMACRO;
// DirectiveKindMap[".noaltmacro"] = DK_NOALTMACRO;
DirectiveKindMap["db"] = DK_DB;
DirectiveKindMap["dd"] = DK_DD;
DirectiveKindMap["df"] = DK_DF;
@ -6494,8 +6448,7 @@ bool MasmParser::parseDirectiveRept(SMLoc DirectiveLoc, StringRef Dir) {
SmallString<256> Buf;
raw_svector_ostream OS(Buf);
while (Count--) {
// Note that the AtPseudoVariable is disabled for instantiations of .rep(t).
if (expandMacro(OS, M->Body, None, None, false, getTok().getLoc()))
if (expandMacro(OS, M->Body, None, None, M->Locals, getTok().getLoc()))
return true;
}
instantiateMacroLikeBody(M, DirectiveLoc, OS);
@ -6526,9 +6479,7 @@ bool MasmParser::parseDirectiveIrp(SMLoc DirectiveLoc) {
raw_svector_ostream OS(Buf);
for (const MCAsmMacroArgument &Arg : A) {
// Note that the AtPseudoVariable is enabled for instantiations of .irp.
// This is undocumented, but GAS seems to support it.
if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc()))
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
return true;
}
@ -6571,9 +6522,7 @@ bool MasmParser::parseDirectiveIrpc(SMLoc DirectiveLoc) {
MCAsmMacroArgument Arg;
Arg.emplace_back(AsmToken::Identifier, Values.slice(I, I + 1));
// Note that the AtPseudoVariable is enabled for instantiations of .irpc.
// This is undocumented, but GAS seems to support it.
if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc()))
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
return true;
}

View File

@ -0,0 +1,106 @@
; RUN: llvm-ml -m64 -filetype=asm %s | FileCheck %s
.data
x1 DWORD ?
x2 DWORD ?
xa1 DWORD ?
.code
substitution_macro macro a1:req, a2:=<7>
mov eax, a1
mov eax, a1&
mov eax, &a1
mov eax, &a1&
mov eax, xa1
mov eax, x&a1
mov eax, x&a1&
mov eax, a2
mov eax, a2&
mov eax, &a2
mov eax, &a2&
endm
substitution_test_with_default PROC
; CHECK-LABEL: substitution_test_with_default:
substitution_macro 1
; CHECK: mov eax, 1
; CHECK-NEXT: mov eax, 1
; CHECK-NEXT: mov eax, 1
; CHECK-NEXT: mov eax, 1
; CHECK: mov eax, dword ptr [rip + xa1]
; CHECK-NEXT: mov eax, dword ptr [rip + x1]
; CHECK-NEXT: mov eax, dword ptr [rip + x1]
; CHECK: mov eax, 7
; CHECK-NEXT: mov eax, 7
; CHECK-NEXT: mov eax, 7
; CHECK-NEXT: mov eax, 7
ret
substitution_test_with_default ENDP
substitution_test_with_value PROC
; CHECK-LABEL: substitution_test_with_value:
substitution_macro 2, 8
; CHECK: mov eax, 2
; CHECK-NEXT: mov eax, 2
; CHECK-NEXT: mov eax, 2
; CHECK-NEXT: mov eax, 2
; CHECK: mov eax, dword ptr [rip + xa1]
; CHECK-NEXT: mov eax, dword ptr [rip + x2]
; CHECK-NEXT: mov eax, dword ptr [rip + x2]
; CHECK: mov eax, 8
; CHECK-NEXT: mov eax, 8
; CHECK-NEXT: mov eax, 8
; CHECK-NEXT: mov eax, 8
ret
substitution_test_with_value ENDP
optional_parameter_macro MACRO a1:req, a2
mov eax, a1
IFNB <a2>
mov eax, a2
ENDIF
ret
ENDM
optional_parameter_test PROC
; CHECK-LABEL: optional_parameter_test:
optional_parameter_macro 4
; CHECK: mov eax, 4
; CHECK: ret
optional_parameter_macro 5, 9
; CHECK: mov eax, 5
; CHECK: mov eax, 9
; CHECK: ret
optional_parameter_test ENDP
local_symbol_macro MACRO
LOCAL a
a: ret
jmp a
ENDM
local_symbol_test PROC
; CHECK-LABEL: local_symbol_test:
local_symbol_macro
; CHECK: "??0000":
; CHECK-NEXT: ret
; CHECK-NEXT: jmp "??0000"
local_symbol_macro
; CHECK: "??0001":
; CHECK-NEXT: ret
; CHECK-NEXT: jmp "??0001"
local_symbol_test ENDP
END

View File

@ -0,0 +1,24 @@
; RUN: not llvm-ml -filetype=asm %s 2>&1 | FileCheck %s --implicit-check-not=error:
.code
; CHECK: error: Vararg parameter 'param' should be last in the list of parameters
; CHECK: error: unexpected 'ENDM' in file, no current macro definition
early_vararg_macro MACRO param:vararg, trailing_param
ENDM
; CHECK: error: macro 'colliding_parameters_macro' has multiple parameters named 'paRAM'
; CHECK: error: unexpected 'ENDM' in file, no current macro definition
colliding_parameters_macro MACRO Param, paRAM
ENDM
; CHECK: error: missing parameter qualifier for 'param' in macro 'missing_qualifier_macro'
; CHECK: error: unexpected 'ENDM' in file, no current macro definition
missing_qualifier_macro MACRO param:
ENDM
; CHECK: error: no matching 'endm' in definition
missing_end_macro MACRO
end