diff --git a/llvm/include/llvm/MC/MCAsmMacro.h b/llvm/include/llvm/MC/MCAsmMacro.h --- a/llvm/include/llvm/MC/MCAsmMacro.h +++ b/llvm/include/llvm/MC/MCAsmMacro.h @@ -143,10 +143,14 @@ StringRef Name; StringRef Body; MCAsmMacroParameters Parameters; + std::vector 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 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()); } diff --git a/llvm/lib/MC/MCAsmMacro.cpp b/llvm/lib/MC/MCAsmMacro.cpp --- a/llvm/lib/MC/MCAsmMacro.cpp +++ b/llvm/lib/MC/MCAsmMacro.cpp @@ -38,6 +38,11 @@ OS << " "; P.dump(); } + if (!Locals.empty()) { + OS << " Locals:\n"; + for (StringRef L : Locals) + OS << " " << L << '\n'; + } OS << " (BEGIN BODY)" << Body << "(END BODY)\n"; } #endif diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp --- a/llvm/lib/MC/MCParser/MasmParser.cpp +++ b/llvm/lib/MC/MCParser/MasmParser.cpp @@ -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 @@ /// 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 @@ ArrayRef Parameters); bool expandMacro(raw_svector_ostream &OS, StringRef Body, ArrayRef Parameters, - ArrayRef A, bool EnableAtPseudoVariable, - SMLoc L); + ArrayRef A, + const std::vector &Locals, SMLoc L); /// Are we inside a macro instantiation? bool isInsideMacroInstantiation() {return !ActiveMacros.empty();} @@ -706,8 +707,6 @@ 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 @@ 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 @@ /// 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 @@ } /// 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 @@ 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 @@ 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 @@ bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body, ArrayRef Parameters, ArrayRef A, - bool EnableAtPseudoVariable, SMLoc L) { + const std::vector &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 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(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 @@ if (Pos == End) break; - if (IsDarwin && !NParameters) { - switch (Body[Pos + 1]) { - // $$ => $ - case '$': - OS << '$'; - break; - - // $n => number of arguments - case 'n': - OS << A.size(); - break; + unsigned I = Pos; + bool InitialAmpersand = (Body[I] == '&'); + if (InitialAmpersand) { + ++I; + ++Pos; + } + while (isIdentifierChar(Body[I]) && I + 1 != End) + ++I; - // $[0-9] => argument - default: { - // Missing arguments are ignored. - unsigned Index = Body[Pos + 1] - '0'; - if (Index >= A.size()) - break; + const char *Begin = Body.data() + Pos; + StringRef Argument(Begin, I - Pos); + unsigned Index = 0; - // Otherwise substitute with the token values, with spaces eliminated. - for (const AsmToken &Token : A[Index]) - OS << Token.getString(); + for (; Index < NParameters; ++Index) + if (Parameters[Index].Name == Argument) 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; - - const char *Begin = Body.data() + Pos + 1; - StringRef Argument(Begin, I - (Pos + 1)); - unsigned Index = 0; + 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(); + } - 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 @@ } // end anonymous namespace bool MasmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) { - if (Vararg) { if (Lexer.isNot(AsmToken::EndOfStatement)) { StringRef Str = parseStringToEndOfStatement(); @@ -2788,6 +2739,17 @@ 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 @@ return TokError("unexpected token in macro instantiation"); if (ParenLevel == 0) { - if (Lexer.is(AsmToken::Comma)) break; @@ -2887,7 +2848,7 @@ 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 @@ 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 @@ 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 Instantiation = MemoryBuffer::getMemBufferCopy(OS.str(), ""); @@ -5312,76 +5263,60 @@ 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 + "'"); - - 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 + "'"); - } - - 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 + "'"); + 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 + + "'"); + + 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 @@ // Eat just the end of statement. Lexer.Lex(); + std::vector 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 @@ // 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 @@ // 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 @@ } 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 @@ } /// parseDirectiveExitMacro -/// ::= .exitm +/// ::= exitm bool MasmParser::parseDirectiveExitMacro(StringRef Directive) { if (parseToken(AsmToken::EndOfStatement, "unexpected token in '" + Directive + "' directive")) @@ -6365,9 +6321,9 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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; } diff --git a/llvm/test/tools/llvm-ml/macro.test b/llvm/test/tools/llvm-ml/macro.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ml/macro.test @@ -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 + 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 diff --git a/llvm/test/tools/llvm-ml/macro_errors.test b/llvm/test/tools/llvm-ml/macro_errors.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ml/macro_errors.test @@ -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