mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 02:33:06 +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:
parent
fab109bb67
commit
5e9623a87c
@ -143,10 +143,14 @@ struct MCAsmMacro {
|
|||||||
StringRef Name;
|
StringRef Name;
|
||||||
StringRef Body;
|
StringRef Body;
|
||||||
MCAsmMacroParameters Parameters;
|
MCAsmMacroParameters Parameters;
|
||||||
|
std::vector<std::string> Locals;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MCAsmMacro(StringRef N, StringRef B, MCAsmMacroParameters P)
|
MCAsmMacro(StringRef N, StringRef B, MCAsmMacroParameters P)
|
||||||
: Name(N), Body(B), Parameters(std::move(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)
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||||
void dump() const { dump(dbgs()); }
|
void dump() const { dump(dbgs()); }
|
||||||
|
@ -38,6 +38,11 @@ void MCAsmMacro::dump(raw_ostream &OS) const {
|
|||||||
OS << " ";
|
OS << " ";
|
||||||
P.dump();
|
P.dump();
|
||||||
}
|
}
|
||||||
|
if (!Locals.empty()) {
|
||||||
|
OS << " Locals:\n";
|
||||||
|
for (StringRef L : Locals)
|
||||||
|
OS << " " << L << '\n';
|
||||||
|
}
|
||||||
OS << " (BEGIN BODY)" << Body << "(END BODY)\n";
|
OS << " (BEGIN BODY)" << Body << "(END BODY)\n";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "llvm/Support/Casting.h"
|
#include "llvm/Support/Casting.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
|
#include "llvm/Support/Format.h"
|
||||||
#include "llvm/Support/MD5.h"
|
#include "llvm/Support/MD5.h"
|
||||||
#include "llvm/Support/MathExtras.h"
|
#include "llvm/Support/MathExtras.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
@ -432,12 +433,12 @@ private:
|
|||||||
/// Did we already inform the user about inconsistent MD5 usage?
|
/// Did we already inform the user about inconsistent MD5 usage?
|
||||||
bool ReportedInconsistentMD5 = false;
|
bool ReportedInconsistentMD5 = false;
|
||||||
|
|
||||||
// Is alt macro mode enabled.
|
|
||||||
bool AltMacroMode = false;
|
|
||||||
|
|
||||||
// Current <...> expression depth.
|
// Current <...> expression depth.
|
||||||
unsigned AngleBracketDepth = 0U;
|
unsigned AngleBracketDepth = 0U;
|
||||||
|
|
||||||
|
// Number of locals defined.
|
||||||
|
uint16_t LocalCounter = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
||||||
const MCAsmInfo &MAI, unsigned CB);
|
const MCAsmInfo &MAI, unsigned CB);
|
||||||
@ -541,8 +542,8 @@ private:
|
|||||||
ArrayRef<MCAsmMacroParameter> Parameters);
|
ArrayRef<MCAsmMacroParameter> Parameters);
|
||||||
bool expandMacro(raw_svector_ostream &OS, StringRef Body,
|
bool expandMacro(raw_svector_ostream &OS, StringRef Body,
|
||||||
ArrayRef<MCAsmMacroParameter> Parameters,
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
||||||
ArrayRef<MCAsmMacroArgument> A, bool EnableAtPseudoVariable,
|
ArrayRef<MCAsmMacroArgument> A,
|
||||||
SMLoc L);
|
const std::vector<std::string> &Locals, SMLoc L);
|
||||||
|
|
||||||
/// Are we inside a macro instantiation?
|
/// Are we inside a macro instantiation?
|
||||||
bool isInsideMacroInstantiation() {return !ActiveMacros.empty();}
|
bool isInsideMacroInstantiation() {return !ActiveMacros.empty();}
|
||||||
@ -706,8 +707,6 @@ private:
|
|||||||
DK_CFI_REGISTER,
|
DK_CFI_REGISTER,
|
||||||
DK_CFI_WINDOW_SAVE,
|
DK_CFI_WINDOW_SAVE,
|
||||||
DK_CFI_B_KEY_FRAME,
|
DK_CFI_B_KEY_FRAME,
|
||||||
DK_ALTMACRO,
|
|
||||||
DK_NOALTMACRO,
|
|
||||||
DK_MACRO,
|
DK_MACRO,
|
||||||
DK_EXITM,
|
DK_EXITM,
|
||||||
DK_ENDM,
|
DK_ENDM,
|
||||||
@ -889,9 +888,7 @@ private:
|
|||||||
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
|
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
|
||||||
bool parseDirectiveExitMacro(StringRef Directive);
|
bool parseDirectiveExitMacro(StringRef Directive);
|
||||||
bool parseDirectiveEndMacro(StringRef Directive);
|
bool parseDirectiveEndMacro(StringRef Directive);
|
||||||
bool parseDirectiveMacro(SMLoc DirectiveLoc);
|
bool parseDirectiveMacro(StringRef Name, SMLoc NameLoc);
|
||||||
// alternate macro mode directives
|
|
||||||
bool parseDirectiveAltmacro(StringRef Directive);
|
|
||||||
|
|
||||||
bool parseDirectiveStruct(StringRef Directive, DirectiveKind DirKind,
|
bool parseDirectiveStruct(StringRef Directive, DirectiveKind DirKind,
|
||||||
StringRef Name, SMLoc NameLoc);
|
StringRef Name, SMLoc NameLoc);
|
||||||
@ -1615,11 +1612,6 @@ bool MasmParser::parseExpression(const MCExpr *&Res) {
|
|||||||
/// If the function returns a 'true' value,
|
/// If the function returns a 'true' value,
|
||||||
/// the End argument will be filled with the last location pointed to the '>'
|
/// the End argument will be filled with the last location pointed to the '>'
|
||||||
/// character.
|
/// 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) {
|
static bool isAngleBracketString(SMLoc &StrLoc, SMLoc &EndLoc) {
|
||||||
assert((StrLoc.getPointer() != nullptr) &&
|
assert((StrLoc.getPointer() != nullptr) &&
|
||||||
"Argument to the function cannot be a NULL value");
|
"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 '!'.
|
/// creating a string without the escape characters '!'.
|
||||||
static std::string angleBracketString(StringRef AltMacroStr) {
|
static std::string angleBracketString(StringRef BracketContents) {
|
||||||
std::string Res;
|
std::string Res;
|
||||||
for (size_t Pos = 0; Pos < AltMacroStr.size(); Pos++) {
|
for (size_t Pos = 0; Pos < BracketContents.size(); Pos++) {
|
||||||
if (AltMacroStr[Pos] == '!')
|
if (BracketContents[Pos] == '!')
|
||||||
Pos++;
|
Pos++;
|
||||||
Res += AltMacroStr[Pos];
|
Res += BracketContents[Pos];
|
||||||
}
|
}
|
||||||
return Res;
|
return Res;
|
||||||
}
|
}
|
||||||
@ -2252,11 +2244,6 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
|
|||||||
return parseDirectiveCFIRegister(IDLoc);
|
return parseDirectiveCFIRegister(IDLoc);
|
||||||
case DK_CFI_WINDOW_SAVE:
|
case DK_CFI_WINDOW_SAVE:
|
||||||
return parseDirectiveCFIWindowSave();
|
return parseDirectiveCFIWindowSave();
|
||||||
case DK_MACRO:
|
|
||||||
return parseDirectiveMacro(IDLoc);
|
|
||||||
case DK_ALTMACRO:
|
|
||||||
case DK_NOALTMACRO:
|
|
||||||
return parseDirectiveAltmacro(IDVal);
|
|
||||||
case DK_EXITM:
|
case DK_EXITM:
|
||||||
return parseDirectiveExitMacro(IDVal);
|
return parseDirectiveExitMacro(IDVal);
|
||||||
case DK_ENDM:
|
case DK_ENDM:
|
||||||
@ -2396,6 +2383,9 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
|
|||||||
case DK_ENDS:
|
case DK_ENDS:
|
||||||
Lex();
|
Lex();
|
||||||
return parseDirectiveEnds(IDVal, IDLoc);
|
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.
|
// 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,
|
bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body,
|
||||||
ArrayRef<MCAsmMacroParameter> Parameters,
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
||||||
ArrayRef<MCAsmMacroArgument> A,
|
ArrayRef<MCAsmMacroArgument> A,
|
||||||
bool EnableAtPseudoVariable, SMLoc L) {
|
const std::vector<std::string> &Locals, SMLoc L) {
|
||||||
unsigned NParameters = Parameters.size();
|
unsigned NParameters = Parameters.size();
|
||||||
bool HasVararg = NParameters ? Parameters.back().Vararg : false;
|
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");
|
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()) {
|
while (!Body.empty()) {
|
||||||
// Scan for the next substitution.
|
// Scan for the next substitution.
|
||||||
std::size_t End = Body.size(), Pos = 0;
|
std::size_t End = Body.size(), Pos = 0;
|
||||||
for (; Pos != End; ++Pos) {
|
for (; Pos != End; ++Pos) {
|
||||||
// Check for a substitution or escape.
|
// Find the next possible identifier
|
||||||
if (IsDarwin && !NParameters) {
|
if (Body[Pos] == '&' || isIdentifierChar(Body[Pos]))
|
||||||
// This macro has no parameters, look for $0, $1, etc.
|
break;
|
||||||
if (Body[Pos] != '$' || Pos + 1 == End)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char Next = Body[Pos + 1];
|
|
||||||
if (Next == '$' || Next == 'n' ||
|
|
||||||
isdigit(static_cast<unsigned char>(Next)))
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// This macro has parameters, look for \foo, \bar, etc.
|
|
||||||
if (Body[Pos] == '\\' && Pos + 1 != End)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the prefix.
|
// Add the prefix.
|
||||||
@ -2637,90 +2623,56 @@ bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body,
|
|||||||
if (Pos == End)
|
if (Pos == End)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (IsDarwin && !NParameters) {
|
unsigned I = Pos;
|
||||||
switch (Body[Pos + 1]) {
|
bool InitialAmpersand = (Body[I] == '&');
|
||||||
// $$ => $
|
if (InitialAmpersand) {
|
||||||
case '$':
|
++I;
|
||||||
OS << '$';
|
++Pos;
|
||||||
|
}
|
||||||
|
while (isIdentifierChar(Body[I]) && I + 1 != End)
|
||||||
|
++I;
|
||||||
|
|
||||||
|
const char *Begin = Body.data() + Pos;
|
||||||
|
StringRef Argument(Begin, I - Pos);
|
||||||
|
unsigned Index = 0;
|
||||||
|
|
||||||
|
for (; Index < NParameters; ++Index)
|
||||||
|
if (Parameters[Index].Name == Argument)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// $n => number of arguments
|
if (Index == NParameters) {
|
||||||
case 'n':
|
if (InitialAmpersand)
|
||||||
OS << A.size();
|
OS << '&';
|
||||||
break;
|
auto it = LocalSymbols.find(Argument.lower());
|
||||||
|
if (it != LocalSymbols.end())
|
||||||
// $[0-9] => argument
|
OS << it->second;
|
||||||
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)
|
|
||||||
++I;
|
|
||||||
else
|
else
|
||||||
while (isIdentifierChar(Body[I]) && I + 1 != End)
|
OS << Argument;
|
||||||
++I;
|
Pos = I;
|
||||||
|
} else {
|
||||||
|
bool VarargParameter = HasVararg && Index == (NParameters - 1);
|
||||||
|
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 (Token.getString().front() == '%' && Token.is(AsmToken::Integer))
|
||||||
|
// Emit an integer value to the buffer.
|
||||||
|
OS << Token.getIntVal();
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
const char *Begin = Body.data() + Pos + 1;
|
Pos += Argument.size();
|
||||||
StringRef Argument(Begin, I - (Pos + 1));
|
if (Pos < End && Body[Pos] == '&') {
|
||||||
unsigned Index = 0;
|
++Pos;
|
||||||
|
|
||||||
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;
|
|
||||||
Pos = I;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bool VarargParameter = HasVararg && Index == (NParameters - 1);
|
|
||||||
for (const AsmToken &Token : A[Index])
|
|
||||||
// For altmacro mode, 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))
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update the scan point.
|
// Update the scan point.
|
||||||
@ -2779,7 +2731,6 @@ private:
|
|||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
|
bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
|
||||||
|
|
||||||
if (Vararg) {
|
if (Vararg) {
|
||||||
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
||||||
StringRef Str = parseStringToEndOfStatement();
|
StringRef Str = parseStringToEndOfStatement();
|
||||||
@ -2788,6 +2739,17 @@ bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
|
|||||||
return false;
|
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;
|
unsigned ParenLevel = 0;
|
||||||
|
|
||||||
// Darwin doesn't use spaces to delmit arguments.
|
// 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");
|
return TokError("unexpected token in macro instantiation");
|
||||||
|
|
||||||
if (ParenLevel == 0) {
|
if (ParenLevel == 0) {
|
||||||
|
|
||||||
if (Lexer.is(AsmToken::Comma))
|
if (Lexer.is(AsmToken::Comma))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2887,7 +2848,7 @@ bool MasmParser::parseMacroArguments(const MCAsmMacro *M,
|
|||||||
|
|
||||||
SMLoc StrLoc = Lexer.getLoc();
|
SMLoc StrLoc = Lexer.getLoc();
|
||||||
SMLoc EndLoc;
|
SMLoc EndLoc;
|
||||||
if (AltMacroMode && Lexer.is(AsmToken::Percent)) {
|
if (Lexer.is(AsmToken::Percent)) {
|
||||||
const MCExpr *AbsoluteExp;
|
const MCExpr *AbsoluteExp;
|
||||||
int64_t Value;
|
int64_t Value;
|
||||||
/// Eat '%'.
|
/// Eat '%'.
|
||||||
@ -2902,17 +2863,7 @@ bool MasmParser::parseMacroArguments(const MCAsmMacro *M,
|
|||||||
AsmToken newToken(AsmToken::Integer,
|
AsmToken newToken(AsmToken::Integer,
|
||||||
StringRef(StrChar, EndChar - StrChar), Value);
|
StringRef(StrChar, EndChar - StrChar), Value);
|
||||||
FA.Value.push_back(newToken);
|
FA.Value.push_back(newToken);
|
||||||
} else if (AltMacroMode && Lexer.is(AsmToken::Less) &&
|
} else if (parseMacroArgument(FA.Value, Vararg))
|
||||||
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;
|
return true;
|
||||||
|
|
||||||
unsigned PI = Parameter;
|
unsigned PI = Parameter;
|
||||||
@ -2992,12 +2943,12 @@ bool MasmParser::handleMacroEntry(const MCAsmMacro *M, SMLoc NameLoc) {
|
|||||||
StringRef Body = M->Body;
|
StringRef Body = M->Body;
|
||||||
raw_svector_ostream OS(Buf);
|
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;
|
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.
|
// instantiation.
|
||||||
OS << ".endmacro\n";
|
OS << "endm\n";
|
||||||
|
|
||||||
std::unique_ptr<MemoryBuffer> Instantiation =
|
std::unique_ptr<MemoryBuffer> Instantiation =
|
||||||
MemoryBuffer::getMemBufferCopy(OS.str(), "<instantiation>");
|
MemoryBuffer::getMemBufferCopy(OS.str(), "<instantiation>");
|
||||||
@ -5312,76 +5263,60 @@ bool MasmParser::parseDirectiveCFIUndefined(SMLoc DirectiveLoc) {
|
|||||||
return false;
|
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
|
/// parseDirectiveMacro
|
||||||
/// ::= .macro name[,] [parameters]
|
/// ::= name macro [parameters]
|
||||||
bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
/// ["LOCAL" identifiers]
|
||||||
StringRef Name;
|
/// parameters ::= parameter [, parameter]*
|
||||||
if (parseIdentifier(Name))
|
/// parameter ::= name ":" qualifier
|
||||||
return TokError("expected identifier in '.macro' directive");
|
/// qualifier ::= "req" | "vararg" | "=" macro_argument
|
||||||
|
bool MasmParser::parseDirectiveMacro(StringRef Name, SMLoc NameLoc) {
|
||||||
if (getLexer().is(AsmToken::Comma))
|
|
||||||
Lex();
|
|
||||||
|
|
||||||
MCAsmMacroParameters Parameters;
|
MCAsmMacroParameters Parameters;
|
||||||
while (getLexer().isNot(AsmToken::EndOfStatement)) {
|
while (getLexer().isNot(AsmToken::EndOfStatement)) {
|
||||||
|
|
||||||
if (!Parameters.empty() && Parameters.back().Vararg)
|
if (!Parameters.empty() && Parameters.back().Vararg)
|
||||||
return Error(Lexer.getLoc(),
|
return Error(Lexer.getLoc(),
|
||||||
"Vararg parameter '" + Parameters.back().Name +
|
"Vararg parameter '" + Parameters.back().Name +
|
||||||
"' should be last one in the list of parameters.");
|
"' should be last in the list of parameters");
|
||||||
|
|
||||||
MCAsmMacroParameter Parameter;
|
MCAsmMacroParameter Parameter;
|
||||||
if (parseIdentifier(Parameter.Name))
|
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.
|
// Emit an error if two (or more) named parameters share the same name.
|
||||||
for (const MCAsmMacroParameter& CurrParam : Parameters)
|
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"
|
return TokError("macro '" + Name + "' has multiple parameters"
|
||||||
" named '" + Parameter.Name + "'");
|
" named '" + Parameter.Name + "'");
|
||||||
|
|
||||||
if (Lexer.is(AsmToken::Colon)) {
|
if (Lexer.is(AsmToken::Colon)) {
|
||||||
Lex(); // consume ':'
|
Lex(); // consume ':'
|
||||||
|
|
||||||
SMLoc QualLoc;
|
if (parseOptionalToken(AsmToken::Equal)) {
|
||||||
StringRef Qualifier;
|
// Default value
|
||||||
|
SMLoc ParamLoc;
|
||||||
|
|
||||||
QualLoc = Lexer.getLoc();
|
ParamLoc = Lexer.getLoc();
|
||||||
if (parseIdentifier(Qualifier))
|
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false))
|
||||||
return Error(QualLoc, "missing parameter qualifier for "
|
return true;
|
||||||
"'" + Parameter.Name + "' in macro '" + Name + "'");
|
} else {
|
||||||
|
SMLoc QualLoc;
|
||||||
|
StringRef Qualifier;
|
||||||
|
|
||||||
if (Qualifier == "req")
|
QualLoc = Lexer.getLoc();
|
||||||
Parameter.Required = true;
|
if (parseIdentifier(Qualifier))
|
||||||
else if (Qualifier == "vararg")
|
return Error(QualLoc, "missing parameter qualifier for "
|
||||||
Parameter.Vararg = true;
|
"'" +
|
||||||
else
|
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)) {
|
if (Qualifier.equals_lower("req"))
|
||||||
Lex();
|
Parameter.Required = true;
|
||||||
|
else if (Qualifier.equals_lower("vararg"))
|
||||||
SMLoc ParamLoc;
|
Parameter.Vararg = true;
|
||||||
|
else
|
||||||
ParamLoc = Lexer.getLoc();
|
return Error(QualLoc,
|
||||||
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false ))
|
Qualifier + " is not a valid parameter qualifier for '" +
|
||||||
return true;
|
Parameter.Name + "' in macro '" + Name + "'");
|
||||||
|
}
|
||||||
if (Parameter.Required)
|
|
||||||
Warning(ParamLoc, "pointless default value for required parameter "
|
|
||||||
"'" + Parameter.Name + "' in macro '" + Name + "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Parameters.push_back(std::move(Parameter));
|
Parameters.push_back(std::move(Parameter));
|
||||||
@ -5393,6 +5328,24 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
|||||||
// Eat just the end of statement.
|
// Eat just the end of statement.
|
||||||
Lexer.Lex();
|
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.
|
// Consuming deferred text, so use Lexer.Lex to ignore Lexing Errors.
|
||||||
AsmToken EndToken, StartToken = getTok();
|
AsmToken EndToken, StartToken = getTok();
|
||||||
unsigned MacroDepth = 0;
|
unsigned MacroDepth = 0;
|
||||||
@ -5405,12 +5358,11 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
|||||||
|
|
||||||
// Check whether we have reached the end of the file.
|
// Check whether we have reached the end of the file.
|
||||||
if (getLexer().is(AsmToken::Eof))
|
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 (getLexer().is(AsmToken::Identifier)) {
|
||||||
if (getTok().getIdentifier() == ".endm" ||
|
if (getTok().getIdentifier().equals_lower("endm")) {
|
||||||
getTok().getIdentifier() == ".endmacro") {
|
|
||||||
if (MacroDepth == 0) { // Outermost macro.
|
if (MacroDepth == 0) { // Outermost macro.
|
||||||
EndToken = getTok();
|
EndToken = getTok();
|
||||||
Lexer.Lex();
|
Lexer.Lex();
|
||||||
@ -5422,10 +5374,14 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
|||||||
// Otherwise we just found the end of an inner macro.
|
// Otherwise we just found the end of an inner macro.
|
||||||
--MacroDepth;
|
--MacroDepth;
|
||||||
}
|
}
|
||||||
} else if (getTok().getIdentifier() == ".macro") {
|
} else {
|
||||||
// We allow nested macros. Those aren't instantiated until the outermost
|
const AsmToken NextTok = getLexer().peekTok();
|
||||||
// macro is expanded so just ignore them for now.
|
if (NextTok.is(AsmToken::Identifier) &&
|
||||||
++MacroDepth;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5434,14 +5390,14 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (getContext().lookupMacro(Name)) {
|
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 *BodyStart = StartToken.getLoc().getPointer();
|
||||||
const char *BodyEnd = EndToken.getLoc().getPointer();
|
const char *BodyEnd = EndToken.getLoc().getPointer();
|
||||||
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
|
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
|
||||||
checkForBadMacro(DirectiveLoc, Name, Body, Parameters);
|
checkForBadMacro(NameLoc, Name, Body, Parameters);
|
||||||
MCAsmMacro Macro(Name, Body, std::move(Parameters));
|
MCAsmMacro Macro(Name, Body, std::move(Parameters), std::move(Locals));
|
||||||
DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n";
|
DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n";
|
||||||
Macro.dump());
|
Macro.dump());
|
||||||
getContext().defineMacro(Name, std::move(Macro));
|
getContext().defineMacro(Name, std::move(Macro));
|
||||||
@ -5551,7 +5507,7 @@ void MasmParser::checkForBadMacro(SMLoc DirectiveLoc, StringRef Name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// parseDirectiveExitMacro
|
/// parseDirectiveExitMacro
|
||||||
/// ::= .exitm
|
/// ::= exitm
|
||||||
bool MasmParser::parseDirectiveExitMacro(StringRef Directive) {
|
bool MasmParser::parseDirectiveExitMacro(StringRef Directive) {
|
||||||
if (parseToken(AsmToken::EndOfStatement,
|
if (parseToken(AsmToken::EndOfStatement,
|
||||||
"unexpected token in '" + Directive + "' directive"))
|
"unexpected token in '" + Directive + "' directive"))
|
||||||
@ -6365,9 +6321,9 @@ void MasmParser::initializeDirectiveKindMap() {
|
|||||||
// DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
|
// DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
|
||||||
// DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
|
// DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
|
||||||
// DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
|
// DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
|
||||||
// DirectiveKindMap[".macro"] = DK_MACRO;
|
DirectiveKindMap["macro"] = DK_MACRO;
|
||||||
// DirectiveKindMap[".exitm"] = DK_EXITM;
|
DirectiveKindMap["exitm"] = DK_EXITM;
|
||||||
// DirectiveKindMap[".endm"] = DK_ENDM;
|
DirectiveKindMap["endm"] = DK_ENDM;
|
||||||
// DirectiveKindMap[".purgem"] = DK_PURGEM;
|
// DirectiveKindMap[".purgem"] = DK_PURGEM;
|
||||||
DirectiveKindMap[".err"] = DK_ERR;
|
DirectiveKindMap[".err"] = DK_ERR;
|
||||||
DirectiveKindMap[".errb"] = DK_ERRB;
|
DirectiveKindMap[".errb"] = DK_ERRB;
|
||||||
@ -6386,8 +6342,6 @@ void MasmParser::initializeDirectiveKindMap() {
|
|||||||
DirectiveKindMap[".savexmm128"] = DK_SAVEXMM128;
|
DirectiveKindMap[".savexmm128"] = DK_SAVEXMM128;
|
||||||
DirectiveKindMap[".setframe"] = DK_SETFRAME;
|
DirectiveKindMap[".setframe"] = DK_SETFRAME;
|
||||||
DirectiveKindMap[".radix"] = DK_RADIX;
|
DirectiveKindMap[".radix"] = DK_RADIX;
|
||||||
// DirectiveKindMap[".altmacro"] = DK_ALTMACRO;
|
|
||||||
// DirectiveKindMap[".noaltmacro"] = DK_NOALTMACRO;
|
|
||||||
DirectiveKindMap["db"] = DK_DB;
|
DirectiveKindMap["db"] = DK_DB;
|
||||||
DirectiveKindMap["dd"] = DK_DD;
|
DirectiveKindMap["dd"] = DK_DD;
|
||||||
DirectiveKindMap["df"] = DK_DF;
|
DirectiveKindMap["df"] = DK_DF;
|
||||||
@ -6494,8 +6448,7 @@ bool MasmParser::parseDirectiveRept(SMLoc DirectiveLoc, StringRef Dir) {
|
|||||||
SmallString<256> Buf;
|
SmallString<256> Buf;
|
||||||
raw_svector_ostream OS(Buf);
|
raw_svector_ostream OS(Buf);
|
||||||
while (Count--) {
|
while (Count--) {
|
||||||
// Note that the AtPseudoVariable is disabled for instantiations of .rep(t).
|
if (expandMacro(OS, M->Body, None, None, M->Locals, getTok().getLoc()))
|
||||||
if (expandMacro(OS, M->Body, None, None, false, getTok().getLoc()))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
||||||
@ -6526,9 +6479,7 @@ bool MasmParser::parseDirectiveIrp(SMLoc DirectiveLoc) {
|
|||||||
raw_svector_ostream OS(Buf);
|
raw_svector_ostream OS(Buf);
|
||||||
|
|
||||||
for (const MCAsmMacroArgument &Arg : A) {
|
for (const MCAsmMacroArgument &Arg : A) {
|
||||||
// Note that the AtPseudoVariable is enabled for instantiations of .irp.
|
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
|
||||||
// This is undocumented, but GAS seems to support it.
|
|
||||||
if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc()))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6571,9 +6522,7 @@ bool MasmParser::parseDirectiveIrpc(SMLoc DirectiveLoc) {
|
|||||||
MCAsmMacroArgument Arg;
|
MCAsmMacroArgument Arg;
|
||||||
Arg.emplace_back(AsmToken::Identifier, Values.slice(I, I + 1));
|
Arg.emplace_back(AsmToken::Identifier, Values.slice(I, I + 1));
|
||||||
|
|
||||||
// Note that the AtPseudoVariable is enabled for instantiations of .irpc.
|
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
|
||||||
// This is undocumented, but GAS seems to support it.
|
|
||||||
if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc()))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
test/tools/llvm-ml/macro.test
Normal file
106
test/tools/llvm-ml/macro.test
Normal 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
|
24
test/tools/llvm-ml/macro_errors.test
Normal file
24
test/tools/llvm-ml/macro_errors.test
Normal 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
|
Loading…
Reference in New Issue
Block a user