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 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()); }
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
break;
|
||||
} else {
|
||||
// This macro has parameters, look for \foo, \bar, etc.
|
||||
if (Body[Pos] == '\\' && Pos + 1 != End)
|
||||
break;
|
||||
}
|
||||
// Find the next possible identifier
|
||||
if (Body[Pos] == '&' || isIdentifierChar(Body[Pos]))
|
||||
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 << '$';
|
||||
unsigned I = Pos;
|
||||
bool InitialAmpersand = (Body[I] == '&');
|
||||
if (InitialAmpersand) {
|
||||
++I;
|
||||
++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;
|
||||
|
||||
// $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)
|
||||
++I;
|
||||
if (Index == NParameters) {
|
||||
if (InitialAmpersand)
|
||||
OS << '&';
|
||||
auto it = LocalSymbols.find(Argument.lower());
|
||||
if (it != LocalSymbols.end())
|
||||
OS << it->second;
|
||||
else
|
||||
while (isIdentifierChar(Body[I]) && I + 1 != End)
|
||||
++I;
|
||||
OS << Argument;
|
||||
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;
|
||||
StringRef Argument(Begin, I - (Pos + 1));
|
||||
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;
|
||||
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();
|
||||
}
|
||||
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,17 +2863,7 @@ 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))
|
||||
} else if (parseMacroArgument(FA.Value, Vararg))
|
||||
return true;
|
||||
|
||||
unsigned PI = Parameter;
|
||||
@ -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 ':'
|
||||
|
||||
SMLoc QualLoc;
|
||||
StringRef Qualifier;
|
||||
if (parseOptionalToken(AsmToken::Equal)) {
|
||||
// Default value
|
||||
SMLoc ParamLoc;
|
||||
|
||||
QualLoc = Lexer.getLoc();
|
||||
if (parseIdentifier(Qualifier))
|
||||
return Error(QualLoc, "missing parameter qualifier for "
|
||||
"'" + Parameter.Name + "' in macro '" + Name + "'");
|
||||
ParamLoc = Lexer.getLoc();
|
||||
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false))
|
||||
return true;
|
||||
} else {
|
||||
SMLoc QualLoc;
|
||||
StringRef Qualifier;
|
||||
|
||||
if (Qualifier == "req")
|
||||
Parameter.Required = true;
|
||||
else if (Qualifier == "vararg")
|
||||
Parameter.Vararg = true;
|
||||
else
|
||||
return Error(QualLoc, Qualifier + " is not a valid parameter qualifier "
|
||||
"for '" + Parameter.Name + "' in macro '" + Name + "'");
|
||||
}
|
||||
QualLoc = Lexer.getLoc();
|
||||
if (parseIdentifier(Qualifier))
|
||||
return Error(QualLoc, "missing 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 + "'");
|
||||
if (Qualifier.equals_lower("req"))
|
||||
Parameter.Required = true;
|
||||
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 + "'");
|
||||
}
|
||||
}
|
||||
|
||||
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,10 +5374,14 @@ 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.
|
||||
++MacroDepth;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5434,14 +5390,14 @@ bool MasmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
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