MC: Add support for mach-o build_version

LC_BUILD_VERSION is a new load command superseding the previously used
LC_XXX_MIN_VERSION commands. This adds an assembler directive along with
encoding/streaming support.

llvm-svn: 320661
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 8f3dccb..3357553 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -137,6 +137,8 @@
   void EmitDataRegion(MCDataRegionType Kind) override;
   void EmitVersionMin(MCVersionMinType Kind, unsigned Major, unsigned Minor,
                       unsigned Update) override;
+  void EmitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor,
+                        unsigned Update) override;
   void EmitThumbFunc(MCSymbol *Func) override;
 
   void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) override;
@@ -471,15 +473,39 @@
   EmitEOL();
 }
 
-void MCAsmStreamer::EmitVersionMin(MCVersionMinType Kind, unsigned Major,
-                                   unsigned Minor, unsigned Update) {
-  switch (Kind) {
-  case MCVM_WatchOSVersionMin:    OS << "\t.watchos_version_min"; break;
-  case MCVM_TvOSVersionMin:       OS << "\t.tvos_version_min"; break;
-  case MCVM_IOSVersionMin:        OS << "\t.ios_version_min"; break;
-  case MCVM_OSXVersionMin:        OS << "\t.macosx_version_min"; break;
+static const char *getVersionMinDirective(MCVersionMinType Type) {
+  switch (Type) {
+  case MCVM_WatchOSVersionMin: return ".watchos_version_min";
+  case MCVM_TvOSVersionMin:    return ".tvos_version_min";
+  case MCVM_IOSVersionMin:     return ".ios_version_min";
+  case MCVM_OSXVersionMin:     return ".macosx_version_min";
   }
-  OS << " " << Major << ", " << Minor;
+  llvm_unreachable("Invalid MC version min type");
+}
+
+void MCAsmStreamer::EmitVersionMin(MCVersionMinType Type, unsigned Major,
+                                   unsigned Minor, unsigned Update) {
+  OS << '\t' << getVersionMinDirective(Type) << ' ' << Major << ", " << Minor;
+  if (Update)
+    OS << ", " << Update;
+  EmitEOL();
+}
+
+static const char *getPlatformName(MachO::PlatformType Type) {
+  switch (Type) {
+  case MachO::PLATFORM_MACOS:    return "macos";
+  case MachO::PLATFORM_IOS:      return "ios";
+  case MachO::PLATFORM_TVOS:     return "tvos";
+  case MachO::PLATFORM_WATCHOS:  return "watchos";
+  case MachO::PLATFORM_BRIDGEOS: return "bridgeos";
+  }
+  llvm_unreachable("Invalid Mach-O platform type");
+}
+
+void MCAsmStreamer::EmitBuildVersion(unsigned Platform, unsigned Major,
+                                     unsigned Minor, unsigned Update) {
+  const char *PlatformName = getPlatformName((MachO::PlatformType)Platform);
+  OS << "\t.build_version " << PlatformName << ", " << Major << ", " << Minor;
   if (Update)
     OS << ", " << Update;
   EmitEOL();
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
index 9a23e61..01d1659 100644
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -88,7 +88,7 @@
     : Context(Context), Backend(Backend), Emitter(Emitter), Writer(Writer),
       BundleAlignSize(0), RelaxAll(false), SubsectionsViaSymbols(false),
       IncrementalLinkerCompatible(false), ELFHeaderEFlags(0) {
-  VersionMinInfo.Major = 0; // Major version == 0 for "none specified"
+  VersionInfo.Major = 0; // Major version == 0 for "none specified"
 }
 
 MCAssembler::~MCAssembler() = default;
@@ -107,7 +107,7 @@
   IncrementalLinkerCompatible = false;
   ELFHeaderEFlags = 0;
   LOHContainer.reset();
-  VersionMinInfo.Major = 0;
+  VersionInfo.Major = 0;
 
   // reset objects owned by us
   getBackend().reset();
diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp
index a5c1b13..6e1dca8 100644
--- a/llvm/lib/MC/MCMachOStreamer.cpp
+++ b/llvm/lib/MC/MCMachOStreamer.cpp
@@ -88,6 +88,8 @@
   void EmitDataRegion(MCDataRegionType Kind) override;
   void EmitVersionMin(MCVersionMinType Kind, unsigned Major,
                       unsigned Minor, unsigned Update) override;
+  void EmitBuildVersion(unsigned Platform, unsigned Major,
+                        unsigned Minor, unsigned Update) override;
   void EmitThumbFunc(MCSymbol *Func) override;
   bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
   void EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override;
@@ -265,7 +267,13 @@
 
 void MCMachOStreamer::EmitVersionMin(MCVersionMinType Kind, unsigned Major,
                                      unsigned Minor, unsigned Update) {
-  getAssembler().setVersionMinInfo(Kind, Major, Minor, Update);
+  getAssembler().setVersionMin(Kind, Major, Minor, Update);
+}
+
+void MCMachOStreamer::EmitBuildVersion(unsigned Platform, unsigned Major,
+                                       unsigned Minor, unsigned Update) {
+  getAssembler().setBuildVersion((MachO::PlatformType)Platform, Major, Minor,
+                                 Update);
 }
 
 void MCMachOStreamer::EmitThumbFunc(MCSymbol *Symbol) {
diff --git a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
index f4152a9..195ddc7 100644
--- a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
@@ -54,7 +54,7 @@
                           unsigned TAA = 0, unsigned ImplicitAlign = 0,
                           unsigned StubSize = 0);
 
-  SMLoc LastVersionMinDirective;
+  SMLoc LastVersionDirective;
 
 public:
   DarwinAsmParser() = default;
@@ -186,14 +186,17 @@
     addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveTLV>(".tlv");
 
     addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveIdent>(".ident");
-    addDirectiveHandler<&DarwinAsmParser::parseVersionMin>(
+    addDirectiveHandler<&DarwinAsmParser::parseWatchOSVersionMin>(
       ".watchos_version_min");
-    addDirectiveHandler<&DarwinAsmParser::parseVersionMin>(".tvos_version_min");
-    addDirectiveHandler<&DarwinAsmParser::parseVersionMin>(".ios_version_min");
-    addDirectiveHandler<&DarwinAsmParser::parseVersionMin>(
+    addDirectiveHandler<&DarwinAsmParser::parseTvOSVersionMin>(
+      ".tvos_version_min");
+    addDirectiveHandler<&DarwinAsmParser::parseIOSVersionMin>(
+      ".ios_version_min");
+    addDirectiveHandler<&DarwinAsmParser::parseMacOSXVersionMin>(
       ".macosx_version_min");
+    addDirectiveHandler<&DarwinAsmParser::parseBuildVersion>(".build_version");
 
-    LastVersionMinDirective = SMLoc();
+    LastVersionDirective = SMLoc();
   }
 
   bool parseDirectiveAltEntry(StringRef, SMLoc);
@@ -441,7 +444,24 @@
                          MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS);
   }
 
-  bool parseVersionMin(StringRef, SMLoc);
+  bool parseWatchOSVersionMin(StringRef Directive, SMLoc Loc) {
+    return parseVersionMin(Directive, Loc, MCVM_WatchOSVersionMin);
+  }
+  bool parseTvOSVersionMin(StringRef Directive, SMLoc Loc) {
+    return parseVersionMin(Directive, Loc, MCVM_TvOSVersionMin);
+  }
+  bool parseIOSVersionMin(StringRef Directive, SMLoc Loc) {
+    return parseVersionMin(Directive, Loc, MCVM_IOSVersionMin);
+  }
+  bool parseMacOSXVersionMin(StringRef Directive, SMLoc Loc) {
+    return parseVersionMin(Directive, Loc, MCVM_OSXVersionMin);
+  }
+
+  bool parseBuildVersion(StringRef Directive, SMLoc Loc);
+  bool parseVersionMin(StringRef Directive, SMLoc Loc, MCVersionMinType Type);
+  bool parseVersion(unsigned *Major, unsigned *Minor, unsigned *Update);
+  void checkVersion(StringRef Directive, StringRef Arg, SMLoc Loc,
+                    Triple::OSType ExpectedOS);
 };
 
 } // end anonymous namespace
