Factor macro argument parsing into helper methods and add support for .irp.
authorRafael Espindola <rafael.espindola@gmail.com>
Fri, 15 Jun 2012 14:02:34 +0000 (14:02 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Fri, 15 Jun 2012 14:02:34 +0000 (14:02 +0000)
Patch extracted from a larger one by the PaX team. I added the testcases
and tightened error handling a bit.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@158523 91177308-0d34-0410-b5e6-96231b3b80d8

lib/MC/MCParser/AsmParser.cpp
test/MC/AsmParser/macro-err1.s [new file with mode: 0644]
test/MC/AsmParser/macro-irp.s [new file with mode: 0644]

index 04603e994a34b0357029b671e54305bf4b864918..f2618e190afb08594e41403a7e05b30d48ef2706 100644 (file)
@@ -206,6 +206,9 @@ private:
 
   void EatToEndOfStatement();
 
+  bool ParseMacroArgument(MacroArgument &MA);
+  bool ParseMacroArguments(const Macro *M, std::vector<MacroArgument> &A);
+
   /// \brief Parse up to the end of statement and a return the contents from the
   /// current token until the end of the statement; the current token on exit
   /// will be either the EndOfStatement or EOF.
@@ -273,6 +276,7 @@ private:
   void InstantiateMacroLikeBody(Macro *M, SMLoc DirectiveLoc,
                                 raw_svector_ostream &OS);
   bool ParseDirectiveRept(SMLoc DirectiveLoc); // ".rept"
+  bool ParseDirectiveIrp(SMLoc DirectiveLoc);  // ".irp"
   bool ParseDirectiveEndr(SMLoc DirectiveLoc); // ".endr"
 };
 
