diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" @@ -2497,6 +2498,84 @@ if ((!IsDarwin || NParameters != 0) && NParameters != A.size()) return Error(L, "Wrong number of arguments"); + // Handles LOCAL directives in alternate macro mode. + std::string Buf; + if (AltMacroMode && Body.find("LOCAL ") != std::string::npos) { + std::size_t Pos = 0; + StringSet<> Locals; + + // Checks if there are duplicate arguments to LOCAL directives + while (true) { + Pos = Body.find("LOCAL ", Pos); + if (Pos == std::string::npos) + break; + if (Pos > 0 && isalpha(Body[Pos - 1])) + continue; + + std::size_t I = Pos + 6; + std::size_t E = Body.find('\n', I); + while (I < E) { + while (I < E && !isIdentifierChar(Body[I])) + ++I; + std::size_t SymB = I; + while (I < E && isIdentifierChar(Body[I])) + ++I; + std::string Sym(Body.data() + SymB, I - SymB); + if (!Locals.insert(Sym).second) + return Error( + L, Sym + " was already used as an argument to a LOCAL directive"); + } + Pos = E + 1; + } + + Buf = std::string(Body); + Pos = 0; + + // Replaces all the uses of the argument to a LOCAL directive with a unique + // name + while (true) { + Pos = Buf.find("LOCAL ", Pos); + if (Pos == std::string::npos) + break; + if (Pos > 0 && isalpha(Buf[Pos - 1])) + continue; + + std::size_t I = Pos + 6; + std::size_t E = Buf.find('\n', I); + while (I < E) { + while (I < E && !isIdentifierChar(Buf[I])) + ++I; + std::size_t SymB = I; + while (I < E && isIdentifierChar(Buf[I])) + ++I; + std::string Sym(Buf.data() + SymB, I - SymB); + std::string LocalSym; + do { + static unsigned LocalCnt = 0; + LocalSym = Sym + std::to_string(LocalCnt++); + } while (getContext().lookupSymbol(LocalSym)); + + // Replace all the uses of the current argument + size_t SymI = E + 1; + while (true) { + SymI = Buf.find(Sym, SymI); + if (SymI == std::string::npos) + break; + std::size_t SymE = SymI + Sym.size(); + if ((SymI > 0 && isIdentifierChar(Buf[SymI - 1])) || + (SymE < Buf.size() && isIdentifierChar(Buf[SymE]))) { + SymI = SymE; + continue; + } + Buf = Buf.substr(0, SymI) + LocalSym + Buf.substr(SymE); + SymI += LocalSym.size(); + } + } + Buf = Buf.substr(0, Pos) + Buf.substr(E + 1); + } + Body = Buf; + } + // A macro without parameters is handled differently on Darwin: // gas accepts no arguments and does no substitutions while (!Body.empty()) { diff --git a/llvm/test/MC/AsmParser/directive_local_alt_macro_mode.s b/llvm/test/MC/AsmParser/directive_local_alt_macro_mode.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AsmParser/directive_local_alt_macro_mode.s @@ -0,0 +1,42 @@ +// RUN: llvm-mc -triple i386 -filetype=asm %s | FileCheck %s +// RUN: not llvm-mc -triple i386 -filetype=asm --defsym ERR=1 %s -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s + +.altmacro + +.macro write arg +LOCAL x,y +.set x, \arg +.set y, \arg +.set z, \arg +LOCAL z +.endm + +.macro err +LOCAL x,y +LOCAL x +.endm + + +.macro read +mov x, %eax +mov y, %ebx +mov z, %ecx +.endm + + +write 16 +read +.ifdef ERR +err +// ERR:[[#@LINE-1]]:4: error: x was already used as an argument to a LOCAL directive +.endif + + +// CHECK: .set x0, 16 +// CHECK-NEXT: .set y1, 16 +// CHECK-NEXT: .set z, 16 +// CHECK-EMPTY: +// CHECK-NEXT: movl x, %eax +// CHECK-NEXT: movl y, %ebx +// CHECK-NEXT: movl 16, %ecx +