@@ -978,70 +998,144 @@
   return false;
 }
 
-/// parseVersionMin
-///  ::= .ios_version_min major,minor[,update]
-///  ::= .macosx_version_min major,minor[,update]
-bool DarwinAsmParser::parseVersionMin(StringRef Directive, SMLoc Loc) {
-  int64_t Major = 0, Minor = 0, Update = 0;
-  int Kind = StringSwitch<int>(Directive)
-    .Case(".watchos_version_min", MCVM_WatchOSVersionMin)
-    .Case(".tvos_version_min", MCVM_TvOSVersionMin)
-    .Case(".ios_version_min", MCVM_IOSVersionMin)
-    .Case(".macosx_version_min", MCVM_OSXVersionMin);
+/// parseVersion ::= major, minor [, update]
+bool DarwinAsmParser::parseVersion(unsigned *Major, unsigned *Minor,
+                                   unsigned *Update) {
   // Get the major version number.
   if (getLexer().isNot(AsmToken::Integer))
+    return TokError("invalid OS major version number, integer expected");
+  int64_t MajorVal = getLexer().getTok().getIntVal();
+  if (MajorVal > 65535 || MajorVal <= 0)
     return TokError("invalid OS major version number");
-  Major = getLexer().getTok().getIntVal();
-  if (Major > 65535 || Major <= 0)
-    return TokError("invalid OS major version number");
+  *Major = (unsigned)MajorVal;
   Lex();
   if (getLexer().isNot(AsmToken::Comma))
-    return TokError("minor OS version number required, comma expected");
+    return TokError("OS minor version number required, comma expected");
   Lex();
   // Get the minor version number.
   if (getLexer().isNot(AsmToken::Integer))
+    return TokError("invalid OS minor version number, integer expected");
+  int64_t MinorVal = getLexer().getTok().getIntVal();
+  if (MinorVal > 255 || MinorVal < 0)
     return TokError("invalid OS minor version number");
-  Minor = getLexer().getTok().getIntVal();
-  if (Minor > 255 || Minor < 0)
-    return TokError("invalid OS minor version number");
+  *Minor = MinorVal;
   Lex();
+
   // Get the update level, if specified
-  if (getLexer().isNot(AsmToken::EndOfStatement)) {
-    if (getLexer().isNot(AsmToken::Comma))
-      return TokError("invalid update specifier, comma expected");
-    Lex();
-    if (getLexer().isNot(AsmToken::Integer))
-      return TokError("invalid OS update number");
-    Update = getLexer().getTok().getIntVal();
-    if (Update > 255 || Update < 0)
-      return TokError("invalid OS update number");
-    Lex();
-  }
-
-  const Triple &T = getContext().getObjectFileInfo()->getTargetTriple();
-  Triple::OSType ExpectedOS = Triple::UnknownOS;
-  switch ((MCVersionMinType)Kind) {
-  case MCVM_WatchOSVersionMin: ExpectedOS = Triple::WatchOS; break;
-  case MCVM_TvOSVersionMin:    ExpectedOS = Triple::TvOS;    break;
-  case MCVM_IOSVersionMin:     ExpectedOS = Triple::IOS;     break;
-  case MCVM_OSXVersionMin:     ExpectedOS = Triple::MacOSX;  break;
-  }
-  if (T.getOS() != ExpectedOS)
-    Warning(Loc, Directive + " should only be used for " +
-            Triple::getOSTypeName(ExpectedOS) + " targets");
-
-  if (LastVersionMinDirective.isValid()) {
-    Warning(Loc, "overriding previous version_min directive");
-    Note(LastVersionMinDirective, "previous definition is here");
-  }
-  LastVersionMinDirective = Loc;
-
-  // We've parsed a correct version specifier, so send it to the streamer.
-  getStreamer().EmitVersionMin((MCVersionMinType)Kind, Major, Minor, Update);
-
+  *Update = 0;
+  if (getLexer().is(AsmToken::EndOfStatement))
+    return false;
+  if (getLexer().isNot(AsmToken::Comma))
+    return TokError("invalid OS update specifier, comma expected");
+  Lex();
+  if (getLexer().isNot(AsmToken::Integer))
+    return TokError("invalid OS update version number, integer expected");
+  int64_t UpdateVal = getLexer().getTok().getIntVal();
+  if (UpdateVal > 255 || UpdateVal < 0)
+    return TokError("invalid OS update version number");
+  *Update = UpdateVal;
+  Lex();
   return false;
 }
 
+void DarwinAsmParser::checkVersion(StringRef Directive, StringRef Arg,
+                                   SMLoc Loc, Triple::OSType ExpectedOS) {
+  const Triple &Target = getContext().getObjectFileInfo()->getTargetTriple();
+  if (Target.getOS() != ExpectedOS)
+    Warning(Loc, Twine(Directive) +
+            (Arg.empty() ? Twine() : Twine(' ') + Arg) +
+            " used while targeting " + Target.getOSName());
+
+  if (LastVersionDirective.isValid()) {
+    Warning(Loc, "overriding previous version directive");
+    Note(LastVersionDirective, "previous definition is here");
+  }
+  LastVersionDirective = Loc;
+}
+
+static Triple::OSType getOSTypeFromMCVM(MCVersionMinType Type) {
+  switch (Type) {
+  case MCVM_WatchOSVersionMin: return Triple::WatchOS;
+  case MCVM_TvOSVersionMin:    return Triple::TvOS;
+  case MCVM_IOSVersionMin:     return Triple::IOS;
+  case MCVM_OSXVersionMin:     return Triple::MacOSX;
+  }
+  llvm_unreachable("Invalid mc version min type");
+}
+
+/// parseVersionMin
+///   ::= .ios_version_min parseVersion
+///   |   .macosx_version_min parseVersion
+///   |   .tvos_version_min parseVersion
+///   |   .watchos_version_min parseVersion
+bool DarwinAsmParser::parseVersionMin(StringRef Directive, SMLoc Loc,
+                                      MCVersionMinType Type) {
+  unsigned Major;
+  unsigned Minor;
+  unsigned Update;
+  if (parseVersion(&Major, &Minor, &Update))
+    return true;
+
+  if (parseToken(AsmToken::EndOfStatement))
+    return addErrorSuffix(Twine(" in '") + Directive + "' directive");
+
+  Triple::OSType ExpectedOS = getOSTypeFromMCVM(Type);
+  checkVersion(Directive, StringRef(), Loc, ExpectedOS);
+
+  getStreamer().EmitVersionMin(Type, Major, Minor, Update);
+  return false;
+}
+
+Triple::OSType getOSTypeFromPlatform(MachO::PlatformType Type) {
+  switch (Type) {
+  case MachO::PLATFORM_MACOS:   return Triple::MacOSX;
+  case MachO::PLATFORM_IOS:     return Triple::IOS;
+  case MachO::PLATFORM_TVOS:    return Triple::TvOS;
+  case MachO::PLATFORM_WATCHOS: return Triple::WatchOS;
+  case MachO::PLATFORM_BRIDGEOS: /* silence warning */break;
+  }
+  llvm_unreachable("Invalid mach-o platform type");
+}
+
+/// parseBuildVersion
+///   ::= .build_version (macos|ios|tvos|watchos), parseVersion
+bool DarwinAsmParser::parseBuildVersion(StringRef Directive, SMLoc Loc) {
+  StringRef PlatformName;
+  SMLoc PlatformLoc = getTok().getLoc();
+  if (getParser().parseIdentifier(PlatformName))
+    return TokError("platform name expected");
+
+  unsigned Platform = StringSwitch<unsigned>(PlatformName)
+    .Case("macos", MachO::PLATFORM_MACOS)
+    .Case("ios", MachO::PLATFORM_IOS)
+    .Case("tvos", MachO::PLATFORM_TVOS)
+    .Case("watchos", MachO::PLATFORM_WATCHOS)
+    .Default(0);
+  if (Platform == 0)
+    return Error(PlatformLoc, "unknown platform name");
+
+  if (getLexer().isNot(AsmToken::Comma))
+    return TokError("version number required, comma expected");
+  Lex();
+
+  unsigned Major;
+  unsigned Minor;
+  unsigned Update;
+  if (parseVersion(&Major, &Minor, &Update))
+    return true;
+
+  if (parseToken(AsmToken::EndOfStatement))
+    return addErrorSuffix(" in '.build_version' directive");
+
+  Triple::OSType ExpectedOS
+    = getOSTypeFromPlatform((MachO::PlatformType)Platform);
+  checkVersion(Directive, PlatformName, Loc, ExpectedOS);
+
+  getStreamer().EmitBuildVersion(Platform, Major, Minor, Update);
+  return false;
+}
+
+
 namespace llvm {
 
 MCAsmParserExtension *createDarwinAsmParser() {
diff --git a/llvm/lib/MC/MachObjectWriter.cpp b/llvm/lib/MC/MachObjectWriter.cpp
index 7dbb84e..c7eaa76 100644
--- a/llvm/lib/MC/MachObjectWriter.cpp
+++ b/llvm/lib/MC/MachObjectWriter.cpp
@@ -721,6 +721,16 @@
   return false;
 }
 
+static MachO::LoadCommandType getLCFromMCVM(MCVersionMinType Type) {
+  switch (Type) {
+  case MCVM_OSXVersionMin:     return MachO::LC_VERSION_MIN_MACOSX;
+  case MCVM_IOSVersionMin:     return MachO::LC_VERSION_MIN_IPHONEOS;
+  case MCVM_TvOSVersionMin:    return MachO::LC_VERSION_MIN_TVOS;
+  case MCVM_WatchOSVersionMin: return MachO::LC_VERSION_MIN_WATCHOS;
+  }
+  llvm_unreachable("Invalid mc version min type");
+}
+
 void MachObjectWriter::writeObject(MCAssembler &Asm,
                                    const MCAsmLayout &Layout) {
   // Compute symbol table information and bind symbol indices.
@@ -728,8 +738,8 @@
                      UndefinedSymbolData);
 
   unsigned NumSections = Asm.size();
-  const MCAssembler::VersionMinInfoType &VersionInfo =
-    Layout.getAssembler().getVersionMinInfo();
+  const MCAssembler::VersionInfoType &VersionInfo =
+    Layout.getAssembler().getVersionInfo();
 
   // The section data starts after the header, the segment load command (and
   // section headers) and the symbol table.
@@ -741,7 +751,10 @@
   // Add the deployment target version info load command size, if used.
   if (VersionInfo.Major != 0) {
     ++NumLoadCommands;
-    LoadCommandsSize += sizeof(MachO::version_min_command);
+    if (VersionInfo.EmitBuildVersion)
+      LoadCommandsSize += sizeof(MachO::build_version_command);
+    else
+      LoadCommandsSize += sizeof(MachO::version_min_command);
   }
 
   // Add the data-in-code load command size, if used.
@@ -832,25 +845,22 @@
     assert(VersionInfo.Major < 65536 && "unencodable major target version");
     uint32_t EncodedVersion = VersionInfo.Update | (VersionInfo.Minor << 8) |
       (VersionInfo.Major << 16);
-    MachO::LoadCommandType LCType;
-    switch (VersionInfo.Kind) {
-    case MCVM_OSXVersionMin:
-      LCType = MachO::LC_VERSION_MIN_MACOSX;
-      break;
-    case MCVM_IOSVersionMin:
-      LCType = MachO::LC_VERSION_MIN_IPHONEOS;
-      break;
-    case MCVM_TvOSVersionMin:
-      LCType = MachO::LC_VERSION_MIN_TVOS;
-      break;
-    case MCVM_WatchOSVersionMin:
-      LCType = MachO::LC_VERSION_MIN_WATCHOS;
-      break;
+    if (VersionInfo.EmitBuildVersion) {
+      // FIXME: Currently empty tools. Add clang version in the future.
+      write32(MachO::LC_BUILD_VERSION);
+      write32(sizeof(MachO::build_version_command));
+      write32(VersionInfo.TypeOrPlatform.Platform);
+      write32(EncodedVersion);
+      write32(0);         // SDK version.
+      write32(0);         // Empty tools list.
+    } else {
+      MachO::LoadCommandType LCType
+        = getLCFromMCVM(VersionInfo.TypeOrPlatform.Type);
+      write32(LCType);
+      write32(sizeof(MachO::version_min_command));
+      write32(EncodedVersion);
+      write32(0);         // reserved.
     }
-    write32(LCType);
-    write32(sizeof(MachO::version_min_command));
-    write32(EncodedVersion);
-    write32(0);         // reserved.
   }
 
   // Write the data-in-code load command, if used.