This patch extends LLVM IR to add metadata that can be used to emit macho files with two build version load commands.
It utilizes "darwin.target_variant.triple" and "darwin.target_variant.SDK Version" metadata names for that,
which will be set by a future patch in clang.
MachO uses two build version load commands to represent an object file / binary that is targeting both the macOS target,
and the Mac Catalyst target. At runtime, a dynamic library that supports both targets can be loaded from either a native
macOS or a Mac Catalyst app on a macOS system. We want to add support to this to upstream to LLVM to be able to build
compiler-rt for both targets, to finish the complete support for the Mac Catalyst platform, which is right now targetable
by upstream clang, but the compiler-rt bits aren't supported because of the lack of this multiple build version support.
Differential Revision: https://reviews.llvm.org/D112189
/// Set the partial sample profile ratio in the profile summary module flag,
/// if applicable.
void setPartialSampleProfileRatio(const ModuleSummaryIndex &Index);
+
+ /// Get the target variant triple which is a string describing a variant of
+ /// the target host platform. For example, Mac Catalyst can be a variant
+ /// target triple for a macOS target.
+ /// @returns a string containing the target variant triple.
+ StringRef getDarwinTargetVariantTriple() const;
+
+ /// Get the target variant version build SDK version metadata.
+ ///
+ /// An empty version is returned if no such metadata is attached.
+ VersionTuple getDarwinTargetVariantSDKVersion() const;
};
/// Given "llvm.used" or "llvm.compiler.used" as a global name, collect the
MCLOHContainer LOHContainer;
VersionInfoType VersionInfo;
+ VersionInfoType DarwinTargetVariantVersionInfo;
/// Evaluate a fixup to a relocatable expression and the value which should be
/// placed into the fixup.
VersionInfo.SDKVersion = SDKVersion;
}
+ const VersionInfoType &getDarwinTargetVariantVersionInfo() const {
+ return DarwinTargetVariantVersionInfo;
+ }
+ void setDarwinTargetVariantBuildVersion(MachO::PlatformType Platform,
+ unsigned Major, unsigned Minor,
+ unsigned Update,
+ VersionTuple SDKVersion) {
+ DarwinTargetVariantVersionInfo.EmitBuildVersion = true;
+ DarwinTargetVariantVersionInfo.TypeOrPlatform.Platform = Platform;
+ DarwinTargetVariantVersionInfo.Major = Major;
+ DarwinTargetVariantVersionInfo.Minor = Minor;
+ DarwinTargetVariantVersionInfo.Update = Update;
+ DarwinTargetVariantVersionInfo.SDKVersion = SDKVersion;
+ }
+
/// Reuse an assembler instance
///
void reset();
bool PositionIndependent = false;
MCContext *Ctx = nullptr;
VersionTuple SDKVersion;
+ Optional<Triple> DarwinTargetVariantTriple;
+ VersionTuple DarwinTargetVariantSDKVersion;
void initMachOMCObjectFileInfo(const Triple &T);
void initELFMCObjectFileInfo(const Triple &T, bool Large);
}
const VersionTuple &getSDKVersion() const { return SDKVersion; }
+
+ void setDarwinTargetVariantTriple(const Triple &T) {
+ DarwinTargetVariantTriple = T;
+ }
+
+ const Triple *getDarwinTargetVariantTriple() const {
+ return DarwinTargetVariantTriple ? DarwinTargetVariantTriple.getPointer()
+ : nullptr;
+ }
+
+ void setDarwinTargetVariantSDKVersion(const VersionTuple &TheSDKVersion) {
+ DarwinTargetVariantSDKVersion = TheSDKVersion;
+ }
+
+ const VersionTuple &getDarwinTargetVariantSDKVersion() const {
+ return DarwinTargetVariantSDKVersion;
+ }
};
} // end namespace llvm
unsigned Minor, unsigned Update,
VersionTuple SDKVersion) {}
+ virtual void emitDarwinTargetVariantBuildVersion(unsigned Platform,
+ unsigned Major,
+ unsigned Minor,
+ unsigned Update,
+ VersionTuple SDKVersion) {}
+
void emitVersionForTarget(const Triple &Target,
- const VersionTuple &SDKVersion);
+ const VersionTuple &SDKVersion,
+ const Triple *DarwinTargetVariantTriple,
+ const VersionTuple &DarwinTargetVariantSDKVersion);
/// Note in the output that the specified \p Func is a Thumb mode
/// function (ARM target only).
// use the directive, where it would need the same conditionalization
// anyway.
const Triple &Target = TM.getTargetTriple();
- OutStreamer->emitVersionForTarget(Target, M.getSDKVersion());
+ Triple TVT(M.getDarwinTargetVariantTriple());
+ OutStreamer->emitVersionForTarget(
+ Target, M.getSDKVersion(),
+ M.getDarwinTargetVariantTriple().empty() ? nullptr : &TVT,
+ M.getDarwinTargetVariantSDKVersion());
// Allow the target to emit any magic that it wants at the start of the file.
emitStartOfAsmFile(M);
ConstantDataArray::get(Context, Entries));
}
-VersionTuple Module::getSDKVersion() const {
- auto *CM = dyn_cast_or_null<ConstantAsMetadata>(getModuleFlag("SDK Version"));
+static VersionTuple getSDKVersionMD(Metadata *MD) {
+ auto *CM = dyn_cast_or_null<ConstantAsMetadata>(MD);
if (!CM)
return {};
auto *Arr = dyn_cast_or_null<ConstantDataArray>(CM->getValue());
return Result;
}
+VersionTuple Module::getSDKVersion() const {
+ return getSDKVersionMD(getModuleFlag("SDK Version"));
+}
+
GlobalVariable *llvm::collectUsedGlobalVariables(
const Module &M, SmallVectorImpl<GlobalValue *> &Vec, bool CompilerUsed) {
const char *Name = CompilerUsed ? "llvm.compiler.used" : "llvm.used";
}
}
}
+
+StringRef Module::getDarwinTargetVariantTriple() const {
+ if (const auto *MD = getModuleFlag("darwin.target_variant.triple"))
+ return cast<MDString>(MD)->getString();
+ return "";
+}
+
+VersionTuple Module::getDarwinTargetVariantSDKVersion() const {
+ return getSDKVersionMD(getModuleFlag("darwin.target_variant.SDK Version"));
+}
unsigned Update, VersionTuple SDKVersion) override;
void emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor,
unsigned Update, VersionTuple SDKVersion) override;
+ void emitDarwinTargetVariantBuildVersion(unsigned Platform, unsigned Major,
+ unsigned Minor, unsigned Update,
+ VersionTuple SDKVersion) override;
void emitThumbFunc(MCSymbol *Func) override;
void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override;
EmitEOL();
}
+void MCAsmStreamer::emitDarwinTargetVariantBuildVersion(
+ unsigned Platform, unsigned Major, unsigned Minor, unsigned Update,
+ VersionTuple SDKVersion) {
+ emitBuildVersion(Platform, Major, Minor, Update, SDKVersion);
+}
+
void MCAsmStreamer::emitThumbFunc(MCSymbol *Func) {
// This needs to emit to a temporary string to get properly quoted
// MCSymbols when they have spaces in them.
BundleAlignSize(0), RelaxAll(false), SubsectionsViaSymbols(false),
IncrementalLinkerCompatible(false), ELFHeaderEFlags(0) {
VersionInfo.Major = 0; // Major version == 0 for "none specified"
+ DarwinTargetVariantVersionInfo.Major = 0;
}
MCAssembler::~MCAssembler() = default;
LOHContainer.reset();
VersionInfo.Major = 0;
VersionInfo.SDKVersion = VersionTuple();
+ DarwinTargetVariantVersionInfo.Major = 0;
+ DarwinTargetVariantVersionInfo.SDKVersion = VersionTuple();
// reset objects owned by us
if (getBackendPtr())
unsigned Update, VersionTuple SDKVersion) override;
void emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor,
unsigned Update, VersionTuple SDKVersion) override;
+ void emitDarwinTargetVariantBuildVersion(unsigned Platform, unsigned Major,
+ unsigned Minor, unsigned Update,
+ VersionTuple SDKVersion) override;
void emitThumbFunc(MCSymbol *Func) override;
bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
void emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override;
Update, SDKVersion);
}
+void MCMachOStreamer::emitDarwinTargetVariantBuildVersion(
+ unsigned Platform, unsigned Major, unsigned Minor, unsigned Update,
+ VersionTuple SDKVersion) {
+ getAssembler().setDarwinTargetVariantBuildVersion(
+ (MachO::PlatformType)Platform, Major, Minor, Update, SDKVersion);
+}
+
void MCMachOStreamer::emitThumbFunc(MCSymbol *Symbol) {
// Remember that the function is a thumb function. Fixup and relocation
// values will need adjusted.
new MCMachOStreamer(Context, std::move(MAB), std::move(OW), std::move(CE),
DWARFMustBeAtTheEnd, LabelSections);
const Triple &Target = Context.getTargetTriple();
- S->emitVersionForTarget(Target, Context.getObjectFileInfo()->getSDKVersion());
+ S->emitVersionForTarget(
+ Target, Context.getObjectFileInfo()->getSDKVersion(),
+ Context.getObjectFileInfo()->getDarwinTargetVariantTriple(),
+ Context.getObjectFileInfo()->getDarwinTargetVariantSDKVersion());
if (RelaxAll)
S->getAssembler().setRelaxAll(true);
return S;
llvm_unreachable("unexpected OS type");
}
-void MCStreamer::emitVersionForTarget(const Triple &Target,
- const VersionTuple &SDKVersion) {
+void MCStreamer::emitVersionForTarget(
+ const Triple &Target, const VersionTuple &SDKVersion,
+ const Triple *DarwinTargetVariantTriple,
+ const VersionTuple &DarwinTargetVariantSDKVersion) {
if (!Target.isOSBinFormatMachO() || !Target.isOSDarwin())
return;
// Do we even know the version?
auto LinkedTargetVersion =
targetVersionOrMinimumSupportedOSVersion(Target, Version);
auto BuildVersionOSVersion = getMachoBuildVersionSupportedOS(Target);
+ bool ShouldEmitBuildVersion = false;
if (BuildVersionOSVersion.empty() ||
- LinkedTargetVersion >= BuildVersionOSVersion)
- return emitBuildVersion(getMachoBuildVersionPlatformType(Target),
- LinkedTargetVersion.getMajor(),
- LinkedTargetVersion.getMinor().getValueOr(0),
- LinkedTargetVersion.getSubminor().getValueOr(0),
- SDKVersion);
+ LinkedTargetVersion >= BuildVersionOSVersion) {
+ if (Target.isMacCatalystEnvironment() && DarwinTargetVariantTriple &&
+ DarwinTargetVariantTriple->isMacOSX()) {
+ emitVersionForTarget(*DarwinTargetVariantTriple,
+ DarwinTargetVariantSDKVersion,
+ /*TargetVariantTriple=*/nullptr,
+ /*TargetVariantSDKVersion=*/VersionTuple());
+ emitDarwinTargetVariantBuildVersion(
+ getMachoBuildVersionPlatformType(Target),
+ LinkedTargetVersion.getMajor(),
+ LinkedTargetVersion.getMinor().getValueOr(0),
+ LinkedTargetVersion.getSubminor().getValueOr(0), SDKVersion);
+ return;
+ }
+ emitBuildVersion(getMachoBuildVersionPlatformType(Target),
+ LinkedTargetVersion.getMajor(),
+ LinkedTargetVersion.getMinor().getValueOr(0),
+ LinkedTargetVersion.getSubminor().getValueOr(0),
+ SDKVersion);
+ ShouldEmitBuildVersion = true;
+ }
+
+ if (const Triple *TVT = DarwinTargetVariantTriple) {
+ if (Target.isMacOSX() && TVT->isMacCatalystEnvironment()) {
+ auto TVLinkedTargetVersion =
+ targetVersionOrMinimumSupportedOSVersion(*TVT, TVT->getiOSVersion());
+ emitDarwinTargetVariantBuildVersion(
+ getMachoBuildVersionPlatformType(*TVT),
+ TVLinkedTargetVersion.getMajor(),
+ TVLinkedTargetVersion.getMinor().getValueOr(0),
+ TVLinkedTargetVersion.getSubminor().getValueOr(0),
+ DarwinTargetVariantSDKVersion);
+ }
+ }
+
+ if (ShouldEmitBuildVersion)
+ return;
emitVersionMin(getMachoVersionMinLoadCommandType(Target),
LinkedTargetVersion.getMajor(),
LoadCommandsSize += sizeof(MachO::version_min_command);
}
+ const MCAssembler::VersionInfoType &TargetVariantVersionInfo =
+ Layout.getAssembler().getDarwinTargetVariantVersionInfo();
+
+ // Add the target variant version info load command size, if used.
+ if (TargetVariantVersionInfo.Major != 0) {
+ ++NumLoadCommands;
+ assert(TargetVariantVersionInfo.EmitBuildVersion &&
+ "target variant should use build version");
+ LoadCommandsSize += sizeof(MachO::build_version_command);
+ }
+
// Add the data-in-code load command size, if used.
unsigned NumDataRegions = Asm.getDataRegions().size();
if (NumDataRegions) {
}
// Write out the deployment target information, if it's available.
- if (VersionInfo.Major != 0) {
- auto EncodeVersion = [](VersionTuple V) -> uint32_t {
- assert(!V.empty() && "empty version");
- unsigned Update = V.getSubminor() ? *V.getSubminor() : 0;
- unsigned Minor = V.getMinor() ? *V.getMinor() : 0;
- assert(Update < 256 && "unencodable update target version");
- assert(Minor < 256 && "unencodable minor target version");
- assert(V.getMajor() < 65536 && "unencodable major target version");
- return Update | (Minor << 8) | (V.getMajor() << 16);
- };
- uint32_t EncodedVersion = EncodeVersion(
- VersionTuple(VersionInfo.Major, VersionInfo.Minor, VersionInfo.Update));
- uint32_t SDKVersion = !VersionInfo.SDKVersion.empty()
- ? EncodeVersion(VersionInfo.SDKVersion)
- : 0;
- if (VersionInfo.EmitBuildVersion) {
- // FIXME: Currently empty tools. Add clang version in the future.
- W.write<uint32_t>(MachO::LC_BUILD_VERSION);
- W.write<uint32_t>(sizeof(MachO::build_version_command));
- W.write<uint32_t>(VersionInfo.TypeOrPlatform.Platform);
- W.write<uint32_t>(EncodedVersion);
- W.write<uint32_t>(SDKVersion);
- W.write<uint32_t>(0); // Empty tools list.
- } else {
- MachO::LoadCommandType LCType
- = getLCFromMCVM(VersionInfo.TypeOrPlatform.Type);
- W.write<uint32_t>(LCType);
- W.write<uint32_t>(sizeof(MachO::version_min_command));
- W.write<uint32_t>(EncodedVersion);
- W.write<uint32_t>(SDKVersion);
- }
- }
+ auto EmitDeploymentTargetVersion =
+ [&](const MCAssembler::VersionInfoType &VersionInfo) {
+ auto EncodeVersion = [](VersionTuple V) -> uint32_t {
+ assert(!V.empty() && "empty version");
+ unsigned Update = V.getSubminor() ? *V.getSubminor() : 0;
+ unsigned Minor = V.getMinor() ? *V.getMinor() : 0;
+ assert(Update < 256 && "unencodable update target version");
+ assert(Minor < 256 && "unencodable minor target version");
+ assert(V.getMajor() < 65536 && "unencodable major target version");
+ return Update | (Minor << 8) | (V.getMajor() << 16);
+ };
+ uint32_t EncodedVersion = EncodeVersion(VersionTuple(
+ VersionInfo.Major, VersionInfo.Minor, VersionInfo.Update));
+ uint32_t SDKVersion = !VersionInfo.SDKVersion.empty()
+ ? EncodeVersion(VersionInfo.SDKVersion)
+ : 0;
+ if (VersionInfo.EmitBuildVersion) {
+ // FIXME: Currently empty tools. Add clang version in the future.
+ W.write<uint32_t>(MachO::LC_BUILD_VERSION);
+ W.write<uint32_t>(sizeof(MachO::build_version_command));
+ W.write<uint32_t>(VersionInfo.TypeOrPlatform.Platform);
+ W.write<uint32_t>(EncodedVersion);
+ W.write<uint32_t>(SDKVersion);
+ W.write<uint32_t>(0); // Empty tools list.
+ } else {
+ MachO::LoadCommandType LCType =
+ getLCFromMCVM(VersionInfo.TypeOrPlatform.Type);
+ W.write<uint32_t>(LCType);
+ W.write<uint32_t>(sizeof(MachO::version_min_command));
+ W.write<uint32_t>(EncodedVersion);
+ W.write<uint32_t>(SDKVersion);
+ }
+ };
+ if (VersionInfo.Major != 0)
+ EmitDeploymentTargetVersion(VersionInfo);
+ if (TargetVariantVersionInfo.Major != 0)
+ EmitDeploymentTargetVersion(TargetVariantVersionInfo);
// Write the data-in-code load command, if used.
uint64_t DataInCodeTableEnd = RelocTableEnd + NumDataRegions * 8;
--- /dev/null
+; RUN: llc %s -filetype=obj -o - | llvm-objdump --macho --private-headers - | FileCheck %s
+
+target triple = "x86_64-apple-ios13.1-macabi";
+!llvm.module.flags = !{!0, !1, !2};
+!0 = !{i32 2, !"SDK Version", [2 x i32] [ i32 13, i32 1 ] };
+!1 = !{i32 1, !"darwin.target_variant.triple", !"x86_64-apple-macos10.15"};
+!2 = !{i32 2, !"darwin.target_variant.SDK Version", [2 x i32] [ i32 10, i32 15 ] };
+
+define void @foo() {
+entry:
+ ret void
+}
+
+; CHECK: cmd LC_BUILD_VERSION
+; CHECK-NEXT: cmdsize 24
+; CHECK-NEXT: platform macos
+; CHECK-NEXT: sdk 10.15
+; CHECK-NEXT: minos 10.15
+; CHECK-NEXT: ntools 0
+; CHECK: cmd LC_BUILD_VERSION
+; CHECK-NEXT: cmdsize 24
+; CHECK-NEXT: platform macCatalyst
+; CHECK-NEXT: sdk 13.1
+; CHECK-NEXT: minos 13.1
+; CHECK-NEXT: ntools 0
--- /dev/null
+; RUN: llc %s -filetype=obj -o - | llvm-objdump --macho --private-headers - | FileCheck %s
+; RUN: llc %s -filetype=asm -o - | FileCheck --check-prefix=ASM %s
+
+target triple = "x86_64-apple-macos10.15";
+!llvm.module.flags = !{!0, !1, !2};
+!0 = !{i32 2, !"SDK Version", [3 x i32] [ i32 10, i32 15, i32 1 ] };
+!1 = !{i32 1, !"darwin.target_variant.triple", !"x86_64-apple-ios13.1-macabi"};
+!2 = !{i32 2, !"darwin.target_variant.SDK Version", [2 x i32] [ i32 13, i32 2 ] };
+
+define void @foo() {
+entry:
+ ret void
+}
+
+; CHECK: cmd LC_BUILD_VERSION
+; CHECK-NEXT: cmdsize 24
+; CHECK-NEXT: platform macos
+; CHECK-NEXT: sdk 10.15.1
+; CHECK-NEXT: minos 10.15
+; CHECK-NEXT: ntools 0
+; CHECK: cmd LC_BUILD_VERSION
+; CHECK-NEXT: cmdsize 24
+; CHECK-NEXT: platform macCatalyst
+; CHECK-NEXT: sdk 13.2
+; CHECK-NEXT: minos 13.1
+; CHECK-NEXT: ntools 0
+
+; ASM: .build_version macos, 10, 15 sdk_version 10, 15, 1
+; ASM: .build_version macCatalyst, 13, 1 sdk_version 13, 2