@@ -1274,6 +1278,8 @@ bool AsmParser::ParseStatement() {
     // Macro-like directives
     if (IDVal == ".rept")
       return ParseDirectiveRept(IDLoc);
+    if (IDVal == ".irp")
+      return ParseDirectiveIrp(IDLoc);
     if (IDVal == ".endr")
       return ParseDirectiveEndr(IDLoc);
 
@@ -1537,44 +1543,76 @@ MacroInstantiation::MacroInstantiation(const Macro *M, SMLoc IL, SMLoc EL,
 {
 }
 
-bool AsmParser::HandleMacroEntry(StringRef Name, SMLoc NameLoc,
-                                 const Macro *M) {
-  // Arbitrarily limit macro nesting depth, to match 'as'. We can eliminate
-  // this, although we should protect against infinite loops.
-  if (ActiveMacros.size() == 20)
-    return TokError("macros cannot be nested more than 20 levels deep");
-
-  // Parse the macro instantiation arguments.
-  std::vector<MacroArgument> MacroArguments;
-  MacroArguments.push_back(MacroArgument());
+/// ParseMacroArgument - Extract AsmTokens for a macro argument.
+/// This is used for both default macro parameter values and the
+/// arguments in macro invocations
+bool AsmParser::ParseMacroArgument(MacroArgument &MA) {
   unsigned ParenLevel = 0;
+
   for (;;) {
-    if (Lexer.is(AsmToken::Eof))
+    SMLoc LastTokenLoc;
+
+    if (Lexer.is(AsmToken::Eof) || Lexer.is(AsmToken::Equal))
       return TokError("unexpected token in macro instantiation");
+
+    // HandleMacroEntry relies on not advancing the lexer here
+    // to be able to fill in the remaining default parameter values
     if (Lexer.is(AsmToken::EndOfStatement))
       break;
+    if (ParenLevel == 0 && Lexer.is(AsmToken::Comma))
+      break;
 
-    // If we aren't inside parentheses and this is a comma, start a new token
-    // list.
-    if (ParenLevel == 0 && Lexer.is(AsmToken::Comma)) {
-      MacroArguments.push_back(MacroArgument());
-    } else {
-      // Adjust the current parentheses level.
-      if (Lexer.is(AsmToken::LParen))
-        ++ParenLevel;
-      else if (Lexer.is(AsmToken::RParen) && ParenLevel)
-        --ParenLevel;
-
-      // Append the token to the current argument list.
-      MacroArguments.back().push_back(getTok());
-    }
+    // Adjust the current parentheses level.
+    if (Lexer.is(AsmToken::LParen))
+      ++ParenLevel;
+    else if (Lexer.is(AsmToken::RParen) && ParenLevel)
+      --ParenLevel;
+
+    // Append the token to the current argument list.
+    MA.push_back(getTok());
     Lex();
   }
-  // If the last argument didn't end up with any tokens, it's not a real
-  // argument and we should remove it from the list. This happens with either
-  // a tailing comma or an empty argument list.
-  if (MacroArguments.back().empty())
-    MacroArguments.pop_back();
+  if (ParenLevel != 0)
+    return TokError("unbalanced parenthesises in macro argument");
+  return false;
+}
+
+// Parse the macro instantiation arguments.
+bool AsmParser::ParseMacroArguments(const Macro *M,
+                                    std::vector<MacroArgument> &A) {
+  const unsigned NParameters = M ? M->Parameters.size() : 0;
+
+  // Parse two kinds of macro invocations:
+  // - macros defined without any parameters accept an arbitrary number of them
+  // - macros defined with parameters accept at most that many of them
+  for (unsigned Parameter = 0; !NParameters || Parameter < NParameters;
+       ++Parameter) {
+    MacroArgument MA;
+
+    if (ParseMacroArgument(MA))
+      return true;
+
+    if (!MA.empty())
+      A.push_back(MA);
+    if (Lexer.is(AsmToken::EndOfStatement))
+      return false;
+
+    if (Lexer.is(AsmToken::Comma))
+      Lex();
+  }
+  return TokError("Too many arguments");
+}
+
+bool AsmParser::HandleMacroEntry(StringRef Name, SMLoc NameLoc,
+                                 const Macro *M) {
+  // Arbitrarily limit macro nesting depth, to match 'as'. We can eliminate
+  // this, although we should protect against infinite loops.
+  if (ActiveMacros.size() == 20)
+    return TokError("macros cannot be nested more than 20 levels deep");
+
+  std::vector<MacroArgument> MacroArguments;
+  if (ParseMacroArguments(M, MacroArguments))
+    return true;
 
   // Macro instantiation is lexical, unfortunately. We construct a new buffer
   // to hold the macro body with substitutions.
@@ -3239,6 +3277,53 @@ bool AsmParser::ParseDirectiveRept(SMLoc DirectiveLoc) {
   return false;
 }
 
+/// ParseDirectiveIrp
+/// ::= .irp symbol,values
+bool AsmParser::ParseDirectiveIrp(SMLoc DirectiveLoc) {
+  std::vector<StringRef> Parameters;
+  StringRef Parameter;
+
+  if (ParseIdentifier(Parameter))
+    return TokError("expected identifier in '.irp' directive");
+
+  Parameters.push_back(Parameter);
+
+  if (Lexer.isNot(AsmToken::Comma))
+    return TokError("expected comma in '.irp' directive");
+
+  Lex();
+
+  std::vector<MacroArgument> A;
+  if (ParseMacroArguments(0, A))
+    return true;
+
+  // Eat the end of statement.
+  Lex();
+
+  // Lex the irp definition.
+  Macro *M = ParseMacroLikeBody(DirectiveLoc);
+  if (!M)
+    return true;
+
+  // Macro instantiation is lexical, unfortunately. We construct a new buffer
+  // to hold the macro body with substitutions.
+  SmallString<256> Buf;
+  raw_svector_ostream OS(Buf);
+
+  for (std::vector<MacroArgument>::iterator i = A.begin(), e = A.end(); i != e;
+       ++i) {
+    std::vector<MacroArgument> Args;
+    Args.push_back(*i);
+
+    if (expandMacro(OS, M->Body, Parameters, Args, getTok().getLoc()))
+      return true;
+  }
+
+  InstantiateMacroLikeBody(M, DirectiveLoc, OS);
+
+  return false;
+}
+
 bool AsmParser::ParseDirectiveEndr(SMLoc DirectiveLoc) {
   if (ActiveMacros.empty())
     return TokError("unexpected '.endr' directive, no current .rept");
diff --git a/test/MC/AsmParser/macro-err1.s b/test/MC/AsmParser/macro-err1.s
new file mode 100644 (file)
index 0000000..924deb0
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: not llvm-mc -triple x86_64-unknown-unknown %s 2> %t
+// RUN: FileCheck < %t %s
+
+.macro foo bar
+        .long \bar
+.endm
+
+foo 42,  42
+
+// CHECK: Too many arguments
diff --git a/test/MC/AsmParser/macro-irp.s b/test/MC/AsmParser/macro-irp.s
new file mode 100644 (file)
index 0000000..a368b74
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: llvm-mc -triple x86_64-unknown-unknown %s | FileCheck %s
+
+.irp reg,%eax,%ebx
+        pushl \reg
+.endr
+
+// CHECK: pushl %eax
+// CHECK: pushl %ebx